diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts
index d075ee2c8c27845337c2f8c3acb1faa5dcabcca8..05debf8ebc86740ce995dd75db4732464a2682d3 100644
--- a/smp-angular/src/app/app.module.ts
+++ b/smp-angular/src/app/app.module.ts
@@ -166,6 +166,28 @@ import {
 } from "./common/dialogs/document-property-dialog/document-property-dialog.component";
 import {NgxTranslateModule} from "./translate/translate.module";
 import {WindowSpinnerService} from "./common/services/window-spinner.service";
+import {
+  ExpandablePanelComponent
+} from "./common/panels/expandable-panel-component/expandable-panel.component";
+import {
+  ExpandableItemComponent
+} from "./common/panels/expandable-panel-component/expandable-item-component/expandable-item.component";
+import {
+  DocumentEventsPanelComponent
+} from "./common/panels/document-events-panel/document-events-panel.component";
+import {
+  DocumentVersionsPanelComponent
+} from "./common/panels/document-versions-panel/document-versions-panel.component";
+import {ReviewTasksComponent} from "./edit/review-task/review-tasks.component";
+import {
+  ReviewTasksPanelComponent
+} from "./common/panels/review-tasks-panel/review-tasks-panel.component";
+import {
+  DocumentEditPanelComponent
+} from "./common/panels/document-edit-panel/document-edit-panel.component";
+import {
+  ReviewDocumentPanelComponent
+} from "./common/panels/review-tasks-panel/review-document-panel/review-document-panel.component";
 
 
 @NgModule({
@@ -198,8 +220,11 @@ import {WindowSpinnerService} from "./common/services/window-spinner.service";
     DnsToolsComponent,
     DnsQueryPanelComponent,
     DocumentWizardDialogComponent,
+    DocumentEditPanelComponent,
+    DocumentEventsPanelComponent,
     DocumentPropertiesPanelComponent,
     DocumentPropertyDialogComponent,
+    DocumentVersionsPanelComponent,
     DomainGroupComponent,
     DomainPanelComponent,
     DomainResourceTypePanelComponent,
@@ -209,6 +234,8 @@ import {WindowSpinnerService} from "./common/services/window-spinner.service";
     EditDomainComponent,
     EditGroupComponent,
     EditResourceComponent,
+    ExpandablePanelComponent,
+    ExpandableItemComponent,
     ExpiredPasswordDialogComponent,
     ExtensionComponent,
     ExtensionPanelComponent,
@@ -236,6 +263,9 @@ import {WindowSpinnerService} from "./common/services/window-spinner.service";
     RowLimiterComponent,
     SaveDialogComponent,
     SearchTableComponent,
+    ReviewDocumentPanelComponent,
+    ReviewTasksComponent,
+    ReviewTasksPanelComponent,
     ResourceSearchComponent,
     SidenavComponent,
     SmpFieldErrorComponent,
diff --git a/smp-angular/src/app/app.routes.ts b/smp-angular/src/app/app.routes.ts
index 792415e137392bb2b3354898568dcd33af61a4db..0bfadef582e106903abcb4cb145ca5caf123d07a 100644
--- a/smp-angular/src/app/app.routes.ts
+++ b/smp-angular/src/app/app.routes.ts
@@ -1,28 +1,69 @@
 import {RouterModule, Routes} from '@angular/router';
 import {LoginComponent} from './login/login.component';
-import {ResourceSearchComponent} from './resource-search/resource-search.component';
-import {PropertyComponent} from "./system-settings/admin-properties/property.component";
-import {UserProfileComponent} from "./user-settings/user-profile/user-profile.component";
+import {
+  ResourceSearchComponent
+} from './resource-search/resource-search.component';
+import {
+  PropertyComponent
+} from "./system-settings/admin-properties/property.component";
+import {
+  UserProfileComponent
+} from "./user-settings/user-profile/user-profile.component";
 import {authenticationGuard} from "./guards/authentication.guard";
-import {UserAccessTokensComponent} from "./user-settings/user-access-tokens/user-access-tokens.component";
-import {UserCertificatesComponent} from "./user-settings/user-certificates/user-certificates.component";
-import {ExtensionComponent} from "./system-settings/admin-extension/extension.component";
-import {AdminTruststoreComponent} from "./system-settings/admin-truststore/admin-truststore.component";
-import {AdminKeystoreComponent} from "./system-settings/admin-keystore/admin-keystore.component";
-import {AdminDomainComponent} from "./system-settings/admin-domain/admin-domain.component";
+import {
+  UserAccessTokensComponent
+} from "./user-settings/user-access-tokens/user-access-tokens.component";
+import {
+  UserCertificatesComponent
+} from "./user-settings/user-certificates/user-certificates.component";
+import {
+  ExtensionComponent
+} from "./system-settings/admin-extension/extension.component";
+import {
+  AdminTruststoreComponent
+} from "./system-settings/admin-truststore/admin-truststore.component";
+import {
+  AdminKeystoreComponent
+} from "./system-settings/admin-keystore/admin-keystore.component";
+import {
+  AdminDomainComponent
+} from "./system-settings/admin-domain/admin-domain.component";
 import {dirtyDeactivateGuard} from "./guards/dirty.guard";
-import {AdminUserComponent} from "./system-settings/admin-users/admin-user.component";
+import {
+  AdminUserComponent
+} from "./system-settings/admin-users/admin-user.component";
 import {EditDomainComponent} from "./edit/edit-domain/edit-domain.component";
 import {EditGroupComponent} from "./edit/edit-group/edit-group.component";
-import {EditResourceComponent} from "./edit/edit-resources/edit-resource.component";
-import {ResourceDocumentPanelComponent} from "./edit/edit-resources/resource-document-panel/resource-document-panel.component";
-import {SubresourceDocumentPanelComponent} from "./edit/edit-resources/subresource-document-panel/subresource-document-panel.component";
-import {authorizeChildSystemAdminGuard} from "./guards/authorize-child-system-admin.guard";
-import {activateChildResourceGuard} from "./guards/activate-child-document.guard";
-import {UserAlertsComponent} from "./user-settings/user-alerts/user-alerts.component";
-import {AdminAlertsComponent} from "./system-settings/admin-alerts/admin-alerts.component";
-import {ResetCredentialComponent} from "./security/reset-credential/reset-credential.component";
+import {
+  EditResourceComponent
+} from "./edit/edit-resources/edit-resource.component";
+import {
+  ResourceDocumentPanelComponent
+} from "./edit/edit-resources/resource-document-panel/resource-document-panel.component";
+import {
+  SubresourceDocumentPanelComponent
+} from "./edit/edit-resources/subresource-document-panel/subresource-document-panel.component";
+import {
+  authorizeChildSystemAdminGuard
+} from "./guards/authorize-child-system-admin.guard";
+import {
+  activateChildResourceGuard
+} from "./guards/activate-child-document.guard";
+import {
+  UserAlertsComponent
+} from "./user-settings/user-alerts/user-alerts.component";
+import {
+  AdminAlertsComponent
+} from "./system-settings/admin-alerts/admin-alerts.component";
+import {
+  ResetCredentialComponent
+} from "./security/reset-credential/reset-credential.component";
 import {DnsToolsComponent} from "./tools/dns-tools/dns-tools.component";
+import {ReviewTasksComponent} from "./edit/review-task/review-tasks.component";
+import {
+  ReviewDocumentPanelComponent
+} from "./common/panels/review-tasks-panel/review-document-panel/review-document-panel.component";
+import {activateChildReviewGuard} from "./guards/activate-child-review.guard";
 
 
 const appRoutes: Routes = [
@@ -36,8 +77,16 @@ const appRoutes: Routes = [
     path: 'edit',
     canActivateChild: [authenticationGuard],
     children: [
-      {path: 'edit-domain', component: EditDomainComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'edit-group', component: EditGroupComponent, canDeactivate: [dirtyDeactivateGuard]},
+      {
+        path: 'edit-domain',
+        component: EditDomainComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'edit-group',
+        component: EditGroupComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
       {
         path: 'edit-resource',
         canDeactivate: [dirtyDeactivateGuard],
@@ -54,8 +103,23 @@ const appRoutes: Routes = [
             component: SubresourceDocumentPanelComponent,
             canDeactivate: [dirtyDeactivateGuard]
           },
-          {path: '', component: EditResourceComponent, canDeactivate: [dirtyDeactivateGuard]},
+          {
+            path: '',
+            component: EditResourceComponent,
+            canDeactivate: [dirtyDeactivateGuard]
+          },
         ]
+      },
+      {
+        path: 'review-tasks',
+        children: [
+          {
+            path: 'review-document',
+            canActivate: [activateChildReviewGuard],
+            component: ReviewDocumentPanelComponent,
+            canDeactivate: [dirtyDeactivateGuard]
+          },
+          {path: '', component: ReviewTasksComponent},]
       }
     ]
   },
@@ -63,24 +127,72 @@ const appRoutes: Routes = [
     path: 'system-settings',
     canActivateChild: [authenticationGuard, authorizeChildSystemAdminGuard],
     children: [
-      {path: 'domain', component: AdminDomainComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'user', component: AdminUserComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'properties', component: PropertyComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'keystore', component: AdminKeystoreComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'truststore', component: AdminTruststoreComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'extension', component: ExtensionComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'alert', component: AdminAlertsComponent, canDeactivate: [dirtyDeactivateGuard]},
+      {
+        path: 'domain',
+        component: AdminDomainComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'user',
+        component: AdminUserComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'properties',
+        component: PropertyComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'keystore',
+        component: AdminKeystoreComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'truststore',
+        component: AdminTruststoreComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'extension',
+        component: ExtensionComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'alert',
+        component: AdminAlertsComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
     ]
   },
   {
     path: 'user-settings',
     canActivateChild: [authenticationGuard],
     children: [
-      {path: 'user-profile', component: UserProfileComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'user-access-token', component: UserAccessTokensComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'user-certificate', component: UserCertificatesComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'user-alert', component: UserAlertsComponent, canDeactivate: [dirtyDeactivateGuard]},
-      {path: 'user-membership', component: UserProfileComponent, canDeactivate: [dirtyDeactivateGuard]},
+      {
+        path: 'user-profile',
+        component: UserProfileComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'user-access-token',
+        component: UserAccessTokensComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'user-certificate',
+        component: UserCertificatesComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'user-alert',
+        component: UserAlertsComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
+      {
+        path: 'user-membership',
+        component: UserProfileComponent,
+        canDeactivate: [dirtyDeactivateGuard]
+      },
     ]
   },
   {path: '**', redirectTo: ''},
diff --git a/smp-angular/src/app/common/alert-message/alert-message.service.ts b/smp-angular/src/app/common/alert-message/alert-message.service.ts
index 95f7ddbb8be69481059ada5c57b24bcf2428b373..1f181edeb43e97c63bb3b58f2099660804820bd0 100644
--- a/smp-angular/src/app/common/alert-message/alert-message.service.ts
+++ b/smp-angular/src/app/common/alert-message/alert-message.service.ts
@@ -1,7 +1,8 @@
 import {Injectable} from '@angular/core';
 import {NavigationEnd, NavigationStart, Router} from '@angular/router';
-import {Observable, Subject} from 'rxjs';
+import {lastValueFrom, Observable, Subject} from 'rxjs';
 import {HttpErrorResponse} from "@angular/common/http";
+import {TranslateService} from "@ngx-translate/core";
 
 /**
  * AlertMessageRO is the object that will be used to display the message in the SMP alert component in overlay.
@@ -25,7 +26,8 @@ export class AlertMessageService {
   private keepAfterNavigationChange:boolean = false;
   private message: AlertMessageRO;
 
-  constructor(private router: Router) {
+  constructor(private router: Router,
+              private translateService: TranslateService) {
     // clear alert message on route change
     router.events.subscribe(event => {
       if (event instanceof NavigationStart) {
@@ -80,7 +82,6 @@ export class AlertMessageService {
    * @param messageObject
    */
   getObjectMessage(messageObject: any): string {
-    let message = 'An error occurred';
     if (typeof messageObject === 'string') {
       return messageObject;
     }
@@ -124,6 +125,15 @@ export class AlertMessageService {
     this.displayCurrentMessage();
   }
 
+  async showMessageForTranslation(translationCode: string,type: string, keepAfterNavigationChange = false, timeoutInSeconds: number = null) : Promise<void> {
+      let message = await lastValueFrom(this.translateService.get(translationCode))
+      this.showMessage(message, type, keepAfterNavigationChange, timeoutInSeconds);
+  }
+
+  successForTranslation(translationCode: string, keepAfterNavigationChange = false, timeoutInSeconds: number = null) {
+    this.showMessageForTranslation(translationCode, 'success', keepAfterNavigationChange, timeoutInSeconds);
+  }
+
   success(message: string, keepAfterNavigationChange = false, timeoutInSeconds: number = null) {
     this.showMessage(message, 'success', keepAfterNavigationChange, timeoutInSeconds);
   }
@@ -132,6 +142,10 @@ export class AlertMessageService {
     this.showMessage(message, 'warning', keepAfterNavigationChange, timeoutInSeconds);
   }
 
+  errorForTranslation(translationCode: string, keepAfterNavigationChange = false, timeoutInSeconds: number = null) {
+    this.showMessageForTranslation(translationCode, 'error', keepAfterNavigationChange, timeoutInSeconds);
+  }
+
   error(message: any, keepAfterNavigationChange = false, timeoutInSeconds: number = null) {
     this.showMessage(message, 'error', keepAfterNavigationChange, timeoutInSeconds);
   }
diff --git a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts
index a3238c96351aa1d8a0579851f92ca28aa5c086bc..953da50d7466d6f2bc139c72cd820a05d2cb043d 100644
--- a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts
+++ b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts
@@ -144,6 +144,7 @@ export class SmpEditorComponent
 
   @Input() set readOnly(readOnly: boolean) {
     this._readOnly = readOnly;
+    console.log("Document readOnly", readOnly)
     this.codeMirror?.dispatch({
       effects: this.readOnlyDocument.reconfigure(EditorState.readOnly.of(readOnly))
     })
diff --git a/smp-angular/src/app/common/dialogs/certificate-dialog/certificate-dialog.component.ts b/smp-angular/src/app/common/dialogs/certificate-dialog/certificate-dialog.component.ts
index bf632f6075f298e1704ea46f263b900ebe0dc1a0..401696bf64407eda8df6910dfb54722bb510c7f9 100644
--- a/smp-angular/src/app/common/dialogs/certificate-dialog/certificate-dialog.component.ts
+++ b/smp-angular/src/app/common/dialogs/certificate-dialog/certificate-dialog.component.ts
@@ -1,7 +1,6 @@
 import {Component, Inject} from '@angular/core';
 import {MAT_DIALOG_DATA} from '@angular/material/dialog';
 import {UntypedFormBuilder} from "@angular/forms";
-import {SmpConstants} from "../../../smp.constants";
 import {CertificateRo} from "../../model/certificate-ro.model";
 import {TranslateService} from "@ngx-translate/core";
 
@@ -10,7 +9,7 @@ import {TranslateService} from "@ngx-translate/core";
   templateUrl: './certificate-dialog.component.html'
 })
 export class CertificateDialogComponent {
-  readonly dateTimeFormat: string = SmpConstants.DATE_TIME_FORMAT;
+
   formTitle: string;
   current: CertificateRo;
 
diff --git a/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts b/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts
index 19912c3d1cc3895e779ed63b0c49b27afecef470..1deb1ae92a62e22ec0bc58de684096588c0c69d3 100644
--- a/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts
+++ b/smp-angular/src/app/common/dialogs/credential-dialog/credential-dialog.component.ts
@@ -1,7 +1,6 @@
 import {Component, Inject} from '@angular/core';
 import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
 import {FormBuilder, FormControl, FormGroup} from "@angular/forms";
-import {SmpConstants} from "../../../smp.constants";
 import {AccessTokenRo} from "../../model/access-token-ro.model";
 import {CredentialRo} from "../../../security/credential.model";
 import {HttpErrorHandlerService} from "../../error/http-error-handler.service";
@@ -19,7 +18,6 @@ export class CredentialDialogComponent {
   public static CERTIFICATE_TYPE: string = "CERTIFICATE";
   public static ACCESS_TOKEN_TYPE: string = "ACCESS_TOKEN";
 
-  dateTimeFormat: string = SmpConstants.DATE_TIME_FORMAT;
   formTitle: string;
   credentialForm: FormGroup;
   certificateForm: FormGroup;
diff --git a/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.css b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.css
index 1c224cec9922cc4489dc8d1c050c783cb15c6255..bcc42673eca6fe33a199cbaf472eafacce461830 100644
--- a/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.css
+++ b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.css
@@ -1,8 +1,11 @@
+.form-field-full-width {
+  width: 100%;
+}
+
 .empty-field-label {
   color: gray;
 }
 
-
 #custom-file-upload {
   display: none;
 }
@@ -11,3 +14,6 @@
   display: inline-block;
   cursor: pointer;
 }
+#member-user-can-review {
+  max-height: 1.5em
+}
diff --git a/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.html b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.html
index c3b40e2f8f0beb8c99e1de57a515e3d47ef2be12..ab7f34ceb5b818fc63ed83ac7127f3bebe57349e 100644
--- a/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.html
+++ b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.html
@@ -2,7 +2,7 @@
 <mat-dialog-content style="width:700px">
   <form [formGroup]="memberForm">
     <b *ngIf="newMode">{{ "member.dialog.label.invite.members" | translate: {target: inviteTarget} }}</b>
-    <mat-form-field  style="width: 100%">
+    <mat-form-field  class="form-field-full-width">
       <mat-label>{{ "member.dialog.label.choose.user" | translate }}</mat-label>
       <input id="member-user" type="text" matInput formControlName="member-user"
              [matAutocomplete]="auto" (keyup)="applyUserFilter($event)"
@@ -16,9 +16,9 @@
     </mat-form-field>
 
 
-    <mat-form-field style="width:100%">
-      <mat-label>Select role for the user</mat-label>
-      <select matNativeControl placeholder="{{ 'member.dialog.placeholder.role.type' | translate }}"
+    <mat-form-field class="form-field-full-width">
+      <mat-label>{{ "member.dialog.label.select.role.type" | translate }}</mat-label>
+      <select matNativeControl placeholder="{{ 'member.dialog.label.permission.review' | translate }}"
                   formControlName="member-roleType"
                   name="Role type"
                   matTooltip="{{ 'member.dialog.tooltip.role.type' | translate }}"
@@ -30,6 +30,17 @@
       </select>
       <mat-hint>{{ "member.dialog.hint.choose.role" | translate }}</mat-hint>
     </mat-form-field>
+
+    <mat-form-field class="form-field-full-width" >
+      <mat-checkbox  *ngIf="isResourceMember" formControlName="member-can-review" id="member-user-can-review"
+                     matTooltip="{{ 'member.dialog.tooltip.permission.review' | translate }}"
+      >{{ "member.dialog.label.permission.review" | translate }}
+        <!-- This input is used to make the mat-checkbox as form filed   -->
+        <input matInput style="display: none;">
+      </mat-checkbox>
+      <mat-hint>{{ "member.dialog.hint.can.review" | translate }}</mat-hint>
+    </mat-form-field>
+
   </form>
 </mat-dialog-content>
 <mat-dialog-actions>
diff --git a/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.ts b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.ts
index f0fc3e278e8e7bceb3e57bb9e63b24bf32b7f756..bed709837ab5bd5b8702c3889afa654ef8b23856 100644
--- a/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.ts
+++ b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.ts
@@ -2,7 +2,7 @@ import {Component, Inject, Input, OnInit} from '@angular/core';
 import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
 import {FormBuilder, FormControl, FormGroup} from "@angular/forms";
 import {MembershipRoleEnum} from "../../enums/membership-role.enum";
-import {firstValueFrom, lastValueFrom, Observable} from "rxjs";
+import {lastValueFrom, Observable} from "rxjs";
 import {SearchUserRo} from "../../model/search-user-ro.model";
 import {MembershipService} from "../../panels/membership-panel/membership.service";
 import {MemberRo} from "../../model/member-ro.model";
@@ -58,7 +58,8 @@ export class MemberDialogComponent implements OnInit {
       'member-user': new FormControl({value: null}),
       'member-fullName': new FormControl({value: null}),
       'member-memberOf': new FormControl({value: null}),
-      'member-roleType': new FormControl({value: null})
+      'member-roleType': new FormControl({value: null}),
+      'member-can-review': new FormControl({value: null})
     });
     this.member = {
       ...data.member
@@ -79,6 +80,7 @@ export class MemberDialogComponent implements OnInit {
     member.fullName = this.memberForm.get('member-fullName').value;
     member.memberOf = this.memberForm.get('member-memberOf').value;
     member.roleType = this.memberForm.get('member-roleType').value;
+    member.hasPermissionReview = this.memberForm.get('member-can-review').value
     return member;
   }
 
@@ -102,12 +104,14 @@ export class MemberDialogComponent implements OnInit {
       this.memberForm.controls['member-fullName'].setValue(value.fullName);
       this.memberForm.controls['member-memberOf'].setValue(value.memberOf);
       this.memberForm.controls['member-roleType'].setValue(value.roleType);
+      this.memberForm.controls['member-can-review'].setValue(value.hasPermissionReview);
 
     } else {
       this.memberForm.controls['member-user'].setValue("");
       this.memberForm.controls['member-fullName'].setValue("");
       this.memberForm.controls['member-memberOf'].setValue("");
       this.memberForm.controls['member-roleType'].setValue("");
+      this.memberForm.controls['member-can-review'].setValue(false);
     }
     this.memberForm.markAsPristine();
   }
@@ -173,4 +177,8 @@ export class MemberDialogComponent implements OnInit {
         return  this.membershipService.addEditMemberToResource(this._currentResource, this._currentGroup,this._currentDomain, this.member)
     }
   }
+
+  get isResourceMember(): boolean {
+    return this.membershipType === MemberTypeEnum.RESOURCE;
+  }
 }
diff --git a/smp-angular/src/app/common/enums/document-versions-status.enum.ts b/smp-angular/src/app/common/enums/document-versions-status.enum.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b36abd74a66336917fbfa2c6ce36da09257bd2f6
--- /dev/null
+++ b/smp-angular/src/app/common/enums/document-versions-status.enum.ts
@@ -0,0 +1,14 @@
+/**
+ * Enum for document versions status codes
+ * @Since 5.0
+ * @Author: Joze RIHTARSIC
+ */
+export enum DocumentVersionsStatus {
+
+  DRAFT="DRAFT",
+  PUBLISHED="PUBLISHED",
+  RETIRED="RETIRED",
+  UNDER_REVIEW="UNDER_REVIEW",
+  APPROVED="APPROVED",
+  REJECTED="REJECTED"
+}
diff --git a/smp-angular/src/app/common/error/http-error-handler.service.ts b/smp-angular/src/app/common/error/http-error-handler.service.ts
index d93cf8a1492810409e5e78fd078d41b309004372..463fbd353d7098ff50f0f68c85c0519a323ab98e 100644
--- a/smp-angular/src/app/common/error/http-error-handler.service.ts
+++ b/smp-angular/src/app/common/error/http-error-handler.service.ts
@@ -7,8 +7,7 @@ import {AlertMessageService} from "../alert-message/alert-message.service";
 export class HttpErrorHandlerService {
 
   constructor (private navigationService: NavigationService,
-               private alertMessageService: AlertMessageService,) {
-
+               private alertMessageService: AlertMessageService) {
   }
 
   public logoutOnInvalidSessionError(err: any): boolean {
@@ -21,4 +20,20 @@ export class HttpErrorHandlerService {
     }
     return false;
   }
+
+  public handleHttpError(err: any) {
+    if (err instanceof HttpErrorResponse) {
+      if (this.logoutOnInvalidSessionError(err)) {
+        return;
+      }
+      if (err.status === 0) {
+        this.alertMessageService.error("Server is not reachable. Please try again later.");
+      } else {
+        this.alertMessageService.error(err.error.errorDescription);
+      }
+    } else {
+      this.alertMessageService.error(err.error?.errorDescription);
+    }
+
+  }
 }
diff --git a/smp-angular/src/app/common/global-lookups.ts b/smp-angular/src/app/common/global-lookups.ts
index 427d4d92d35e2e76052946647b52d4059337fad9..cec7984f7dd357fa44dd7091a3a92bbd9ce06b79 100644
--- a/smp-angular/src/app/common/global-lookups.ts
+++ b/smp-angular/src/app/common/global-lookups.ts
@@ -14,6 +14,13 @@ import {DateAdapter} from "@angular/material/core";
 import {NgxMatDateAdapter} from "@angular-material-components/datetime-picker";
 import {DomainRo} from "./model/domain-ro.model";
 import {Subject} from "rxjs";
+import {
+  FormatWidth,
+  getLocaleDateFormat,
+  getLocaleDateTimeFormat,
+  getLocaleTimeFormat
+} from "@angular/common";
+import StringUtils from "./utils/string-utils";
 
 /**
  * Purpose of object is to fetch lookups as domains and users
@@ -24,6 +31,7 @@ export class GlobalLookups {
   // global data observers. The components will subscribe to these Subject to get
   // data updates.
   private smpInfoUpdateSubject: Subject<SmpInfo> = new Subject<SmpInfo>();
+  private readonly DEFAULT_LOCALE: string = 'fr';
 
   domainObserver: Observable<SearchTableResult>
   userObserver: Observable<SearchTableResult>
@@ -60,8 +68,8 @@ export class GlobalLookups {
       }
     );
     // set default locale
-    dateAdapter.setLocale('fr');
-    ngxMatDateAdapter.setLocale('fr');
+    dateAdapter.setLocale(this.DEFAULT_LOCALE);
+    ngxMatDateAdapter.setLocale(this.DEFAULT_LOCALE);
 
   }
 
@@ -91,6 +99,31 @@ export class GlobalLookups {
     });
   }
 
+  getCurrentLocale(): string {
+    if (this.securityService.getCurrentUser() == null) {
+      return this.DEFAULT_LOCALE;
+    }
+    return this.securityService.getCurrentUser().smpLocale;
+  }
+
+  public getDateTimeFormat(withSeconds: boolean = true): string {
+    let locale = this.getCurrentLocale();
+    locale = locale ? locale : this.DEFAULT_LOCALE;
+    let format: string = getLocaleDateTimeFormat(locale, FormatWidth.Short);
+    let fullTime = getLocaleTimeFormat(locale,withSeconds? FormatWidth.Medium:FormatWidth.Short);
+    let fullDate = getLocaleDateFormat(locale, FormatWidth.Short);
+    let result = StringUtils.format(format, [fullTime, fullDate]);
+    return result;
+  }
+
+  private format(str, opt_values) {
+    if (opt_values) {
+      str = str.replace(/\{([^}]+)}/g, function (match, key) {
+        return (opt_values != null && key in opt_values) ? opt_values[key] : match;
+      });
+    }
+    return str;
+  }
 
   public refreshApplicationInfo() {
 
@@ -114,13 +147,14 @@ export class GlobalLookups {
       console.log("Refresh application configuration is authenticated " + isAuthenticated)
       if (isAuthenticated) {
         this.http.get<SmpConfig>(SmpConstants.REST_PUBLIC_APPLICATION_CONFIG)
-          .subscribe({next: (res :SmpConfig):void => {
-          this.cachedApplicationConfig = res;
-        },
-        error: (err: any)=> {
-          console.log("getSmpConfig:" + err);
-        }
-      });
+          .subscribe({
+            next: (res: SmpConfig): void => {
+              this.cachedApplicationConfig = res;
+            },
+            error: (err: any) => {
+              console.log("getSmpConfig:" + err);
+            }
+          });
       }
     });
   }
diff --git a/smp-angular/src/app/common/model/document-ro.model.ts b/smp-angular/src/app/common/model/document-ro.model.ts
index cf0a058179bbb01d17fe30b2a2c6864f25314888..c5ec808d8cebbcdbfbe88fbaef0f1ce687d0532f 100644
--- a/smp-angular/src/app/common/model/document-ro.model.ts
+++ b/smp-angular/src/app/common/model/document-ro.model.ts
@@ -1,6 +1,11 @@
 import {DocumentPropertyRo} from "./document-property-ro.model";
 import {SearchTableEntity} from "../search-table/search-table-entity.model";
 import {EntityStatus} from "../enums/entity-status.enum";
+import {
+  DocumentVersionRo
+} from "./document-version-ro.model";
+import {DocumentVersionEventRo} from "./document-version-event-ro.model";
+import {DocumentVersionsStatus} from "../enums/document-versions-status.enum";
 
 export interface DocumentRo extends SearchTableEntity  {
   mimeType?: string;
@@ -13,5 +18,8 @@ export interface DocumentRo extends SearchTableEntity  {
   payload?:string;
   payloadStatus: EntityStatus;
   properties?: DocumentPropertyRo[];
+  documentVersionStatus?: DocumentVersionsStatus;
+  documentVersionEvents?: DocumentVersionEventRo[];
+  documentVersions?: DocumentVersionRo[];
 }
 
diff --git a/smp-angular/src/app/common/model/document-version-event-ro.model.ts b/smp-angular/src/app/common/model/document-version-event-ro.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..17ba3452cb5e9845d821fa7fcadb66eb656afc17
--- /dev/null
+++ b/smp-angular/src/app/common/model/document-version-event-ro.model.ts
@@ -0,0 +1,10 @@
+import {SearchTableEntity} from "../search-table/search-table-entity.model";
+
+export interface DocumentVersionEventRo extends SearchTableEntity {
+  eventType: string;
+  eventOn: Date;
+  username: string;
+  eventSourceType: string;
+  details: string;
+}
+
diff --git a/smp-angular/src/app/common/model/document-version-ro.model.ts b/smp-angular/src/app/common/model/document-version-ro.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bdd3218bdf7d9d450a3bacfecae8c41cef46c5b2
--- /dev/null
+++ b/smp-angular/src/app/common/model/document-version-ro.model.ts
@@ -0,0 +1,9 @@
+import {SearchTableEntity} from "../search-table/search-table-entity.model";
+
+export interface DocumentVersionRo extends SearchTableEntity {
+  version: number;
+  versionStatus:string;
+  createdOn: Date;
+  lastUpdatedOn: Date;
+}
+
diff --git a/smp-angular/src/app/common/model/member-ro.model.ts b/smp-angular/src/app/common/model/member-ro.model.ts
index 618e326fcbab2302486f00f6d78e261b6cac3c89..6d042f0f5f476dd924879f3c6fc8a8b622243199 100644
--- a/smp-angular/src/app/common/model/member-ro.model.ts
+++ b/smp-angular/src/app/common/model/member-ro.model.ts
@@ -10,4 +10,7 @@ export interface MemberRo extends SearchTableEntity {
   memberOf:MemberTypeEnum;
   fullName:string;
   roleType:MembershipRoleEnum;
+
+  // only for resource members
+  hasPermissionReview?:boolean;
 }
diff --git a/smp-angular/src/app/common/model/resource-ro.model.ts b/smp-angular/src/app/common/model/resource-ro.model.ts
index 11e4cc5408beec91f56a3f384601269b0548bf58..068c333c91d06b8c297a2f9bb98c8f3a8ac984d2 100644
--- a/smp-angular/src/app/common/model/resource-ro.model.ts
+++ b/smp-angular/src/app/common/model/resource-ro.model.ts
@@ -12,6 +12,7 @@ export interface ResourceRo extends SearchTableEntity {
 
   smlRegistered: boolean;
 
+  reviewEnabled?: boolean;
 
   visibility: VisibilityEnum;
 }
diff --git a/smp-angular/src/app/common/model/review-document-version-ro.model.ts b/smp-angular/src/app/common/model/review-document-version-ro.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a6534026f1a4cc195cbda2d145f4f3cf4417945f
--- /dev/null
+++ b/smp-angular/src/app/common/model/review-document-version-ro.model.ts
@@ -0,0 +1,17 @@
+import {SearchTableEntity} from "../search-table/search-table-entity.model";
+
+export interface ReviewDocumentVersionRo extends SearchTableEntity {
+
+  documentId: string;
+  documentVersionId: string;
+  resourceId: string;
+  subresourceId?: string;
+  version: number;
+  currentStatus: string;
+  resourceIdentifierValue: string;
+  resourceIdentifierScheme: string;
+  subresourceIdentifierValue?: string;
+  subresourceIdentifierScheme?: string;
+  target: string;
+  lastUpdatedOn: Date;
+}
diff --git a/smp-angular/src/app/common/model/subresource-ro.model.ts b/smp-angular/src/app/common/model/subresource-ro.model.ts
index 9fa29080ccaa96d3b6ead3b323096d4e326abbc9..807e40d4bcdd1bab99c65915a95f0e066a0917d1 100644
--- a/smp-angular/src/app/common/model/subresource-ro.model.ts
+++ b/smp-angular/src/app/common/model/subresource-ro.model.ts
@@ -1,12 +1,9 @@
 import {SearchTableEntity} from "../search-table/search-table-entity.model";
-import {VisibilityEnum} from "../enums/visibility.enum";
 
 export interface SubresourceRo extends SearchTableEntity {
 
   subresourceId?: string;
   subresourceTypeIdentifier?: string;
-
   identifierValue: string;
-
   identifierScheme?: string;
 }
diff --git a/smp-angular/src/app/common/panels/alert-panel/alert-panel.component.ts b/smp-angular/src/app/common/panels/alert-panel/alert-panel.component.ts
index 9e9dd40d8c13f0c4e25a423e60b93d256d9d39b4..f102462f56351440ef4134a61761bf47f2fb5d5f 100644
--- a/smp-angular/src/app/common/panels/alert-panel/alert-panel.component.ts
+++ b/smp-angular/src/app/common/panels/alert-panel/alert-panel.component.ts
@@ -2,7 +2,8 @@ import {
   AfterViewChecked,
   AfterViewInit,
   ChangeDetectorRef,
-  Component, Input,
+  Component,
+  Input,
   OnInit,
   TemplateRef,
   ViewChild
@@ -13,11 +14,12 @@ import {MatDialog} from '@angular/material/dialog';
 import {AlertMessageService} from '../../alert-message/alert-message.service';
 import {AlertController} from './alert-controller';
 import {HttpClient} from '@angular/common/http';
-import {SmpConstants} from "../../../smp.constants";
 import {GlobalLookups} from "../../global-lookups";
 import {SearchTableComponent} from "../../search-table/search-table.component";
 import {SecurityService} from "../../../security/security.service";
-import {ObjectPropertiesDialogComponent} from "../../dialogs/object-properties-dialog/object-properties-dialog.component";
+import {
+  ObjectPropertiesDialogComponent
+} from "../../dialogs/object-properties-dialog/object-properties-dialog.component";
 
 /**
  * This is a generic alert panel component for previewing alert list
@@ -37,14 +39,10 @@ export class AlertPanelComponent implements OnInit, AfterViewInit, AfterViewChec
   @ViewChild('credentialType') credentialType: TemplateRef<any>;
   @ViewChild('forUser') forUser: TemplateRef<any>;
 
-  readonly dateTimeFormat: string = SmpConstants.DATE_TIME_FORMAT;
-  readonly dateFormat: string = SmpConstants.DATE_FORMAT;
-
-  @Input()  baseUrl = null;
+  @Input() baseUrl = null;
   columnPicker: ColumnPicker = new ColumnPicker();
   alertController: AlertController;
   filter: any = {};
-  isSMPIntegrationOn: boolean = false;
 
   constructor(public securityService: SecurityService,
               protected lookups: GlobalLookups,
@@ -142,4 +140,8 @@ export class AlertPanelComponent implements OnInit, AfterViewInit, AfterViewChec
   isDirty(): boolean {
     return this.searchTable.isDirty();
   }
+
+  get dateTimeFormat(): string {
+    return this.lookups.getDateTimeFormat();
+  }
 }
diff --git a/smp-angular/src/app/common/panels/document-edit-panel/document-edit-panel.component.html b/smp-angular/src/app/common/panels/document-edit-panel/document-edit-panel.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..7d74a66f4be763ecc472bb24ee13f39e4c5847c5
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-edit-panel/document-edit-panel.component.html
@@ -0,0 +1,201 @@
+<div id="resource-document-panel">
+
+  <mat-toolbar class="mat-elevation-z2" style="min-height: 50px !important;">
+    <mat-toolbar-row class="smp-toolbar-row"
+                     style="justify-content: space-between;min-height: 50px !important;">
+      <button id="newVersion_id"
+              *ngIf="isNotReviewMode"
+              mat-raised-button
+              color="primary"
+              matTooltip="{{ 'document.edit.panel.tooltip.version.new' | translate }}"
+              [disabled]="emptyDocument"
+              (click)="onNewDocumentVersionButtonClicked()">
+        <mat-icon>add_circle</mat-icon>
+        <span>{{ "document.edit.panel.button.version.new" | translate }}</span>
+      </button>
+      <button id="validateResource_id" mat-raised-button
+              color="primary"
+              matTooltip="{{ 'document.edit.panel.tooltip.validate' | translate }}"
+              [disabled]="emptyDocument"
+              (click)="onDocumentValidateButtonClicked()">
+        <mat-icon>check_circle</mat-icon>
+        <span>{{ "document.edit.panel.button.validate" | translate }}</span>
+      </button>
+      <button id="GenerateResource_id"
+              *ngIf="isNotReviewMode"
+              mat-raised-button
+              color="primary"
+              [disabled]="!documentEditable"
+              matTooltip="{{ 'document.edit.panel.tooltip.generate' | translate }}"
+              (click)="onGenerateButtonClicked()">
+        <mat-icon>change_circle</mat-icon>
+        <span>{{ "document.edit.panel.button.generate" | translate }}</span>
+      </button>
+      <button id="documentWizard_id" mat-raised-button
+              color="primary"
+              matTooltip="{{ 'document.edit.panel.tooltip.document.wizard' | translate }}"
+              [disabled]="!documentEditable"
+              *ngIf="showWizardDialog && isNotReviewMode"
+              (click)="onShowDocumentWizardDialog()">
+        <mat-icon>code_block</mat-icon>
+        <span>{{ "document.edit.panel.button.document.wizard" | translate }}</span>
+      </button>
+      <span style="flex: 1 1 auto;"></span>
+    </mat-toolbar-row>
+  </mat-toolbar>
+
+  <div class="panel" [formGroup]="documentForm"
+       style="display: flex;flex-direction: row;flex-grow: 1; ">
+    <div
+      style="display:flex; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;">
+
+      <div style="display: flex;flex-direction: row">
+        <mat-form-field style="min-width: 140px"
+                        subscriptSizing="dynamic"
+                        appearance="fill"
+        >
+          <mat-label>{{ "document.edit.panel.label.selected.version" | translate }}</mat-label>
+          <mat-select
+
+            placeholder="{{ 'document.edit.panel.placeholder.version' | translate }}"
+            matTooltip="{{ 'document.edit.panel.tooltip.version' | translate }}"
+            id="document-version_id"
+            formControlName="payloadVersion"
+            (selectionChange)="onSelectionDocumentVersionChanged()">
+            <mat-option *ngFor="let version of getDocumentVersions"
+                        [value]="version">
+              {{ version }}
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+
+        <mat-form-field style="min-width: 180px"
+                        subscriptSizing="dynamic"
+                        appearance="fill">
+          <mat-label>{{ "document.edit.panel.label.selected.status" | translate }}</mat-label>
+          <input matInput id="status_id"
+                 formControlName="documentVersionStatus"
+                 readonly>
+        </mat-form-field>
+        <mat-form-field style="width:100%"
+                        subscriptSizing="dynamic"
+                        appearance="fill">
+          <mat-label>{{ "document.edit.panel.label.selected.created.on" | translate }}</mat-label>
+          <input id="payloadCreatedOn_id"
+                 matInput [ngxMatDatetimePicker]="payloadCreatedOnPicker"
+                 formControlName="payloadCreatedOn"
+                 readonly>
+          <mat-datepicker-toggle matSuffix [for]="payloadCreatedOnPicker"
+                                 style="visibility: hidden"></mat-datepicker-toggle>
+          <ngx-mat-datetime-picker #payloadCreatedOnPicker [showSpinners]="true"
+                                   [showSeconds]="false"
+                                   [hideTime]="false"></ngx-mat-datetime-picker>
+        </mat-form-field>
+      </div>
+      <div
+        style="display:block; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;border: ridge 3px #b0bec5"
+        (click)="onEditPanelClick()">
+        <smp-editor #smpDocumentEditor formControlName="payload"
+                    ngDefaultControl></smp-editor>
+
+      </div>
+      <smp-warning-panel *ngIf="!documentEditable"
+                         icon="info"
+                         type="desc"
+                         [label]="'document.edit.panel.note.editable' | translate: { editableDocStatusList: editableDocStatusList }">
+      </smp-warning-panel>
+    </div>
+    <expandable-panel>
+      <expandable-item icon="settings"
+                       title="{{'document.properties.panel.tab.title' | translate }}"
+                       buttonLabel="{{'document.properties.panel.tab.button.properties' | translate }}">
+        <document-properties-panel formControlName="properties"
+                                   [showEditToolbarButton]="isNotReviewMode"
+                                   ngDefaultControl></document-properties-panel>
+      </expandable-item>
+      <expandable-item icon="list"
+                       *ngIf="isNotReviewMode"
+                       title="{{'document.versions.panel.tab.title' | translate }}"
+                       buttonLabel="{{'document.versions.panel.tab.button.versions' | translate }}">
+        <document-versions-panel
+                              formControlName="documentVersions"
+                                [selectedVersion]="currentDocumentVersion"
+                                 (selectedVersionChange) = "loadDocumentForVersion($event)"
+                                 ngDefaultControl></document-versions-panel>
+      </expandable-item>
+      <expandable-item icon="event"
+                       title="{{'document.events.panel.tab.title' | translate }}"
+                       buttonLabel="{{'document.events.panel.tab.button.events' | translate }}">
+        <document-events-panel formControlName="documentVersionEvents"
+                               ngDefaultControl></document-events-panel>
+      </expandable-item>
+    </expandable-panel>
+  </div>
+
+  <mat-toolbar class="mat-elevation-z2"
+               style="flex-grow: 0;">
+    <mat-toolbar-row class="smp-toolbar-row">
+      <button id="back_id" mat-raised-button color="primary"
+              (click)="onBackButtonClicked()">
+        <mat-icon>arrow_circle_left</mat-icon>
+        <span>{{ "document.edit.panel.button.back" | translate }}</span>
+      </button>
+      <button id="cancel_id"
+              *ngIf="isNotReviewMode"
+              mat-raised-button
+              color="primary"
+              [disabled]="cancelButtonDisabled"
+              (click)="onDocumentResetButtonClicked()">
+        <mat-icon>cancel</mat-icon>
+        <span>{{ "document.edit.panel.button.cancel" | translate }}</span>
+      </button>
+      <tool-button-spacer></tool-button-spacer>
+      <button id="saveResource_id"
+              *ngIf="isNotReviewMode"
+              mat-raised-button
+              color="primary"
+              matTooltip="{{ 'document.edit.panel.tooltip.save' | translate }}"
+              [disabled]="saveButtonDisabled"
+              (click)="onSaveButtonClicked()">
+        <mat-icon>save</mat-icon>
+        <span>{{ "document.edit.panel.button.save" | translate }}</span>
+      </button>
+      <button id="publishResource_id" mat-raised-button
+              *ngIf="isNotReviewMode"
+              color="primary"
+              matTooltip="{{ 'document.edit.panel.tooltip.version.publish' | translate }}"
+              [disabled]="publishButtonDisabled"
+              (click)="onPublishButtonClicked()">
+        <mat-icon>publish</mat-icon>
+        <span>{{ "document.edit.panel.button.version.publish" | translate }}</span>
+      </button>
+      <tool-button-spacer></tool-button-spacer>
+      <button *ngIf="reviewEnabled && isNotReviewMode" id="reviewResource_id" mat-raised-button
+              color="primary"
+              matTooltip="{{ 'document.edit.panel.tooltip.version.review' | translate }}"
+              [disabled]="reviewButtonDisabled"
+              (click)="onReviewRequestButtonClicked()">
+        <mat-icon>task</mat-icon>
+        <span>{{ "document.edit.panel.button.version.review" | translate }}</span>
+      </button>
+      <button mat-raised-button
+              *ngIf="!isNotReviewMode || reviewEnabled"
+              color="primary"
+              matTooltip="{{ 'document.edit.panel.tooltip.version.approve' | translate }}"
+              [disabled]="reviewActionButtonDisabled"
+              (click)="onApproveButtonClicked()">
+        <mat-icon>check_circle</mat-icon>
+        <span>{{ "document.edit.panel.button.version.approve" | translate }}</span>
+      </button>
+      <button mat-raised-button
+              *ngIf="!isNotReviewMode || reviewEnabled"
+              color="primary"
+              matTooltip="{{ 'document.edit.panel.tooltip.version.reject' | translate }}"
+              [disabled]="reviewActionButtonDisabled"
+              (click)="onRejectButtonClicked()">
+        <mat-icon>unpublished</mat-icon>
+        <span>{{ "document.edit.panel.button.version.reject" | translate }}</span>
+      </button>
+    </mat-toolbar-row>
+  </mat-toolbar>
+</div>
diff --git a/smp-angular/src/app/common/panels/document-edit-panel/document-edit-panel.component.scss b/smp-angular/src/app/common/panels/document-edit-panel/document-edit-panel.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..629dfc616c3572d572df60f2224542a1870ebfa5
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-edit-panel/document-edit-panel.component.scss
@@ -0,0 +1,10 @@
+#resource-document-panel {
+  display: flex;
+  height: 100%;
+  flex-direction: column;
+
+}
+
+.CodeMirror {
+  height: auto;
+}
diff --git a/smp-angular/src/app/common/panels/document-edit-panel/document-edit-panel.component.ts b/smp-angular/src/app/common/panels/document-edit-panel/document-edit-panel.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb676f82bd2ab500e8a2ee832891af5babb209b6
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-edit-panel/document-edit-panel.component.ts
@@ -0,0 +1,619 @@
+import {
+  Component,
+  Input,
+  OnInit,
+  ViewChild,
+  ViewEncapsulation,
+} from '@angular/core';
+import {MatDialog, MatDialogRef} from "@angular/material/dialog";
+import {
+  BeforeLeaveGuard
+} from "../../../window/sidenav/navigation-on-leave-guard";
+import {GroupRo} from "../../../common/model/group-ro.model";
+import {ResourceRo} from "../../../common/model/resource-ro.model";
+import {
+  AlertMessageService
+} from "../../../common/alert-message/alert-message.service";
+import {DomainRo} from "../../../common/model/domain-ro.model";
+import {
+  ResourceDefinitionRo
+} from "../../../system-settings/admin-extension/resource-definition-ro.model";
+import {FormBuilder, FormControl, FormGroup} from "@angular/forms";
+import {DocumentRo} from "../../../common/model/document-ro.model";
+import {
+  NavigationService
+} from "../../../window/sidenav/navigation-model.service";
+
+import {
+  ConfirmationDialogComponent
+} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component";
+import {
+  SmpEditorComponent
+} from "../../../common/components/smp-editor/smp-editor.component";
+import {EntityStatus} from "../../../common/enums/entity-status.enum";
+import {TranslateService} from "@ngx-translate/core";
+import {lastValueFrom, Observer} from "rxjs";
+import {
+  DocumentVersionsStatus
+} from "../../../common/enums/document-versions-status.enum";
+import {
+  HttpErrorHandlerService
+} from "../../../common/error/http-error-handler.service";
+import {
+  DocumentWizardDialogComponent
+} from "../../../edit/edit-resources/document-wizard-dialog/document-wizard-dialog.component";
+import {
+  EditResourceService
+} from "../../../edit/edit-resources/edit-resource.service";
+import {SubresourceRo} from "../../model/subresource-ro.model";
+import {
+  SubresourceWizardRo
+} from "../../../edit/edit-resources/subresource-document-wizard-dialog/subresource-wizard-edit-ro.model";
+import {
+  SubresourceDocumentWizardComponent
+} from "../../../edit/edit-resources/subresource-document-wizard-dialog/subresource-document-wizard.component";
+import {
+  ReviewDocumentVersionRo
+} from "../../model/review-document-version-ro.model";
+
+export enum SmpDocumentEditorType {
+  RESOURCE_EDITOR = "RESOURCE_EDITOR",
+  SUBRESOURCE_EDITOR = "SUBRESOURCE_EDITOR",
+  REVIEW_EDITOR = "REVIEW_EDITOR"
+}
+
+export enum SmpReviewDocumentTarget {
+  RESOURCE = "RESOURCE",
+  SUBRESOURCE = "SUBRESOURCE",
+}
+
+/**
+ * Component edit panel for document and document version management.
+ * Please not the document version management can change the document (version) entities only
+ * and does not edit resource or subresource entities.
+ *
+ * The document (versions) actions can be
+ * <ul>
+ *   <li>new version created: a new version of the document can be created for the document</li>
+ *   <li>edited: a document version payload, or document version can be loaded </li>
+ *   <li>saved: a document changed can be persisted</li>
+ *   <li>published: the document can be set as current version</li>
+ *   <li>validated: the selected document can be validated </li>
+ *   <li>generated: the button allows option to generate new document from the plugin template</li>
+ *   <li>reviewed process: the component allows action needed for the review process
+ *    <ul>
+ *      <li>request review</li>
+ *      <li>approve</li>
+ *      <li>reject</li>
+ *   </ul>
+ *   </li>
+ * </ul>
+ */
+@Component({
+  selector: 'document-edit-panel',
+  templateUrl: './document-edit-panel.component.html',
+  styleUrls: ['./document-edit-panel.component.scss'],
+  encapsulation: ViewEncapsulation.None,
+})
+export class DocumentEditPanelComponent implements BeforeLeaveGuard, OnInit {
+  readonly reviewAllowedStatusList: DocumentVersionsStatus[] = [DocumentVersionsStatus.DRAFT, DocumentVersionsStatus.REJECTED, DocumentVersionsStatus.RETIRED];
+  readonly editableDocStatusList: DocumentVersionsStatus[] = [DocumentVersionsStatus.DRAFT, DocumentVersionsStatus.REJECTED, DocumentVersionsStatus.RETIRED];
+  readonly publishableDocStatusList: DocumentVersionsStatus[] = [DocumentVersionsStatus.DRAFT, DocumentVersionsStatus.APPROVED, DocumentVersionsStatus.RETIRED];
+  private resource: ResourceRo;
+  private subresource: SubresourceRo;
+  private reviewDocument: ReviewDocumentVersionRo;
+  private isResourceDocument:boolean = true;
+
+  _document: DocumentRo;
+  @Input() private group: GroupRo;
+  @Input() domain: DomainRo;
+  @Input() domainResourceDefs: ResourceDefinitionRo[];
+  private _editorMode: SmpDocumentEditorType = SmpDocumentEditorType.RESOURCE_EDITOR;
+
+  @Input() set editorMode(edType: string) {
+    this._editorMode = SmpDocumentEditorType[edType];
+  }
+
+  get editorMode(): SmpDocumentEditorType {
+    return !this._editorMode ? SmpDocumentEditorType.RESOURCE_EDITOR : this._editorMode;
+  }
+
+  @ViewChild("smpDocumentEditor") documentEditor: SmpEditorComponent;
+  // ----
+  // defined observers
+  loadDocumentObserver: Partial<Observer<DocumentRo>> = {
+    next: async (doc: DocumentRo) => {
+      if (!doc) {
+        this.document = null;
+      } else {
+        this.document = doc;
+      }
+    },
+    error: (err: any) => {
+      this.httpErrorHandlerService.handleHttpError(err)
+    }
+  };
+
+  reviewActionDocumentObserver: Partial<Observer<DocumentRo>> = {
+    next: async (doc: DocumentRo) => {
+      if (!doc) {
+        this.document = null;
+      } else {
+        this.document = doc;
+      }
+    },
+    error: (err: any) => {
+      this.httpErrorHandlerService.handleHttpError(err)
+    }
+  };
+
+  // save event observer
+  saveDocumentObserver: Partial<Observer<DocumentRo>> = {
+    next: async (doc: DocumentRo) => {
+      if (!doc) {
+        this.document = null;
+      } else {
+        this.alertService.success(await lastValueFrom(this.translateService.get("document.edit.panel.success.save", {currentResourceVersion: doc.payloadVersion})));
+        this.document = doc;
+      }
+    },
+    error: (err: any) => {
+      this.httpErrorHandlerService.handleHttpError(err)
+    }
+  };
+
+  // save event observer
+  validateDocumentObserver: Partial<Observer<DocumentRo>> = {
+    next: async (doc: DocumentRo) => {
+      this.alertService.success(await lastValueFrom(this.translateService.get("document.edit.panel.success.valid")))
+    },
+    error: (err: any) => {
+      this.httpErrorHandlerService.handleHttpError(err)
+    }
+  };
+
+  // save event observer
+  generateDocumentObserver: Partial<Observer<DocumentRo>> = {
+    next: async (doc: DocumentRo) => {
+      if (!doc) {
+        this.document = null;
+      } else {
+        this.alertService.success(await lastValueFrom(this.translateService.get("document.edit.panel.success.generate")))
+        this.documentForm.controls['payload'].setValue(doc.payload);
+        this.documentForm.controls['payload'].markAsDirty();
+      }
+    },
+    error: (err: any) => {
+      this.httpErrorHandlerService.handleHttpError(err)
+    }
+  };
+
+  documentForm: FormGroup;
+
+  constructor(private editResourceService: EditResourceService,
+              private alertService: AlertMessageService,
+              private dialog: MatDialog,
+              private navigationService: NavigationService,
+              private formBuilder: FormBuilder,
+              private translateService: TranslateService,
+              private httpErrorHandlerService: HttpErrorHandlerService) {
+
+
+    this.documentForm = formBuilder.group({
+      'mimeType': new FormControl({value: null}),
+      'name': new FormControl({value: null}),
+      'currentResourceVersion': new FormControl({value: null}),
+      'payloadCreatedOn': new FormControl({value: null}),
+      'payloadVersion': new FormControl({value: null}),
+      'payload': new FormControl({value: null}),
+      'properties': new FormControl({value: null}),
+      'documentVersionStatus': new FormControl({value: null}),
+      'documentVersionEvents': new FormControl({value: null}),
+      'documentVersions': new FormControl({value: null}),
+    });
+
+    this.resource = editResourceService.selectedResource;
+    this.subresource = editResourceService.selectedSubresource;
+    this.reviewDocument = editResourceService.selectedReviewDocument;
+    this.documentForm.controls['payload'].setValue("")
+  }
+
+  /**
+   * Methods created resource and subresource from documentReview object
+   */
+  initFromDocumentReview() {
+    if (!this.reviewDocument) {
+      return
+    }
+    this.resource = {
+      resourceId: this.reviewDocument.resourceId,
+      identifierScheme: this.reviewDocument.resourceIdentifierScheme,
+      identifierValue: this.reviewDocument.resourceIdentifierValue,
+      reviewEnabled: true,
+      visibility: null,
+      resourceTypeIdentifier: null,
+    } as ResourceRo;
+
+    if (this.reviewDocument.subresourceId) {
+      this.subresource = {
+        subresourceId: this.reviewDocument.subresourceId,
+        identifierScheme: this.reviewDocument.subresourceIdentifierScheme,
+        identifierValue: this.reviewDocument.subresourceIdentifierValue,
+        subresourceTypeIdentifier: null,
+      } as SubresourceRo;
+    }
+    if (this.reviewDocument.target === "RESOURCE") {
+      this.isResourceDocument = true;
+    } else {
+      this.isResourceDocument = false;
+    }
+
+
+  }
+
+
+  ngOnInit(): void {
+    if (this.editorMode === SmpDocumentEditorType.REVIEW_EDITOR) {
+      this.initFromDocumentReview();
+    }
+
+    if (this.editorMode === SmpDocumentEditorType.REVIEW_EDITOR && !this.reviewDocument
+      || this.editorMode !== SmpDocumentEditorType.REVIEW_EDITOR && !this.resource) {
+      this.alertService.errorForTranslation("document.edit.panel.error.document.null");
+      this.navigationService.navigateUp();
+      return;
+    }
+    if (this.editorMode === SmpDocumentEditorType.REVIEW_EDITOR) {
+      this.loadDocumentForVersion(this.reviewDocument.version);
+    } else {
+      this.loadDocumentForVersion();
+    }
+  }
+
+
+  @Input() set document(value: DocumentRo) {
+    this._document = value;
+    this.documentForm.disable();
+    console.log("Document with properties: " + value?.properties)
+    if (!!value) {
+      this.documentEditor.mimeType = value.mimeType;
+      this.documentForm.controls['mimeType'].setValue(value.mimeType);
+      this.documentForm.controls['name'].setValue(value.name);
+      this.documentForm.controls['currentResourceVersion'].setValue(value.currentResourceVersion);
+      this.documentForm.controls['payloadVersion'].setValue(value.payloadVersion);
+      this.documentForm.controls['payloadCreatedOn'].setValue(value.payloadCreatedOn);
+      this.documentForm.controls['payload'].setValue(value.payload);
+      this.documentForm.controls['properties'].setValue(value.properties);
+      this.documentForm.controls['documentVersionStatus'].setValue(value.documentVersionStatus);
+      this.documentForm.controls['documentVersionEvents'].setValue(value.documentVersionEvents);
+      this.documentForm.controls['documentVersions'].setValue(value.documentVersions);
+      // the method documentVersionsExists already uses the current value to check if versions exists
+      if (this.documentVersionsExists && this.isNotReviewMode) {
+        this.documentForm.controls['payloadVersion'].enable();
+      }
+      if (this.documentEditable) {
+        this.documentForm.controls['payload'].enable();
+      }
+    } else {
+      this.documentForm.controls['name'].setValue("");
+      this.documentForm.controls['payload'].setValue("");
+      this.documentForm.controls['currentResourceVersion'].setValue("");
+      this.documentForm.controls['payloadVersion'].setValue("");
+      this.documentForm.controls['payloadCreatedOn'].setValue("");
+      this.documentForm.controls['payload'].setValue("");
+      this.documentForm.controls['properties'].setValue([]);
+      this.documentForm.controls['documentVersionStatus'].setValue("");
+      this.documentForm.controls['documentVersionEvents'].setValue([]);
+      this.documentForm.controls['documentVersions'].setValue([]);
+    }
+    this.documentForm.markAsPristine();
+  }
+
+  get document(): DocumentRo {
+    let doc: DocumentRo = {...this._document};
+    if (this.documentForm.controls['payload'].dirty) {
+      doc.payload = this.documentForm.controls['payload'].value;
+      doc.payloadStatus = EntityStatus.UPDATED;
+    }
+    doc.properties = this.documentForm.controls['properties'].value;
+    return doc;
+  }
+
+  onBackButtonClicked(): void {
+    this.navigationService.navigateUp();
+  }
+
+  async onDocumentResetButtonClicked() {
+    this.dialog.open(ConfirmationDialogComponent, {
+      data: {
+        title: await lastValueFrom(this.translateService.get("document.edit.panel.cancel.confirmation.dialog.title")),
+        description: await lastValueFrom(this.translateService.get("document.edit.panel.cancel.confirmation.dialog.description"))
+      }
+    }).afterClosed().subscribe(result => {
+      if (result) {
+        this.resetChanges()
+      }
+    });
+  }
+
+  resetChanges() {
+    let currentVersion = this._document?.payloadVersion;
+    if (!currentVersion) {
+      this.documentForm.controls['payload'].setValue("");
+      this.documentForm.markAsPristine();
+    } else {
+      this.loadDocumentForVersion(currentVersion);
+    }
+  }
+
+  get currentDocumentVersion(): number {
+    return this.isNotReviewMode? this.documentForm.controls['payloadVersion']?.value:
+      this.reviewDocument?.version;
+  }
+
+  onSaveButtonClicked(): void {
+    let onSaveObservable = this.isResourceDocument ?
+      this.editResourceService.saveResourceDocumentObservable(this.resource, this.document) :
+      this.editResourceService.saveSubresourceDocumentObservable(this.subresource, this.resource, this.document);
+    onSaveObservable.subscribe(this.saveDocumentObserver);
+  }
+
+  onReviewRequestButtonClicked(): void {
+    // create lightweight document object
+    let docRequest: DocumentRo = {
+      documentId: this._document.documentId,
+      payloadVersion: this._document.payloadVersion,
+    } as DocumentRo;
+
+    let onReviewRequestObservable = this.isResourceDocument ?
+      this.editResourceService.reviewRequestForResourceDocumentObservable(this.resource, docRequest) :
+      this.editResourceService.reviewRequestForSubresourceDocumentObservable(this.subresource, this.resource, docRequest);
+    // request review
+    onReviewRequestObservable.subscribe(this.loadDocumentObserver);
+  }
+
+  onApproveButtonClicked(): void {
+    // create lightweight document object
+    let docRequest: DocumentRo = {
+      documentId: this._document.documentId,
+      payloadVersion: this._document.payloadVersion,
+    } as DocumentRo;
+
+    let onReviewRequestObservable = this.isResourceDocument ?
+      this.editResourceService.reviewApproveForResourceDocumentObservable(this.resource, docRequest) :
+      this.editResourceService.reviewApproveForSubresourceDocumentObservable(this.subresource, this.resource, docRequest);
+    // request review
+    onReviewRequestObservable.subscribe(this.reviewActionDocumentObserver);
+  }
+
+  onRejectButtonClicked(): void {
+    // create lightweight document object
+    let docRequest: DocumentRo = {
+      documentId: this._document.documentId,
+      payloadVersion: this._document.payloadVersion,
+    } as DocumentRo;
+
+    let onReviewRequestObservable = this.isResourceDocument ?
+      this.editResourceService.reviewRejectResourceDocumentObservable(this.resource, docRequest) :
+      this.editResourceService.reviewRejectSubresourceDocumentObservable(this.subresource, this.resource, docRequest);
+    // request review
+    onReviewRequestObservable.subscribe(this.reviewActionDocumentObserver);
+  }
+
+  /**
+   * Publish the current document version
+   *
+   */
+  onPublishButtonClicked(): void {
+    // create lightweight document object
+    let docRequest: DocumentRo = {
+      documentId: this._document.documentId,
+      payloadVersion: this._document.payloadVersion,
+    } as DocumentRo;
+
+    let onReviewRequestObservable = this.isResourceDocument ?
+      this.editResourceService.publishResourceDocumentObservable(this.resource, docRequest) :
+      this.editResourceService.publishSubresourceDocumentObservable(this.subresource, this.resource, docRequest);
+    // request review
+    onReviewRequestObservable.subscribe(this.loadDocumentObserver);
+  }
+
+  onGenerateButtonClicked(): void {
+    let generateObservable = this.isResourceDocument ?
+      this.editResourceService.generateResourceDocumentObservable(this.resource) :
+      this.editResourceService.generateSubresourceDocumentObservable(this.subresource, this.resource);
+    generateObservable.subscribe(this.generateDocumentObserver);
+  }
+
+  async onShowDocumentWizardDialog() {
+    if (this.isResourceDocument) {
+      await this.onShowResourceDocumentWizardDialog();
+    } else {
+      await this.onShowSubresourceDocumentWizardDialog();
+    }
+  }
+
+  async onShowResourceDocumentWizardDialog() {
+    const formRef: MatDialogRef<any> = this.dialog.open(DocumentWizardDialogComponent, {
+      data: {
+        title: await lastValueFrom(this.translateService.get("document.edit.panel.document.wizard.dialog.title")),
+        resource: this.resource,
+
+      }
+    });
+    formRef.afterClosed().subscribe(result => {
+      if (result) {
+        let val = formRef.componentInstance.getExtensionXML();
+        this.documentForm.controls['payload'].setValue(val);
+        this.documentForm.controls['payload'].markAsDirty();
+      }
+    });
+  }
+
+  async onShowSubresourceDocumentWizardDialog() {
+
+    let serviceMetadataWizard: SubresourceWizardRo = {
+      isNewSubresource: false,
+      participantIdentifier: this.resource.identifierValue,
+      participantScheme: this.resource.identifierScheme,
+      documentIdentifier: this.subresource.identifierValue,
+      documentIdentifierScheme: this.subresource.identifierScheme,
+      processIdentifier: '',
+      processScheme: '',
+      transportProfile: 'bdxr-transport-ebms3-as4-v1p0', // default value for oasis AS4
+
+      endpointUrl: '',
+      endpointCertificate: '',
+
+      serviceDescription: '',
+      technicalContactUrl: '',
+
+    }
+
+    const formRef: MatDialogRef<any> = this.dialog.open(SubresourceDocumentWizardComponent, {
+      data: serviceMetadataWizard
+    });
+    formRef.afterClosed().subscribe(result => {
+      if (result) {
+        let smw: SubresourceWizardRo = formRef.componentInstance.getCurrent();
+        this.documentForm.controls['payload'].setValue(smw.contentXML);
+        this.documentForm.controls['payload'].markAsDirty();
+      }
+    });
+  }
+
+  /**
+   * 'loadDocumentForVersion' load the document for the given version
+   * @param version
+   */
+  loadDocumentForVersion(version: number = null): void {
+
+    let loadObservable = this.isResourceDocument ?
+      this.editResourceService.getResourceDocumentObservable(this.resource, version) :
+      this.editResourceService.getSubresourceDocumentObservable(this.subresource, this.resource, version);
+    loadObservable.subscribe(this.loadDocumentObserver);
+  }
+
+  /**
+   * Submit the current document for validation to the server
+   */
+  validateCurrentDocument(): void {
+    let validateObservable = this.isResourceDocument ?
+      this.editResourceService.validateResourceDocumentObservable(this.resource, this.document) :
+      this.editResourceService.validateSubresourceDocumentObservable(this.subresource, this.resource, this.document);
+    validateObservable.subscribe(this.validateDocumentObserver);
+  }
+
+
+  onDocumentValidateButtonClicked(): void {
+    this.validateCurrentDocument();
+  }
+
+  onNewDocumentVersionButtonClicked(): void {
+    // create a new version of the document
+    let docRequest: DocumentRo = {
+      documentVersionStatus: DocumentVersionsStatus.DRAFT,
+      payloadStatus: EntityStatus.NEW,
+      status: EntityStatus.UPDATED,
+      payload: this.documentForm.controls['payload'].value,
+      allVersions: this.getDocumentVersions,
+      // set from current document
+      documentVersions: this.documentForm.controls['documentVersions'].value,
+      properties: this.documentForm.controls['properties'].value,
+    } as DocumentRo;
+    // set as current
+    this.document = docRequest;
+    this.documentForm.markAsDirty();
+  }
+
+  onSelectionDocumentVersionChanged(): void {
+    this.loadDocumentForVersion(this.documentForm.controls['payloadVersion'].value)
+  }
+
+  public onEditPanelClick() {
+
+    if (this.documentEditor.hasFocus) {
+      return;
+    }
+    this.documentEditor.focusAndCursorToEnd();
+  }
+
+  get getDocumentVersions(): number[] {
+    return !this._document?.allVersions ? [] : this._document?.allVersions;
+  }
+
+  get emptyDocument(): boolean {
+    return !this.documentForm.controls['payload']?.value
+  }
+
+  get documentVersionsExists(): boolean {
+    return this.getDocumentVersions.length > 0
+  }
+
+  get cancelButtonDisabled(): boolean {
+    return !this.documentForm.dirty;
+  }
+
+  get saveButtonDisabled(): boolean {
+    return !this.documentForm.dirty
+      || !this.documentForm.controls['payload']?.value;
+  }
+
+  /**
+   * Review button is disabled if review is not enabled,
+   * if changes are not persisted, or not in  status, rejected, retired or draft
+   */
+  get reviewButtonDisabled(): boolean {
+    return !this.reviewEnabled
+      || !this.documentSubmitReviewAllowed
+      || this.isDirty();
+  }
+
+  get reviewActionButtonDisabled(): boolean {
+    return !this.reviewEnabled
+      || this.documentForm.controls['documentVersionStatus']?.value !== DocumentVersionsStatus.UNDER_REVIEW
+  }
+
+  get publishButtonDisabled(): boolean {
+    // can not publish changed document
+    if (this.isDirty()) {
+      return true;
+    }
+    let status = this.documentForm.controls['documentVersionStatus']?.value
+
+    return this.reviewEnabled ?
+      status !== DocumentVersionsStatus.APPROVED :
+      !this.publishableDocStatusList.find(i => i === status)
+  }
+
+  get documentEditable(): boolean {
+    let status = this.documentForm.controls['documentVersionStatus']?.value
+    return !!this.editableDocStatusList.find(i => i === status)
+  }
+
+  get documentSubmitReviewAllowed(): boolean {
+    let status = this.documentForm.controls['documentVersionStatus']?.value
+    return !!this.reviewAllowedStatusList.find(i => i === status)
+  }
+
+  get reviewEnabled(): boolean {
+    return this.resource?.reviewEnabled;
+  }
+
+  get isNotReviewMode(): boolean {
+    return this.editorMode !== SmpDocumentEditorType.REVIEW_EDITOR;
+  }
+
+  isDirty(): boolean {
+    return this.documentForm.dirty
+  }
+
+  get showWizardDialog(): boolean {
+    if (this.isResourceDocument) {
+      // in version DomiSMP 5.0 CR show only the wizard for edelivery-oasis-smp-1.0-servicegroup
+      return this.resource?.resourceTypeIdentifier === 'edelivery-oasis-smp-1.0-servicegroup';
+    } else {
+      return this.subresource?.subresourceTypeIdentifier === 'edelivery-oasis-smp-1.0-servicemetadata';
+    }
+  }
+}
diff --git a/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.html b/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..595803b18bee6c38ec1ab5f23f31667a9cfcbfc9
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.html
@@ -0,0 +1,58 @@
+<div style="display:flex; flex-grow: 1; flex-direction: column">
+  <mat-form-field style="width: 100%">
+    <mat-label>{{ "document.events.panel.label.filter" | translate }}</mat-label>
+    <input matInput (keyup)="applyFilter($event)" #input>
+  </mat-form-field>
+  <div id="events-table-container">
+    <table #DocumentEventTable mat-table class="mat-elevation-z1"
+           style="max-height: 100%;flex-grow: 1;"
+           id="document-events-table"
+           [dataSource]="eventDataSource" matSort>
+
+      <ng-container matColumnDef="date">
+        <th mat-header-cell *matHeaderCellDef
+            mat-sort-header>{{ "document.events.panel.label.date" | translate }}
+        </th>
+        <td mat-cell *matCellDef="let row;">
+          {{ row.eventOn | date: dateTimeFormat }}
+        </td>
+      </ng-container>
+      <ng-container matColumnDef="eventType">
+        <th mat-header-cell *matHeaderCellDef
+            mat-sort-header>{{ "document.events.panel.label.type" | translate }}
+        </th>
+        <td mat-cell *matCellDef="let row">{{ row.eventType }}</td>
+      </ng-container>
+
+      <ng-container matColumnDef="username">
+        <th mat-header-cell *matHeaderCellDef
+            mat-sort-header>{{ "document.events.panel.label.username" | translate }}
+        </th>
+        <td mat-cell *matCellDef="let row">{{ row.username }}</td>
+      </ng-container>
+      <ng-container matColumnDef="eventSource">
+        <th mat-header-cell *matHeaderCellDef
+            mat-sort-header>{{ "document.events.panel.label.source" | translate }}
+        </th>
+        <td mat-cell *matCellDef="let row">{{ row.eventSourceType }}</td>
+      </ng-container>
+
+      <tr mat-header-row
+          *matHeaderRowDef="displayedColumns; sticky:true"></tr>
+      <tr mat-row
+          *matRowDef="let odd = odd; let row; columns: displayedColumns;"
+          [ngClass]="getRowClass(row, odd)"
+      ></tr>
+      <tr class="mat-row" *matNoDataRow>
+        <td class="mat-cell"
+            colspan="2">{{ "document.events.panel.label.no.properties.found" | translate }}
+        </td>
+      </tr>
+    </table>
+  </div>
+  <mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons
+                 [pageSize]="10" [length]="eventDataSource.data?.length"
+                 attr.aria-label="{{ 'document.events.panel.label.select.page' | translate }}"></mat-paginator>
+
+</div>
+
diff --git a/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.scss b/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..ba3e29071a33c3c07c2de2cf915be8d46c226833
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.scss
@@ -0,0 +1,20 @@
+#document-events-panel {
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+  padding: 0 1em;
+  min-width: 600px;
+}
+
+#events-table-container {
+  position: relative;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  overflow-y: scroll;
+}
+
+#events-table-container table {
+  width: 100%;
+}
diff --git a/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.ts b/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c22cc2a6298b302fc024799744238fe8e47348ee
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.ts
@@ -0,0 +1,141 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+import {
+  AfterViewInit,
+  Component,
+  forwardRef,
+  Input,
+  ViewChild,
+} from '@angular/core';
+import {MatTable, MatTableDataSource} from "@angular/material/table";
+import {
+  ControlContainer,
+  ControlValueAccessor,
+  FormControl,
+  FormControlDirective,
+  NG_VALUE_ACCESSOR
+} from "@angular/forms";
+import {MatSort} from "@angular/material/sort";
+import {MatDialog} from "@angular/material/dialog";
+import {MatPaginator} from "@angular/material/paginator";
+import {DocumentVersionEventRo} from "../../model/document-version-event-ro.model";
+import {
+  BeforeLeaveGuard
+} from "../../../window/sidenav/navigation-on-leave-guard";
+import {GlobalLookups} from "../../global-lookups";
+
+/**
+ * Component to display the properties of a document in a table. The properties can be edited and saved.
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+@Component({
+  selector: 'document-events-panel',
+  templateUrl: './document-events-panel.component.html',
+  styleUrls: ['./document-events-panel.component.scss'],
+  providers: [
+    {
+      provide: NG_VALUE_ACCESSOR,
+      useExisting: forwardRef(() => DocumentEventsPanelComponent),
+      multi: true
+    }
+  ]
+})
+export class DocumentEventsPanelComponent implements AfterViewInit, BeforeLeaveGuard, ControlValueAccessor {
+
+
+  displayedColumns: string[] = [ 'date', 'eventType', 'username', 'eventSource'];
+  private onChangeCallback: (_: any) => void = () => {
+  };
+  eventDataSource: MatTableDataSource<DocumentVersionEventRo> = new MatTableDataSource();
+  dataChanged: boolean = false;
+  selected: DocumentVersionEventRo;
+  @ViewChild("DocumentVersionEventTable") table: MatTable<DocumentVersionEventRo>;
+  @ViewChild(MatPaginator) paginator: MatPaginator;
+  @ViewChild(MatSort) sort: MatSort;
+
+  constructor(
+    private globalLookups: GlobalLookups,
+           public dialog: MatDialog,
+              private controlContainer: ControlContainer) {
+  }
+
+  get dateTimeFormat(): string {
+    return this.globalLookups.getDateTimeFormat();
+  }
+
+  @ViewChild(FormControlDirective, {static: true})
+  formControlDirective: FormControlDirective;
+  @Input()
+  formControl: FormControl;
+
+  @Input()
+  formControlName: string;  /* get hold of FormControl instance no matter formControl or    formControlName is given. If formControlName is given, then this.controlContainer.control is the parent FormGroup (or FormArray) instance. */
+  get control() {
+    return this.formControl || this.controlContainer.control.get(this.formControlName);
+  }
+
+  /**
+   * Implementation of the ControlValueAccessor method to  write value to the component.
+   * @param eventList
+   */
+  writeValue(eventList: DocumentVersionEventRo[]): void {
+    this.eventDataSource.data = !eventList?.length ? [] : [...eventList];
+    this.dataChanged = false;
+  }
+
+  ngAfterViewInit() {
+    this.eventDataSource.paginator = this.paginator;
+    this.eventDataSource.sort = this.sort;
+  }
+
+  applyFilter(event: Event) {
+    const filterValue: string = (event.target as HTMLInputElement).value;
+    this.eventDataSource.filter = filterValue?.trim().toLowerCase();
+
+    if (this.eventDataSource.paginator) {
+      this.eventDataSource.paginator.firstPage();
+    }
+  }
+
+  isDirty(): boolean {
+    return this.dataChanged;
+  }
+
+  registerOnChange(fn: any): void {
+    this.onChangeCallback = fn;
+
+  }
+
+  registerOnTouched(fn: any): void {
+    // not implemented
+  }
+
+  setDisabledState(isDisabled: boolean): void {
+    // not implemented
+  }
+
+  getRowClass(row, oddRow: boolean) {
+    return {
+      'datatable-row-selected': row === this.selected,
+      'datatable-row-odd': oddRow
+    };
+  }
+
+}
diff --git a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html
index b6047e8f2d9efdd4879c4802851bed64efff53f7..f1dc9a7eab8c8f754220293913435c77df8fbe89 100644
--- a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html
+++ b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html
@@ -1,6 +1,36 @@
-<div style="display: flex;flex-direction: row;height: 100%">
-  <div *ngIf="showPropertyPanel" id="document-properties-panel">
-    <div style="display:flex; flex-grow: 1; flex-direction: column">
+<div style="display:flex; flex-grow: 1; flex-direction: column">
+      <mat-toolbar *ngIf="shotToolbar"
+        class="mat-elevation-z2"
+                   style="flex-grow: 0;">
+        <mat-toolbar-row class="smp-toolbar-row">
+          <button id="createButton" mat-mini-fab attr.aria-label="{{ 'document.properties.panel.label.create' | translate }}"
+                  matTooltip="{{ 'document.properties.panel.tooltip.create' | translate }}"
+                  (click)="onCreateProperty()">
+            <mat-icon>add</mat-icon>
+          </button>
+          <button id="editButton" mat-mini-fab attr.aria-label="{{ 'document.properties.panel.label.edit' | translate }}"
+                  matTooltip="{{ 'document.properties.panel.tooltip.edit' | translate }}"
+                  (click)="onEditSelectedProperty()"
+                  [disabled]="editButtonDisabled">
+            <mat-icon>edit</mat-icon>
+          </button>
+          <button id="deleteButton" mat-mini-fab attr.aria-label="{{ 'document.properties.panel.label.delete.remove' | translate }}"
+                  matTooltip="{{ 'document.properties.panel.tooltip.delete.remove' | translate }}"
+                  (click)="onDeleteSelectedProperty()"
+                  [disabled]="deleteButtonDisabled"
+          >
+            <mat-icon>remove</mat-icon>
+          </button>
+          <tool-button-spacer></tool-button-spacer>
+          <button id="resetButton" mat-mini-fab attr.aria-label="{{ 'document.properties.panel.label.reset' | translate }}"
+                  matTooltip="{{ 'document.properties.panel.tooltip.reset' | translate }}"
+                  (click)="onResetButtonClicked()"
+                  color="primary"
+                  [disabled]="!cancelButtonEnabled">
+            <mat-icon>refresh</mat-icon>
+          </button>
+        </mat-toolbar-row>
+      </mat-toolbar>
       <mat-form-field style="width: 100%">
         <mat-label>{{ "document.properties.panel.label.filter" | translate }}</mat-label>
         <input matInput (keyup)="applyFilter($event)" #input>
@@ -39,41 +69,3 @@
                      attr.aria-label="{{ 'document.properties.panel.label.select.page' | translate }}"></mat-paginator>
 
     </div>
-  </div>
-  <!-- table Toolbar -->
-  <div style="width: 42px">
-    <button mat-mini-fab attr.aria-label="{{ 'document.properties.panel.label.expand.collapse' | translate }}"
-            matTooltip="{{ 'document.properties.panel.tooltip.expand.collapse' | translate }}"
-            (click)="onToggleButtonClicked()">
-      <mat-icon *ngIf="showPropertyPanel">chevron_left</mat-icon>
-      <mat-icon *ngIf="!showPropertyPanel">chevron_right</mat-icon>
-    </button>
-    <tool-button-spacer [vertical]="false"></tool-button-spacer>
-    <button id="createButton" mat-mini-fab attr.aria-label="{{ 'document.properties.panel.label.create' | translate }}"
-            matTooltip="{{ 'document.properties.panel.tooltip.create' | translate }}"
-            (click)="onCreateProperty()">
-      <mat-icon>add</mat-icon>
-    </button>
-    <button id="editButton" mat-mini-fab attr.aria-label="{{ 'document.properties.panel.label.edit' | translate }}"
-            matTooltip="{{ 'document.properties.panel.tooltip.edit' | translate }}"
-            (click)="onEditSelectedProperty()"
-            [disabled]="editButtonDisabled">
-      <mat-icon>edit</mat-icon>
-    </button>
-    <button id="deleteButton" mat-mini-fab attr.aria-label="{{ 'document.properties.panel.label.delete.remove' | translate }}"
-            matTooltip="{{ 'document.properties.panel.tooltip.delete.remove' | translate }}"
-            (click)="onDeleteSelectedProperty()"
-            [disabled]="deleteButtonDisabled"
-    >
-      <mat-icon>remove</mat-icon>
-    </button>
-    <tool-button-spacer [vertical]="false"></tool-button-spacer>
-    <button id="resetButton" mat-mini-fab attr.aria-label="{{ 'document.properties.panel.label.reset' | translate }}"
-            matTooltip="{{ 'document.properties.panel.tooltip.reset' | translate }}"
-            (click)="onResetButtonClicked()"
-            color="primary"
-            [disabled]="!cancelButtonEnabled">
-      <mat-icon>refresh</mat-icon>
-    </button>
-  </div>
-</div>
diff --git a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts
index 3e067286b9a3ad539e3ebf02ba679208dea2c416..5c346cf7ce0b54b4fd422a03f3802c50d54c92b2 100644
--- a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts
+++ b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts
@@ -68,13 +68,13 @@ export class DocumentPropertiesPanelComponent implements AfterViewInit, BeforeLe
   private onChangeCallback: (_: any) => void = () => {
   };
   selected?: DocumentPropertyRo = null;
-  dataChanged: boolean = false
-  showPropertyPanel: boolean = true;
+  @Input() showEditToolbarButton: boolean = true;
+  dataChanged: boolean = false;
   initPropertyList: DocumentPropertyRo[] = [];
   propertyDataSource: MatTableDataSource<DocumentPropertyRo> = new MatTableDataSource();
 
   @ViewChild("DocumentPropertyTable") table: MatTable<DocumentPropertyRo>;
-  @ViewChild(MatPaginator) paginator: MatPaginator
+  @ViewChild(MatPaginator) paginator: MatPaginator;
   @ViewChild(MatSort) sort: MatSort;
 
   constructor(public dialog: MatDialog, private controlContainer: ControlContainer) {
@@ -115,8 +115,8 @@ export class DocumentPropertiesPanelComponent implements AfterViewInit, BeforeLe
     return this.dataChanged;
   }
 
-  public onToggleButtonClicked(): void {
-    this.showPropertyPanel = !this.showPropertyPanel;
+  get shotToolbar(): boolean {
+    return this.showEditToolbarButton;
   }
 
   /**
diff --git a/smp-angular/src/app/common/panels/document-versions-panel/document-versions-panel.component.html b/smp-angular/src/app/common/panels/document-versions-panel/document-versions-panel.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..888070643239c2374d704d1dae8e81343e0a52b0
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-versions-panel/document-versions-panel.component.html
@@ -0,0 +1,59 @@
+<div style="display:flex; flex-grow: 1; flex-direction: column">
+  <mat-form-field style="width: 100%">
+    <mat-label>{{ "document.versions.panel.label.filter" | translate }}</mat-label>
+    <input matInput (keyup)="applyFilter($event)" #input>
+  </mat-form-field>
+  <div id="versions-table-container">
+    <table #DocumentVersionsTable mat-table class="mat-elevation-z1"
+           style="max-height: 100%;flex-grow: 1;"
+           id="document-versions-table"
+           [dataSource]="versionDataSource" matSort>
+
+      <ng-container matColumnDef="version">
+        <th mat-header-cell *matHeaderCellDef
+            mat-sort-header>{{ "document.versions.panel.label.version" | translate }}
+        </th>
+        <td mat-cell *matCellDef="let row">{{ row.version }}</td>
+      </ng-container>
+      <ng-container matColumnDef="status">
+        <th mat-header-cell *matHeaderCellDef
+            mat-sort-header>{{ "document.versions.panel.label.status" | translate }}
+        </th>
+        <td mat-cell *matCellDef="let row">{{ row.versionStatus }}</td>
+      </ng-container>
+      <ng-container matColumnDef="createdOn">
+        <th mat-header-cell *matHeaderCellDef
+            mat-sort-header>{{ "document.versions.panel.label.created" | translate }}
+        </th>
+        <td mat-cell *matCellDef="let row;">
+          {{ row.createdOn | date: dateTimeFormat }}
+      </ng-container>
+
+      <ng-container matColumnDef="lastUpdatedOn">
+        <th mat-header-cell *matHeaderCellDef
+            mat-sort-header>{{ "document.versions.panel.label.updated" | translate }}
+        </th>
+        <td mat-cell *matCellDef="let row;">
+          {{ row.lastUpdatedOn | date: dateTimeFormat }}
+      </ng-container>
+
+      <tr mat-header-row
+          *matHeaderRowDef="displayedColumns; sticky:true"></tr>
+      <tr mat-row
+          *matRowDef="let odd = odd; let row; columns: displayedColumns;"
+          (dblclick)="onRowSelect(row)"
+          [ngClass]="getRowClass(row, odd)"
+      ></tr>
+      <tr class="mat-row" *matNoDataRow>
+        <td class="mat-cell"
+            colspan="2">{{ "document.versions.panel.label.no.properties.found" | translate }}
+        </td>
+      </tr>
+    </table>
+  </div>
+  <mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons
+                 [pageSize]="10" [length]="versionDataSource.data?.length"
+                 attr.aria-label="{{ 'document.versions.panel.label.select.page' | translate }}"></mat-paginator>
+
+</div>
+
diff --git a/smp-angular/src/app/common/panels/document-versions-panel/document-versions-panel.component.scss b/smp-angular/src/app/common/panels/document-versions-panel/document-versions-panel.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..8d66e8a15434e1df7016833fcfe5d3ec7e87a935
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-versions-panel/document-versions-panel.component.scss
@@ -0,0 +1,20 @@
+#document-version-panel {
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+  padding: 0 1em;
+  min-width: 600px;
+}
+
+#version-table-container {
+  position: relative;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  overflow-y: scroll;
+}
+
+#version-table-container table {
+  width: 100%;
+}
diff --git a/smp-angular/src/app/common/panels/document-versions-panel/document-versions-panel.component.ts b/smp-angular/src/app/common/panels/document-versions-panel/document-versions-panel.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..559fcba50d9c5d7ff6169b9d23b9e900f4c5aec4
--- /dev/null
+++ b/smp-angular/src/app/common/panels/document-versions-panel/document-versions-panel.component.ts
@@ -0,0 +1,178 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+import {
+  AfterViewInit,
+  Component,
+  EventEmitter,
+  forwardRef,
+  Input,
+  Output,
+  ViewChild,
+} from '@angular/core';
+import {MatTable, MatTableDataSource} from "@angular/material/table";
+import {
+  ControlContainer,
+  ControlValueAccessor,
+  FormControl,
+  FormControlDirective,
+  NG_VALUE_ACCESSOR
+} from "@angular/forms";
+import {MatSort} from "@angular/material/sort";
+import {MatDialog} from "@angular/material/dialog";
+import {MatPaginator} from "@angular/material/paginator";
+import {DocumentVersionRo} from "../../model/document-version-ro.model";
+import {
+  BeforeLeaveGuard
+} from "../../../window/sidenav/navigation-on-leave-guard";
+import {GlobalLookups} from "../../global-lookups";
+
+/**
+ * Component to display the properties of a document in a table. The properties can be edited and saved.
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+@Component({
+  selector: 'document-versions-panel',
+  templateUrl: './document-versions-panel.component.html',
+  styleUrls: ['./document-versions-panel.component.scss'],
+  providers: [
+    {
+      provide: NG_VALUE_ACCESSOR,
+      useExisting: forwardRef(() => DocumentVersionsPanelComponent),
+      multi: true
+    }
+  ]
+})
+export class DocumentVersionsPanelComponent implements AfterViewInit, BeforeLeaveGuard, ControlValueAccessor {
+  @Output() selectedVersionChange: EventEmitter<number> = new EventEmitter<number>();
+
+  displayedColumns: string[] = ['version', 'status', 'createdOn', 'lastUpdatedOn'];
+  private onChangeCallback: (_: any) => void = () => {
+  };
+  versionDataSource: MatTableDataSource<DocumentVersionRo> = new MatTableDataSource();
+  dataChanged: boolean = false;
+  selected: DocumentVersionRo;
+  _currentVersion: number;
+
+  @ViewChild("DocumentVersionsTable") table: MatTable<DocumentVersionRo>;
+  @ViewChild(MatPaginator) paginator: MatPaginator;
+  @ViewChild(MatSort) sort: MatSort;
+
+  constructor(
+    private globalLookups: GlobalLookups,
+    public dialog: MatDialog,
+    private controlContainer: ControlContainer) {
+  }
+
+  get dateTimeFormat(): string {
+    return this.globalLookups.getDateTimeFormat();
+  }
+
+  @Input() set selectedVersion(version: number) {
+    this._currentVersion = version;
+    // find selected version
+    this.updateSelectedVersion();
+  }
+
+  get selectedVersion(): number {
+    return this._currentVersion;
+  }
+
+  /**
+   * Private method to locate selected row for current version
+   * @private
+   */
+  private updateSelectedVersion(): void {
+
+    const selectedVersion :DocumentVersionRo = this.versionDataSource.data.find(v => v.version === this._currentVersion);
+    this.selected = selectedVersion;
+  }
+
+
+
+  /**
+   * Method to handle row selection
+   * @param row
+   */
+  onRowSelect(row: DocumentVersionRo) {
+    this.selected = row;
+    this.selectedVersionChange.emit(row.version);
+  }
+
+
+  @ViewChild(FormControlDirective, {static: true})
+  formControlDirective: FormControlDirective;
+  @Input()
+  formControl: FormControl;
+
+  @Input()
+  formControlName: string;  /* get hold of FormControl instance no matter formControl or    formControlName is given. If formControlName is given, then this.controlContainer.control is the parent FormGroup (or FormArray) instance. */
+  get control() {
+    return this.formControl || this.controlContainer.control.get(this.formControlName);
+  }
+
+  /**
+   * Implementation of the ControlValueAccessor method to  write value to the component.
+   * @param eventList
+   */
+  writeValue(eventList: DocumentVersionRo[]): void {
+    this.versionDataSource.data = !eventList?.length ? [] : [...eventList];
+    this.updateSelectedVersion();
+    this.dataChanged = false;
+  }
+
+  ngAfterViewInit() {
+    this.versionDataSource.paginator = this.paginator;
+    this.versionDataSource.sort = this.sort;
+  }
+
+  applyFilter(event: Event) {
+    const filterValue: string = (event.target as HTMLInputElement).value;
+    this.versionDataSource.filter = filterValue?.trim().toLowerCase();
+
+    if (this.versionDataSource.paginator) {
+      this.versionDataSource.paginator.firstPage();
+    }
+  }
+
+  isDirty(): boolean {
+    return this.dataChanged;
+  }
+
+  registerOnChange(fn: any): void {
+    this.onChangeCallback = fn;
+
+  }
+
+  registerOnTouched(fn: any): void {
+    // not implemented
+  }
+
+  setDisabledState(isDisabled: boolean): void {
+    // not implemented
+  }
+
+  getRowClass(row, oddRow: boolean) {
+    return {
+      'datatable-row-selected': row === this.selected,
+      'datatable-row-odd': oddRow
+    };
+  }
+
+}
diff --git a/smp-angular/src/app/common/panels/expandable-panel-component/expandable-item-component/expandable-item.component.html b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-item-component/expandable-item.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..16e7ffacebb0cefa0415bde66106d566a2bec681
--- /dev/null
+++ b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-item-component/expandable-item.component.html
@@ -0,0 +1,4 @@
+<div #root style="display: none;flex-direction: column">
+  <div class="expandable-item-title">{{title}}</div>
+  <ng-content></ng-content>
+</div>
diff --git a/smp-angular/src/app/common/panels/expandable-panel-component/expandable-item-component/expandable-item.component.scss b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-item-component/expandable-item.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..292fbbf6eba80e1abcbd1d504af8a19ff50fbbe0
--- /dev/null
+++ b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-item-component/expandable-item.component.scss
@@ -0,0 +1,10 @@
+.expandable-item-title {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-weight: bold;
+  padding: 2px 2px 2px 10px;
+  border-bottom: 1px solid #e0e0e0;
+  width: 100%;
+
+}
diff --git a/smp-angular/src/app/common/panels/expandable-panel-component/expandable-item-component/expandable-item.component.ts b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-item-component/expandable-item.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ab38db9e37fdbec975e79c843ec94bcbc7dc665f
--- /dev/null
+++ b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-item-component/expandable-item.component.ts
@@ -0,0 +1,50 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+import {Component, Input, ViewChild,} from '@angular/core';
+
+
+/**
+ * Component to display the properties of a document in a table. The properties can be edited and saved.
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+@Component({
+  selector: 'expandable-item',
+  templateUrl: './expandable-item.component.html',
+  styleUrls: ['./expandable-item.component.scss'],
+})
+export class ExpandableItemComponent {
+
+  @Input() title: string;
+  @Input() buttonLabel: string;
+  @Input() icon: string;
+  @Input() tooltip: string;
+
+  @ViewChild('root') expandableItem: any;
+
+  constructor() {
+
+  }
+
+  showItem(show: boolean) {
+    this.expandableItem.nativeElement.style.display = show ? 'block' : 'none';
+  }
+
+
+}
diff --git a/smp-angular/src/app/common/panels/expandable-panel-component/expandable-panel.component.html b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-panel.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..d73712f802c86499f80d393d5ae48514ba4a6340
--- /dev/null
+++ b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-panel.component.html
@@ -0,0 +1,25 @@
+<div style="display: flex;flex-direction: row;height: 100%">
+  <div *ngIf="expandPanel" id="expand-panel_id">
+    <ng-content></ng-content>
+  </div>
+  <!-- table Toolbar -->
+  <div style="width: 42px">
+    <button mat-mini-fab
+            attr.aria-label="{{ 'expandable.panel.label.expand.collapse' | translate }}"
+            matTooltip="{{ 'document.properties.panel.tooltip.expand.collapse' | translate }}"
+            (click)="onToggleExpandButtonClicked()">
+      <mat-icon *ngIf="expandPanel">chevron_left</mat-icon>
+      <mat-icon *ngIf="!expandPanel">chevron_right</mat-icon>
+    </button>
+    <tool-button-spacer [vertical]="false"></tool-button-spacer>
+    <button *ngFor="let item of expandableItems; let i = index;"
+            [class]="getButtonClass(i)"
+            (click)="selectItem(item, i)"
+            [matTooltip]="item.title"
+    >
+      <mat-icon *ngIf="item.icon">{{ item.icon }}</mat-icon>
+      <span *ngIf="showButtonLabel"
+            class="vertical-button-label">{{item.buttonLabel}}</span>
+    </button>
+  </div>
+</div>
diff --git a/smp-angular/src/app/common/panels/expandable-panel-component/expandable-panel.component.scss b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-panel.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..dcc099038e537562550909e7c49f647368335edf
--- /dev/null
+++ b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-panel.component.scss
@@ -0,0 +1,20 @@
+#expand-panel_id {
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+  padding: 0 1em;
+  min-width: 600px;
+}
+
+.vertical-button-label {
+  font-size: 0.8em;
+  min-height: 100px;
+  writing-mode: vertical-rl;
+}
+
+.button-deselected {
+  margin: 1px 3px !important;
+  background: transparent;
+  border-style: none;
+}
+
diff --git a/smp-angular/src/app/common/panels/expandable-panel-component/expandable-panel.component.ts b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-panel.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d91ada553e0fd249111c8bb55eb987fb4b1bdbd4
--- /dev/null
+++ b/smp-angular/src/app/common/panels/expandable-panel-component/expandable-panel.component.ts
@@ -0,0 +1,82 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+import {
+  AfterViewInit,
+  Component,
+  ContentChildren,
+  Input,
+  QueryList,
+} from '@angular/core';
+import {
+  ExpandableItemComponent
+} from "./expandable-item-component/expandable-item.component";
+
+/**
+ * Component to display the properties of a document in a table. The properties can be edited and saved.
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+@Component({
+  selector: 'expandable-panel',
+  templateUrl: './expandable-panel.component.html',
+  styleUrls: ['./expandable-panel.component.scss'],
+})
+export class ExpandablePanelComponent implements AfterViewInit {
+
+  @ContentChildren(ExpandableItemComponent) private _expandableItems: QueryList<ExpandableItemComponent>;
+  @Input() showButtonLabel: boolean = false;
+  expandPanel: boolean = true;
+  selectedIndex: number = 0;
+
+  constructor() {
+
+  }
+
+  ngAfterViewInit(): void {
+    this.updateShowItem()
+  }
+
+  get expandableItems(): ExpandableItemComponent[] {
+    return this._expandableItems?.toArray();
+  }
+
+  public onToggleExpandButtonClicked(): void {
+    this.expandPanel = !this.expandPanel;
+  }
+
+  selectItem(item: ExpandableItemComponent, index: number): void {
+    this.selectedIndex = index;
+    this.updateShowItem();
+  }
+
+  // show item at index and hide all others
+  updateShowItem(): void {
+    if (!this._expandableItems) {
+      return;
+    }
+    this._expandableItems.forEach((item: ExpandableItemComponent, i: number) => {
+      item.showItem(i === this.selectedIndex);
+    });
+  }
+
+  getButtonClass(index: number) {
+    return index === this.selectedIndex ? 'mat-raised-button' : 'mat-raised-button button-deselected';
+  }
+
+}
diff --git a/smp-angular/src/app/common/panels/membership-panel/membership-panel.component.html b/smp-angular/src/app/common/panels/membership-panel/membership-panel.component.html
index d4ed1406ec4deadcd229d3dedacbe946b1c3e0d3..42bebb727f9f8ba464221529dd083c709943ee1e 100644
--- a/smp-angular/src/app/common/panels/membership-panel/membership-panel.component.html
+++ b/smp-angular/src/app/common/panels/membership-panel/membership-panel.component.html
@@ -58,6 +58,11 @@
           <td mat-cell *matCellDef="let row">{{row.roleType}}</td>
         </ng-container>
 
+        <ng-container matColumnDef="hasPermissionToReview">
+          <th mat-header-cell *matHeaderCellDef>{{ "membership.panel.label.permission.review" | translate }}</th>
+          <td mat-cell *matCellDef="let row">{{row.hasPermissionReview}}</td>
+        </ng-container>
+
         <ng-container matColumnDef="memberOf">
           <th mat-header-cell *matHeaderCellDef>{{ "membership.panel.label.member.of" | translate }}</th>
           <td mat-cell *matCellDef="let row">{{row.memberOf}}</td>
diff --git a/smp-angular/src/app/common/panels/membership-panel/membership-panel.component.ts b/smp-angular/src/app/common/panels/membership-panel/membership-panel.component.ts
index e6673916de4f94a493d22b90cd119c2b7f3d9f5d..c3ea322dfeec2a4a5f1a4bb6f4c440c26d8a675c 100644
--- a/smp-angular/src/app/common/panels/membership-panel/membership-panel.component.ts
+++ b/smp-angular/src/app/common/panels/membership-panel/membership-panel.component.ts
@@ -30,12 +30,14 @@ export class MembershipPanelComponent implements BeforeLeaveGuard {
   pageSize: number = 10;
   @Input() membershipType: MemberTypeEnum = MemberTypeEnum.DOMAIN;
 
+
   private _domain: DomainRo;
   private _group: GroupRo;
   private _resource: ResourceRo;
 
 
-  displayedColumns: string[] = ['username', 'fullName', 'roleType', 'memberOf'];
+  _displayedColumns: string[] = ['username', 'fullName', 'roleType', 'memberOf'];
+
   data: MemberRo[] = [];
   selectedMember: MemberRo;
   filter: any = {};
@@ -77,6 +79,17 @@ export class MembershipPanelComponent implements BeforeLeaveGuard {
     return this.resultsLength;
   }
 
+  public get displayedColumns(): string[] {
+    switch (this.membershipType) {
+      case MemberTypeEnum.DOMAIN:
+        return  ['username', 'fullName', 'roleType'];
+      case MemberTypeEnum.GROUP:
+        return ['username', 'fullName', 'roleType'];
+      case MemberTypeEnum.RESOURCE:
+        return ['username', 'fullName', 'roleType', 'hasPermissionToReview'];
+    }
+  }
+
   @Input() set domain(value: DomainRo) {
     this._domain = value;
     if (!!value) {
diff --git a/smp-angular/src/app/common/panels/review-tasks-panel/review-document-panel/review-document-panel.component.html b/smp-angular/src/app/common/panels/review-tasks-panel/review-document-panel/review-document-panel.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..96e810bbeec61dbb082c1985fb76d5461c0c4473
--- /dev/null
+++ b/smp-angular/src/app/common/panels/review-tasks-panel/review-document-panel/review-document-panel.component.html
@@ -0,0 +1,3 @@
+<document-edit-panel #reviewDocumentEditor
+                     editorMode="REVIEW_EDITOR">
+</document-edit-panel>
diff --git a/smp-angular/src/app/common/panels/review-tasks-panel/review-document-panel/review-document-panel.component.scss b/smp-angular/src/app/common/panels/review-tasks-panel/review-document-panel/review-document-panel.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/smp-angular/src/app/common/panels/review-tasks-panel/review-document-panel/review-document-panel.component.ts b/smp-angular/src/app/common/panels/review-tasks-panel/review-document-panel/review-document-panel.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..40b21eee818a255b8d760b3b8326acba6db9ec78
--- /dev/null
+++ b/smp-angular/src/app/common/panels/review-tasks-panel/review-document-panel/review-document-panel.component.ts
@@ -0,0 +1,25 @@
+import {Component, ViewChild, ViewEncapsulation,} from '@angular/core';
+import {
+  BeforeLeaveGuard
+} from "../../../../window/sidenav/navigation-on-leave-guard";
+import {
+  DocumentEditPanelComponent
+} from "../../document-edit-panel/document-edit-panel.component";
+
+@Component({
+  templateUrl: './review-document-panel.component.html',
+  styleUrls: ['./review-document-panel.component.scss'],
+  encapsulation: ViewEncapsulation.None,
+})
+export class ReviewDocumentPanelComponent implements BeforeLeaveGuard {
+
+  @ViewChild('reviewDocumentEditor') documentEditor: DocumentEditPanelComponent;
+
+  constructor() {
+  }
+
+  isDirty(): boolean {
+    return false;
+  }
+
+}
diff --git a/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-controller.ts b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d1104a4adca67bb41ecaeb034d150db6bc3402f9
--- /dev/null
+++ b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-controller.ts
@@ -0,0 +1,55 @@
+import {
+  SearchTableController
+} from '../../search-table/search-table-controller';
+import {MatDialog, MatDialogRef} from '@angular/material/dialog';
+import {GlobalLookups} from "../../global-lookups";
+import {SearchTableEntity} from "../../search-table/search-table-entity.model";
+import {
+  ObjectPropertiesDialogComponent
+} from "../../dialogs/object-properties-dialog/object-properties-dialog.component";
+
+export class ReviewTasksController implements SearchTableController {
+
+  constructor(protected lookups: GlobalLookups, public dialog: MatDialog) {
+  }
+
+  validateDeleteOperation(rows: SearchTableEntity[]) {
+    return null;
+  }
+
+  newRow(): SearchTableEntity {
+    return null;
+  }
+
+  dataSaved() {
+
+  }
+
+  isRecordChanged(oldModel: any, newModel: any): boolean {
+    return false;
+  }
+
+  isRowExpanderDisabled(row: SearchTableEntity): boolean {
+    return true;
+  }
+
+  public showDetails(row: any): MatDialogRef<any> {
+    return this.dialog.open(ObjectPropertiesDialogComponent, {
+      data: {
+        title: "Review tasks details",
+        object: row,
+      }
+    });
+  }
+
+  public edit(row: any): MatDialogRef<any> {
+    return null;
+  }
+
+  public delete(row: any) {
+  }
+
+  newDialog(config): MatDialogRef<any> {
+    return null;
+  }
+}
diff --git a/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-panel.component.css b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-panel.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..df34c7d7f1a359fa3f6ca5e7c1eea2cb4fd74114
--- /dev/null
+++ b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-panel.component.css
@@ -0,0 +1,41 @@
+/* --- Select ---*/
+.mat-select{
+  padding:20px 0;
+}
+
+/* --- Button  ---*/
+.group-btn {
+  margin-top:20px;
+}
+
+#hiddenButtonId {
+  position: fixed;
+}
+
+::ng-deep .missingKey {
+  text-decoration: line-through !important;
+  font-weight: bold;
+  color:red;
+}
+
+::ng-deep .deleted  {
+  text-decoration: line-through !important;
+  font-weight: bold;
+}
+::ng-deep .table-row-new  {
+
+  color: darkgreen !important;
+  font-weight: bold;
+}
+::ng-deep .table-row-updated  {
+  font-weight: bold;
+}
+::ng-deep .table-row  {
+  font-weight: normal;
+}
+
+.truncate-text {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
diff --git a/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-panel.component.html b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-panel.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..2c4902dd3b7383616cec6b4b546ae50e38d006f7
--- /dev/null
+++ b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-panel.component.html
@@ -0,0 +1,21 @@
+<smp-search-table
+  #searchTable
+  [columnPicker]="columnPicker"
+  [url]="baseUrl"
+  [additionalToolButtons]="additionalToolButtons"
+  [searchTableController]="reviewTaskController"
+  [showSearchPanel]="false"
+  [filter]="filter"
+  [allowNewItems]="false"
+  [allowDeleteItems]="false"
+  [allowEditItems]="true"
+  [showActionButtons]="false"
+  (onRowDoubleClicked)="onRowDoubleClicked($event)"
+>
+  <ng-template #additionalToolButtons>
+    <span style="width: 2px">&nbsp;</span>
+  </ng-template>
+  <ng-template #dateTimeColumn let-value="value" ngx-datatable-cell-template>
+    <div class='truncate-text' title="{{value | date:dateTimeFormat}}">{{ value | date:dateTimeFormat }}</div>
+  </ng-template>
+</smp-search-table>
diff --git a/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-panel.component.ts b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-panel.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..492db70b46db4f313294502a7f2c40b4052094ca
--- /dev/null
+++ b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-panel.component.ts
@@ -0,0 +1,174 @@
+import {
+  AfterViewChecked,
+  AfterViewInit,
+  ChangeDetectorRef,
+  Component,
+  Input,
+  OnInit,
+  TemplateRef,
+  ViewChild
+} from '@angular/core';
+import {ColumnPicker} from '../../column-picker/column-picker.model';
+import {MatDialog} from '@angular/material/dialog';
+
+import {AlertMessageService} from '../../alert-message/alert-message.service';
+import {HttpClient} from '@angular/common/http';
+import {GlobalLookups} from "../../global-lookups";
+import {SearchTableComponent} from "../../search-table/search-table.component";
+import {SecurityService} from "../../../security/security.service";
+import {ReviewTasksController} from "./review-tasks-controller";
+import {lastValueFrom} from "rxjs";
+import {TranslateService} from "@ngx-translate/core";
+import {
+  NavigationNode,
+  NavigationService
+} from "../../../window/sidenav/navigation-model.service";
+import {
+  EditResourceService
+} from "../../../edit/edit-resources/edit-resource.service";
+import {
+  ReviewDocumentVersionRo
+} from "../../model/review-document-version-ro.model";
+
+/**
+ * This is a generic alert panel component for previewing alert list
+ */
+@Component({
+  selector: 'review-tasks-panel',
+  templateUrl: './review-tasks-panel.component.html',
+  styleUrls: ['./review-tasks-panel.component.css']
+})
+export class ReviewTasksPanelComponent implements OnInit, AfterViewInit, AfterViewChecked {
+
+  @ViewChild('rowMetadataAction') rowMetadataAction: TemplateRef<any>;
+  @ViewChild('rowActions') rowActions: TemplateRef<any>;
+  @ViewChild('searchTable') searchTable: SearchTableComponent;
+  @ViewChild('dateTimeColumn') dateTimeColumn: TemplateRef<any>;
+  @ViewChild('truncateText') truncateText: TemplateRef<any>;
+  @ViewChild('credentialType') credentialType: TemplateRef<any>;
+  @ViewChild('forUser') forUser: TemplateRef<any>;
+
+
+  @Input() baseUrl = null;
+  columnPicker: ColumnPicker = new ColumnPicker();
+  reviewTaskController: ReviewTasksController;
+  filter: any = {};
+  selected: any;
+
+  constructor(public securityService: SecurityService,
+              protected lookups: GlobalLookups,
+              protected http: HttpClient,
+              protected alertService: AlertMessageService,
+              private translateService: TranslateService,
+              public dialog: MatDialog,
+              private changeDetector: ChangeDetectorRef,
+              private navigationService: NavigationService,
+              private editResourceService: EditResourceService,) {
+  }
+
+  ngOnInit() {
+    this.reviewTaskController = new ReviewTasksController(this.lookups, this.dialog);
+  }
+
+  ngAfterViewChecked() {
+    this.changeDetector.detectChanges();
+  }
+
+  async initColumns() {
+    this.columnPicker.allColumns = [
+      {
+        name: 'Review date',
+        title: "Review date",
+        prop: 'lastUpdatedOn',
+        showInitially: true,
+        maxWidth: 200,
+        cellTemplate: this.dateTimeColumn,
+      },
+      {
+        name: await lastValueFrom(this.translateService.get("review.edit.panel.label.column.target")),
+        prop: 'target',
+        maxWidth: 160,
+        showInitially: true,
+      },
+      {
+        name: await lastValueFrom(this.translateService.get("review.edit.panel.label.column.version")),
+        prop: 'version',
+        maxWidth: 60,
+        showInitially: true,
+      },
+      {
+        name: await lastValueFrom(this.translateService.get("review.edit.panel.label.column.resource.scheme")),
+        prop: 'resourceIdentifierScheme',
+        width: 250,
+        maxWidth: 250,
+        resizable: 'true',
+        showInitially: true,
+      },
+      {
+        name: await lastValueFrom(this.translateService.get("review.edit.panel.label.column.resource.value")),
+        prop: 'resourceIdentifierValue',
+        resizable: 'true',
+        showInitially: true,
+      },
+      {
+        name: await lastValueFrom(this.translateService.get("review.edit.panel.label.column.subresource.scheme")),
+        prop: 'subresourceIdentifierScheme',
+        width: 250,
+        maxWidth: 250,
+        resizable: 'true',
+        showInitially: true,
+      },
+      {
+        name: await lastValueFrom(this.translateService.get("review.edit.panel.label.column.subresource.value")),
+        prop: 'subresourceIdentifierValue',
+        resizable: 'true',
+        showInitially: true,
+      },
+
+
+
+    ];
+    this.columnPicker.selectedColumns = this.columnPicker.allColumns.filter(col => col.showInitially);
+    this.searchTable.tableColumnInit();
+  }
+
+  ngAfterViewInit() {
+    this.initColumns();
+  }
+
+
+  details(row: any) {
+  }
+
+  // for dirty guard...
+  isDirty(): boolean {
+    return this.searchTable.isDirty();
+  }
+
+  get dateTimeFormat(): string {
+    return this.lookups.getDateTimeFormat();
+  }
+
+  async onRowDoubleClicked(row: ReviewDocumentVersionRo) {
+    // set selected resource
+    this.editResourceService.selectedReviewDocument = row;
+    let node: NavigationNode = await this.createNewReviewDocumentNavigationNode();
+    this.navigationService.selected.children = [node]
+    this.navigationService.select(node);
+
+  }
+
+  public async createNewReviewDocumentNavigationNode() {
+    return {
+      code: "review-document",
+      icon: "note",
+      name: await lastValueFrom(this.translateService.get("review.edit.panel.label.review")),
+      routerLink: "review-document",
+      selected: true,
+      tooltip: "",
+      transient: true,
+      i18n: "navigation.label.edit.document.review"
+    }
+  }
+}
+
diff --git a/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-ro.model.ts b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-ro.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..67af8e91fdfbc070bc7c89d0596d9debdc412f8a
--- /dev/null
+++ b/smp-angular/src/app/common/panels/review-tasks-panel/review-tasks-ro.model.ts
@@ -0,0 +1,14 @@
+import {SearchTableEntity} from '../../search-table/search-table-entity.model';
+
+export interface AlertRo extends SearchTableEntity {
+  sid: string;
+  alertType: string;
+  alertStatus: string;
+  alertStatusDesc?:string;
+  alertLevel: string;
+  processedTime?: Date;
+  reportingTime: Date;
+  mailTo?:string;
+  alertDetails?: Object;
+}
+
diff --git a/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.html b/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.html
index 8430ae6435f924e0ff2179326dbb1e5226d37516..5d9cec86d2a07718c0d696e4421934aab26a0ae4 100644
--- a/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.html
+++ b/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.html
@@ -64,6 +64,7 @@
                   (change)="onLocaleSelect($event.target.value)"
                   formControlName="smpLocale"
           >
+            <!-- currently supported locales  see the main.ts-->
             <option value="bg">{{ "user.profile.panel.label.language.bg" | translate }}</option>
             <option value="cs">{{ "user.profile.panel.label.language.cs" | translate }}</option>
             <option value="da">{{ "user.profile.panel.label.language.da" | translate }}</option>
diff --git a/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.ts b/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.ts
index 186d76bd9e4b4d566fd298b79d4ac09032d8f88e..19c2b6ad317f939f9b61d567365e22c49fe2fb56 100644
--- a/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.ts
+++ b/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.ts
@@ -1,5 +1,11 @@
-import {Component, ElementRef, EventEmitter, Input, Output, ViewChild,} from '@angular/core';
-import {SmpConstants} from "../../../smp.constants";
+import {
+  Component,
+  ElementRef,
+  EventEmitter,
+  Input,
+  Output,
+  ViewChild,
+} from '@angular/core';
 import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
 import {SecurityService} from "../../../security/security.service";
 import {ThemeService} from "../../theme-service/theme.service";
@@ -11,7 +17,6 @@ import {NgxMatDateAdapter} from "@angular-material-components/datetime-picker";
 import {ApplicationRoleEnum} from "../../enums/application-role.enum";
 import {UserRo} from "../../model/user-ro.model";
 import {UserController} from "../../services/user-controller";
-import {TranslateService} from "@ngx-translate/core";
 
 @Component({
   selector: 'user-profile-panel',
@@ -25,9 +30,6 @@ export class UserProfilePanelComponent {
   @Output() onChangeUserPasswordEvent: EventEmitter<UserRo> = new EventEmitter();
 
   readonly emailPattern = '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}';
-  readonly dateFormat: string = 'yyyy-MM-dd HH:mm:ssZ';
-  readonly dateTimeFormat: string = SmpConstants.DATE_TIME_FORMAT;
-  readonly nullValue: string = SmpConstants.NULL_VALUE;
 
   readonly applicationRoles = Object.keys(ApplicationRoleEnum).map(el => {
     return {key: el, value: ApplicationRoleEnum[el]}
@@ -62,7 +64,10 @@ export class UserProfilePanelComponent {
       'username': new FormControl({value: '', disabled: true}),
       'role': new FormControl({value: '', disabled: true}),
       'active': new FormControl({value: '', disabled: true}),
-      'emailAddress': new FormControl({value: '', disabled: false}, [Validators.pattern(this.emailPattern),
+      'emailAddress': new FormControl({
+        value: '',
+        disabled: false
+      }, [Validators.pattern(this.emailPattern),
         Validators.maxLength(255)]),
       'fullName': new FormControl({value: '', disabled: false}),
       'smpTheme': new FormControl({value: 'default_theme', disabled: false}),
@@ -73,7 +78,10 @@ export class UserProfilePanelComponent {
     this.userCredentialForm = formBuilder.group({
       'passwordUpdatedOn': new FormControl({value: '', disabled: true}),
       'passwordExpireOn': new FormControl({value: '', disabled: true}),
-      'sequentialLoginFailureCount': new FormControl({value: '0', disabled: true}),
+      'sequentialLoginFailureCount': new FormControl({
+        value: '0',
+        disabled: true
+      }),
       'lastFailedLoginAttempt': new FormControl({value: '', disabled: true}),
       'suspendedUtil': new FormControl({value: '', disabled: true}),
     });
@@ -131,7 +139,6 @@ export class UserProfilePanelComponent {
   }
 
 
-
   private updatePwdCredential(value: UserRo) {
     // form is always disabled
     this.userCredentialForm.disable()
@@ -144,7 +151,7 @@ export class UserProfilePanelComponent {
     } else {
       this.userCredentialForm.controls['passwordUpdatedOn'].setValue(value.passwordUpdatedOn);
       this.userCredentialForm.controls['passwordExpireOn'].setValue(value.passwordExpireOn);
-      this.userCredentialForm.controls['sequentialLoginFailureCount'].setValue(!(value.sequentialLoginFailureCount)?"---":value.sequentialLoginFailureCount);
+      this.userCredentialForm.controls['sequentialLoginFailureCount'].setValue(!(value.sequentialLoginFailureCount) ? "---" : value.sequentialLoginFailureCount);
       this.userCredentialForm.controls['lastFailedLoginAttempt'].setValue(value.lastFailedLoginAttempt);
       this.userCredentialForm.controls['suspendedUtil'].setValue(value.suspendedUtil);
     }
@@ -215,11 +222,11 @@ export class UserProfilePanelComponent {
     return !this._managedUserData?.userId;
   }
 
-  get canChangeRole ():boolean {
+  get canChangeRole(): boolean {
     return !this.isUserDataLoggedInUserData
   }
 
-  get isUserDataLoggedInUserData(){
+  get isUserDataLoggedInUserData() {
     return this.securityService.getCurrentUser()?.userId == this._managedUserData?.userId
   }
 
diff --git a/smp-angular/src/app/common/search-table/search-table-controller.ts b/smp-angular/src/app/common/search-table/search-table-controller.ts
index 4d0961431a36d92a64bd163a8559f64ee7f8e52c..01e6770778c65d078d8e167cf623068d58378107 100644
--- a/smp-angular/src/app/common/search-table/search-table-controller.ts
+++ b/smp-angular/src/app/common/search-table/search-table-controller.ts
@@ -25,4 +25,5 @@ export interface SearchTableController {
    * @param row the row for which the row expander should be disabled or not
    */
   isRowExpanderDisabled(row: SearchTableEntity): boolean;
+
 }
diff --git a/smp-angular/src/app/common/search-table/search-table.component.ts b/smp-angular/src/app/common/search-table/search-table.component.ts
index 758ee14828f07fa8fd261d26660499e8a08919e8..095324ad0f63b80acee062693ed537406dd280ba 100644
--- a/smp-angular/src/app/common/search-table/search-table.component.ts
+++ b/smp-angular/src/app/common/search-table/search-table.component.ts
@@ -1,4 +1,11 @@
-import {Component, Input, OnInit, TemplateRef, ViewChild} from '@angular/core';
+import {
+  Component, EventEmitter,
+  Input,
+  OnInit,
+  Output,
+  TemplateRef,
+  ViewChild
+} from '@angular/core';
 import {SearchTableResult} from './search-table-result.model';
 import {lastValueFrom, Observable} from 'rxjs';
 import {AlertMessageService} from '../alert-message/alert-message.service';
@@ -9,12 +16,20 @@ import {SearchTableController} from './search-table-controller';
 import {finalize} from 'rxjs/operators';
 import {SearchTableEntity} from './search-table-entity.model';
 import {EntityStatus} from '../enums/entity-status.enum';
-import {CancelDialogComponent} from '../dialogs/cancel-dialog/cancel-dialog.component';
-import {SaveDialogComponent} from '../dialogs/save-dialog/save-dialog.component';
+import {
+  CancelDialogComponent
+} from '../dialogs/cancel-dialog/cancel-dialog.component';
+import {
+  SaveDialogComponent
+} from '../dialogs/save-dialog/save-dialog.component';
 import {DownloadService} from '../../download/download.service';
 import {HttpParams} from '@angular/common/http';
-import {ConfirmationDialogComponent} from "../dialogs/confirmation-dialog/confirmation-dialog.component";
-import {SearchTableValidationResult} from "./search-table-validation-result.model";
+import {
+  ConfirmationDialogComponent
+} from "../dialogs/confirmation-dialog/confirmation-dialog.component";
+import {
+  SearchTableValidationResult
+} from "./search-table-validation-result.model";
 import {ExtendedHttpClient} from "../../http/extended-http-client";
 import {Router} from "@angular/router";
 import ObjectUtils from "../utils/object-utils";
@@ -26,6 +41,8 @@ import {TranslateService} from "@ngx-translate/core";
   styleUrls: ['./search-table.component.css']
 })
 export class SearchTableComponent implements OnInit {
+  @Output() onRowDoubleClicked: EventEmitter<SearchTableEntity> = new EventEmitter<SearchTableEntity>();
+
   @ViewChild('searchTable', {static: true}) searchTable: any;
   @ViewChild('rowActions', {static: true}) rowActions: TemplateRef<any>;
   @ViewChild('rowExpand', {static: true}) rowExpand: TemplateRef<any>;
@@ -71,18 +88,18 @@ export class SearchTableComponent implements OnInit {
   forceRefresh: boolean = false;
   showSpinner: boolean = false;
   currentResult: SearchTableResult = null;
- // override datatable messages to remove selectedMessage message
- datatableMessages: any =  {
-  // Message to show when array is presented
-  // but contains no values
-  emptyMessage: 'No data to display',
+  // override datatable messages to remove selectedMessage message
+  datatableMessages: any = {
+    // Message to show when array is presented
+    // but contains no values
+    emptyMessage: 'No data to display',
 
-  // Footer total message
-  totalMessage: 'total',
+    // Footer total message
+    totalMessage: 'total',
 
-  // Footer selected message
-  selectedMessage: null
-};
+    // Footer selected message
+    selectedMessage: null
+  };
 
   constructor(protected http: ExtendedHttpClient,
               protected alertService: AlertMessageService,
@@ -121,7 +138,7 @@ export class SearchTableComponent implements OnInit {
   }
 
 
-  tableColumnInit(){
+  tableColumnInit() {
     // Add actions to last column
     if (this.columnPicker) {
       // prepend columns
@@ -242,6 +259,7 @@ export class SearchTableComponent implements OnInit {
 
   onActivate(event) {
     if ("dblclick" === event.type) {
+      this.onRowDoubleClicked.emit(event.row);
       this.editSearchTableEntityRow(event.row);
     }
   }
@@ -256,7 +274,7 @@ export class SearchTableComponent implements OnInit {
 
 
   onNewButtonClicked() {
-        this.fireCreateNewEntityEvent();
+    this.fireCreateNewEntityEvent();
   }
 
   fireCreateNewEntityEvent() {
@@ -277,7 +295,7 @@ export class SearchTableComponent implements OnInit {
   }
 
   onDeleteButtonClicked() {
-        this.fireDeleteEntityEvent();
+    this.fireDeleteEntityEvent();
   }
 
   fireDeleteEntityEvent() {
@@ -285,11 +303,11 @@ export class SearchTableComponent implements OnInit {
   }
 
   onDeleteRowActionClicked(row: SearchTableEntity) {
-        this.deleteSearchTableEntities([row]);
+    this.deleteSearchTableEntities([row]);
   }
 
   onEditButtonClicked() {
-        this.fireEditEntityEvent();
+    this.fireEditEntityEvent();
   }
 
   fireEditEntityEvent() {
@@ -357,7 +375,8 @@ export class SearchTableComponent implements OnInit {
   getRowsAsString(): number {
     return this.rows.length;
   }
-  getCurrentResult(){
+
+  getCurrentResult() {
     return this.currentResult;
   }
 
@@ -366,7 +385,7 @@ export class SearchTableComponent implements OnInit {
   }
 
   get managementUrl(): string {
-    return (this.manageUrl == null || this.manageUrl.length === 0)? this.url:this.manageUrl;
+    return (this.manageUrl == null || this.manageUrl.length === 0) ? this.url : this.manageUrl;
   }
 
   get deleteButtonEnabled(): boolean {
@@ -402,7 +421,10 @@ export class SearchTableComponent implements OnInit {
           const status = ObjectUtils.isEqual(row.status, EntityStatus.PERSISTED)
             ? EntityStatus.UPDATED
             : row.status;
-          this.rows[rowNumber] = {...formRef.componentInstance.getCurrent(), status};
+          this.rows[rowNumber] = {
+            ...formRef.componentInstance.getCurrent(),
+            status
+          };
           this.rows = [...this.rows];
         }
       }
diff --git a/smp-angular/src/app/common/utils/string-utils.ts b/smp-angular/src/app/common/utils/string-utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..59b99a34d3c9566b59418d605571dc5b93b65215
--- /dev/null
+++ b/smp-angular/src/app/common/utils/string-utils.ts
@@ -0,0 +1,35 @@
+/**
+ * String utils
+ */
+export default class StringUtils {
+  /**
+   * Capitalize the first letter of the string
+   * @param value
+   * @returns {string} Capitalized string
+   */
+  static capitalizeFirst(value: string): string {
+    if (value === null) {
+      return 'Not assigned';
+    }
+    return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
+  }
+
+  /**
+   * Format string
+   * @param format - the format as example: 'Hello {1} {0}'
+   * @param parameters - the parameters as example: ['Janez', 'Novak']
+   * @returns {string} Formatted string as example: 'Hello Novak Janez'
+   */
+  static format(format: string, parameters: string[]): string {
+    if (parameters) {
+      format = format.replace(/\{([^}]+)}/g, function (match, key) {
+        return (parameters != null && key in parameters) ? parameters[key] : match;
+      });
+    }
+    return format;
+  }
+
+  static isEmpty(str): boolean {
+    return (!str || 0 === str.length);
+  }
+}
diff --git a/smp-angular/src/app/edit/edit-group/edit-group.service.ts b/smp-angular/src/app/edit/edit-group/edit-group.service.ts
index 82c952e3a3c99dbb7413af86e0cdce803d54a63a..325143cb43542456d2d782f4fdf1758643fa4e99 100644
--- a/smp-angular/src/app/edit/edit-group/edit-group.service.ts
+++ b/smp-angular/src/app/edit/edit-group/edit-group.service.ts
@@ -13,7 +13,6 @@ import {DomainRo} from "../../common/model/domain-ro.model";
 @Injectable()
 export class EditGroupService {
 
-
   constructor(
     private http: HttpClient,
     private securityService: SecurityService) {
@@ -79,14 +78,4 @@ export class EditGroupService {
       .replace(SmpConstants.PATH_PARAM_ENC_DOMAIN_ID, domain?.domainId)
       .replace(SmpConstants.PATH_PARAM_ENC_GROUP_ID, group?.groupId), resource);
   }
-
-  updateResourceForGroup(resource: ResourceRo, group: GroupRo, domain: DomainRo): Observable<ResourceRo> {
-    const currentUser: User = this.securityService.getCurrentUser();
-
-    return this.http.post<ResourceRo>(SmpConstants.REST_EDIT_RESOURCE_UPDATE
-      .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
-      .replace(SmpConstants.PATH_PARAM_ENC_DOMAIN_ID, domain?.domainId)
-      .replace(SmpConstants.PATH_PARAM_ENC_GROUP_ID, group?.groupId)
-      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId), resource);
-  }
 }
diff --git a/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.html b/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.html
index 38d4cfcc1fa9ffea3e52e3fa2d00e2f33edeb610..2cbf4607ea9242d3deda34245839cc057b8c9367 100644
--- a/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.html
+++ b/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.html
@@ -19,7 +19,7 @@
       <mat-label>{{ "resource.dialog.label.resource.id" | translate }}</mat-label>
       <input id="identifierValue_id" type="text" matInput #identifierValue
              formControlName="identifierValue"
-             required auto-focus-directive
+             auto-focus-directive
              maxlength="255" required>
       <div
         *ngIf="(newMode && resourceForm.controls['identifierValue'].touched ) &&  resourceForm.controls['identifierValue'].hasError('required')"
@@ -51,7 +51,11 @@
         {{participantSchemeMessage}}
       </div>
     </mat-form-field>
-
+    <mat-checkbox formControlName="reviewEnabled"
+                  matTooltip="{{ 'resource.dialog.label.resource.review.enabled' | translate }}"
+                  id="reviewEnabled_id">
+      {{ "resource.dialog.tooltip.resource.review.enabled" | translate }}
+    </mat-checkbox>
 
     <mat-form-field style="width:100%">
       <mat-label>{{ "resource.dialog.label.resource.visibility" | translate }}</mat-label>
diff --git a/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.ts b/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.ts
index 6ed953ced52e4dcce9b9e5bc81252007d3511ecc..1c4d4153fb15e16be026dcae266d7ec863075996 100644
--- a/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.ts
+++ b/smp-angular/src/app/edit/edit-group/group-resource-panel/resource-dialog/resource-dialog.component.ts
@@ -1,14 +1,21 @@
 import {Component, ElementRef, Inject, Input, ViewChild} from '@angular/core';
 import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
 import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
-import {AlertMessageService} from "../../../../common/alert-message/alert-message.service";
+import {
+  AlertMessageService
+} from "../../../../common/alert-message/alert-message.service";
 import {VisibilityEnum} from "../../../../common/enums/visibility.enum";
 import {GroupRo} from "../../../../common/model/group-ro.model";
 import {ResourceRo} from "../../../../common/model/resource-ro.model";
 import {DomainRo} from "../../../../common/model/domain-ro.model";
-import {ResourceDefinitionRo} from "../../../../system-settings/admin-extension/resource-definition-ro.model";
+import {
+  ResourceDefinitionRo
+} from "../../../../system-settings/admin-extension/resource-definition-ro.model";
 import {EditGroupService} from "../../edit-group.service";
 import {GlobalLookups} from "../../../../common/global-lookups";
+import {
+  EditResourceService
+} from "../../../edit-resources/edit-resource.service";
 
 
 @Component({
@@ -18,7 +25,7 @@ import {GlobalLookups} from "../../../../common/global-lookups";
 export class ResourceDialogComponent {
 
   readonly groupVisibilityOptions = Object.keys(VisibilityEnum)
-   .map(el => {
+    .map(el => {
       return {key: el, value: VisibilityEnum[el]}
     });
 
@@ -28,18 +35,20 @@ export class ResourceDialogComponent {
   messageType: string = "alert-error";
   group: GroupRo;
   _resource: ResourceRo
-  domain:DomainRo;
-  domainResourceDefs:ResourceDefinitionRo[];
+  domain: DomainRo;
+  domainResourceDefs: ResourceDefinitionRo[];
 
   participantSchemePattern = '^[a-z0-9]+-[a-z0-9]+-[a-z0-9]+$';
-  participantSchemeMessage:string;
-  submitInProgress:boolean = false;
+  participantSchemeMessage: string;
+  submitInProgress: boolean = false;
 
   @ViewChild('identifierValue', {static: false}) identifierValue: ElementRef;
+
   constructor(@Inject(MAT_DIALOG_DATA) public data: any,
               public lookups: GlobalLookups,
               public dialogRef: MatDialogRef<ResourceDialogComponent>,
               private editGroupService: EditGroupService,
+              private editResourceService: EditResourceService,
               private alertService: AlertMessageService,
               private formBuilder: FormBuilder
   ) {
@@ -56,9 +65,10 @@ export class ResourceDialogComponent {
 
 
     this.resourceForm = formBuilder.group({
-      'identifierValue': new FormControl({value: null}, ),
-      'identifierScheme': new FormControl({value: null},[Validators.pattern(this.participantSchemePattern)]),
+      'identifierValue': new FormControl({value: null},),
+      'identifierScheme': new FormControl({value: null}, [Validators.pattern(this.participantSchemePattern)]),
       'visibility': new FormControl({value: null}),
+      'reviewEnabled': new FormControl({value: null}),
       'resourceTypeIdentifier': new FormControl({value: null}),
       '': new FormControl({value: null})
     });
@@ -106,6 +116,7 @@ export class ResourceDialogComponent {
       }
 
       this.resourceForm.controls['visibility'].setValue(value.visibility);
+      this.resourceForm.controls['reviewEnabled'].setValue(value.reviewEnabled);
 
     } else {
       this.resourceForm.disable();
@@ -144,22 +155,8 @@ export class ResourceDialogComponent {
 
   public createResource(resource: ResourceRo) {
 
-        this.submitInProgress = true;
-        this.editGroupService.createResourceForGroup(this.resource, this.group, this.domain).subscribe((result: ResourceRo) => {
-          if (!!result) {
-            this.closeDialog();
-          }
-          this.submitInProgress = false;
-        }, (error) => {
-          this.alertService.error(error.error?.errorDescription)
-          this.submitInProgress = false;
-        });
-
-  }
-
-  public saveResource(resource: ResourceRo) {
     this.submitInProgress = true;
-    this.editGroupService.updateResourceForGroup(this.resource, this.group, this.domain).subscribe((result: ResourceRo) => {
+    this.editGroupService.createResourceForGroup(this.resource, this.group, this.domain).subscribe((result: ResourceRo) => {
       if (!!result) {
         this.closeDialog();
       }
@@ -168,6 +165,22 @@ export class ResourceDialogComponent {
       this.alertService.error(error.error?.errorDescription)
       this.submitInProgress = false;
     });
+
+  }
+
+  public saveResource(resource: ResourceRo): void {
+    this.submitInProgress = true;
+    this.editResourceService.updateResourceForGroup(resource, this.group, this.domain).subscribe({
+      next: (result: ResourceRo): void => {
+        if (!!result) {
+          this.closeDialog();
+        }
+        this.submitInProgress = false;
+      }, error: (err: any): void => {
+        this.alertService.error(err.error?.errorDescription)
+        this.submitInProgress = false;
+      }
+    });
   }
 
   public setFocus() {
diff --git a/smp-angular/src/app/edit/edit-resources/edit-resource.controller.ts b/smp-angular/src/app/edit/edit-resources/edit-resource.controller.ts
index a114b874c61b33b23561ac5c87f13079aa08f6fd..a9b699b438544fa229e9fdafc10800f173aed114 100644
--- a/smp-angular/src/app/edit/edit-resources/edit-resource.controller.ts
+++ b/smp-angular/src/app/edit/edit-resources/edit-resource.controller.ts
@@ -88,12 +88,12 @@ export class EditResourceController extends MatTableDataSource<ResourceRo>{
   refreshDomains() {
     this.isLoadingResults = true;
     this.domainService.getDomainsForResourceAdminUserObservable()
-      .subscribe((result: DomainRo[]) => {
+      .subscribe({next: (result: DomainRo[]) => {
         this.updateDomainList(result)
-      }, (error: any) => {
-        this.alertService.error(error.error?.errorDescription)
+      }, error: (err: any) => {
+        this.alertService.error(err.error?.errorDescription)
         this.isLoadingResults = false;
-      });
+      }});
   }
 
   refreshGroups() {
diff --git a/smp-angular/src/app/edit/edit-resources/edit-resource.service.ts b/smp-angular/src/app/edit/edit-resources/edit-resource.service.ts
index 3d83d8eb719811f5496b454a76b9901a2327e519..0076279d3ea0cb334737f24724628094e80fad9b 100644
--- a/smp-angular/src/app/edit/edit-resources/edit-resource.service.ts
+++ b/smp-angular/src/app/edit/edit-resources/edit-resource.service.ts
@@ -11,6 +11,9 @@ import {TableResult} from "../../common/model/table-result.model";
 import {DomainRo} from "../../common/model/domain-ro.model";
 import {DocumentRo} from "../../common/model/document-ro.model";
 import {SubresourceRo} from "../../common/model/subresource-ro.model";
+import {
+  ReviewDocumentVersionRo
+} from "../../common/model/review-document-version-ro.model";
 
 /**
  * The EditResourceService is used for server interaction on resources, sub-resources and it's documents.
@@ -23,6 +26,7 @@ export class EditResourceService {
 
   selectedResource: ResourceRo;
   selectedSubresource: SubresourceRo;
+  selectedReviewDocument: ReviewDocumentVersionRo;
 
   constructor(
     private http: HttpClient,
@@ -43,6 +47,22 @@ export class EditResourceService {
     return this.getGroupResourcesForUserTypeObservable('resource-admin', group, domain, filter, page, pageSize);
   }
 
+  /**
+   * Method allows group admin to update the resource properties
+   * @param resource
+   * @param group
+   * @param domain
+   */
+  updateResourceForGroup(resource: ResourceRo, group: GroupRo, domain: DomainRo): Observable<ResourceRo> {
+    const currentUser: User = this.securityService.getCurrentUser();
+
+    return this.http.post<ResourceRo>(SmpConstants.REST_EDIT_RESOURCE_UPDATE
+      .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
+      .replace(SmpConstants.PATH_PARAM_ENC_DOMAIN_ID, domain?.domainId)
+      .replace(SmpConstants.PATH_PARAM_ENC_GROUP_ID, group?.groupId)
+      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId), resource);
+  }
+
   /**
    * Method return observable of resource list from the server for resource-admin role for selected domain and group filter and paginating data.
    *
@@ -83,99 +103,220 @@ export class EditResourceService {
    * @param version version of document - if null current version is returned.
    * @returns observable of DocumentRo
    */
-  public getDocumentObservable(resource: ResourceRo, version: number = null): Observable<DocumentRo> {
+  public getResourceDocumentObservable(resource: ResourceRo, version: number = null): Observable<DocumentRo> {
     let params: HttpParams = null;
     if (version) {
       params = new HttpParams()
         .set('version', version);
     }
     const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.get<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT
+    return this.http.get<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_RESOURCE
       .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
       .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId), {params});
   }
 
   /**
-   * Method submits the document to be saved to server and return observable of the saved Document object from the server.
-   *
-   * @param resource resource for which document belongs to.
-   * @param document document to be saved.
+   * Method return observable of Document object for subresource from the server.
+   * @param subresource subresource for which document is returned.
+   * @param resource resource of the subresource.
+   * @param version version of document - if null current version is returned.
    * @returns observable of DocumentRo
    */
-  public saveDocumentObservable(resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+  public getSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, version: number = null): Observable<DocumentRo> {
+    let params: HttpParams = null;
+    if (version) {
+      params = new HttpParams()
+        .set('version', version);
+    }
     const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.put<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT
+    return this.http.get<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE
       .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
-      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId), document);
+      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId)
+      .replace(SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID, subresource?.subresourceId), {params});
   }
 
-  public validateDocumentObservable(resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+  /**
+   * Method returns observable for saving the document for resource to the server.
+   *
+   * @param resource resource for which document belongs to.
+   * @param document document to be saved.
+   * @returns observable of DocumentRo
+   */
+  public saveResourceDocumentObservable(resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
     const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.post<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_VALIDATE
+    return this.http.put<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_RESOURCE
       .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
       .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId), document);
   }
 
-  public generateDocumentObservable(resource: ResourceRo): Observable<DocumentRo> {
+  /**
+   * Method returns observable for saving the document for subresource to the server.
+   *
+   * @param subresource subresource for which document belongs to.
+   * @param resource parent resource of the subresource.
+   * @param document document to be saved.
+   * @returns observable of DocumentRo
+   */
+  public saveSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
     const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.post<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_GENERATE
+    return this.http.put<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE
       .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
-      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId), null);
+      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId)
+      .replace(SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID, subresource?.subresourceId), document);
   }
 
-  getSubResourcesForResource(resource: ResourceRo): Observable<SubresourceRo[]> {
+  /**
+   * Method returns observable for validating the document for resource on the server.
+   * @param resource resource for which document belongs to.
+   * @param document document to be validated.
+   * @returns document DocumentRo to be validated.
+   */
+  public validateResourceDocumentObservable(resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
     const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.get<SubresourceRo[]>(SmpConstants.REST_EDIT_SUBRESOURCE
+    return this.http.post<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_RESOURCE_VALIDATE
       .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
-      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId));
+      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId), document);
   }
 
-  deleteSubresourceFromResource(subResource: SubresourceRo, resource: ResourceRo): Observable<SubresourceRo> {
+  /**
+   * Method returns observable for validating the document for subresource on the server.
+   * @param subresource subresource for which document belongs to.
+   * @param resource parent resource of the subresource.
+   * @param document DocumentRo to be validated.
+   */
+  public validateSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
     const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.delete<ResourceRo>(SmpConstants.REST_EDIT_SUBRESOURCE_DELETE
+    return this.http.post<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE_VALIDATE
       .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
       .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId)
-      .replace(SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID, subResource?.subresourceId));
+      .replace(SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID, subresource?.subresourceId), document);
   }
 
-  createSubResourceForResource(subresource: SubresourceRo, resource: ResourceRo): Observable<SubresourceRo> {
-    const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.put<SubresourceRo>(SmpConstants.REST_EDIT_SUBRESOURCE_CREATE
-        .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
-        .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId),
-      subresource);
+  /**
+   * Method returns observable for publishing the document for resource on the server.
+   * @param resource resource for which document belongs to.
+   * @param document document to be published.
+   */
+  public publishResourceDocumentObservable(resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+    return this.resourceDocumentActionObservable(resource, document, SmpConstants.REST_EDIT_DOCUMENT_RESOURCE_PUBLISH);
   }
 
+  /**
+   * Method returns observable for publishing the document for subresource on the server.
+   * @param subresource subresource for which document belongs to.
+   * @param resource resource of the subresource.
+   * @param document document to be published.
+   */
+  public publishSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+    return this.subresourceDocumentActionObservable(subresource, resource, document, SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE_PUBLISH);
+  }
 
-  public getSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, version: number = null): Observable<DocumentRo> {
-    let params: HttpParams = null;
-    if (version) {
-      params = new HttpParams()
-        .set('version', version);
-    }
+  /**
+   * Method returns observable for review request of the document for resource on the server.
+   * @param resource resource for which document belongs to.
+   * @param document document to be reviewed.
+   */
+  public reviewRequestForResourceDocumentObservable(resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+    return this.resourceDocumentActionObservable(resource, document, SmpConstants.REST_EDIT_DOCUMENT_RESOURCE_REVIEW_REQUEST);
+  }
+
+  /**
+   * Method returns observable for review request of the document for subresource on the server.
+   * @param subresource subresource for which document belongs to.
+   * @param resource resource of the subresource.
+   * @param document document to be reviewed.
+   */
+  public reviewRequestForSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+    return this.subresourceDocumentActionObservable(subresource, resource, document, SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE_REVIEW_REQUEST);
+  }
+
+  /**
+   * Method returns observable for review approve of the document for resource on the server.
+   * @param resource resource for which document belongs to.
+   * @param document document to be approved.
+   */
+  public reviewApproveForResourceDocumentObservable(resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+    return this.resourceDocumentActionObservable(resource, document, SmpConstants.REST_EDIT_DOCUMENT_RESOURCE_REVIEW_APPROVE);
+  }
+
+  /**
+   * Method returns observable for review approve of the document for resource on the server.
+   * @param subresource subresource for which document belongs to.
+   * @param resource resource for which document belongs to.
+   * @param document document to be approved.
+   */
+  public reviewApproveForSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+    return this.subresourceDocumentActionObservable(subresource, resource, document, SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE_REVIEW_APPROVE);
+  }
+
+  /**
+   * Method returns observable for review reject of the document for resource on the server.
+   * @param resource resource for which document belongs to.
+   * @param document document to be rejected.
+   */
+  public reviewRejectResourceDocumentObservable(resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+    return this.resourceDocumentActionObservable(resource, document, SmpConstants.REST_EDIT_DOCUMENT_RESOURCE_REVIEW_REJECT);
+  }
+
+  /**
+   * Method returns observable for review reject of the document for resource on the server.
+   * @param resource resource for which document belongs to.
+   * @param document document to be rejected.
+   */
+  public reviewRejectSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+    return this.subresourceDocumentActionObservable(subresource, resource, document, SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE_REVIEW_REJECT);
+  }
+
+  /**
+   * 'Method returns http-post observable for document requests for given url template address. The template should have properties
+   * user-id and resource-id which are replaced with current user id and resource id. The document is sent as payload.
+   * @param resource  resource for which document belongs to.
+   * @param document document to be sent.
+   * @param reviewUrlTemplate url template for document action.
+   * @returns observable of DocumentRo
+   */
+  public resourceDocumentActionObservable(resource: ResourceRo, document: DocumentRo, reviewUrlTemplate: string): Observable<DocumentRo> {
     const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.get<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE
+    return this.http.post<DocumentRo>(reviewUrlTemplate
       .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
-      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId)
-      .replace(SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID, subresource?.subresourceId), {params});
+      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId), document);
   }
 
-  public saveSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+  /**
+   * 'Method returns http-post observable for document requests for given url template address. The template should have properties
+   * user-id, resource-id and subresource-id. which are replaced with current user id, resource id and subresource-id. The document is sent as payload.
+   * @param subresource  subresource for which document belongs to.
+   * @param resource  resource for which document belongs to.
+   * @param document document to be sent.
+   * @param reviewUrlTemplate url template for document action.
+   * @returns observable of DocumentRo
+   */
+  public subresourceDocumentActionObservable(subresource: SubresourceRo, resource: ResourceRo, document: DocumentRo, reviewUrlTemplate: string): Observable<DocumentRo> {
     const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.put<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE
+    return this.http.post<DocumentRo>(reviewUrlTemplate
       .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
       .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId)
       .replace(SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID, subresource?.subresourceId), document);
   }
 
-  public validateSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo, document: DocumentRo): Observable<DocumentRo> {
+  /**
+   * 'Method returns http-post observable to generate of new payload for resource document.
+   * @param resource  resource for which document belongs to.
+   * @returns observable of DocumentRo
+   */
+  public generateResourceDocumentObservable(resource: ResourceRo): Observable<DocumentRo> {
     const currentUser: User = this.securityService.getCurrentUser();
-    return this.http.post<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE_VALIDATE
+    return this.http.post<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_RESOURCE_GENERATE
       .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
-      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId)
-      .replace(SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID, subresource?.subresourceId), document);
+      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId), null);
   }
 
+  /**
+   * 'Method returns http-post observable to generate of new payload for subresource document.
+   * @param subresource  subresource for which document belongs to.
+   * @param resource  resource for which document belongs to.
+   * @returns observable of DocumentRo
+   */
   public generateSubresourceDocumentObservable(subresource: SubresourceRo, resource: ResourceRo): Observable<DocumentRo> {
     const currentUser: User = this.securityService.getCurrentUser();
     return this.http.post<DocumentRo>(SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE_GENERATE
@@ -183,4 +324,27 @@ export class EditResourceService {
       .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId)
       .replace(SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID, subresource?.subresourceId), null);
   }
+
+  getSubResourcesForResource(resource: ResourceRo): Observable<SubresourceRo[]> {
+    const currentUser: User = this.securityService.getCurrentUser();
+    return this.http.get<SubresourceRo[]>(SmpConstants.REST_EDIT_SUBRESOURCE
+      .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
+      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId));
+  }
+
+  deleteSubresourceFromResource(subResource: SubresourceRo, resource: ResourceRo): Observable<SubresourceRo> {
+    const currentUser: User = this.securityService.getCurrentUser();
+    return this.http.delete<ResourceRo>(SmpConstants.REST_EDIT_SUBRESOURCE_DELETE
+      .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
+      .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId)
+      .replace(SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID, subResource?.subresourceId));
+  }
+
+  createSubResourceForResource(subresource: SubresourceRo, resource: ResourceRo): Observable<SubresourceRo> {
+    const currentUser: User = this.securityService.getCurrentUser();
+    return this.http.put<SubresourceRo>(SmpConstants.REST_EDIT_SUBRESOURCE_CREATE
+        .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)
+        .replace(SmpConstants.PATH_PARAM_ENC_RESOURCE_ID, resource?.resourceId),
+      subresource);
+  }
 }
diff --git a/smp-angular/src/app/edit/edit-resources/resource-details-panel/resource-details-panel.component.html b/smp-angular/src/app/edit/edit-resources/resource-details-panel/resource-details-panel.component.html
index a20246c05a800c9a3393e9e090f23736328de828..fe07b3bbaf6f3cd3c246f2637f779d53c0c98419 100644
--- a/smp-angular/src/app/edit/edit-resources/resource-details-panel/resource-details-panel.component.html
+++ b/smp-angular/src/app/edit/edit-resources/resource-details-panel/resource-details-panel.component.html
@@ -27,31 +27,49 @@
         </option>
       </select>
     </mat-form-field>
-    <mat-form-field  style="width: 100%">
+    <mat-form-field style="width: 100%">
       <mat-label>{{ "resource.details.panel.label.resource.id" | translate }}</mat-label>
       <input id="identifierValue_id" type="text" matInput #identifierValue
              formControlName="identifierValue"
              required auto-focus-directive>
     </mat-form-field>
 
-    <mat-form-field  style="width: 100%">
+    <mat-form-field style="width: 100%">
       <mat-label>{{ "resource.details.panel.label.resource.scheme" | translate }}</mat-label>
       <input id="identifierScheme_id" type="text" matInput
              formControlName="identifierScheme"
       >
     </mat-form-field>
+    <mat-checkbox formControlName="reviewEnabled"
+                  matTooltip="{{ 'resource.details.panel.label.resource.review.enabled' | translate }}"
+                  id="reviewEnabled_id">
+      {{ "resource.details.panel.tooltip.resource.review.enabled" | translate }}
+    </mat-checkbox>
     <mat-form-field style="width:100%">
       <mat-label>{{ "resource.details.panel.label.resource.visibility" | translate }}</mat-label>
       <select matNativeControl formControlName="visibility"
-                  matTooltip="{{ 'resource.details.panel.tooltip.resource.visibility' | translate }}"
-                  id="visibility_id" required>
+              matTooltip="{{ 'resource.details.panel.tooltip.resource.visibility' | translate }}"
+              id="visibility_id" required>
         <option *ngFor="let visibility of groupVisibilityOptions"
-                    [value]="visibility.value">
-          {{visibility.key}}
+                [value]="visibility.value">
+          {{ visibility.key }}
         </option>
       </select>
     </mat-form-field>
-
+    <mat-toolbar class ="mat-elevation-z2">
+      <mat-toolbar-row  class="smp-toolbar-row">
+        <button id="cancelButton" mat-raised-button (click)="onResetButtonClicked()" color="primary"
+                [disabled]="!resetButtonEnabled">
+          <mat-icon>refresh</mat-icon>
+          <span>{{ "resource.details.panel.button.reset" | translate }}</span>
+        </button>
+        <button id="saveButton" mat-raised-button (click)="onSaveButtonClicked()" color="primary"
+                [disabled]="!submitButtonEnabled">
+          <mat-icon>save</mat-icon>
+          <span>{{ "resource.details.panel.button.save" | translate }}</span>
+        </button>
+      </mat-toolbar-row>
+    </mat-toolbar>
     <smp-warning-panel
       type="desc"
       icon="info"
diff --git a/smp-angular/src/app/edit/edit-resources/resource-details-panel/resource-details-panel.component.ts b/smp-angular/src/app/edit/edit-resources/resource-details-panel/resource-details-panel.component.ts
index 4ed1a074b5825238788660a2faa3c77e796d2e4f..0329de97e2d1b94e2fd4e178365c61d26f24ecee 100644
--- a/smp-angular/src/app/edit/edit-resources/resource-details-panel/resource-details-panel.component.ts
+++ b/smp-angular/src/app/edit/edit-resources/resource-details-panel/resource-details-panel.component.ts
@@ -1,17 +1,30 @@
 import {Component, Input,} from '@angular/core';
 import {MatDialog} from "@angular/material/dialog";
-import {BeforeLeaveGuard} from "../../../window/sidenav/navigation-on-leave-guard";
+import {
+  BeforeLeaveGuard
+} from "../../../window/sidenav/navigation-on-leave-guard";
 import {GroupRo} from "../../../common/model/group-ro.model";
 import {ResourceRo} from "../../../common/model/resource-ro.model";
-import {AlertMessageService} from "../../../common/alert-message/alert-message.service";
+import {
+  AlertMessageService
+} from "../../../common/alert-message/alert-message.service";
 import {DomainRo} from "../../../common/model/domain-ro.model";
-import {ResourceDefinitionRo} from "../../../system-settings/admin-extension/resource-definition-ro.model";
+import {
+  ResourceDefinitionRo
+} from "../../../system-settings/admin-extension/resource-definition-ro.model";
 import {EditResourceService} from "../edit-resource.service";
 import {FormBuilder, FormControl, FormGroup} from "@angular/forms";
 import {VisibilityEnum} from "../../../common/enums/visibility.enum";
-import {NavigationNode, NavigationService} from "../../../window/sidenav/navigation-model.service";
+import {
+  NavigationNode,
+  NavigationService
+} from "../../../window/sidenav/navigation-model.service";
 import {TranslateService} from "@ngx-translate/core";
 import {lastValueFrom} from "rxjs";
+import {
+  WindowSpinnerService
+} from "../../../common/services/window-spinner.service";
+import {EditResourceController} from "../edit-resource.controller";
 
 
 @Component({
@@ -37,8 +50,10 @@ export class ResourceDetailsPanelComponent implements BeforeLeaveGuard {
 
 
   constructor(private editResourceService: EditResourceService,
+              private editResourceController: EditResourceController,
               private navigationService: NavigationService,
               private alertService: AlertMessageService,
+              private windowSpinnerService: WindowSpinnerService,
               private dialog: MatDialog,
               private formBuilder: FormBuilder,
               private translateService: TranslateService) {
@@ -47,6 +62,7 @@ export class ResourceDetailsPanelComponent implements BeforeLeaveGuard {
       'identifierScheme': new FormControl({value: null}),
       'visibility': new FormControl({value: null}),
       'resourceTypeIdentifier': new FormControl({value: null}),
+      'reviewEnabled': new FormControl({value: null}),
       '': new FormControl({value: null})
     });
     this.translateService.get("resource.details.panel.title").subscribe(value => this.title = value);
@@ -59,6 +75,7 @@ export class ResourceDetailsPanelComponent implements BeforeLeaveGuard {
     resource.identifierValue = this.resourceForm.get('identifierValue').value;
     resource.resourceTypeIdentifier = this.resourceForm.get('resourceTypeIdentifier').value;
     resource.visibility = this.resourceForm.get('visibility').value;
+    resource.reviewEnabled = this.resourceForm.get('reviewEnabled').value;
     return resource;
   }
 
@@ -70,12 +87,17 @@ export class ResourceDetailsPanelComponent implements BeforeLeaveGuard {
       this.resourceForm.controls['identifierScheme'].setValue(value.identifierScheme);
       this.resourceForm.controls['resourceTypeIdentifier'].setValue(value.resourceTypeIdentifier);
       this.resourceForm.controls['visibility'].setValue(value.visibility);
+      this.resourceForm.controls['reviewEnabled'].setValue(value.reviewEnabled);
+      // only allow visibility and reviewEnabled changes for group-admin and resource-admin
+      this.resourceForm.controls['visibility'].enable();
+      this.resourceForm.controls['reviewEnabled'].enable();
 
     } else {
       this.resourceForm.controls['identifierValue'].setValue("");
       this.resourceForm.controls['identifierScheme'].setValue("");
       this.resourceForm.controls['resourceTypeIdentifier'].setValue("");
       this.resourceForm.controls['visibility'].setValue("");
+      this.resourceForm.controls['reviewEnabled'].setValue(false);
     }
     (async () => await this.updateVisibilityDescription())();
     this.resourceForm.markAsPristine();
@@ -85,12 +107,12 @@ export class ResourceDetailsPanelComponent implements BeforeLeaveGuard {
     // set selected resource
     this.editResourceService.selectedResource = this.resource;
 
-    let node: NavigationNode = await this.createNew();
+    let node: NavigationNode = await this.createNewDocumentNavigationNode();
     this.navigationService.selected.children = [node]
     this.navigationService.select(node);
   }
 
-  public async createNew() {
+  public async createNewDocumentNavigationNode() {
     return {
       code: "resource-document",
       icon: "note",
@@ -110,12 +132,46 @@ export class ResourceDetailsPanelComponent implements BeforeLeaveGuard {
   async updateVisibilityDescription() {
     if (this.resourceForm.get('visibility').value == VisibilityEnum.Private) {
       this.visibilityDescription = await lastValueFrom(this.translateService.get("resource.details.panel.label.resource.visibility.private"));
-    } else if (this.group.visibility == VisibilityEnum.Private) {
+    } else if (this.group?.visibility == VisibilityEnum.Private) {
       this.visibilityDescription = await lastValueFrom(this.translateService.get("resource.details.panel.label.resource.visibility.private.group"));
-    } else if (this.domain.visibility == VisibilityEnum.Private) {
+    } else if (this.domain?.visibility == VisibilityEnum.Private) {
       this.visibilityDescription = await lastValueFrom(this.translateService.get("resource.details.panel.label.resource.visibility.private.domain"));
     } else {
       this.visibilityDescription = await lastValueFrom(this.translateService.get("resource.details.panel.label.resource.visibility.public"));
     }
   }
+
+  get submitButtonEnabled(): boolean {
+    return this.resourceForm.valid && this.resourceForm.dirty;
+  }
+
+  get resetButtonEnabled(): boolean {
+    return this.resourceForm.dirty;
+  }
+
+  public onSaveButtonClicked(): void {
+    this.windowSpinnerService.showSpinner = true;
+    let updatedResource: ResourceRo = this.resource;
+    this.editResourceService.updateResourceForGroup(updatedResource, this.group, this.domain).subscribe({
+      next: (result: ResourceRo): void => {
+        try {
+          if (!!result) {
+            this.alertService.successForTranslation("resource.details.panel.alert.resource.saved");
+            this.editResourceController.selectedResource = result;
+            this._resource = result;
+            this.resourceForm.markAsPristine();
+          }
+        } finally {
+          this.windowSpinnerService.showSpinner = false;
+        }
+      }, error: (err: any): void => {
+        this.alertService.error(err.error?.errorDescription)
+        this.windowSpinnerService.showSpinner = false;
+      }
+    });
+  }
+
+  public onResetButtonClicked() {
+    this.resourceForm.reset(this._resource);
+  }
 }
diff --git a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html
index d63c1ba96d581cea00734bb2a2d5c1faec74c0ac..0cbbc33f12568a01aee20c8079e6e5b8d790e54e 100644
--- a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html
+++ b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html
@@ -1,109 +1,3 @@
-<div id="resource-document-panel">
-
-  <mat-toolbar class="mat-elevation-z2" style="min-height: 50px !important;">
-    <mat-toolbar-row class="smp-toolbar-row"
-                     style="justify-content: space-between;min-height: 50px !important;">
-
-      <button id="validateResource_id" mat-raised-button
-              color="primary"
-              matTooltip="{{ 'resource.document.panel.tooltip.validate' | translate }}"
-              [disabled]="emptyDocument"
-              (click)="onDocumentValidateButtonClicked()"
-      >
-        <mat-icon>check_circle</mat-icon>
-        <span>{{ "resource.document.panel.button.validate" | translate }}</span>
-      </button>
-      <button id="GenerateResource_id" mat-raised-button
-              color="primary"
-              matTooltip="{{ 'resource.document.panel.tooltip.generate' | translate }}"
-
-              (click)="onGenerateButtonClicked()"
-      >
-        <mat-icon>add_circle</mat-icon>
-        <span>{{ "resource.document.panel.button.generate" | translate }}</span>
-      </button>
-      <button id="documentWizard_id" mat-raised-button
-              color="primary"
-              matTooltip="{{ 'resource.document.panel.tooltip.document.wizard' | translate }}"
-              *ngIf="showWizardDialog"
-              (click)="onShowDocumentWizardDialog()"
-      >
-        <mat-icon>code_block</mat-icon>
-        <span>{{ "resource.document.panel.button.document.wizard" | translate }}</span>
-      </button>
-      <span style="flex: 1 1 auto;"></span>
-
-      <div [formGroup]="documentForm"
-           style="float: right;  vertical-align:middle;display: flex;align-items: center;justify-content: center; gap:0.4em;padding-right: 10px">
-        <span style="font-size: 0.8em">{{ "resource.document.panel.label.show.version" | translate }}</span>
-        <select matNativeControl
-                style="width: 100px; border-bottom: grey solid 1px"
-                placeholder="{{ 'resource.document.panel.placeholder.version' | translate }}"
-                matTooltip="{{ 'resource.document.panel.tooltip.version' | translate }}"
-                formControlName="payloadVersion"
-                id="document version_id"
-                (change)="onSelectionDocumentVersionChanged()"
-        >
-          <option *ngFor="let version of getDocumentVersions"
-                  [value]="version"
-          >
-            {{ version }}
-          </option>
-        </select>
-
-        <span style="font-size: 0.8em">{{ "resource.document.panel.label.created.on" | translate }}</span>
-        <input id="payloadCreatedOn_id"
-               matInput [ngxMatDatetimePicker]="payloadCreatedOnPicker"
-               formControlName="payloadCreatedOn"
-               readonly>
-        <mat-datepicker-toggle matSuffix [for]="payloadCreatedOnPicker"
-                               style="visibility: hidden"></mat-datepicker-toggle>
-        <ngx-mat-datetime-picker #payloadCreatedOnPicker [showSpinners]="true"
-                                 [showSeconds]="false"
-                                 [hideTime]="false"></ngx-mat-datetime-picker>
-
-      </div>
-    </mat-toolbar-row>
-  </mat-toolbar>
-
-  <div class="panel" [formGroup]="documentForm"
-       style="display: flex;flex-direction: row;flex-grow: 1; ">
-
-    <div
-      style="display:block; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;border: ridge 3px #b0bec5"
-      (click)="onEditPanelClick()"
-    >
-      <smp-editor #smpDocumentEditor formControlName="payload"
-                  ngDefaultControl></smp-editor>
-    </div>
-    <document-properties-panel style="min-height: 0; overflow: auto"
-                               formControlName="properties"
-                               ngDefaultControl></document-properties-panel>
-  </div>
-
-  <mat-toolbar class="mat-elevation-z2"
-               style="flex-grow: 0;">
-    <mat-toolbar-row class="smp-toolbar-row">
-      <button id="back_id" mat-raised-button color="primary"
-              (click)="onBackButtonClicked()">
-        <mat-icon>arrow_circle_left</mat-icon>
-        <span>{{ "resource.document.panel.button.back" | translate }}</span>
-      </button>
-      <button id="cancel_id" mat-raised-button color="primary"
-              [disabled]="cancelButtonDisabled"
-              (click)="onDocumentResetButtonClicked()">
-        <mat-icon>cancel</mat-icon>
-        <span>{{ "resource.document.panel.button.cancel" | translate }}</span>
-      </button>
-      <button id="saveResource_id" mat-raised-button
-              color="primary"
-              matTooltip="{{ 'resource.document.panel.tooltip.save' | translate }}"
-              [disabled]="saveButtonDisabled"
-              (click)="onSaveButtonClicked()"
-      >
-        <mat-icon>save</mat-icon>
-        <span>{{ "resource.document.panel.button.save" | translate }}</span>
-      </button>
-    </mat-toolbar-row>
-  </mat-toolbar>
-</div>
+<document-edit-panel #documentEditor
+                     editorMode="RESOURCE_EDITOR">
+</document-edit-panel>
diff --git a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.scss b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.scss
index 629dfc616c3572d572df60f2224542a1870ebfa5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.scss
+++ b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.scss
@@ -1,10 +0,0 @@
-#resource-document-panel {
-  display: flex;
-  height: 100%;
-  flex-direction: column;
-
-}
-
-.CodeMirror {
-  height: auto;
-}
diff --git a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts
index 49ee1748702995f381a54b7204be0f5aaa5ea4ed..cece275aa7924ee5122c96cc6ddc023edbb0b9ee 100644
--- a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts
+++ b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts
@@ -1,35 +1,10 @@
-import {Component, Input, ViewChild, ViewEncapsulation,} from '@angular/core';
-import {MatDialog, MatDialogRef} from "@angular/material/dialog";
+import {Component, ViewChild, ViewEncapsulation,} from '@angular/core';
 import {
   BeforeLeaveGuard
 } from "../../../window/sidenav/navigation-on-leave-guard";
-import {GroupRo} from "../../../common/model/group-ro.model";
-import {ResourceRo} from "../../../common/model/resource-ro.model";
 import {
-  AlertMessageService
-} from "../../../common/alert-message/alert-message.service";
-import {DomainRo} from "../../../common/model/domain-ro.model";
-import {
-  ResourceDefinitionRo
-} from "../../../system-settings/admin-extension/resource-definition-ro.model";
-import {EditResourceService} from "../edit-resource.service";
-import {FormBuilder, FormControl, FormGroup} from "@angular/forms";
-import {DocumentRo} from "../../../common/model/document-ro.model";
-import {
-  NavigationService
-} from "../../../window/sidenav/navigation-model.service";
-import {
-  DocumentWizardDialogComponent
-} from "../document-wizard-dialog/document-wizard-dialog.component";
-import {
-  ConfirmationDialogComponent
-} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component";
-import {
-  SmpEditorComponent
-} from "../../../common/components/smp-editor/smp-editor.component";
-import {EntityStatus} from "../../../common/enums/entity-status.enum";
-import {TranslateService} from "@ngx-translate/core";
-import {lastValueFrom} from "rxjs";
+  DocumentEditPanelComponent
+} from "../../../common/panels/document-edit-panel/document-edit-panel.component";
 
 @Component({
   templateUrl: './resource-document-panel.component.html',
@@ -37,255 +12,15 @@ import {lastValueFrom} from "rxjs";
   encapsulation: ViewEncapsulation.None,
 })
 export class ResourceDocumentPanelComponent implements BeforeLeaveGuard {
-  private _resource: ResourceRo;
-
-  _document: DocumentRo;
-  @Input() private group: GroupRo;
-  @Input() domain: DomainRo;
-  @Input() domainResourceDefs: ResourceDefinitionRo[];
-
-  @ViewChild("smpDocumentEditor") documentEditor: SmpEditorComponent;
-
-
-  resourceForm: FormGroup;
-  documentForm: FormGroup;
-
-  constructor(private editResourceService: EditResourceService,
-              private alertService: AlertMessageService,
-              private dialog: MatDialog,
-              private navigationService: NavigationService,
-              private formBuilder: FormBuilder,
-              private translateService: TranslateService) {
-    this.resourceForm = formBuilder.group({
-      'identifierValue': new FormControl({value: null}),
-      'identifierScheme': new FormControl({value: null}),
-      'visibility': new FormControl({value: null}),
-      'resourceTypeIdentifier': new FormControl({value: null}),
-    });
-    this.documentForm = formBuilder.group({
-      'mimeType': new FormControl({value: null}),
-      'name': new FormControl({value: null}),
-      'currentResourceVersion': new FormControl({value: null}),
-      'payloadCreatedOn': new FormControl({value: null}),
-      'payloadVersion': new FormControl({value: null}),
-      'payload': new FormControl({value: null}),
-      'properties': new FormControl({value: null}),
-    });
-    this.resource = editResourceService.selectedResource
-
-    this.documentForm.controls['payload'].setValue("")
-  }
-
-  get resource(): ResourceRo {
-    let resource = {...this._resource};
-    resource.identifierScheme = this.resourceForm.get('identifierScheme').value;
-    resource.identifierValue = this.resourceForm.get('identifierValue').value;
-    resource.resourceTypeIdentifier = this.resourceForm.get('resourceTypeIdentifier').value;
-    resource.visibility = this.resourceForm.get('visibility').value;
-    return resource;
-  }
-
-  @Input() set resource(value: ResourceRo) {
-    this._resource = value;
-    if (!this._resource) {
-      this.navigationService.navigateToHome();
-      return;
-    }
-
-    this.resourceForm.enable();
-    this.resourceForm.controls['identifierValue'].setValue(value.identifierValue);
-    this.resourceForm.controls['identifierScheme'].setValue(value.identifierScheme);
-    this.resourceForm.controls['resourceTypeIdentifier'].setValue(value.resourceTypeIdentifier);
-    this.resourceForm.controls['visibility'].setValue(value.visibility);
-    // control disable enable did not work??
-
-    this.resourceForm.controls['identifierValue'].disable();
-    this.resourceForm.controls['identifierScheme'].disable();
-    this.resourceForm.controls['resourceTypeIdentifier'].disable();
-    this.resourceForm.controls['visibility'].disable();
-    this.resourceForm.markAsPristine();
-    // load current document for the resource
-    this.loadDocumentForVersion();
-  }
-
-  @Input() set document(value: DocumentRo) {
-    this._document = value;
-    this.documentForm.disable();
-    if (!!value) {
-      this.documentEditor.mimeType = value.mimeType;
-      this.documentForm.controls['mimeType'].setValue(value.mimeType);
-      this.documentForm.controls['name'].setValue(value.name);
-      this.documentForm.controls['currentResourceVersion'].setValue(value.currentResourceVersion);
-      this.documentForm.controls['payloadVersion'].setValue(value.payloadVersion);
-      this.documentForm.controls['payloadCreatedOn'].setValue(value.payloadCreatedOn);
-      this.documentForm.controls['payload'].setValue(value.payload);
-      this.documentForm.controls['payload'].enable();
-      this.documentForm.controls['properties'].setValue(value.properties);
-      // the method documentVersionsExists already uses the current value to check if versions exists
-      if (!this.documentVersionsExists) {
-        this.documentForm.controls['payloadVersion'].disable();
-      } else {
-        this.documentForm.controls['payloadVersion'].enable();
-      }
-    } else {
-      this.documentForm.controls['name'].setValue("");
-      this.documentForm.controls['payload'].setValue("");
-      this.documentForm.controls['currentResourceVersion'].setValue("");
-      this.documentForm.controls['payloadVersion'].setValue("");
-      this.documentForm.controls['payloadCreatedOn'].setValue("");
-      this.documentForm.controls['payload'].setValue("");
-      this.documentForm.controls['properties'].setValue([]);
-    }
-    this.documentForm.markAsPristine();
-  }
-
-  get document(): DocumentRo {
-    let doc: DocumentRo = {...this._document};
-    if(this.documentForm.controls['payload'].dirty){
-      doc.payload = this.documentForm.controls['payload'].value;
-      doc.payloadStatus = EntityStatus.UPDATED;
-    }
-    doc.properties = this.documentForm.controls['properties'].value;
-    return doc;
-  }
-
-  onBackButtonClicked(): void {
-    this.navigationService.navigateUp();
-  }
-
-  async onDocumentResetButtonClicked() {
-    this.dialog.open(ConfirmationDialogComponent, {
-      data: {
-        title: await lastValueFrom(this.translateService.get("resource.document.panel.cancel.confirmation.dialog.title")),
-        description: await lastValueFrom(this.translateService.get("resource.document.panel.cancel.confirmation.dialog.description"))
-      }
-    }).afterClosed().subscribe(result => {
-      if (result) {
-        this.resetChanges()
-      }
-    });
-  }
-
-  resetChanges() {
-
-    let currentVersion = this._document?.payloadVersion;
-    if (!currentVersion) {
-      this.documentForm.controls['payload'].setValue("");
-      this.documentForm.markAsPristine();
-    } else {
-      this.loadDocumentForVersion(currentVersion);
-    }
-  }
 
+  @ViewChild('documentEditor') documentEditor: DocumentEditPanelComponent;
 
-  onSaveButtonClicked(): void {
+  constructor() {
 
-    this.editResourceService.saveDocumentObservable(this._resource, this.document).subscribe(async (value: DocumentRo) => {
-      if (value) {
-        this.alertService.success(await lastValueFrom(this.translateService.get("resource.document.panel.success.save", {currentResourceVersion: value.currentResourceVersion})));
-        this.document = value;
-      } else {
-        this.document = null;
-      }
-    }, (error: any) => {
-      this.alertService.error(error.error?.errorDescription)
-    })
-  }
-
-  onGenerateButtonClicked(): void {
-    this.editResourceService.generateDocumentObservable(this._resource).subscribe(async (value: DocumentRo) => {
-      if (value) {
-        this.alertService.success(await lastValueFrom(this.translateService.get("resource.document.panel.success.generate")))
-        this.documentForm.controls['payload'].setValue(value.payload);
-        this.documentForm.controls['payload'].markAsDirty();
-      } else {
-        this.document = null;
-      }
-    }, (error: any) => {
-      this.alertService.error(error.error?.errorDescription)
-    })
-  }
-
-  async onShowDocumentWizardDialog() {
-
-    const formRef: MatDialogRef<any> = this.dialog.open(DocumentWizardDialogComponent, {
-      data: {
-        title: await lastValueFrom(this.translateService.get("resource.document.panel.document.wizard.dialog.title")),
-        resource: this._resource,
-
-      }
-    });
-    formRef.afterClosed().subscribe(result => {
-      if (result) {
-        let val = formRef.componentInstance.getExtensionXML();
-        this.documentForm.controls['payload'].setValue(val);
-        this.documentForm.controls['payload'].markAsDirty();
-      }
-    });
-  }
-
-  loadDocumentForVersion(version: number = null): void {
-    this.editResourceService.getDocumentObservable(this._resource, version).subscribe((value: DocumentRo) => {
-      if (value) {
-        this.document = value;
-      } else {
-        this.document = null;
-      }
-    }, (error: any) => {
-      this.alertService.error(error.error?.errorDescription)
-    });
-  }
-
-  validateCurrentDocument(): void {
-    this.editResourceService.validateDocumentObservable(this._resource, this.document).subscribe(async (value: DocumentRo) => {
-      this.alertService.success(await lastValueFrom(this.translateService.get("resource.document.panel.success.valid")))
-    }, (error: any) => {
-      this.alertService.error(error.error?.errorDescription)
-    });
-  }
-
-  onDocumentValidateButtonClicked(): void {
-    this.validateCurrentDocument();
-  }
-
-  onSelectionDocumentVersionChanged(): void {
-    this.loadDocumentForVersion(this.documentForm.controls['payloadVersion'].value)
-  }
-
-  public onEditPanelClick() {
-
-    if (this.documentEditor.hasFocus) {
-      return;
-    }
-    this.documentEditor.focusAndCursorToEnd();
-  }
-
-  get getDocumentVersions(): number[] {
-    return !this._document?.allVersions ? [] : this._document?.allVersions;
-  }
-
-  get emptyDocument(): boolean {
-    return !this.documentForm.controls['payload']?.value
-  }
-
-  get documentVersionsExists(): boolean {
-    return this.getDocumentVersions.length > 0
-  }
-
-  get cancelButtonDisabled(): boolean {
-    return !this.documentForm.dirty;
-  }
-
-  get saveButtonDisabled(): boolean {
-    return !this.documentForm.dirty || !this.documentForm.controls['payload']?.value;
   }
 
   isDirty(): boolean {
-    return this.documentForm.dirty
+    return this.documentEditor.isDirty();
   }
 
-  get showWizardDialog(): boolean {
-    // in version DomiSMP 5.0 CR show only the wizard for edelivery-oasis-smp-1.0-servicegroup
-    return this._resource?.resourceTypeIdentifier === 'edelivery-oasis-smp-1.0-servicegroup';
-  }
 }
diff --git a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html
index a2c14f775dced76a1f840d2cb755de1bdcc2f2d5..87ea7817e91f2df51a4847ebb51dfa368b1ed20d 100644
--- a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html
+++ b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html
@@ -1,101 +1,3 @@
-<div id="resource-document-panel">
-
-  <mat-toolbar class="mat-elevation-z2" style="min-height: 50px !important;">
-    <mat-toolbar-row class="smp-toolbar-row" style="justify-content: space-between;min-height: 50px !important;">
-      <button id="validateResource_id" mat-raised-button
-              color="primary"
-              [disabled]="emptyDocument"
-              matTooltip="{{ 'subresource.document.panel.tooltip.validate' | translate }}"
-              (click)="onDocumentValidateButtonClicked()"
-      >
-        <mat-icon>check_circle</mat-icon>
-        <span>{{ "subresource.document.panel.button.validate" | translate }}</span>
-      </button>
-      <button id="GenerateResource_id" mat-raised-button
-              color="primary"
-              matTooltip="{{ 'subresource.document.panel.tooltip.generate' | translate }}"
-              (click)="onGenerateButtonClicked()"
-      >
-        <mat-icon>add_circle</mat-icon>
-        <span>{{ "subresource.document.panel.button.generate" | translate }}</span>
-      </button>
-      <button id="documentWizard_id" mat-raised-button
-              color="primary"
-              matTooltip="{{ 'subresource.document.panel.tooltip.document.wizard' | translate }}"
-              *ngIf="showWizardDialog"
-              (click)="onShowDocumentWizardDialog()"
-      >
-        <mat-icon>code_block</mat-icon>
-        <span>{{ "subresource.document.panel.button.document.wizard" | translate }}</span>
-      </button>
-      <span style="flex: 1 1 auto;"></span>
-
-      <div [formGroup]="documentForm"
-           style="float: right;  vertical-align:middle;display: flex;align-items: center;justify-content: center; gap:0.4em;padding-right: 10px">
-        <span style="font-size: 0.8em">{{ "subresource.document.panel.label.show.version" | translate }}</span>
-        <select matNativeControl style="width: 100px; border-bottom: grey solid 1px"
-                placeholder="{{ 'subresource.document.panel.placeholder.show.version' | translate }}"
-                matTooltip="{{ 'subresource.document.panel.tooltip.show.version' | translate }}"
-                formControlName="payloadVersion"
-                id="document version_id"
-                (change)="onSelectionDocumentVersionChanged()"
-        >
-          <option *ngFor="let version of getDocumentVersions"
-                  [value]="version"
-          >
-            {{version}}
-          </option>
-        </select>
-        <span style="font-size: 0.8em">{{ "subresource.document.panel.label.created.on" | translate }}</span>
-        <input id="payloadCreatedOn_id"
-               matInput [ngxMatDatetimePicker]="payloadCreatedOnPicker"
-               formControlName="payloadCreatedOn"
-               readonly>
-        <mat-datepicker-toggle matSuffix [for]="payloadCreatedOnPicker"
-                               style="visibility: hidden"></mat-datepicker-toggle>
-        <ngx-mat-datetime-picker #payloadCreatedOnPicker [showSpinners]="true" [showSeconds]="false"
-                                 [hideTime]="false"></ngx-mat-datetime-picker>
-      </div>
-    </mat-toolbar-row>
-  </mat-toolbar>
-  <div class="panel" [formGroup]="documentForm"
-       style="display: flex;flex-direction: row;flex-grow: 1; ">
-
-    <div
-      style="display:block; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;border: ridge 3px #b0bec5"
-      (click)="onEditPanelClick()"
-    >
-      <smp-editor #smpDocumentEditor formControlName="payload"
-                  ngDefaultControl></smp-editor>
-    </div>
-    <document-properties-panel style="min-height: 0; overflow: auto"
-                               formControlName="properties"
-                               ngDefaultControl></document-properties-panel>
-  </div>
-
-  <mat-toolbar class="mat-elevation-z2"
-               style="flex-grow: 0;">
-    <mat-toolbar-row class="smp-toolbar-row">
-      <button id="back_id" mat-raised-button color="primary"
-              (click)="onBackButtonClicked()">
-        <mat-icon>arrow_circle_left</mat-icon>
-        <span>{{ "subresource.document.panel.button.back" | translate }}</span>
-      </button>
-      <button id="cancel_id" mat-raised-button color="primary"
-              [disabled]="cancelButtonDisabled"
-              (click)="onDocumentResetButtonClicked()">
-        <mat-icon>cancel</mat-icon>
-        <span>{{ "subresource.document.panel.button.cancel" | translate }}</span>
-      </button>
-      <button id="saveResource_id" mat-raised-button
-              color="primary"
-              matTooltip="{{ 'subresource.document.panel.tooltip.save' | translate }}"
-              [disabled]="saveButtonDisabled"
-              (click)="onSaveButtonClicked()"
-      >
-        <mat-icon>save</mat-icon>
-        <span>{{ "subresource.document.panel.button.save" | translate }}</span>
-      </button>
-    </mat-toolbar-row>
-  </mat-toolbar>
-</div>
+<document-edit-panel #subresourceDocumentEditor
+                     editorMode="SUBRESOURCE_EDITOR">
+</document-edit-panel>
diff --git a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.scss b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.scss
index b422b79b3fe74ad2d66417ea0e53f50bf4663229..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.scss
+++ b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.scss
@@ -1,10 +0,0 @@
-#resource-document-panel {
-  display: flex;
-  height: 100%;
-  flex-direction: column;
-  gap:0.5em;
-}
-
-.CodeMirror {
-  height: auto;
-}
diff --git a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts
index 1cc9f421fdcddfd82946a7aa1e6b14a01fb4abbc..6f870813750aaa226aa2b05b3e40e761f07ce2be 100644
--- a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts
+++ b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts
@@ -1,328 +1,26 @@
-import {AfterViewInit, Component, Input, ViewChild, ViewEncapsulation,} from '@angular/core';
-import {MatDialog, MatDialogRef} from "@angular/material/dialog";
-import {BeforeLeaveGuard} from "../../../window/sidenav/navigation-on-leave-guard";
-import {GroupRo} from "../../../common/model/group-ro.model";
-import {ResourceRo} from "../../../common/model/resource-ro.model";
-import {AlertMessageService} from "../../../common/alert-message/alert-message.service";
-import {DomainRo} from "../../../common/model/domain-ro.model";
-import {ResourceDefinitionRo} from "../../../system-settings/admin-extension/resource-definition-ro.model";
-import {EditResourceService} from "../edit-resource.service";
-import {FormBuilder, FormControl, FormGroup} from "@angular/forms";
-import {DocumentRo} from "../../../common/model/document-ro.model";
-import {NavigationService} from "../../../window/sidenav/navigation-model.service";
-import {SubresourceRo} from "../../../common/model/subresource-ro.model";
+import {Component, ViewChild, ViewEncapsulation,} from '@angular/core';
 import {
-  SubresourceDocumentWizardComponent
-} from "../subresource-document-wizard-dialog/subresource-document-wizard.component";
+  BeforeLeaveGuard
+} from "../../../window/sidenav/navigation-on-leave-guard";
 import {
-  SubresourceWizardRo
-} from "../subresource-document-wizard-dialog/subresource-wizard-edit-ro.model";
-import {ConfirmationDialogComponent} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component";
-import {SmpEditorComponent} from "../../../common/components/smp-editor/smp-editor.component";
-import {EntityStatus} from "../../../common/enums/entity-status.enum";
-import {TranslateService} from "@ngx-translate/core";
-import {lastValueFrom} from "rxjs";
+  DocumentEditPanelComponent
+} from "../../../common/panels/document-edit-panel/document-edit-panel.component";
 
 @Component({
   templateUrl: './subresource-document-panel.component.html',
   styleUrls: ['./subresource-document-panel.component.scss'],
   encapsulation: ViewEncapsulation.None,
 })
-export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeLeaveGuard {
+export class SubresourceDocumentPanelComponent implements BeforeLeaveGuard {
 
-  private _resource: ResourceRo;
-  private _subresource: SubresourceRo;
+  @ViewChild('subresourceDocumentEditor') documentEditor: DocumentEditPanelComponent;
 
-  _document: DocumentRo;
-  @Input() private group: GroupRo;
-  @Input() domain: DomainRo;
-  @Input() domainResourceDefs: ResourceDefinitionRo[];
+  constructor() {
 
-  @ViewChild("smpDocumentEditor") documentEditor: SmpEditorComponent;
-
-  resourceForm: FormGroup;
-  subresourceForm: FormGroup;
-  documentForm: FormGroup;
-
-  constructor(private editResourceService: EditResourceService,
-              private alertService: AlertMessageService,
-              private dialog: MatDialog,
-              private navigationService: NavigationService,
-              private formBuilder: FormBuilder,
-              private translateService: TranslateService) {
-    this.resourceForm = this.formBuilder.group({
-      'identifierValue': new FormControl({value: null}),
-      'identifierScheme': new FormControl({value: null}),
-      'visibility': new FormControl({value: null}),
-      'resourceTypeIdentifier': new FormControl({value: null}),
-    });
-    this.subresourceForm = this.formBuilder.group({
-      'identifierValue': new FormControl({value: null}),
-      'identifierScheme': new FormControl({value: null}),
-      'subresourceTypeIdentifier': new FormControl({value: null}),
-    });
-
-    this.documentForm = this.formBuilder.group({
-      'mimeType': new FormControl({value: null}),
-      'name': new FormControl({value: null}),
-      'currentResourceVersion': new FormControl({value: null}),
-      'payloadVersion': new FormControl({value: null}),
-      'payloadCreatedOn': new FormControl({value: null}),
-      'payload': new FormControl({value: null}),
-      'properties': new FormControl({value: null}),
-    });
-    this.documentForm.controls['payload'].setValue("")
-
-    this.resource = editResourceService.selectedResource
-    this.subresource = editResourceService.selectedSubresource
-
-
-  }
-
-  ngAfterViewInit(): void {
-    // this.codemirror.codeMirror.setSize('100%', '100%');
-  }
-
-  get resource(): ResourceRo {
-    let resource = {...this._resource};
-    resource.identifierScheme = this.resourceForm.get('identifierScheme').value;
-    resource.identifierValue = this.resourceForm.get('identifierValue').value;
-    resource.resourceTypeIdentifier = this.resourceForm.get('resourceTypeIdentifier').value;
-    resource.visibility = this.resourceForm.get('visibility').value;
-    return resource;
-  }
-
-  @Input() set resource(value: ResourceRo) {
-    this._resource = value;
-
-    if (!this._resource) {
-      this.navigationService.reset();
-      return;
-    }
-
-    this.resourceForm.disable();
-    this.resourceForm.controls['identifierValue'].setValue(value.identifierValue);
-    this.resourceForm.controls['identifierScheme'].setValue(value.identifierScheme);
-    this.resourceForm.controls['resourceTypeIdentifier'].setValue(value.resourceTypeIdentifier);
-    this.resourceForm.controls['visibility'].setValue(value.visibility);
-    this.resourceForm.markAsPristine();
-  }
-
-  get subresource(): SubresourceRo {
-    let subresource = {...this._subresource};
-    subresource.identifierScheme = this.subresourceForm.get('identifierScheme').value;
-    subresource.identifierValue = this.subresourceForm.get('identifierValue').value;
-    subresource.subresourceTypeIdentifier = this.subresourceForm.get('subresourceTypeIdentifier').value;
-    return subresource;
-  }
-
-  @Input() set subresource(value: SubresourceRo) {
-    this._subresource = value;
-
-    if (!this._subresource) {
-      this.navigationService.reset();
-      return;
-    }
-
-
-    this.subresourceForm.disable();
-    this.subresourceForm.controls['identifierValue'].setValue(value.identifierValue);
-    this.subresourceForm.controls['identifierScheme'].setValue(value.identifierScheme);
-    this.subresourceForm.controls['subresourceTypeIdentifier'].setValue(value.subresourceTypeIdentifier);
-    this.resourceForm.markAsPristine();
-    // load current document for the resource
-    this.loadDocumentForVersion();
-  }
-
-  @Input() set document(value: DocumentRo) {
-    this._document = value;
-    this.documentForm.disable();
-    if (!!value){
-      this.documentEditor.mimeType = value.mimeType;
-      this.documentForm.controls['mimeType'].setValue(value.mimeType);
-      this.documentForm.controls['name'].setValue(value.name);
-      this.documentForm.controls['currentResourceVersion'].setValue(value.currentResourceVersion);
-      this.documentForm.controls['payloadVersion'].setValue(value.payloadVersion);
-      this.documentForm.controls['payloadCreatedOn'].setValue(value.payloadCreatedOn);
-      this.documentForm.controls['payload'].setValue(!value.payload ? "" : value.payload);
-      this.documentForm.controls['payload'].enable();
-      this.documentForm.controls['properties'].setValue(value.properties);
-
-      if (!this.documentVersionsExists) {
-        this.documentForm.controls['payloadVersion'].disable();
-      } else {
-        this.documentForm.controls['payloadVersion'].enable();
-      }
-
-    } else {
-      this.documentForm.controls['name'].setValue("");
-      this.documentForm.controls['payload'].setValue("");
-      this.documentForm.controls['currentResourceVersion'].setValue("");
-      this.documentForm.controls['payloadVersion'].setValue("");
-      this.documentForm.controls['payloadCreatedOn'].setValue("");
-      this.documentForm.controls['payload'].setValue("");
-      this.documentForm.controls['properties'].setValue([]);
-    }
-    this.documentForm.markAsPristine();
-  }
-
-  get document(): DocumentRo {
-    let doc: DocumentRo = {...this._document};
-    if(this.documentForm.controls['payload'].dirty){
-      doc.payload = this.documentForm.controls['payload'].value;
-      doc.payloadStatus = EntityStatus.UPDATED;
-    }
-    doc.properties = this.documentForm.controls['properties'].value;
-    return doc;
-  }
-
-  onBackButtonClicked(): void {
-    this.navigationService.navigateUp();
-  }
-
-  onSaveButtonClicked(): void {
-    this.editResourceService.saveSubresourceDocumentObservable(this.subresource, this._resource, this.document).subscribe(async (value: DocumentRo) => {
-      if (value) {
-        this.alertService.success(await lastValueFrom(this.translateService.get("subresource.document.panel.success.save", {currentResourceVersion: value.currentResourceVersion})));
-        this.document = value;
-      } else {
-        this.document = null;
-      }
-    }, (error: any) => {
-      this.alertService.error(error.error?.errorDescription)
-    })
-  }
-
-  onGenerateButtonClicked(): void {
-    this.editResourceService.generateSubresourceDocumentObservable(this.subresource, this._resource).subscribe(async (value: DocumentRo) => {
-      if (value) {
-        this.alertService.success(await lastValueFrom(this.translateService.get("subresource.document.panel.success.generate")))
-        this.documentForm.controls['payload'].setValue(value.payload);
-        this.documentForm.controls['payload'].markAsDirty();
-      } else {
-        this.document = null;
-      }
-    }, (error: any) => {
-      this.alertService.error(error.error?.errorDescription)
-    })
-  }
-
-  onShowDocumentWizardDialog() {
-
-    let serviceMetadataWizard: SubresourceWizardRo = {
-      isNewSubresource: false,
-      participantIdentifier: this._resource.identifierValue,
-      participantScheme: this._resource.identifierScheme,
-      documentIdentifier: this._subresource.identifierValue,
-      documentIdentifierScheme: this._subresource.identifierScheme,
-      processIdentifier: '',
-      processScheme: '',
-      transportProfile: 'bdxr-transport-ebms3-as4-v1p0', // default value for oasis AS4
-
-      endpointUrl: '',
-      endpointCertificate: '',
-
-      serviceDescription: '',
-      technicalContactUrl: '',
-
-    }
-
-    const formRef: MatDialogRef<any> = this.dialog.open(SubresourceDocumentWizardComponent, {
-      data: serviceMetadataWizard
-    });
-    formRef.afterClosed().subscribe(result => {
-      if (result) {
-        let smw: SubresourceWizardRo = formRef.componentInstance.getCurrent();
-        this.documentForm.controls['payload'].setValue(smw.contentXML);
-        this.documentForm.controls['payload'].markAsDirty();
-      }
-    });
-  }
-
-  loadDocumentForVersion(version: number = null): void {
-    this.editResourceService.getSubresourceDocumentObservable(this._subresource, this._resource, version).subscribe((value: DocumentRo) => {
-      if (value) {
-        this.document = value;
-      } else {
-        this.document = null;
-      }
-    }, (error: any) => {
-      this.alertService.error(error.error?.errorDescription)
-    });
-  }
-
-  validateCurrentDocument(): void {
-    this.editResourceService.validateSubresourceDocumentObservable(this.subresource, this._resource, this.document).subscribe(async (value: DocumentRo) => {
-      this.alertService.success(await lastValueFrom(this.translateService.get("subresource.document.panel.success.valid")))
-    }, (error: any) => {
-      this.alertService.error(error.error?.errorDescription)
-    });
-  }
-
-  onDocumentValidateButtonClicked(): void {
-    this.validateCurrentDocument();
-  }
-
-  onSelectionDocumentVersionChanged(): void {
-    this.loadDocumentForVersion(this.documentForm.controls['payloadVersion'].value)
-  }
-
-  public onEditPanelClick() {
-    if (this.documentEditor.hasFocus) {
-      return;
-    }
-    this.documentEditor.focusAndCursorToEnd();
-  }
-
-  get getDocumentVersions(): number[] {
-    return !this._document?.allVersions ? [] : this._document?.allVersions;
-  }
-
-  get documentVersionsExists(): boolean {
-    return this.getDocumentVersions.length > 0
-  }
-
-  get emptyDocument(): boolean {
-    return !this.documentForm.controls['payload']?.value
-  }
-
-  get cancelButtonDisabled(): boolean {
-    return !this.documentForm.dirty;
-  }
-
-  get saveButtonDisabled(): boolean {
-    return !this.documentForm.dirty || !this.documentForm.controls['payload']?.value;
   }
 
   isDirty(): boolean {
-    return this.documentForm.dirty
-  }
-
-  async onDocumentResetButtonClicked() {
-    this.dialog.open(ConfirmationDialogComponent, {
-      data: {
-        title: await lastValueFrom(this.translateService.get("subresource.document.panel.cancel.confirmation.dialog.title")),
-        description: await lastValueFrom(this.translateService.get("subresource.document.panel.cancel.confirmation.dialog.description"))
-      }
-    }).afterClosed().subscribe(result => {
-      if (result) {
-        this.resetChanges()
-      }
-    });
-  }
-
-  resetChanges() {
-    let currentVersion = this._document?.payloadVersion;
-    if (!currentVersion) {
-      this.documentForm.controls['payload'].setValue("");
-      this.documentForm.markAsPristine();
-    } else {
-      this.loadDocumentForVersion(currentVersion);
-    }
+    return this.documentEditor.isDirty();
   }
 
-  get showWizardDialog(): boolean {
-    // in version DomiSMP 5.0 CR show only the wizard for edelivery-oasis-smp-1.0-servicemetadata
-    return this._subresource?.subresourceTypeIdentifier === 'edelivery-oasis-smp-1.0-servicemetadata';
-  }
 }
diff --git a/smp-angular/src/app/edit/review-task/review-tasks.component.css b/smp-angular/src/app/edit/review-task/review-tasks.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/smp-angular/src/app/edit/review-task/review-tasks.component.html b/smp-angular/src/app/edit/review-task/review-tasks.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..bc3956fafde408e71843cdd19ed7620a3c901bc8
--- /dev/null
+++ b/smp-angular/src/app/edit/review-task/review-tasks.component.html
@@ -0,0 +1 @@
+<review-tasks-panel id="review-tasks-panel_id" [baseUrl]="reviewTaskUrl"></review-tasks-panel>
diff --git a/smp-angular/src/app/edit/review-task/review-tasks.component.ts b/smp-angular/src/app/edit/review-task/review-tasks.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..30319707f825e4d7e43dfee06aee32d6c8041db2
--- /dev/null
+++ b/smp-angular/src/app/edit/review-task/review-tasks.component.ts
@@ -0,0 +1,20 @@
+import {Component} from '@angular/core';
+import {SmpConstants} from "../../smp.constants";
+import {SecurityService} from "../../security/security.service";
+
+@Component({
+  selector: 'smp-review-tasks',
+  templateUrl: './review-tasks.component.html',
+  styleUrls: ['./review-tasks.component.css']
+})
+export class ReviewTasksComponent {
+
+  constructor(private securityService: SecurityService) {
+  }
+
+  get reviewTaskUrl(): string {
+    return SmpConstants.REST_EDIT_REVIEW_TASK
+      .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, this.securityService.getCurrentUser()?.userId);
+  }
+
+}
diff --git a/smp-angular/src/app/guards/activate-child-document.guard.ts b/smp-angular/src/app/guards/activate-child-document.guard.ts
index c244d19497c3eeb00702807f818c517a37ed2137..f1eea88bc4adcd2d0e359c293fed11353639cfc2 100644
--- a/smp-angular/src/app/guards/activate-child-document.guard.ts
+++ b/smp-angular/src/app/guards/activate-child-document.guard.ts
@@ -1,18 +1,23 @@
 import {inject} from '@angular/core';
-import {SecurityService} from '../security/security.service';
-import {AlertMessageService} from "../common/alert-message/alert-message.service";
-import {Authority} from "../security/authority.model";
-import {ActivatedRouteSnapshot, CanActivateChildFn, CanActivateFn, RouterStateSnapshot} from "@angular/router";
-import {EditResourceService} from "../edit/edit-resources/edit-resource.service";
+import {
+  AlertMessageService
+} from "../common/alert-message/alert-message.service";
+import {
+  ActivatedRouteSnapshot,
+  CanActivateFn,
+  RouterStateSnapshot
+} from "@angular/router";
+import {
+  EditResourceService
+} from "../edit/edit-resources/edit-resource.service";
 
 
 export const activateChildResourceGuard: CanActivateFn =
   (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
-    console.log("Is user is authorized");
     const alertService: AlertMessageService = inject(AlertMessageService);
     const editResourceService: EditResourceService = inject(EditResourceService);
 
-    let resourceUndefined:boolean = !editResourceService.selectedResource;
+    let resourceUndefined: boolean = !editResourceService.selectedResource;
     if (resourceUndefined) {
       alertService.error('Resource/Subresource is not selected! Please select resource from resource edit panel.', true);
     }
diff --git a/smp-angular/src/app/guards/activate-child-review.guard.ts b/smp-angular/src/app/guards/activate-child-review.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..39d67ffdafe557827fa6ba96e7b54e21a0c064b8
--- /dev/null
+++ b/smp-angular/src/app/guards/activate-child-review.guard.ts
@@ -0,0 +1,21 @@
+import {inject} from '@angular/core';
+import {
+  AlertMessageService
+} from "../common/alert-message/alert-message.service";
+import {
+  ActivatedRouteSnapshot,
+  CanActivateFn,
+  RouterStateSnapshot
+} from "@angular/router";
+import {
+  EditResourceService
+} from "../edit/edit-resources/edit-resource.service";
+
+
+export const activateChildReviewGuard: CanActivateFn =
+  (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
+    const alertService: AlertMessageService = inject(AlertMessageService);
+    const editResourceService: EditResourceService = inject(EditResourceService);
+
+    return true;
+  };
diff --git a/smp-angular/src/app/smp.constants.ts b/smp-angular/src/app/smp.constants.ts
index 1581c54ebc485d3abf2ae5f48cb8008cb533efb8..8d8947ec3da841882c0c42840dc2a0ca19462e94 100644
--- a/smp-angular/src/app/smp.constants.ts
+++ b/smp-angular/src/app/smp.constants.ts
@@ -12,6 +12,10 @@ export class SmpConstants {
   public static readonly PATH_ACTION_CREATE: string = 'create';
 
   public static readonly PATH_ACTION_GENERATE: string = 'generate';
+  public static readonly PATH_ACTION_PUBLISH: string = 'publish';
+  public static readonly PATH_ACTION_REVIEW_REQUEST: string = 'review-request';
+  public static readonly PATH_ACTION_REVIEW_APPROVE: string = 'review-approve';
+  public static readonly PATH_ACTION_REVIEW_REJECT: string = 'review-reject';
   public static readonly PATH_ACTION_VALIDATE: string = 'validate';
   public static readonly PATH_ACTION_PUT: string = 'put';
   public static readonly PATH_ACTION_RETRIEVE: string = 'retrieve';
@@ -49,6 +53,7 @@ export class SmpConstants {
   public static readonly PATH_RESOURCE_TYPE_SUBRESOURCE: string = 'subresource';
   public static readonly PATH_RESOURCE_TYPE_DOCUMENT: string = 'document';
   public static readonly PATH_QUERY_FILTER_TYPE: string = 'type'
+  public static readonly PATH_RESOURCE_TYPE_REVIEW: string = 'review-task';
 
 
   //------------------------------
@@ -60,20 +65,32 @@ export class SmpConstants {
 
   public static readonly REST_EDIT_RESOURCE_SHORT = SmpConstants.REST_EDIT + SmpConstants.PATH_RESOURCE_TYPE_RESOURCE + '/' + SmpConstants.PATH_PARAM_ENC_RESOURCE_ID;
 
-  public static readonly REST_EDIT_DOCUMENT = SmpConstants.REST_EDIT_RESOURCE_SHORT + '/' + SmpConstants.PATH_RESOURCE_TYPE_DOCUMENT;
-  public static readonly REST_EDIT_DOCUMENT_VALIDATE = SmpConstants.REST_EDIT_DOCUMENT + '/' + SmpConstants.PATH_ACTION_VALIDATE;
-  public static readonly REST_EDIT_DOCUMENT_GENERATE = SmpConstants.REST_EDIT_DOCUMENT + '/' + SmpConstants.PATH_ACTION_GENERATE;
+  public static readonly REST_EDIT_DOCUMENT_RESOURCE = SmpConstants.REST_EDIT_RESOURCE_SHORT + '/' + SmpConstants.PATH_RESOURCE_TYPE_DOCUMENT;
+  public static readonly REST_EDIT_DOCUMENT_RESOURCE_VALIDATE = SmpConstants.REST_EDIT_DOCUMENT_RESOURCE + '/' + SmpConstants.PATH_ACTION_VALIDATE;
+  public static readonly REST_EDIT_DOCUMENT_RESOURCE_GENERATE = SmpConstants.REST_EDIT_DOCUMENT_RESOURCE + '/' + SmpConstants.PATH_ACTION_GENERATE;
+  public static readonly REST_EDIT_DOCUMENT_RESOURCE_PUBLISH = SmpConstants.REST_EDIT_DOCUMENT_RESOURCE + '/' + SmpConstants.PATH_ACTION_PUBLISH;
+  public static readonly REST_EDIT_DOCUMENT_RESOURCE_REVIEW_REQUEST = SmpConstants.REST_EDIT_DOCUMENT_RESOURCE + '/' + SmpConstants.PATH_ACTION_REVIEW_REQUEST;
+  public static readonly REST_EDIT_DOCUMENT_RESOURCE_REVIEW_APPROVE = SmpConstants.REST_EDIT_DOCUMENT_RESOURCE + '/' + SmpConstants.PATH_ACTION_REVIEW_APPROVE;
+  public static readonly REST_EDIT_DOCUMENT_RESOURCE_REVIEW_REJECT = SmpConstants.REST_EDIT_DOCUMENT_RESOURCE + '/' + SmpConstants.PATH_ACTION_REVIEW_REJECT;
+
   public static readonly REST_EDIT_DOCUMENT_SUBRESOURCE = SmpConstants.REST_EDIT_RESOURCE_SHORT + '/' + SmpConstants.PATH_RESOURCE_TYPE_SUBRESOURCE + '/' + SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID
     + '/' + SmpConstants.PATH_RESOURCE_TYPE_DOCUMENT;
-
   public static readonly REST_EDIT_DOCUMENT_SUBRESOURCE_VALIDATE = SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE + '/' + SmpConstants.PATH_ACTION_VALIDATE;
   public static readonly REST_EDIT_DOCUMENT_SUBRESOURCE_GENERATE = SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE + '/' + SmpConstants.PATH_ACTION_GENERATE;
+  public static readonly REST_EDIT_DOCUMENT_SUBRESOURCE_PUBLISH = SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE + '/' + SmpConstants.PATH_ACTION_PUBLISH;
+  public static readonly REST_EDIT_DOCUMENT_SUBRESOURCE_REVIEW_REQUEST = SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE + '/' + SmpConstants.PATH_ACTION_REVIEW_REQUEST;
+  public static readonly REST_EDIT_DOCUMENT_SUBRESOURCE_REVIEW_APPROVE = SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE + '/' + SmpConstants.PATH_ACTION_REVIEW_APPROVE;
+  public static readonly REST_EDIT_DOCUMENT_SUBRESOURCE_REVIEW_REJECT = SmpConstants.REST_EDIT_DOCUMENT_SUBRESOURCE + '/' + SmpConstants.PATH_ACTION_REVIEW_REJECT;
 
   public static readonly REST_EDIT_SUBRESOURCE = SmpConstants.REST_EDIT_RESOURCE_SHORT + '/' + SmpConstants.PATH_RESOURCE_TYPE_SUBRESOURCE;
   public static readonly REST_EDIT_SUBRESOURCE_DELETE = SmpConstants.REST_EDIT_SUBRESOURCE + '/' + SmpConstants.PATH_PARAM_ENC_SUBRESOURCE_ID
     + '/' + SmpConstants.PATH_ACTION_DELETE;
   public static readonly REST_EDIT_SUBRESOURCE_CREATE = SmpConstants.REST_EDIT_SUBRESOURCE + '/' + SmpConstants.PATH_ACTION_CREATE;
 
+
+  public static readonly REST_EDIT_REVIEW_TASK = SmpConstants.REST_EDIT + SmpConstants.PATH_RESOURCE_TYPE_REVIEW +  '/'
+
+
   /* Public services */
   public static readonly REST_PUBLIC_SEARCH_RESOURCE = SmpConstants.REST_PUBLIC + SmpConstants.PATH_ACTION_SEARCH;
   public static readonly REST_PUBLIC_SEARCH_RESOURCE_METADATA = SmpConstants.REST_PUBLIC + SmpConstants.PATH_ACTION_SEARCH + "/metadata";
diff --git a/smp-angular/src/app/system-settings/admin-domain/domain-panel/domain-panel.component.html b/smp-angular/src/app/system-settings/admin-domain/domain-panel/domain-panel.component.html
index 534a31ba28b36e1924ec95410baf85e0eb5d3e7c..afd05698d7bd6cdfe390d3adb443a1dfd174849a 100644
--- a/smp-angular/src/app/system-settings/admin-domain/domain-panel/domain-panel.component.html
+++ b/smp-angular/src/app/system-settings/admin-domain/domain-panel/domain-panel.component.html
@@ -1,13 +1,14 @@
-<div id="domain-panel" class="mat-elevation-z2" >
-  <form [formGroup]="domainForm" >
+<div id="domain-panel" class="mat-elevation-z2">
+  <form [formGroup]="domainForm">
     <h3>{{ "domain.panel.title" | translate }}</h3>
-    <div class="panel" *ngIf="_domain!=null &&  !_domain.domainId"><p style="font-weight: bold">{{ "domain.panel.text" | translate }}</div>
+    <div class="panel" *ngIf="_domain!=null &&  !_domain.domainId"><p
+      style="font-weight: bold">{{ "domain.panel.text" | translate }}</div>
     <smp-warning-panel *ngIf="showWarning"
                        icon="warning"
                        type="warning"
                        [htmlContent]="warningMessage"></smp-warning-panel>
     <mat-form-field style="width:100%">
-      <mat-label>{{ "domain.panel.label.domain" | translate: { value: domainForm.controls['adminMemberCount'].value } }}</mat-label>
+      <mat-label>{{ "domain.panel.label.domain" | translate: {value: domainForm.controls['adminMemberCount'].value} }}</mat-label>
       <input matInput
              id="domainCode_id" #domainCode
              matTooltip="{{ 'domain.panel.tooltip.domain' | translate }}"
@@ -15,7 +16,8 @@
              (keydown)="onFieldKeyPressed('domainCode', 'domainCodeTimeout')"
              required
              auto-focus-directive>
-      <mat-hint align="end">{{ "domain.panel.hint.domain" | translate }}</mat-hint>
+      <mat-hint align="end">{{ "domain.panel.hint.domain" | translate }}
+      </mat-hint>
       <div
         *ngIf="(!editMode && domainForm.controls['domainCode'].touched || editMode) &&  domainForm.controls['domainCode'].hasError('pattern')"
         style="color:red; font-size: 70%">
@@ -37,51 +39,58 @@
       <mat-select formControlName="signatureKeyAlias"
                   matTooltip="{{ 'domain.panel.tooltip.signature.cert.alias' | translate }}"
                   id="signatureKeyAlias_id">
-        <mat-option [value]="''" ></mat-option>
-        <mat-option *ngFor="let cert of keystoreCertificates" [value]="cert.alias">
-          {{cert.alias}} ({{cert.certificateId}})
+        <mat-option [value]="''"></mat-option>
+        <mat-option *ngFor="let cert of keystoreCertificates"
+                    [value]="cert.alias">
+          {{ cert.alias }} ({{ cert.certificateId }})
         </mat-option>
       </mat-select>
-      <mat-hint align="end">{{ "domain.panel.hint.signature.cert.alias" | translate }}
+      <mat-hint
+        align="end">{{ "domain.panel.hint.signature.cert.alias" | translate }}
       </mat-hint>
     </mat-form-field>
 
     <mat-form-field style="width:100%">
       <mat-label>{{ "domain.panel.label.domain.visibility" | translate }}</mat-label>
       <select matNativeControl formControlName="visibility"
-                  name="visibility"
-                  matTooltip="{{ 'domain.panel.tooltip.domain.visibility' | translate }}"
-                  id="domainVisibility_id">
+              name="visibility"
+              matTooltip="{{ 'domain.panel.tooltip.domain.visibility' | translate }}"
+              id="domainVisibility_id">
         <option *ngFor="let visibility of domainVisibilityOptions"
-                    [value]="visibility.value">
-          {{visibility.key}}
+                [value]="visibility.value">
+          {{ visibility.key }}
         </option>
       </select>
-      <mat-hint align="end">{{ "domain.panel.hint.domain.visibility" | translate }}
+      <mat-hint
+        align="end">{{ "domain.panel.hint.domain.visibility" | translate }}
       </mat-hint>
     </mat-form-field>
 
-  <mat-form-field *ngIf="domainResourceTypes?.length" style="width:100%">
-    <mat-label>{{ "domain.panel.label.domain.default.resource.type" | translate }}</mat-label>
-    <select matNativeControl formControlName="defaultResourceTypeIdentifier"
-                matTooltip="{{ 'domain.panel.tooltip.domain.default.resource.type' | translate }}"
-                id="domainDefaultResourceType_id">
-      <option [value]="''" disabled></option>
-      <option *ngFor="let resDef of domainResourceTypes"
-                  [value]="resDef.identifier">
-        {{resDef.name}} ({{resDef.identifier}})
-      </option>
-    </select>
-    <mat-hint align="end">{{ "domain.panel.hint.domain.visibility" | translate }}</mat-hint>
-  </mat-form-field>
-    <mat-toolbar class ="mat-elevation-z2">
-      <mat-toolbar-row  class="smp-toolbar-row">
-        <button id="cancelButton" mat-raised-button (click)="onResetButtonClicked()" color="primary"
+    <mat-form-field *ngIf="domainResourceTypes?.length" style="width:100%">
+      <mat-label>{{ "domain.panel.label.domain.default.resource.type" | translate }}</mat-label>
+      <select matNativeControl formControlName="defaultResourceTypeIdentifier"
+              matTooltip="{{ 'domain.panel.tooltip.domain.default.resource.type' | translate }}"
+              id="domainDefaultResourceType_id">
+        <option [value]="''" disabled></option>
+        <option *ngFor="let resDef of domainResourceTypes"
+                [value]="resDef.identifier">
+          {{ resDef.name }} ({{ resDef.identifier }})
+        </option>
+      </select>
+      <mat-hint
+        align="end">{{ "domain.panel.hint.domain.visibility" | translate }}
+      </mat-hint>
+    </mat-form-field>
+    <mat-toolbar class="mat-elevation-z2">
+      <mat-toolbar-row class="smp-toolbar-row">
+        <button id="cancelButton" mat-raised-button
+                (click)="onResetButtonClicked()" color="primary"
                 [disabled]="!resetButtonEnabled">
           <mat-icon>refresh</mat-icon>
           <span>{{ "domain.panel.button.reset" | translate }}</span>
         </button>
-        <button id="saveButton" mat-raised-button (click)="onSaveButtonClicked()" color="primary"
+        <button id="saveButton" mat-raised-button
+                (click)="onSaveButtonClicked()" color="primary"
                 [disabled]="!submitButtonEnabled">
           <mat-icon>save</mat-icon>
           <span>{{ "domain.panel.button.save" | translate }}</span>
diff --git a/smp-angular/src/app/system-settings/admin-users/admin-user.service.ts b/smp-angular/src/app/system-settings/admin-users/admin-user.service.ts
index e678dcafc13b2085b12eb1ba7c727facd436f1e0..708b6a19b4e85fa8d11f9b7062773a02970d9e6e 100644
--- a/smp-angular/src/app/system-settings/admin-users/admin-user.service.ts
+++ b/smp-angular/src/app/system-settings/admin-users/admin-user.service.ts
@@ -9,11 +9,9 @@ import {MemberRo} from "../../common/model/member-ro.model";
 import {SmpConstants} from "../../smp.constants";
 import {UserRo} from "../../common/model/user-ro.model";
 
-
 @Injectable()
 export class AdminUserService {
 
-
   constructor(
     private http: HttpClient,
     private securityService: SecurityService) {
diff --git a/smp-angular/src/assets/i18n/en.json b/smp-angular/src/assets/i18n/en.json
index a0ccdde8b3a85c8a8992a311fe8aafce51bda099..33d514440f85b313f845fa009322c3352862f506 100644
--- a/smp-angular/src/assets/i18n/en.json
+++ b/smp-angular/src/assets/i18n/en.json
@@ -64,12 +64,16 @@
   "member.dialog.button.save": "Save",
   "member.dialog.hint.choose.role": "Choose member role",
   "member.dialog.hint.type.username": "Type username or name to locate the user and select user from the list",
+  "member.dialog.hint.can.review": "Check if user has permission to review the resource/subresource documents",
   "member.dialog.label.choose.user": "Choose User to invite",
   "member.dialog.label.invite.members": "You're inviting members to {{target}}.",
+  "member.dialog.label.select.role.type": "Select role for the user",
+  "member.dialog.label.permission.review": "Permission to review",
   "member.dialog.placeholder.role.type": "Role for the member",
   "manage.dialog.title.invite.mode": "Invite {{membershipType}} member",
   "manage.dialog.title.edit.mode": "Edit {{membershipType}} member",
   "member.dialog.tooltip.role.type": "Role type for the member.",
+  "member.dialog.tooltip.permission.review": "Can review document.",
 
   "object.properties.dialog.button.close": "Close",
   "object.properties.dialog.header.key": "Key",
@@ -132,8 +136,6 @@
   "document.properties.panel.label.value": "Value",
   "document.properties.panel.label.no.properties.found": "No Document properties found",
   "document.properties.panel.label.select.page": "Select page of properties",
-  "document.properties.panel.label.expand.collapse": "Expand/Collapse",
-  "document.properties.panel.tooltip.expand.collapse": "Expand/Collapse property panel",
   "document.properties.panel.label.create": "Create",
   "document.properties.panel.tooltip.create": "Create new property",
   "document.properties.panel.label.edit": "Edit",
@@ -142,6 +144,28 @@
   "document.properties.panel.tooltip.delete.remove": "Delete selected property",
   "document.properties.panel.label.reset": "Reset changes",
   "document.properties.panel.tooltip.reset": "Reset changes",
+  "document.properties.panel.tab.title": "Document properties",
+  "document.properties.panel.tab.button.properties": "Properties",
+
+  "document.events.panel.tab.title": "Current Document version events",
+  "document.events.panel.tab.button.events": "Events",
+  "document.events.panel.label.filter": "Filter",
+  "document.events.panel.label.date": "Date",
+  "document.events.panel.label.type": "Event type",
+  "document.events.panel.label.username": "Username",
+  "document.events.panel.label.source": "Event Source",
+  "document.events.panel.label.no.properties.found": "No DocumentVersion events found",
+  "document.events.panel.label.select.page": "Select page of events",
+
+  "document.versions.panel.tab.title": "All Document Versions",
+  "document.versions.panel.tab.button.versions": "Versions",
+  "document.versions.panel.label.filter": "Filter",
+  "document.versions.panel.label.no.properties.found": "No Document versions found",
+  "document.versions.panel.label.select.page": "Select page of document versions",
+  "document.versions.panel.label.version": "Version",
+  "document.versions.panel.label.created": "Created",
+  "document.versions.panel.label.updated": "Last updated",
+  "document.versions.panel.label.status": "status",
 
   "membership.panel.button.invite.member": "Invite member",
   "membership.panel.button.edit.membership": "Edit",
@@ -155,7 +179,9 @@
   "membership.panel.label.no.data.found": "No direct members for the domain",
   "membership.panel.label.role.type": "Role type",
   "membership.panel.label.username": "Username",
+  "membership.panel.label.permission.review": "Can review",
   "membership.panel.placeholder.filter": "Member filter",
+
   "membership.panel.title.domain": "Direct Domain members {{value}}",
   "membership.panel.title.group": "Direct Group members {{value}}",
   "membership.panel.title.resource": "Resource direct members",
@@ -295,6 +321,7 @@
   "resource.dialog.placeholder.resource.visibility": "Resource visibility",
   "resource.dialog.tooltip.resource.type": "Select type for the resource.",
   "resource.dialog.tooltip.resource.visibility": "Resource visibility.",
+  "resource.dialog.tooltip.resource.review.enabled": "Review process enabled",
 
   "group.resource.panel.button.create": "Create",
   "group.resource.panel.button.delete": "Delete",
@@ -336,10 +363,12 @@
   "document.wizard.dialog.button.ok": "OK",
   "document.wizard.dialog.title": "Resource Extension Wizard",
 
+  "resource.details.panel.alert.resource.saved": "Resource data are persisted.",
   "resource.details.panel.label.resource.id": "Resource identifier",
   "resource.details.panel.label.resource.name": "Edit resource document",
   "resource.details.panel.label.resource.scheme": "Resource scheme",
   "resource.details.panel.label.resource.type": "Resource type",
+  "resource.details.panel.label.resource.review.enabled": "Review process enabled",
   "resource.details.panel.label.resource.visibility": "Resource visibility",
   "resource.details.panel.label.resource.visibility.private": "The private resource is accessible only to the resource members!",
   "resource.details.panel.label.resource.visibility.private.group": "The resource belongs to the private group. Only the group members and group resource members can access the resource!",
@@ -349,50 +378,57 @@
   "resource.details.panel.placeholder.resource.type": "Resource type for the resource",
   "resource.details.panel.tooltip.resource.type": "Select type for the resource.",
   "resource.details.panel.tooltip.resource.visibility": "Resource visibility.",
+  "resource.details.panel.tooltip.resource.review.enabled": "Review process enabled",
   "resource.details.panel.tooltip.show.resource": "Show resource",
   "resource.details.panel.title": "Resources",
+  "resource.details.panel.button.reset": "Reset",
+  "resource.details.panel.button.save": "Save",
+
+  "document.edit.panel.button.back": "Back",
+  "document.edit.panel.button.cancel": "Cancel",
+  "document.edit.panel.button.document.wizard": "Document wizard",
+  "document.edit.panel.button.generate": "Generate",
+  "document.edit.panel.button.validate": "Validate",
+  "document.edit.panel.button.version.new": "New version",
+  "document.edit.panel.button.version.publish": "Publish",
+  "document.edit.panel.button.version.review": "Review",
+  "document.edit.panel.button.version.approve": "Approve",
+  "document.edit.panel.button.version.reject": "Reject",
+  "document.edit.panel.button.save": "Save",
+  "document.edit.panel.cancel.confirmation.dialog.description": "Do you want to cancel all changes on the document?",
+  "document.edit.panel.cancel.confirmation.dialog.title": "Cancel changes",
+  "document.edit.panel.document.wizard.dialog.title": "Resource wizard",
+  "document.edit.panel.label.selected.created.on": "Created on:",
+  "document.edit.panel.label.selected.version": "Selected version:",
+  "document.edit.panel.label.selected.status": "Status:",
+  "document.edit.panel.placeholder.version": "All document version",
+  "document.edit.panel.success.save": "Document is saved with current version [{{currentResourceVersion}}].",
+  "document.edit.panel.success.generate": "Document is generated.",
+  "document.edit.panel.success.valid": "Document is Valid.",
+  "document.edit.panel.error.document.null": "Can not show document because it is not selected.",
+  "document.edit.panel.title": "Resources",
+  "document.edit.panel.tooltip.document.wizard": "Show document wizard dialog",
+  "document.edit.panel.tooltip.generate": "Generate resource",
+  "document.edit.panel.tooltip.save": "Save resource",
+  "document.edit.panel.tooltip.validate": "Validate resource",
+  "document.edit.panel.tooltip.version": "Select version to display.",
+  "document.edit.panel.tooltip.version.new": "Create new document version for the resource",
+  "document.edit.panel.tooltip.version.publish": "Publish selected document version for the resource",
+  "document.edit.panel.tooltip.version.review": "Submit selected document version for the review",
+  "document.edit.panel.tooltip.version.approve": "Submit selected document version for the review",
+  "document.edit.panel.tooltip.version.reject": "Submit selected document version for the review",
+  "document.edit.panel.note.editable": "Note: Only documents in following status can be edited: [{{editableDocStatusList}}]",
+
+  "review.edit.panel.label.review": "Review",
+  "review.edit.panel.label.column.date": "Review date",
+  "review.edit.panel.label.column.version": "Version",
+  "review.edit.panel.label.column.target": "Target",
+  "review.edit.panel.label.column.resource.scheme": "Res. scheme",
+  "review.edit.panel.label.column.resource.value": "Res. value",
+  "review.edit.panel.label.column.subresource.scheme": "Subr. scheme",
+  "review.edit.panel.label.column.subresource.value": "Subr. value",
+
 
-  "resource.document.panel.button.back": "Back",
-  "resource.document.panel.button.cancel": "Cancel",
-  "resource.document.panel.button.document.wizard": "Document wizard",
-  "resource.document.panel.button.generate": "Generate",
-  "resource.document.panel.button.validate": "Validate",
-  "resource.document.panel.button.save": "Save",
-  "resource.document.panel.cancel.confirmation.dialog.description": "Do you want to cancel all changes on the document?",
-  "resource.document.panel.cancel.confirmation.dialog.title": "Cancel changes",
-  "resource.document.panel.document.wizard.dialog.title": "Resource wizard",
-  "resource.document.panel.label.created.on": "Created on:",
-  "resource.document.panel.label.show.version": "Show version:",
-  "resource.document.panel.placeholder.version": "All document version",
-  "resource.document.panel.success.save": "Document is saved with current version [{{currentResourceVersion}}].",
-  "resource.document.panel.success.generate": "Document is generated.",
-  "resource.document.panel.success.valid": "Document is Valid.",
-  "resource.document.panel.title": "Resources",
-  "resource.document.panel.tooltip.document.wizard": "Show document wizard dialog",
-  "resource.document.panel.tooltip.generate": "Generate resource",
-  "resource.document.panel.tooltip.save": "Save resource",
-  "resource.document.panel.tooltip.validate": "Validate resource",
-  "resource.document.panel.tooltip.version": "Select version to display.",
-
-  "subresource.document.panel.button.back": "Back",
-  "subresource.document.panel.button.cancel": "Cancel",
-  "subresource.document.panel.button.document.wizard": "Document wizard",
-  "subresource.document.panel.button.generate": "Generate",
-  "subresource.document.panel.button.validate": "Validate",
-  "subresource.document.panel.button.save": "Save",
-  "subresource.document.panel.cancel.confirmation.dialog.description": "Do you want to cancel all changes on the document?",
-  "subresource.document.panel.cancel.confirmation.dialog.title": "Cancel changes",
-  "subresource.document.panel.label.created.on": "Created on:",
-  "subresource.document.panel.label.show.version": "Show version:",
-  "subresource.document.panel.placeholder.show.version": "All document version",
-  "subresource.document.panel.success.generate": "Document is generated.",
-  "subresource.document.panel.success.save": "Document is saved with current version [{{currentResourceVersion}}].",
-  "subresource.document.panel.success.valid": "Document is Valid.",
-  "subresource.document.panel.tooltip.document.wizard": "Show document wizard dialog",
-  "subresource.document.panel.tooltip.generate": "Generate resource",
-  "subresource.document.panel.tooltip.save": "Save resource",
-  "subresource.document.panel.tooltip.show.version": "Select version to display.",
-  "subresource.document.panel.tooltip.validate": "Validate resource",
 
   "subresource.document.wizard.button.cancel": "Cancel",
   "subresource.document.wizard.button.ok": "OK",
@@ -819,6 +855,9 @@
   "toolbar.component.label.system.administrator.role": "System administrator",
   "toolbar.component.label.user.role": "SMP user",
 
+  "expandable.panel.label.expand.collapse": "Expand/Collapse",
+  "expandable.panel.tooltip.expand.collapse": "Expand/Collapse property panel",
+
   "app.component.button.collapse": "Collapse sidebar",
   "app.component.tooltip.collapse": "Collapse",
   "app.component.tooltip.expand": "Expand",
@@ -833,8 +872,10 @@
   "navigation.label.edit.domains": "Edit Domains",
   "navigation.label.edit.groups": "Edit Groups",
   "navigation.label.edit.resources": "Edit Resources",
+  "navigation.label.edit.document.review": "Review Document",
   "navigation.label.edit.resource.document": "Edit Resource Document",
   "navigation.label.edit.subresource.document": "Edit Subresource Document",
+  "navigation.label.review.tasks": "Review Tasks",
   "navigation.label.system.settings": "System Settings",
   "navigation.label.system.settings.alerts": "Alerts",
   "navigation.label.system.settings.domains": "Domain",
@@ -852,4 +893,5 @@
   "navigation.tooltip.search.resources": "Search registered resources",
   "navigation.tooltip.search.tools": "Search tools",
   "navigation.tooltip.search.dns.tools": "DNS lookup tools"
+
 }
diff --git a/smp-angular/src/main.ts b/smp-angular/src/main.ts
index 46c1c73e209ee7d73a0112cb31fb229314813ae6..d2d023651523c5b590f5741c2a468dafb55261c0 100644
--- a/smp-angular/src/main.ts
+++ b/smp-angular/src/main.ts
@@ -2,6 +2,30 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
 import { enableProdMode } from '@angular/core';
 import { environment } from './environments/environment';
 import { AppModule } from './app/app.module';
+// add the following EU locales
+import '@angular/common/locales/global/bg';
+import '@angular/common/locales/global/cs';
+import '@angular/common/locales/global/da';
+import '@angular/common/locales/global/de';
+import '@angular/common/locales/global/el';
+import '@angular/common/locales/global/en';
+import '@angular/common/locales/global/es';
+import '@angular/common/locales/global/et';
+import '@angular/common/locales/global/fi';
+import '@angular/common/locales/global/fr';
+import '@angular/common/locales/global/hr';
+import '@angular/common/locales/global/hu';
+import '@angular/common/locales/global/it';
+import '@angular/common/locales/global/lt';
+import '@angular/common/locales/global/lv';
+import '@angular/common/locales/global/mt';
+import '@angular/common/locales/global/nl';
+import '@angular/common/locales/global/pl';
+import '@angular/common/locales/global/pt';
+import '@angular/common/locales/global/ro';
+import '@angular/common/locales/global/sk';
+import '@angular/common/locales/global/sl';
+import '@angular/common/locales/global/sv';
 
 if (environment.production) {
   enableProdMode();
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBDocumentVersionToDocumentVersionROConverter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBDocumentVersionToDocumentVersionROConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fe8f410a7346b2b894b4df277ee4021060f273b
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBDocumentVersionToDocumentVersionROConverter.java
@@ -0,0 +1,54 @@
+/*-
+ * #START_LICENSE#
+ * smp-server-library
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+package eu.europa.ec.edelivery.smp.conversion;
+
+import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion;
+import eu.europa.ec.edelivery.smp.data.ui.DocumentVersionRO;
+import eu.europa.ec.edelivery.smp.logging.SMPLogger;
+import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
+import org.apache.commons.beanutils.BeanUtils;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Converter for DBDocumentVersion to DocumentVersionRO
+ *
+ * @author Joze RIHTARSIC
+ * @since 5.1
+ */
+@Component
+public class DBDocumentVersionToDocumentVersionROConverter implements Converter<DBDocumentVersion, DocumentVersionRO> {
+    private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DBDocumentVersionToDocumentVersionROConverter.class);
+
+    @Override
+    public DocumentVersionRO convert(DBDocumentVersion source) {
+
+        DocumentVersionRO target = new DocumentVersionRO();
+        try {
+            BeanUtils.copyProperties(target, source);
+            target.setVersionStatus(source.getStatus());
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            LOG.error("Error occurred while converting DBResource", e);
+            return null;
+        }
+        return target;
+    }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBResourceMemberToMemberROConverter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBResourceMemberToMemberROConverter.java
index b1c96bfeb7ef02445901c6ced2531b3a552fe15f..6708e312ab87cc89c4d21371b6d7cfbf18f27c01 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBResourceMemberToMemberROConverter.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBResourceMemberToMemberROConverter.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -25,6 +25,8 @@ import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.stereotype.Component;
 
+import static org.apache.commons.lang3.BooleanUtils.isTrue;
+
 
 /**
  *
@@ -40,6 +42,7 @@ public class DBResourceMemberToMemberROConverter implements Converter<DBResource
         target.setFullName(source.getUser().getFullName());
         target.setRoleType(source.getRole());
         target.setMemberId(SessionSecurityUtils.encryptedEntityId(source.getId()));
+        target.setHasPermissionReview(isTrue(source.hasPermissionToReview()));
         return target;
     }
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBResourceToResourceROConverter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBResourceToResourceROConverter.java
index 84f7167195ab6d33d543f8fd02dc7f297332fcbb..637dca1fd6469b3ad6a5ccb18194139361fdfbdc 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBResourceToResourceROConverter.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBResourceToResourceROConverter.java
@@ -45,6 +45,7 @@ public class DBResourceToResourceROConverter implements Converter<DBResource, Re
             BeanUtils.copyProperties(target, source);
             target.setResourceTypeIdentifier(source.getDomainResourceDef().getResourceDef().getIdentifier());
             target.setResourceId(SessionSecurityUtils.encryptedEntityId(source.getId()));
+            target.setReviewEnabled(source.isReviewEnabled());
         } catch (IllegalAccessException | InvocationTargetException e) {
             LOG.error("Error occurred while converting DBResource", e);
             return null;
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBReviewDocumentVersionToReviewDocumentVersionROConverter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBReviewDocumentVersionToReviewDocumentVersionROConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..2698e529d5bb26e92fe82d89ffc0fc737062b33f
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/DBReviewDocumentVersionToReviewDocumentVersionROConverter.java
@@ -0,0 +1,60 @@
+/*-
+ * #START_LICENSE#
+ * smp-server-library
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+package eu.europa.ec.edelivery.smp.conversion;
+
+import eu.europa.ec.edelivery.smp.data.model.doc.DBReviewDocumentVersion;
+import eu.europa.ec.edelivery.smp.data.ui.ReviewDocumentVersionRO;
+import eu.europa.ec.edelivery.smp.logging.SMPLogger;
+import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
+import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils;
+import org.apache.commons.beanutils.BeanUtils;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.InvocationTargetException;
+
+
+/**
+ * Converter for DBReviewDocumentVersion to ReviewDocumentVersionRO
+ *
+ * @author Joze RIHTARSIC
+ * @since 5.1
+ */
+@Component
+public class DBReviewDocumentVersionToReviewDocumentVersionROConverter implements Converter<DBReviewDocumentVersion, ReviewDocumentVersionRO> {
+    private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DBReviewDocumentVersionToReviewDocumentVersionROConverter.class);
+
+    @Override
+    public ReviewDocumentVersionRO convert(DBReviewDocumentVersion source) {
+
+        ReviewDocumentVersionRO target = new ReviewDocumentVersionRO();
+        try {
+            BeanUtils.copyProperties(target, source);
+            target.setDocumentVersionId(SessionSecurityUtils.encryptedEntityId(source.getDocumentVersionId()));
+            target.setDocumentId(SessionSecurityUtils.encryptedEntityId(source.getDocumentId()));
+            target.setResourceId(SessionSecurityUtils.encryptedEntityId(source.getResourceId()));
+            target.setSubresourceId(SessionSecurityUtils.encryptedEntityId(source.getSubresourceId()));
+            target.setCurrentStatus(source.getStatus());
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            LOG.error("Error occurred while converting DBResource", e);
+            return null;
+        }
+        return target;
+    }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java
index b3f7542653983820b195b2e5177e77eab7bdd0c5..41367b1bd43adb803e6f980ad390ff4be390acbf 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java
@@ -19,10 +19,8 @@
 package eu.europa.ec.edelivery.smp.data.dao;
 
 
-import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument;
-import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion;
-import eu.europa.ec.edelivery.smp.data.model.doc.DBResource;
-import eu.europa.ec.edelivery.smp.data.model.doc.DBSubresource;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+import eu.europa.ec.edelivery.smp.data.model.doc.*;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import org.springframework.stereotype.Repository;
@@ -133,8 +131,20 @@ public class DocumentDao extends BaseDao<DBDocument> {
      * @return document version list
      */
     public List<DBDocumentVersion> getDocumentVersionsForSubresource(DBSubresource subresource) {
-        TypedQuery<DBDocumentVersion> query = memEManager.createNamedQuery(QUERY_DOCUMENT_VERSION_LIST_FOR_SUBRESOURCE, DBDocumentVersion.class);
+        TypedQuery<DBDocumentVersion> query = memEManager.createNamedQuery(QUERY_DOCUMENT_VERSION_LIST_FOR_SUBRESOURCE,
+                DBDocumentVersion.class);
         query.setParameter(PARAM_SUBRESOURCE_ID, subresource.getId());
         return query.getResultList();
     }
+
+    public List<DBReviewDocumentVersion> getDocumentReviewListForUser(Long dbUserId) {
+        TypedQuery<DBReviewDocumentVersion> query = memEManager.createNamedQuery(
+                QUERY_DOCUMENT_VERSION_UNDER_REVIEW_FOR_USER, DBReviewDocumentVersion.class);
+        query.setParameter(PARAM_USER_ID, dbUserId);
+        query.setParameter(PARAM_PERMISSION_CAN_REVIEW, true);
+        query.setParameter(PARAM_REVIEW_ENABLED, true);
+        query.setParameter(PARAM_STATUS, DocumentVersionStatusType.UNDER_REVIEW.name());
+        return query.getResultList();
+
+    }
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java
index 5b77e0bfa65e78a49bf3c67a275d9d3de33da7c4..3906c65d563f0aa85a655082f999baed9da59e2f 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java
@@ -121,7 +121,9 @@ public class QueryNames {
     public static final String QUERY_RESOURCE_DEF_BY_IDENTIFIER_EXTENSION = "DBExtResourceDef.getByIdentifierExtension";
 
     public static final String QUERY_DOCUMENT_FOR_RESOURCE = "DBDocument.getForResource";
+    public static final String QUERY_DOCUMENT_BY_RESOURCE_DEF_SHARING = "DBDocument.getForResourceDEfAndSharingEnabled";
     public static final String QUERY_DOCUMENT_FOR_SUBRESOURCE = "DBDocument.getForSubresource";
+    public static final String QUERY_DOCUMENT_BY_SUBRESOURCE_DEF_SHARING = "DBDocument.getForSubresourceDEfAndSharingEnabled";
 
     public static final String QUERY_DOCUMENT_VERSION_CURRENT_FOR_RESOURCE = "DBDocumentVersion.forCurrentForResource";
     public static final String QUERY_DOCUMENT_VERSION_LIST_FOR_RESOURCE = "DBDocumentVersion.getAllForResource";
@@ -129,6 +131,7 @@ public class QueryNames {
 
     public static final String QUERY_DOCUMENT_VERSION_CURRENT_FOR_SUBRESOURCE = "DBDocumentVersion.forCurrentForSubresource";
     public static final String QUERY_DOCUMENT_VERSION_LIST_FOR_SUBRESOURCE = "DBDocumentVersion.getAllForSubresource";
+    public static final String QUERY_DOCUMENT_VERSION_UNDER_REVIEW_FOR_USER = "DBDocumentVersion.getAllReviewTasksForUser";
 
     public static final String QUERY_GROUP_MEMBER_ALL = "DBGroupMember.getAll";
     public static final String QUERY_GROUP_MEMBER_BY_USER_GROUPS_COUNT = "DBGroupMember.getByUserAndGroupsCount";
@@ -182,6 +185,7 @@ public class QueryNames {
     public static final String PARAM_RESOURCE_DEF_IDENTIFIER = "resource_def_identifier";
     public static final String PARAM_SUBRESOURCE_DEF_ID = "subresource_def_id";
 
+    public static final String PARAM_REVIEW_ENABLED = "review_enabled";
 
     public static final String PARAM_SUBRESOURCE_DEF_IDENTIFIER = "subresource_def_identifier";
     public static final String PARAM_DOMAIN_ID = "domain_id";
@@ -192,10 +196,13 @@ public class QueryNames {
 
     public static final String PARAM_DOCUMENT_ID = "document_id";
     public static final String PARAM_DOCUMENT_TYPE = "document_type";
+    public static final String PARAM_SHARING_ENABLED = "sharing_enabled";
+    public static final String PARAM_STATUS = "status";
 
     public static final String PARAM_GROUP_ID = "group_id";
     public static final String PARAM_GROUP_IDS = "group_ids";
     public static final String PARAM_MEMBERSHIP_ROLE = "membership_role";
+    public static final String PARAM_PERMISSION_CAN_REVIEW = "permission_can_review";
 
     public static final String PARAM_MEMBERSHIP_ROLES = "membership_roles";
     public static final String PARAM_USER_USERNAME = "username";
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/ResourceMemberDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/ResourceMemberDao.java
index 483dc2b0a343ffdd1d7105caeb31283b29762803..2107b59a4093387370d1154618cb3aa76db203c7 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/ResourceMemberDao.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/ResourceMemberDao.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -56,7 +56,7 @@ public class ResourceMemberDao extends BaseDao<DBResourceMember> {
     }
 
     public boolean isUserResourceMemberWithRole(Long userId, Long resourceId, MembershipRoleType roleType) {
-        LOG.debug("User id [{}], Domain id [{}], role [{}]", userId, resourceId, roleType);
+        LOG.debug("User id [{}], Resource id [{}], role [{}]", userId, resourceId, roleType);
         TypedQuery<DBResourceMember> query = memEManager.createNamedQuery(QUERY_RESOURCE_MEMBER_BY_USER_RESOURCE, DBResourceMember.class);
 
         query.setParameter(PARAM_USER_ID, userId);
@@ -64,6 +64,16 @@ public class ResourceMemberDao extends BaseDao<DBResourceMember> {
         return query.getResultList().stream().anyMatch(member -> member.getRole() == roleType);
     }
 
+
+    public boolean isUserResourceMemberWithReviewPermission(Long userId, Long resourceId) {
+        LOG.debug("User id [{}], Resource id [{}], with review permission", userId, resourceId);
+        TypedQuery<DBResourceMember> query = memEManager.createNamedQuery(QUERY_RESOURCE_MEMBER_BY_USER_RESOURCE, DBResourceMember.class);
+
+        query.setParameter(PARAM_USER_ID, userId);
+        query.setParameter(PARAM_RESOURCE_ID, resourceId);
+        return query.getResultList().stream().anyMatch(DBResourceMember::hasPermissionToReview);
+    }
+
     public boolean isUserAnyDomainResourceMember(DBUser user, DBDomain domain) {
         LOG.debug("User [{}], Domain [{}]", user, domain);
         TypedQuery<Long> query = memEManager.createNamedQuery(QUERY_RESOURCE_MEMBER_BY_USER_DOMAIN_RESOURCE_COUNT, Long.class);
@@ -151,11 +161,14 @@ public class ResourceMemberDao extends BaseDao<DBResourceMember> {
     }
 
 
-    public DBResourceMember addMemberToResource(DBResource resource, DBUser user, MembershipRoleType role) {
+    public DBResourceMember addMemberToResource(DBResource resource, DBUser user,
+                                                MembershipRoleType role,
+                                                boolean hasPermissionReview) {
         DBResourceMember resourceMember = new DBResourceMember();
         resourceMember.setRole(role);
         resourceMember.setUser(user);
         resourceMember.setResource(resource);
+        resourceMember.setHasPermissionToReview(hasPermissionReview);
         resourceMember = merge(resourceMember);
         return resourceMember;
     }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/enums/DocumentVersionEventType.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/enums/DocumentVersionEventType.java
new file mode 100644
index 0000000000000000000000000000000000000000..7dc30b30b45f1a2e10dd5c7dec0c0a86acb922a4
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/enums/DocumentVersionEventType.java
@@ -0,0 +1,19 @@
+package eu.europa.ec.edelivery.smp.data.enums;
+
+/**
+ * Document version event types. The event status allows user to track
+ * changes in the document version.
+ *
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+public enum DocumentVersionEventType {
+    CREATE,
+    UPDATE,
+    PUBLISH,
+    REQUEST_REVIEW,
+    RETIRE,
+    APPROVE,
+    REJECT,
+    ERROR
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/enums/DocumentVersionStatusType.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/enums/DocumentVersionStatusType.java
new file mode 100644
index 0000000000000000000000000000000000000000..a19af0f902b29d62a39b5e0254d7c2e192a760c0
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/enums/DocumentVersionStatusType.java
@@ -0,0 +1,11 @@
+package eu.europa.ec.edelivery.smp.data.enums;
+
+
+public enum DocumentVersionStatusType {
+    DRAFT,
+    PUBLISHED,
+    RETIRED,
+    UNDER_REVIEW,
+    APPROVED,
+    REJECTED,
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/enums/EventSourceType.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/enums/EventSourceType.java
new file mode 100644
index 0000000000000000000000000000000000000000..d64e3b85bac7f04e7c981d28846e78859b587a62
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/enums/EventSourceType.java
@@ -0,0 +1,16 @@
+package eu.europa.ec.edelivery.smp.data.enums;
+
+/**
+ * Document version event source types. The event source can be UI, REST API,
+ * Automatic cron or any other custom plugin.
+ *
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+public enum EventSourceType {
+    UI,
+    REST_API,
+    CRON,
+    PLUGIN,
+    OTHER
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java
index 5e9d842eeae664501f16579ae9c1726cc233c1b7..9456463925e90f56e6aba2e52071701c130bf3b4 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java
@@ -19,6 +19,7 @@
 package eu.europa.ec.edelivery.smp.data.model.doc;
 
 import eu.europa.ec.edelivery.smp.data.dao.utils.ColumnDescription;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
 import eu.europa.ec.edelivery.smp.data.model.BaseEntity;
 import eu.europa.ec.edelivery.smp.data.model.CommonColumnsLengths;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
@@ -31,8 +32,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
-import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOCUMENT_FOR_RESOURCE;
-import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOCUMENT_FOR_SUBRESOURCE;
+import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.*;
 
 /**
  * Database optimization: load service metadata xml only when needed and
@@ -46,10 +46,17 @@ import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOCUMENT_FOR_
 @Audited
 @Table(name = "SMP_DOCUMENT")
 @org.hibernate.annotations.Table(appliesTo = "SMP_DOCUMENT", comment = "SMP document entity for resources and subresources")
-@NamedQueries({
-        @NamedQuery(name = QUERY_DOCUMENT_FOR_RESOURCE, query = "SELECT d FROM DBResource r JOIN r.document d WHERE r.id =:resource_id"),
+
+        @NamedQuery(name = QUERY_DOCUMENT_FOR_RESOURCE, query = "SELECT d FROM DBResource r JOIN r.document d WHERE r.id =:resource_id")
+        @NamedQuery(name = QUERY_DOCUMENT_BY_RESOURCE_DEF_SHARING, query = "SELECT d FROM DBResource r " +
+                "INNER JOIN r.document d " +
+                "INNER JOIN r.domainResourceDef.resourceDef rdef " +
+                "  WHERE rdef.identifier =:resource_def_identifier and d.sharingEnabled =:sharing_enabled")
         @NamedQuery(name = QUERY_DOCUMENT_FOR_SUBRESOURCE, query = "SELECT d FROM DBSubresource  sr JOIN sr.document d WHERE sr.id =:subresource_id")
-})
+        @NamedQuery(name = QUERY_DOCUMENT_BY_SUBRESOURCE_DEF_SHARING, query = "SELECT d FROM DBSubresource rs " +
+                "INNER JOIN rs.document d " +
+                "INNER JOIN rs.subresourceDef rdef " +
+                "  WHERE rdef.identifier =:subresource_def_identifier and d.sharingEnabled =:sharing_enabled")
 public class DBDocument extends BaseEntity {
     private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DBDocument.class);
     @Id
@@ -59,6 +66,10 @@ public class DBDocument extends BaseEntity {
     @ColumnDescription(comment = "Unique document id")
     Long id;
 
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "FK_REF_DOCUMENT_ID")
+    private DBDocument referenceDocument;
+
     // list of all version with the latest version first!
     @OneToMany(
             mappedBy = "document",
@@ -66,6 +77,7 @@ public class DBDocument extends BaseEntity {
             orphanRemoval = true,
             fetch = FetchType.LAZY
     )
+    @OrderBy("id DESC")
     List<DBDocumentVersion> documentVersions;
 
     @Column(name = "CURRENT_VERSION", nullable = false)
@@ -78,6 +90,9 @@ public class DBDocument extends BaseEntity {
     @Column(name = "NAME")
     private String name;
 
+    @Column(name = "SHARING_ENABLED")
+    private Boolean sharingEnabled = Boolean.FALSE;
+
     @OneToMany(
             mappedBy = "document",
             cascade = CascadeType.ALL,
@@ -95,6 +110,13 @@ public class DBDocument extends BaseEntity {
         this.id = id;
     }
 
+    public DBDocument getReferenceDocument() {
+        return referenceDocument;
+    }
+
+    public void setReferenceDocument(DBDocument referenceDocument) {
+        this.referenceDocument = referenceDocument;
+    }
 
     /**
      * Returns document version ordered from the latest version to first version
@@ -108,15 +130,25 @@ public class DBDocument extends BaseEntity {
         return documentVersions;
     }
 
+    /**
+     * Method add new document version to the document and set the version number.
+     * The version number is set to the highest version number + 1 and set as current version.
+     * Also existing published version is set to retired.
+     *
+     * @param documentVersion document version
+     * @return document version
+     */
     public DBDocumentVersion addNewDocumentVersion(DBDocumentVersion documentVersion) {
         if (documentVersion.getId() != null && getDocumentVersions().contains(documentVersion)) {
             LOG.info("Document version [{}] already exists on document [{}]", documentVersion, this);
             return documentVersion;
-        }
+       }
+
         documentVersion.setVersion(getNextVersionIndex());
-        getDocumentVersions().add(documentVersion);
         documentVersion.setDocument(this);
         setCurrentVersion(documentVersion.getVersion());
+        // ADD TO THE LIST to the first position (latest version)
+        getDocumentVersions().add(0, documentVersion);
         return documentVersion;
     }
 
@@ -135,7 +167,6 @@ public class DBDocument extends BaseEntity {
                 .reduce(0, Integer::max) + 1;
     }
 
-
     public int getCurrentVersion() {
         return currentVersion;
     }
@@ -155,6 +186,14 @@ public class DBDocument extends BaseEntity {
         getDocumentProperties().removeIf(DBDocumentProperty::isTransient);
     }
 
+    public Boolean getSharingEnabled() {
+        return sharingEnabled;
+    }
+
+    public void setSharingEnabled(Boolean sharingEnabled) {
+        this.sharingEnabled = sharingEnabled;
+    }
+
     @Override
     public void prePersist() {
         super.prePersist();
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java
index 02cd71db827e029e6cf52c0a941ebd9f887ad4e4..293fff778281519481adc9fda72d0cc2c4ea3e60 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -19,11 +19,16 @@
 package eu.europa.ec.edelivery.smp.data.model.doc;
 
 import eu.europa.ec.edelivery.smp.data.dao.utils.ColumnDescription;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
 import eu.europa.ec.edelivery.smp.data.model.BaseEntity;
 import org.hibernate.annotations.GenericGenerator;
 import org.hibernate.envers.Audited;
+import org.hibernate.envers.NotAudited;
 
 import javax.persistence.*;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.*;
@@ -45,25 +50,68 @@ import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.*;
 
         })
 @org.hibernate.annotations.Table(appliesTo = "SMP_DOCUMENT_VERSION", comment = "Document content for the document version.")
-
-@NamedQueries({
-        @NamedQuery(name = QUERY_DOCUMENT_VERSION_CURRENT_FOR_RESOURCE, query = "SELECT dv FROM DBResource r join r.document d join d.documentVersions dv " +
-                " WHERE dv.version = d.currentVersion " +
-                " AND r.id= :resource_id "),
-        @NamedQuery(name = QUERY_DOCUMENT_VERSION_LIST_FOR_RESOURCE, query = "SELECT dv FROM DBResource r join r.document.documentVersions dv " +
-                " WHERE r.id= :resource_id order by dv.version desc"),
-
-        @NamedQuery(name = QUERY_DOCUMENT_VERSION_CURRENT_FOR_SUBRESOURCE, query = "SELECT dv FROM " +
-                "   DBSubresource sr join sr.document d join d.documentVersions dv " +
-                " WHERE dv.version = d.currentVersion " +
-                " AND sr.id= :subresource_id "),
-        @NamedQuery(name = QUERY_DOCUMENT_VERSION_LIST_FOR_SUBRESOURCE, query = "SELECT dv FROM " +
-                "   DBSubresource sr join sr.document.documentVersions dv " +
-                " WHERE sr.id= :subresource_id order by dv.version desc")
-})
-
+@NamedQuery(name = QUERY_DOCUMENT_VERSION_CURRENT_FOR_RESOURCE, query = "SELECT dv FROM DBResource r join r.document d join d.documentVersions dv " +
+        " WHERE dv.version = d.currentVersion " +
+        " AND r.id= :resource_id ")
+@NamedQuery(name = QUERY_DOCUMENT_VERSION_LIST_FOR_RESOURCE, query = "SELECT dv FROM DBResource r join r.document.documentVersions dv " +
+        " WHERE r.id= :resource_id order by dv.version desc")
+@NamedQuery(name = QUERY_DOCUMENT_VERSION_CURRENT_FOR_SUBRESOURCE, query = "SELECT dv FROM " +
+        "   DBSubresource sr join sr.document d join d.documentVersions dv " +
+        " WHERE dv.version = d.currentVersion " +
+        " AND sr.id= :subresource_id ")
+@NamedQuery(name = QUERY_DOCUMENT_VERSION_LIST_FOR_SUBRESOURCE, query = "SELECT dv FROM " +
+        "   DBSubresource sr join sr.document.documentVersions dv " +
+        " WHERE sr.id= :subresource_id order by dv.version desc")
+@NamedNativeQuery(name = QUERY_DOCUMENT_VERSION_UNDER_REVIEW_FOR_USER,
+        query = "SELECT " +
+                "    dv.ID AS ID, " +
+                "    dv.LAST_UPDATED_ON AS LAST_UPDATED_ON," +
+                "    dv.FK_DOCUMENT_ID AS DOCUMENT_ID," +
+                "    dv.STATUS AS STATUS," +
+                "    dv.VERSION AS VERSION," +
+                "    r.ID AS RESOURCE_ID," +
+                "    sr.ID AS SUBRESOURCE_ID," +
+                "    r.IDENTIFIER_VALUE AS RIDENTIFIER_VALUE," +
+                "    r.IDENTIFIER_SCHEME AS RIDENTIFIER_SCHEME," +
+                "    sr.IDENTIFIER_VALUE AS SRIDENTIFIER_VALUE," +
+                "    sr.IDENTIFIER_SCHEME AS SRIDENTIFIER_SCHEME," +
+                "    CASE " +
+                "        WHEN sr.ID IS NOT NULL THEN 'SUBRESOURCE'" +
+                "        ELSE 'RESOURCE'" +
+                "    END AS TARGET" +
+                " FROM " +
+                "    SMP_DOCUMENT_VERSION dv" +
+                "    INNER JOIN SMP_DOCUMENT d ON dv.FK_DOCUMENT_ID = d.ID" +
+                "    LEFT JOIN SMP_SUBRESOURCE sr ON d.ID = sr.FK_DOCUMENT_ID" +
+                "    LEFT JOIN SMP_RESOURCE r ON d.ID = r.FK_DOCUMENT_ID OR r.ID = sr.FK_RESOURCE_ID" +
+                "    INNER JOIN SMP_RESOURCE_MEMBER rmu ON r.ID = rmu.FK_RESOURCE_ID" +
+                " WHERE " +
+                "    dv.STATUS = :status" +
+                "    AND r.REVIEW_ENABLED = :review_enabled" +
+                "    AND rmu.FK_USER_ID = :user_id" +
+                "    AND rmu.PERMISSION_REVIEW = :permission_can_review",
+
+        resultSetMapping = "DBReviewDocumentVersionsMapping")
+
+@SqlResultSetMapping(name = "DBReviewDocumentVersionsMapping",
+        classes = {
+                @ConstructorResult(targetClass = DBReviewDocumentVersion.class,
+                        columns = {
+                                @ColumnResult(name = "ID", type = Long.class),
+                                @ColumnResult(name = "DOCUMENT_ID", type = Long.class),
+                                @ColumnResult(name = "RESOURCE_ID", type = Long.class),
+                                @ColumnResult(name = "SUBRESOURCE_ID", type = Long.class),
+                                @ColumnResult(name = "VERSION", type = Integer.class),
+                                @ColumnResult(name = "STATUS", type = String.class),
+                                @ColumnResult(name = "RIDENTIFIER_VALUE", type = String.class),
+                                @ColumnResult(name = "RIDENTIFIER_SCHEME", type = String.class),
+                                @ColumnResult(name = "SRIDENTIFIER_VALUE", type = String.class),
+                                @ColumnResult(name = "SRIDENTIFIER_SCHEME", type = String.class),
+                                @ColumnResult(name = "TARGET", type = String.class),
+                                @ColumnResult(name = "LAST_UPDATED_ON", type = OffsetDateTime.class),
+                        })
+        })
 public class DBDocumentVersion extends BaseEntity {
-
     @Id
     @GeneratedValue(strategy = GenerationType.AUTO, generator = "SMP_DOCUMENT_VERSION_SEQ")
     @GenericGenerator(name = "SMP_DOCUMENT_VERSION_SEQ", strategy = "native")
@@ -74,11 +122,19 @@ public class DBDocumentVersion extends BaseEntity {
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "FK_DOCUMENT_ID")
     private DBDocument document;
-
+    // list of all document events  with the latest event first!
+    @OneToMany(
+            mappedBy = "documentVersion",
+            cascade = CascadeType.ALL,
+            orphanRemoval = true,
+            fetch = FetchType.LAZY
+    )
+    @NotAudited
+    @OrderBy("id desc")
+    List<DBDocumentVersionEvent> documentVersionEvents;
+    // version of the document
     @Column(name = "VERSION", nullable = false)
     private int version;
-
-
     // lob fetch it only when needed!
     @Lob
     @Basic(fetch = FetchType.LAZY)
@@ -86,6 +142,11 @@ public class DBDocumentVersion extends BaseEntity {
     @ColumnDescription(comment = "Document content")
     byte[] content;
 
+    @Enumerated(EnumType.STRING)
+    @Column(name = "STATUS", nullable = false)
+    @ColumnDescription(comment = "Document version status")
+    private DocumentVersionStatusType status = DocumentVersionStatusType.DRAFT;
+
     @Override
     public Long getId() {
         return id;
@@ -119,6 +180,39 @@ public class DBDocumentVersion extends BaseEntity {
         this.content = content;
     }
 
+    public DocumentVersionStatusType getStatus() {
+        return status;
+    }
+
+    public void setStatus(DocumentVersionStatusType status) {
+        this.status = status;
+    }
+
+    /**
+     * Returns document version events
+     *
+     * @return list of events for the document versions
+     */
+    public List<DBDocumentVersionEvent> getDocumentVersionEvents() {
+        if (documentVersionEvents == null) {
+            documentVersionEvents = new ArrayList<>();
+        }
+        return documentVersionEvents;
+    }
+
+    /**
+     * Add new document version event. Beause of the order of the events,
+     * the new event is added to the beginning of the list.*
+     *
+     * @param event event to be added
+     * @return added event
+     */
+    public DBDocumentVersionEvent addNewDocumentVersionEvent(DBDocumentVersionEvent event) {
+        event.setDocumentVersion(this);
+        getDocumentVersionEvents().add(0, event);
+        return event;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -132,4 +226,14 @@ public class DBDocumentVersion extends BaseEntity {
     public int hashCode() {
         return Objects.hash(super.hashCode(), id);
     }
+
+    @Override
+    public String toString() {
+        return "DBDocumentVersion{" +
+                "id=" + id +
+                ", document=" + document.id +
+                ", version=" + version +
+                ", status=" + status +
+                '}';
+    }
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersionEvent.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersionEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..54fa7c8dc2274b4164451c4c68b4e19067fdd3b6
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersionEvent.java
@@ -0,0 +1,159 @@
+/*-
+ * #START_LICENSE#
+ * smp-server-library
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+package eu.europa.ec.edelivery.smp.data.model.doc;
+
+import eu.europa.ec.edelivery.smp.data.dao.utils.ColumnDescription;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionEventType;
+import eu.europa.ec.edelivery.smp.data.enums.EventSourceType;
+import eu.europa.ec.edelivery.smp.data.model.BaseEntity;
+import eu.europa.ec.edelivery.smp.data.model.CommonColumnsLengths;
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.*;
+import java.time.OffsetDateTime;
+import java.util.Objects;
+
+/**
+ * Document version event entity. The event entity allows user to track
+ * changes in the document version. Note that the event is audited, because
+ * the record is a special audit record, and it is not expected
+ *
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+@Entity
+@Table(name = "SMP_DOCUMENT_VERSION_EVENT",
+        indexes = {
+                @Index(name = "SMP_DOCVEREVNT_DOCVER_IDX", columnList = "FK_DOCUMENT_VERSION_ID"),
+        })
+@org.hibernate.annotations.Table(appliesTo = "SMP_DOCUMENT_VERSION_EVENT", comment = "Document version Events.")
+public class DBDocumentVersionEvent extends BaseEntity {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO, generator = "SMP_DOCVER_EVENT_SEQ")
+    @GenericGenerator(name = "SMP_DOCVER_EVENT_SEQ", strategy = "native")
+    @Column(name = "ID")
+    @ColumnDescription(comment = "Unique document version event identifier")
+    Long id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "FK_DOCUMENT_VERSION_ID")
+    private DBDocumentVersion documentVersion;
+
+    @Enumerated(EnumType.STRING)
+    @Column(name = "EVENT_TYPE", nullable = false)
+    @ColumnDescription(comment = "Document version event type")
+    private DocumentVersionEventType eventType = DocumentVersionEventType.CREATE;
+
+    @Column(name = "EVENT_ON")
+    @ColumnDescription(comment = "Date time of the event")
+    private OffsetDateTime eventOn;
+
+    @Column(name = "EVENT_BY_USERNAME", length = CommonColumnsLengths.MAX_USERNAME_LENGTH)
+    @ColumnDescription(comment = "username identifier of the user who triggered the event")
+    private String username;
+
+    @Enumerated(EnumType.STRING)
+    @Column(name = "EVENT_SOURCE", nullable = false)
+    @ColumnDescription(comment = "Event source UI, API")
+    private EventSourceType eventSourceType = EventSourceType.OTHER;
+
+    @Column(name = "DETAILS", length = CommonColumnsLengths.MAX_MEDIUM_TEXT_LENGTH)
+    @ColumnDescription(comment = "Details of the event")
+    private String details;
+
+    @Override
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public DBDocumentVersion getDocumentVersion() {
+        return documentVersion;
+    }
+
+    public void setDocumentVersion(DBDocumentVersion documentVersion) {
+        this.documentVersion = documentVersion;
+    }
+
+    public DocumentVersionEventType getEventType() {
+        return eventType;
+    }
+
+    public void setEventType(DocumentVersionEventType eventType) {
+        this.eventType = eventType;
+    }
+
+    public OffsetDateTime getEventOn() {
+        return eventOn;
+    }
+
+    public void setEventOn(OffsetDateTime eventOn) {
+        this.eventOn = eventOn;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public EventSourceType getEventSourceType() {
+        return eventSourceType;
+    }
+
+    public void setEventSourceType(EventSourceType eventSourceType) {
+        this.eventSourceType = eventSourceType;
+    }
+
+    public String getDetails() {
+        return details;
+    }
+
+    public void setDetails(String details) {
+        this.details = details;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        DBDocumentVersionEvent that = (DBDocumentVersionEvent) o;
+        return Objects.equals(id, that.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), id);
+    }
+
+    @Override
+    public String toString() {
+        return "DBDocumentVersion{" +
+                "id=" + id +
+                ", documentVersion=" + documentVersion.id +
+                '}';
+    }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResource.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResource.java
index 1f1b06cf4b7264f4e468775751e819df197bfc55..86e27f001a79936045a519d082913d770147dd9a 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResource.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBResource.java
@@ -151,9 +151,12 @@ public class DBResource extends BaseEntity {
     @Column(name = "IDENTIFIER_SCHEME", length = CommonColumnsLengths.MAX_IDENTIFIER_VALUE_SCHEME_LENGTH)
     String identifierScheme;
 
-    @Column(name = "SML_REGISTERED", nullable = false)
+    @Column(name = "SML_REGISTERED")
     private boolean smlRegistered = false;
 
+    @Column(name = "REVIEW_ENABLED")
+    private Boolean reviewEnabled = Boolean.FALSE;
+
     @Enumerated(EnumType.STRING)
     @Column(name = "VISIBILITY", length = CommonColumnsLengths.MAX_TEXT_LENGTH_128)
     private VisibilityType visibility = VisibilityType.PUBLIC;
@@ -271,6 +274,14 @@ public class DBResource extends BaseEntity {
         this.smlRegistered = smlRegistered;
     }
 
+    public Boolean isReviewEnabled() {
+        return reviewEnabled == null ? Boolean.FALSE : reviewEnabled;
+    }
+
+    public void setReviewEnabled(Boolean reviewEnabled) {
+        this.reviewEnabled = reviewEnabled;
+    }
+
     /**
      * Id is database suragete id + natural key!
      *
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBReviewDocumentVersion.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBReviewDocumentVersion.java
new file mode 100644
index 0000000000000000000000000000000000000000..f34bfdcb66adf7f4aaef2eaaab1fe5d26a03998d
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBReviewDocumentVersion.java
@@ -0,0 +1,156 @@
+package eu.europa.ec.edelivery.smp.data.model.doc;
+
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.time.OffsetDateTime;
+
+/**
+ * Class represents the document version for the review. It is used with query
+ * to get all review tasks for the user
+ *
+ * @author Joze RIHARSIC
+ * @since 5.1
+ */
+public class DBReviewDocumentVersion implements Serializable {
+
+    private Long documentId;
+    private Long documentVersionId;
+    private Long resourceId;
+    private Long subresourceId;
+    private int version;
+    private DocumentVersionStatusType status = DocumentVersionStatusType.DRAFT;
+    private String resourceIdentifierValue;
+    private String resourceIdentifierScheme;
+    private String subresourceIdentifierValue;
+    private String subresourceIdentifierScheme;
+    private String target;
+    private OffsetDateTime lastUpdatedOn;
+
+    public DBReviewDocumentVersion() {
+    }
+
+    public DBReviewDocumentVersion(
+            Long id,
+            Long documentId,
+            Long resourceId,
+            Long subresourceId,
+            int version,
+            String status,
+            String resourceIdentifierValue,
+            String resourceIdentifierScheme,
+            String subresourceIdentifierValue,
+            String subresourceIdentifierScheme,
+            String target,
+            OffsetDateTime lastUpdatedOn) {
+        this.documentId = documentId;
+        this.documentVersionId = id;
+        this.resourceId = resourceId;
+        this.subresourceId = subresourceId;
+        this.version = version;
+        this.status = StringUtils.isNotBlank(status) ? DocumentVersionStatusType.valueOf(status) : null;
+        this.resourceIdentifierValue = resourceIdentifierValue;
+        this.resourceIdentifierScheme = resourceIdentifierScheme;
+        this.subresourceIdentifierValue = subresourceIdentifierValue;
+        this.subresourceIdentifierScheme = subresourceIdentifierScheme;
+        this.target = target;
+        this.lastUpdatedOn = lastUpdatedOn;
+    }
+
+    public Long getDocumentId() {
+        return documentId;
+    }
+
+    public void setDocumentId(Long documentId) {
+        this.documentId = documentId;
+    }
+
+    public Long getDocumentVersionId() {
+        return documentVersionId;
+    }
+
+    public void setDocumentVersionId(Long documentVersionId) {
+        this.documentVersionId = documentVersionId;
+    }
+
+    public Long getResourceId() {
+        return resourceId;
+    }
+
+    public void setResourceId(Long resourceId) {
+        this.resourceId = resourceId;
+    }
+
+    public Long getSubresourceId() {
+        return subresourceId;
+    }
+
+    public void setSubresourceId(Long subresourceId) {
+        this.subresourceId = subresourceId;
+    }
+
+    public OffsetDateTime getLastUpdatedOn() {
+        return lastUpdatedOn;
+    }
+
+    public void setLastUpdatedOn(OffsetDateTime lastUpdatedOn) {
+        this.lastUpdatedOn = lastUpdatedOn;
+    }
+
+    public int getVersion() {
+        return version;
+    }
+
+    public void setVersion(int version) {
+        this.version = version;
+    }
+
+    public DocumentVersionStatusType getStatus() {
+        return status;
+    }
+
+    public void setStatus(DocumentVersionStatusType status) {
+        this.status = status;
+    }
+
+    public String getResourceIdentifierValue() {
+        return resourceIdentifierValue;
+    }
+
+    public void setResourceIdentifierValue(String resourceIdentifierValue) {
+        this.resourceIdentifierValue = resourceIdentifierValue;
+    }
+
+    public String getResourceIdentifierScheme() {
+        return resourceIdentifierScheme;
+    }
+
+    public void setResourceIdentifierScheme(String resourceIdentifierScheme) {
+        this.resourceIdentifierScheme = resourceIdentifierScheme;
+    }
+
+    public String getSubresourceIdentifierValue() {
+        return subresourceIdentifierValue;
+    }
+
+    public void setSubresourceIdentifierValue(String subresourceIdentifierValue) {
+        this.subresourceIdentifierValue = subresourceIdentifierValue;
+    }
+
+    public String getSubresourceIdentifierScheme() {
+        return subresourceIdentifierScheme;
+    }
+
+    public void setSubresourceIdentifierScheme(String subresourceIdentifierScheme) {
+        this.subresourceIdentifierScheme = subresourceIdentifierScheme;
+    }
+
+    public String getTarget() {
+        return target;
+    }
+
+    public void setTarget(String target) {
+        this.target = target;
+    }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBResourceMember.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBResourceMember.java
index ad1cd56f7c7bec3644f0109f485366cc21433fc9..b736ac5ca4aa0c24960c96904356a4db10060555 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBResourceMember.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBResourceMember.java
@@ -18,6 +18,7 @@
  */
 package eu.europa.ec.edelivery.smp.data.model.user;
 
+import eu.europa.ec.edelivery.smp.data.dao.utils.ColumnDescription;
 import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType;
 import eu.europa.ec.edelivery.smp.data.model.BaseEntity;
 import eu.europa.ec.edelivery.smp.data.model.CommonColumnsLengths;
@@ -84,6 +85,10 @@ public class DBResourceMember extends BaseEntity {
     @Column(name = "MEMBERSHIP_ROLE", length = CommonColumnsLengths.MAX_TEXT_LENGTH_64)
     private MembershipRoleType role = MembershipRoleType.VIEWER;
 
+    @Column(name = "PERMISSION_REVIEW")
+    @ColumnDescription(comment = "User permission to review the resource document")
+    private Boolean hasPermissionToReview = false;
+
     public DBResourceMember() {
         //Need this method for hibernate
         // Caused by: java.lang.NoSuchMethodException: eu.europa.ec.edelivery.smp.data.model.DBServiceGroupDomain_$$_jvst7ad_2.<init>()
@@ -128,4 +133,12 @@ public class DBResourceMember extends BaseEntity {
     public void setRole(MembershipRoleType role) {
         this.role = role;
     }
+
+    public Boolean hasPermissionToReview() {
+        return hasPermissionToReview;
+    }
+
+    public void setHasPermissionToReview(Boolean permissionReview) {
+        this.hasPermissionToReview = permissionReview;
+    }
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java
index d115312a5b676e50b51817f7f95cbcb9e2768e5e..bfb1199bdf5f928e100b4bb847e0d4f8473e2480 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java
@@ -19,6 +19,7 @@
 package eu.europa.ec.edelivery.smp.data.ui;
 
 import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
 import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus;
 
 import java.time.OffsetDateTime;
@@ -26,18 +27,24 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class DocumentRO extends BaseRO {
-
+    private static final long serialVersionUID = 9008583888835630038L;
     String documentId;
+    String referenceDocumentId;
     String mimeType;
     Integer currentResourceVersion;
     List<Integer> allVersions;
     String name;
+    Boolean sharingEnabled = Boolean.FALSE;
+
     Integer payloadVersion;
     String payload;
     private int payloadStatus = EntityROStatus.PERSISTED.getStatusNumber();
     OffsetDateTime payloadCreatedOn;
+    DocumentVersionStatusType documentVersionStatus;
 
     List<DocumentPropertyRO> properties = new ArrayList<>();
+    List<DocumentVersionEventRO> documentVersionEvents = new ArrayList<>();
+    List<DocumentVersionRO> documentVersions = new ArrayList<>();
 
     public String getDocumentId() {
         return documentId;
@@ -47,6 +54,14 @@ public class DocumentRO extends BaseRO {
         this.documentId = documentId;
     }
 
+    public String getReferenceDocumentId() {
+        return referenceDocumentId;
+    }
+
+    public void setReferenceDocumentId(String referenceDocumentId) {
+        this.referenceDocumentId = referenceDocumentId;
+    }
+
     public String getMimeType() {
         return mimeType;
     }
@@ -59,6 +74,14 @@ public class DocumentRO extends BaseRO {
         return currentResourceVersion;
     }
 
+    public Boolean getSharingEnabled() {
+        return sharingEnabled;
+    }
+
+    public void setSharingEnabled(Boolean sharingEnabled) {
+        this.sharingEnabled = sharingEnabled;
+    }
+
     public void setCurrentResourceVersion(Integer currentResourceVersion) {
         this.currentResourceVersion = currentResourceVersion;
     }
@@ -86,6 +109,14 @@ public class DocumentRO extends BaseRO {
         this.payloadVersion = payloadVersion;
     }
 
+    public DocumentVersionStatusType getDocumentVersionStatus() {
+        return documentVersionStatus;
+    }
+
+    public void setDocumentVersionStatus(DocumentVersionStatusType documentVersionStatus) {
+        this.documentVersionStatus = documentVersionStatus;
+    }
+
     public String getPayload() {
         return payload;
     }
@@ -120,4 +151,20 @@ public class DocumentRO extends BaseRO {
 
         this.properties.add(propertyRO);
     }
+
+    public List<DocumentVersionRO> getDocumentVersions() {
+        return documentVersions;
+    }
+
+    public void setDocumentVersions(List<DocumentVersionRO> documentVersions) {
+        this.documentVersions = documentVersions;
+    }
+
+    public List<DocumentVersionEventRO> getDocumentVersionEvents() {
+        return documentVersionEvents;
+    }
+
+    public void addDocumentVersionEvent(DocumentVersionEventRO event) {
+        this.documentVersionEvents.add(event);
+    }
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentVersionEventRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentVersionEventRO.java
new file mode 100644
index 0000000000000000000000000000000000000000..da9f630f73c4f35957b6e49d93901da5494efd00
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentVersionEventRO.java
@@ -0,0 +1,66 @@
+package eu.europa.ec.edelivery.smp.data.ui;
+
+
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionEventType;
+import eu.europa.ec.edelivery.smp.data.enums.EventSourceType;
+
+import java.time.OffsetDateTime;
+
+/**
+ * Document version event. The event entity allows user to track
+ * changes in the document version.
+ *
+ * @author Joze Rihtarsic
+ * @since 5.1
+ */
+public class DocumentVersionEventRO extends BaseRO {
+
+    private static final long serialVersionUID = 9008583888835630037L;
+
+    private DocumentVersionEventType eventType = DocumentVersionEventType.CREATE;
+    private OffsetDateTime eventOn;
+    private String username;
+    private EventSourceType eventSourceType = EventSourceType.OTHER;
+    private String details;
+
+
+    public DocumentVersionEventType getEventType() {
+        return eventType;
+    }
+
+    public void setEventType(DocumentVersionEventType eventType) {
+        this.eventType = eventType;
+    }
+
+    public OffsetDateTime getEventOn() {
+        return eventOn;
+    }
+
+    public void setEventOn(OffsetDateTime eventOn) {
+        this.eventOn = eventOn;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public EventSourceType getEventSourceType() {
+        return eventSourceType;
+    }
+
+    public void setEventSourceType(EventSourceType eventSourceType) {
+        this.eventSourceType = eventSourceType;
+    }
+
+    public String getDetails() {
+        return details;
+    }
+
+    public void setDetails(String details) {
+        this.details = details;
+    }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentVersionRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentVersionRO.java
new file mode 100644
index 0000000000000000000000000000000000000000..a09a8bd302cca95b9f248bce941016a9ceb49c73
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentVersionRO.java
@@ -0,0 +1,59 @@
+package eu.europa.ec.edelivery.smp.data.ui;
+
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+
+import java.io.Serializable;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DocumentVersionRO implements Serializable {
+    private static final long serialVersionUID = 9008583888835630039L;
+
+    private int version;
+    private DocumentVersionStatusType versionStatus;
+    private OffsetDateTime createdOn;
+    private OffsetDateTime lastUpdatedOn;
+
+
+    List<DocumentVersionEventRO> documentVersionEvents = new ArrayList<>();
+
+    public int getVersion() {
+        return version;
+    }
+
+    public void setVersion(int version) {
+        this.version = version;
+    }
+
+    public DocumentVersionStatusType getVersionStatus() {
+        return versionStatus;
+    }
+
+    public void setVersionStatus(DocumentVersionStatusType versionStatus) {
+        this.versionStatus = versionStatus;
+    }
+
+    public List<DocumentVersionEventRO> getDocumentVersionEvents() {
+        if (documentVersionEvents == null) {
+            documentVersionEvents = new ArrayList<>();
+        }
+        return documentVersionEvents;
+    }
+
+    public OffsetDateTime getCreatedOn() {
+        return createdOn;
+    }
+
+    public void setCreatedOn(OffsetDateTime createdOn) {
+        this.createdOn = createdOn;
+    }
+
+    public OffsetDateTime getLastUpdatedOn() {
+        return lastUpdatedOn;
+    }
+
+    public void setLastUpdatedOn(OffsetDateTime lastUpdatedOn) {
+        this.lastUpdatedOn = lastUpdatedOn;
+    }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/MemberRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/MemberRO.java
index 369a5709e0fc410a064b8adafc3f49850223ab1e..aea12e5a96a5dc36a71701c1a6e6e7feca18b853 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/MemberRO.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/MemberRO.java
@@ -28,6 +28,9 @@ public class MemberRO {
     MemberOfType memberOf;
     String fullName;
     MembershipRoleType roleType;
+    // Resource specific fields
+    Boolean hasPermissionReview;
+
 
     public String getMemberId() {
         return memberId;
@@ -65,6 +68,14 @@ public class MemberRO {
         return roleType;
     }
 
+    public Boolean getHasPermissionReview() {
+        return hasPermissionReview == null ? Boolean.FALSE : hasPermissionReview;
+    }
+
+    public void setHasPermissionReview(Boolean hasPermissionReview) {
+        this.hasPermissionReview = hasPermissionReview;
+    }
+
     public void setRoleType(MembershipRoleType roleType) {
         this.roleType = roleType;
     }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ResourceRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ResourceRO.java
index 5a91b3834385bd01cd80a2da30e6370580bc3395..52021bf363a96cada90c8e2a84732ab5522747d8 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ResourceRO.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ResourceRO.java
@@ -42,6 +42,8 @@ public class ResourceRO extends BaseRO {
 
     private boolean smlRegistered = false;
 
+    private Boolean reviewEnabled;
+
     private VisibilityType visibility = VisibilityType.PUBLIC;
 
     public String getResourceId() {
@@ -84,6 +86,14 @@ public class ResourceRO extends BaseRO {
         this.smlRegistered = smlRegistered;
     }
 
+    public Boolean isReviewEnabled() {
+        return reviewEnabled;
+    }
+
+    public void setReviewEnabled(Boolean reviewEnabled) {
+        this.reviewEnabled = reviewEnabled;
+    }
+
     public VisibilityType getVisibility() {
         return visibility;
     }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ReviewDocumentVersionRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ReviewDocumentVersionRO.java
new file mode 100644
index 0000000000000000000000000000000000000000..9502fec65e2a1414f3c094c2cc13d97df2115ea5
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ReviewDocumentVersionRO.java
@@ -0,0 +1,124 @@
+package eu.europa.ec.edelivery.smp.data.ui;
+
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+
+import java.time.OffsetDateTime;
+/**
+ * Class represents RO for the document version for the review
+ *
+ * @since 5.1
+ * @author Joze RIHARSIC
+ */
+public class ReviewDocumentVersionRO {
+
+    private String documentId;
+    private String documentVersionId;
+    private String resourceId;
+    private String subresourceId;
+    private int version;
+    private DocumentVersionStatusType currentStatus = DocumentVersionStatusType.DRAFT;
+    private String resourceIdentifierValue;
+    private String resourceIdentifierScheme;
+    private String subresourceIdentifierValue;
+    private String subresourceIdentifierScheme;
+    private String target;
+    private OffsetDateTime lastUpdatedOn;
+
+
+
+    public String getDocumentId() {
+        return documentId;
+    }
+
+    public void setDocumentId(String documentId) {
+        this.documentId = documentId;
+    }
+
+    public String getDocumentVersionId() {
+        return documentVersionId;
+    }
+
+    public void setDocumentVersionId(String documentVersionId) {
+        this.documentVersionId = documentVersionId;
+    }
+
+    public String getResourceId() {
+        return resourceId;
+    }
+
+    public void setResourceId(String resourceId) {
+        this.resourceId = resourceId;
+    }
+
+    public int getVersion() {
+        return version;
+    }
+
+    public void setVersion(int version) {
+        this.version = version;
+    }
+
+    public DocumentVersionStatusType getCurrentStatus() {
+        return currentStatus;
+    }
+
+    public void setCurrentStatus(DocumentVersionStatusType currentStatus) {
+        this.currentStatus = currentStatus;
+    }
+
+    public String getSubresourceId() {
+        return subresourceId;
+    }
+
+    public void setSubresourceId(String subresourceId) {
+        this.subresourceId = subresourceId;
+    }
+
+    public String getResourceIdentifierValue() {
+        return resourceIdentifierValue;
+    }
+
+    public void setResourceIdentifierValue(String resourceIdentifierValue) {
+        this.resourceIdentifierValue = resourceIdentifierValue;
+    }
+
+    public String getResourceIdentifierScheme() {
+        return resourceIdentifierScheme;
+    }
+
+    public void setResourceIdentifierScheme(String resourceIdentifierScheme) {
+        this.resourceIdentifierScheme = resourceIdentifierScheme;
+    }
+
+    public String getSubresourceIdentifierValue() {
+        return subresourceIdentifierValue;
+    }
+
+    public void setSubresourceIdentifierValue(String subresourceIdentifierValue) {
+        this.subresourceIdentifierValue = subresourceIdentifierValue;
+    }
+
+    public String getSubresourceIdentifierScheme() {
+        return subresourceIdentifierScheme;
+    }
+
+    public void setSubresourceIdentifierScheme(String subresourceIdentifierScheme) {
+        this.subresourceIdentifierScheme = subresourceIdentifierScheme;
+    }
+
+    public String getTarget() {
+        return target;
+    }
+
+    public void setTarget(String target) {
+        this.target = target;
+    }
+
+    public OffsetDateTime getLastUpdatedOn() {
+        return lastUpdatedOn;
+    }
+
+    public void setLastUpdatedOn(OffsetDateTime lastUpdatedOn) {
+        this.lastUpdatedOn = lastUpdatedOn;
+    }
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/DocumentVersionService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/DocumentVersionService.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3ba7d8e7fb5b479a9734d86e2fba7f3cb62523b
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/DocumentVersionService.java
@@ -0,0 +1,155 @@
+package eu.europa.ec.edelivery.smp.services.resource;
+
+import eu.europa.ec.edelivery.smp.auth.SMPUserDetails;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionEventType;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+import eu.europa.ec.edelivery.smp.data.enums.EventSourceType;
+import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion;
+import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersionEvent;
+import eu.europa.ec.edelivery.smp.logging.SMPLogger;
+import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
+import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils;
+import org.springframework.stereotype.Service;
+
+import java.time.OffsetDateTime;
+
+/**
+ * Service for document version events
+ *
+ * @author Joze RIHTARSIC
+ * @since 5.1
+ */
+@Service
+public class DocumentVersionService {
+    protected static final SMPLogger LOG = SMPLoggerFactory.getLogger(DocumentVersionService.class);
+    public static final String DOCUMENT_VERSION_INITIALIZED_BY_GROUP_ADMIN = "Create and publish resource by group admin";
+
+    /**
+     * Create document version initialized by group admin. This is used when group admin creates and publishes resource.
+     *
+     * @param eventSourceType
+     * @return DBDocumentVersion initialized by group admin
+     */
+    public DBDocumentVersion initializeDocumentVersionByGroupAdmin(EventSourceType eventSourceType) {
+
+        return createDocumentVersionForCreate(eventSourceType, DOCUMENT_VERSION_INITIALIZED_BY_GROUP_ADMIN, true);
+    }
+
+
+    /**
+     * Create document version
+     *
+     * @param eventSourceType
+     * @param details
+     * @return
+     */
+    public DBDocumentVersion createDocumentVersionForCreate(EventSourceType eventSourceType,
+                                                            String details, boolean publish) {
+
+        DBDocumentVersion dbDocumentVersion = new DBDocumentVersion();
+        DBDocumentVersionEvent dbEvent = createDocumentVersionEvent(DocumentVersionEventType.CREATE, eventSourceType, details);
+        dbDocumentVersion.addNewDocumentVersionEvent(dbEvent);
+        dbDocumentVersion.setStatus(DocumentVersionStatusType.DRAFT);
+        if (publish) {
+            LOG.debug("Creating And Publish event for document version");
+            publishDocumentVersion(dbDocumentVersion, eventSourceType);
+        }
+        return dbDocumentVersion;
+    }
+
+    /**
+     * Method sets document version status to retired and adds retire event to the document version list of events
+     *
+     * @param dbDocumentVersion document version to be retired
+     * @param eventSourceType   event source type
+     * @param details           details of the event
+     */
+    public void retireDocumentVersion(DBDocumentVersion dbDocumentVersion, EventSourceType eventSourceType, String details) {
+        DBDocumentVersionEvent dbEvent = createDocumentVersionEvent(DocumentVersionEventType.RETIRE, eventSourceType, details);
+        dbDocumentVersion.addNewDocumentVersionEvent(dbEvent);
+        dbDocumentVersion.setStatus(DocumentVersionStatusType.RETIRED);
+    }
+
+    /**
+     * Method sets document version status to published and adds Publish event to the document version list of events
+     *
+     * @param dbDocumentVersion document version to be published
+     * @param eventSourceType   event source type
+     */
+    public void publishDocumentVersion(DBDocumentVersion dbDocumentVersion, EventSourceType eventSourceType) {
+        DBDocumentVersionEvent dbEvent = createDocumentVersionEvent(DocumentVersionEventType.PUBLISH, eventSourceType, null);
+        dbDocumentVersion.addNewDocumentVersionEvent(dbEvent);
+        dbDocumentVersion.setStatus(DocumentVersionStatusType.PUBLISHED);
+    }
+
+    /**
+     * Method sets document version status to approved and add new event to list of events
+     * It also submits request mails to the review requesters
+     *
+     * @param dbDocumentVersion document version to be resource administrators
+     * @param eventSourceType   event source type
+     * @param message           message to be sent to the resource administrators
+     */
+    public void rejectDocumentVersion(DBDocumentVersion dbDocumentVersion, EventSourceType eventSourceType, String message) {
+        DBDocumentVersionEvent dbEvent = createDocumentVersionEvent(DocumentVersionEventType.REJECT, eventSourceType, message);
+        dbDocumentVersion.addNewDocumentVersionEvent(dbEvent);
+        dbDocumentVersion.setStatus(DocumentVersionStatusType.REJECTED);
+    }
+
+    /**
+     * Method sets document version status to approved and add new event to list of events
+     * It also submits request mails to the review requesters
+     *
+     * @param dbDocumentVersion document version to be resource administrators
+     * @param eventSourceType   event source type
+     * @param message           message to be sent to the resource administrators
+     */
+    public void approveDocumentVersion(DBDocumentVersion dbDocumentVersion, EventSourceType eventSourceType, String message) {
+        DBDocumentVersionEvent dbEvent = createDocumentVersionEvent(DocumentVersionEventType.APPROVE, eventSourceType, message);
+        dbDocumentVersion.addNewDocumentVersionEvent(dbEvent);
+        dbDocumentVersion.setStatus(DocumentVersionStatusType.APPROVED);
+    }
+
+    /**
+     * Method sets document version status to under_review and add new event to list of events
+     * It also submits request mails to the reviewers
+     *
+     * @param dbDocumentVersion document version to be reviewed
+     * @param eventSourceType   event source type
+     */
+    public void requestReviewDocumentVersion(DBDocumentVersion dbDocumentVersion, EventSourceType eventSourceType) {
+        DBDocumentVersionEvent dbEvent = createDocumentVersionEvent(DocumentVersionEventType.REQUEST_REVIEW, eventSourceType, null);
+        dbDocumentVersion.addNewDocumentVersionEvent(dbEvent);
+        dbDocumentVersion.setStatus(DocumentVersionStatusType.UNDER_REVIEW);
+    }
+
+
+    /**
+     * Create document version event
+     *
+     * @param eventType
+     * @param eventSourceType
+     * @param details
+     * @return
+     */
+    public DBDocumentVersionEvent createDocumentVersionEvent(DocumentVersionEventType eventType,
+                                                             EventSourceType eventSourceType,
+                                                             String details) {
+
+        SMPUserDetails userDetails = SessionSecurityUtils.getSessionUserDetails();
+        DBDocumentVersionEvent dbEvent = new DBDocumentVersionEvent();
+
+        if (userDetails != null && userDetails.getUser() != null) {
+            dbEvent.setUsername(userDetails.getUser().getUsername());
+        } else {
+            LOG.debug("User details not found for event creation ");
+        }
+
+        dbEvent.setEventOn(OffsetDateTime.now());
+        dbEvent.setEventType(eventType);
+        dbEvent.setEventSourceType(eventSourceType);
+        dbEvent.setDetails(details);
+        return dbEvent;
+    }
+
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java
index 50f35d88d0f5c6e244638631173c53eb0a6bf880..d4c8e3081342c07b54e05eede1dc4bee0f5a3bdb 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -20,7 +20,9 @@ package eu.europa.ec.edelivery.smp.services.resource;
 
 
 import eu.europa.ec.edelivery.smp.data.dao.GroupDao;
+import eu.europa.ec.edelivery.smp.data.dao.ResourceDao;
 import eu.europa.ec.edelivery.smp.data.dao.ResourceMemberDao;
+import eu.europa.ec.edelivery.smp.data.enums.EventSourceType;
 import eu.europa.ec.edelivery.smp.data.model.DBDomain;
 import eu.europa.ec.edelivery.smp.data.model.DBGroup;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument;
@@ -67,16 +69,21 @@ public class ResourceHandlerService extends AbstractResourceHandler {
     final ResourceMemberDao resourceMemberDao;
     final GroupDao groupDao;
     final SMLIntegrationService integrationService;
+    final DocumentVersionService documentVersionService;
+    private final ResourceDao resourceDao;
 
     public ResourceHandlerService(List<ResourceDefinitionSpi> resourceDefinitionSpiList,
                                   ResourceMemberDao resourceMemberDao,
                                   GroupDao groupDao,
                                   ResourceStorage resourceStorage,
-                                  SMLIntegrationService integrationService) {
+                                  SMLIntegrationService integrationService,
+                                  DocumentVersionService documentVersionService, ResourceDao resourceDao) {
         super(resourceDefinitionSpiList, resourceStorage);
         this.resourceMemberDao = resourceMemberDao;
         this.groupDao = groupDao;
         this.integrationService = integrationService;
+        this.documentVersionService = documentVersionService;
+        this.resourceDao = resourceDao;
     }
 
     public void readResource(ResourceRequest resourceRequest,
@@ -164,7 +171,7 @@ public class ResourceHandlerService extends AbstractResourceHandler {
                     () -> resolvedData.getResourceDef().getMimeType()));
         }
         // create new document version
-        DBDocumentVersion documentVersion = new DBDocumentVersion();
+        DBDocumentVersion documentVersion = documentVersionService.initializeDocumentVersionByGroupAdmin(EventSourceType.REST_API);
         documentVersion.setContent(baos.toByteArray());
         DBResource managedResource = resourceStorage.addDocumentVersionForResource(resource, documentVersion);
 
@@ -236,7 +243,7 @@ public class ResourceHandlerService extends AbstractResourceHandler {
                     () -> resolvedData.getResourceDef().getMimeType()));
         }
         // create new document version
-        DBDocumentVersion documentVersion = new DBDocumentVersion();
+        DBDocumentVersion documentVersion = documentVersionService.initializeDocumentVersionByGroupAdmin(EventSourceType.REST_API);
         documentVersion.setContent(baos.toByteArray());
         resourceStorage.addDocumentVersionForSubresource(resolvedSubresource, documentVersion);
 
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java
index 9d8aba290f9a907848f55aa96a07169e9c7f6ff9..f348d62c29b2ae4b7f319e4c3e2f2869c5ffea76 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java
@@ -22,6 +22,9 @@ package eu.europa.ec.edelivery.smp.services.resource;
 import eu.europa.ec.edelivery.smp.data.dao.DocumentDao;
 import eu.europa.ec.edelivery.smp.data.dao.ResourceDao;
 import eu.europa.ec.edelivery.smp.data.dao.SubresourceDao;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionEventType;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+import eu.europa.ec.edelivery.smp.data.enums.EventSourceType;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBResource;
@@ -51,11 +54,13 @@ public class ResourceStorage {
     final DocumentDao documentDao;
     final ResourceDao resourceDao;
     final SubresourceDao subresourceDao;
+    private final DocumentVersionService documentVersionService;
 
-    public ResourceStorage(DocumentDao documentDao, ResourceDao resourceDao, SubresourceDao subresourceDao) {
+    public ResourceStorage(DocumentDao documentDao, ResourceDao resourceDao, SubresourceDao subresourceDao, DocumentVersionService documentVersionService) {
         this.documentDao = documentDao;
         this.resourceDao = resourceDao;
         this.subresourceDao = subresourceDao;
+        this.documentVersionService = documentVersionService;
     }
 
     public byte[] getDocumentContentForResource(DBResource dbResource) {
@@ -145,6 +150,13 @@ public class ResourceStorage {
             resource.setDocument(new DBDocument());
         }
         DBResource managedResource = resource.getId() != null ? resourceDao.find(resource.getId()) : resourceDao.merge(resource);
+        DBDocument document = managedResource.getDocument();
+
+            // if document is not and alread have published version, retire it
+        document.getDocumentVersions().stream().filter(v -> v.getStatus() == DocumentVersionStatusType.PUBLISHED)
+                .forEach(documentVersion -> documentVersionService.retireDocumentVersion(documentVersion, EventSourceType.REST_API, null));
+
+
         managedResource.getDocument().addNewDocumentVersion(version);
         return managedResource;
     }
@@ -156,6 +168,12 @@ public class ResourceStorage {
             subresource.setDocument(new DBDocument());
         }
         DBSubresource managedResource = subresource.getId() != null ? subresourceDao.find(subresource.getId()) : subresourceDao.merge(subresource);
+        DBDocument document = managedResource.getDocument();
+        // retire existing published version
+        document.getDocumentVersions().stream()
+                .filter(v -> v.getStatus() == DocumentVersionStatusType.PUBLISHED)
+                .forEach(v -> {v.setStatus(DocumentVersionStatusType.RETIRED);
+                    v.getDocumentVersionEvents().add(documentVersionService.createDocumentVersionEvent(DocumentVersionEventType.RETIRE, EventSourceType.REST_API, null));});
         managedResource.getDocument().addNewDocumentVersion(version);
         return managedResource;
     }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java
index fe70089bf84bc5c88b29143e2ec0e7f47ea43354..d992be9dfbd613ece82856091eb2413d7d27f16b 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java
@@ -22,32 +22,40 @@ import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum;
 import eu.europa.ec.edelivery.smp.data.dao.DocumentDao;
 import eu.europa.ec.edelivery.smp.data.dao.ResourceDao;
 import eu.europa.ec.edelivery.smp.data.dao.SubresourceDao;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionEventType;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+import eu.europa.ec.edelivery.smp.data.enums.EventSourceType;
 import eu.europa.ec.edelivery.smp.data.model.DBDomainResourceDef;
 import eu.europa.ec.edelivery.smp.data.model.doc.*;
 import eu.europa.ec.edelivery.smp.data.model.ext.DBSubresourceDef;
-import eu.europa.ec.edelivery.smp.data.ui.DocumentPropertyRO;
-import eu.europa.ec.edelivery.smp.data.ui.DocumentRO;
+import eu.europa.ec.edelivery.smp.data.ui.*;
 import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
+import eu.europa.ec.edelivery.smp.services.resource.DocumentVersionService;
 import eu.europa.ec.edelivery.smp.services.resource.ResourceHandlerService;
 import eu.europa.ec.edelivery.smp.services.spi.data.SpiResponseData;
+import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils;
 import eu.europa.ec.smp.spi.api.model.RequestData;
 import eu.europa.ec.smp.spi.api.model.ResponseData;
 import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType;
 import eu.europa.ec.smp.spi.exceptions.ResourceException;
 import eu.europa.ec.smp.spi.resource.ResourceHandlerSpi;
 import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.springframework.core.convert.ConversionService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*;
 
@@ -55,16 +63,24 @@ import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*;
 public class UIDocumentService {
 
     private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIDocumentService.class);
-    ResourceDao resourceDao;
-    SubresourceDao subresourceDao;
-    DocumentDao documentDao;
-    ResourceHandlerService resourceHandlerService;
-
-    public UIDocumentService(ResourceDao resourceDao, SubresourceDao subresourceDao, DocumentDao documentDao, ResourceHandlerService resourceHandlerService) {
+    final ResourceDao resourceDao;
+    final SubresourceDao subresourceDao;
+    final DocumentDao documentDao;
+    final ResourceHandlerService resourceHandlerService;
+    final DocumentVersionService documentVersionService;
+    final ConversionService conversionService;
+
+    public UIDocumentService(ResourceDao resourceDao,
+                             SubresourceDao subresourceDao,
+                             DocumentDao documentDao,
+                             ResourceHandlerService resourceHandlerService,
+                             DocumentVersionService documentVersionService, ConversionService conversionService) {
         this.resourceDao = resourceDao;
         this.subresourceDao = subresourceDao;
         this.documentDao = documentDao;
         this.resourceHandlerService = resourceHandlerService;
+        this.documentVersionService = documentVersionService;
+        this.conversionService = conversionService;
     }
 
     @Transactional
@@ -94,29 +110,214 @@ public class UIDocumentService {
     }
 
     @Transactional
-    public DocumentRO generateDocumentForResource(Long resourceId, DocumentRO documentRo) {
+    public DocumentRO publishDocumentVersionForResource(Long resourceId, Long documentId, int version) {
+        LOG.info("Publish Document For Resource [{}], version [{}]", resourceId, version);
+        DBResource resource = resourceDao.find(resourceId);
+        DBDocument document = resource.getDocument();
+        if (!Objects.equals(document.getId() ,documentId)) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentIdMismatch", "Document id does not match the resource document id");
+        }
+        return publishDocumentVersion(document, version, resource.isReviewEnabled(), getInitialProperties(resource));
+    }
+
+    @Transactional
+    public DocumentRO publishDocumentVersionForSubresource(Long subresourceId, Long resourceId, Long documentId, int version) {
+        LOG.info("Publish Document For subresource [{}], resource [{}], version [{}]", subresourceId, resourceId, version);
+
+        DBSubresource subresource = subresourceDao.find(subresourceId);
+        DBResource resource = resourceDao.find(resourceId);
+        DBDocument document = subresource.getDocument();
+        if (!Objects.equals(document.getId() ,documentId)) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentIdMismatch", "Document id does not match the resource document id");
+        }
+        return publishDocumentVersion(document, version, resource.isReviewEnabled(), getInitialProperties(subresource));
+    }
+
+
+    private DocumentRO publishDocumentVersion(DBDocument document, int version, boolean isReviewEnabled, List<DocumentPropertyRO> initialProperties) {
+
+        DBDocumentVersion documentVersion = document.getDocumentVersions().stream()
+                .filter(dv -> dv.getVersion() == version)
+                .findFirst().orElse(null);
+        if (documentVersion == null) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentVersionNotFound", "Document version not found");
+        }
+        if (isReviewEnabled && documentVersion.getStatus() != DocumentVersionStatusType.APPROVED) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentVersionAlreadyPublished", "Document version has wrong status");
+        }
+        if (document.getDocumentVersions() != null && document.getCurrentVersion() == version) {
+            LOG.warn("Document version [{}] is already current version for the document [{}]", version, document.getId());
+            return convertWithVersion(document, version, initialProperties);
+        }
+        //retire all other versions
+        document.getDocumentVersions().stream()
+                .filter(dv -> dv.getVersion() != version)
+                .filter(dv -> dv.getStatus() == DocumentVersionStatusType.PUBLISHED)
+                .forEach(dv -> documentVersionService.retireDocumentVersion(dv, EventSourceType.UI, "Retire document version"));
+        document.setCurrentVersion(documentVersion.getVersion());
+        documentVersionService.publishDocumentVersion(documentVersion, EventSourceType.UI);
+        // return the document with the new version
+        return convertWithVersion(document, version, initialProperties);
+    }
+
+    @Transactional
+    public DocumentRO requestReviewDocumentVersionForResource(Long resourceId, Long documentId, int version) {
+        LOG.info("Request review Document For Resource [{}], version [{}]", resourceId, version);
+        DBResource resource = resourceDao.find(resourceId);
+        DBDocument document = resource.getDocument();
+        if (!Objects.equals(document.getId() ,documentId)) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentIdMismatch", "Document id does not match the resource document id");
+        }
+        return requestReviewDocumentVersion(document, version, resource.isReviewEnabled(), getInitialProperties(resource));
+    }
+
+    @Transactional
+    public DocumentRO requestReviewDocumentVersionForSubresource(Long subresourceId, Long resourceId, Long documentId, int version) {
+        LOG.info("Request review Document For subResource [{}], resource [{}],  version [{}]", subresourceId, resourceId, version);
+        DBResource resource = resourceDao.find(resourceId);
+        DBSubresource subresource = subresourceDao.find(subresourceId);
+        DBDocument document = subresource.getDocument();
+        if (!Objects.equals(document.getId(), documentId)) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentIdMismatch", "Document id does not match the resource document id");
+        }
+        return requestReviewDocumentVersion(document, version, resource.isReviewEnabled(), getInitialProperties(subresource));
+    }
+
+
+    private DocumentRO requestReviewDocumentVersion(DBDocument document, int version, boolean isReviewEnabled, List<DocumentPropertyRO> initialProperties) {
+        DBDocumentVersion documentVersion = document.getDocumentVersions().stream()
+                .filter(dv -> dv.getVersion() == version)
+                .findFirst().orElse(null);
+        if (documentVersion == null) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentVersionNotFound", "Document version not found");
+        }
+
+        if (!isReviewEnabled) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentReviewNotEnabled", "Document Review is not enabled for the document");
+        }
+
+        if (documentVersion.getStatus() == DocumentVersionStatusType.PUBLISHED) {
+            LOG.warn("Document version [{}] request review action for document [{}] is not allowed. Wrong status", version, document.getId());
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentReviewNotAllowed", "Document Review is not allowed for the document");
+        }
+
+        if (documentVersion.getStatus() == DocumentVersionStatusType.UNDER_REVIEW) {
+            LOG.warn("Document version review [{}] for document [{}] is already under review", version, document.getId());
+            return convertWithVersion(document, version, initialProperties);
+        }
+        //retire all other versions
+        documentVersionService.requestReviewDocumentVersion(documentVersion, EventSourceType.UI);
+        // return the document with the new version
+
+        return convertWithVersion(document, version, initialProperties);
+    }
+
+    @Transactional
+    public DocumentRO reviewActionDocumentVersionForResource(Long resourceId, Long documentId, int version, DocumentVersionEventType action, String message) {
+        LOG.info("Approve review Document version For Resource [{}], version [{}]", resourceId, version);
+        DBResource resource = resourceDao.find(resourceId);
+        DBDocument document = resource.getDocument();
+        if (!Objects.equals(document.getId(), documentId)) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentIdMismatch", "Document id does not match the resource document id");
+        }
+        return reviewActionDocumentVersion(document, version, resource.isReviewEnabled(), action, message, getInitialProperties(resource));
+    }
+
+    @Transactional
+    public DocumentRO reviewActionDocumentVersionForSubresource(Long subresourceId, Long resourceId, Long documentId, int version, DocumentVersionEventType action, String message) {
+        LOG.info("Approve review Document version For subResource [{}], resource [{}], version [{}]", subresourceId, resourceId, version);
+        DBResource resource = resourceDao.find(resourceId);
+        DBSubresource subresource = subresourceDao.find(subresourceId);
+        DBDocument document = subresource.getDocument();
+        if (!Objects.equals(document.getId(), documentId)) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentIdMismatch", "Document id does not match the subresource document id");
+        }
+        return reviewActionDocumentVersion(document, version, resource.isReviewEnabled(), action, message, getInitialProperties(subresource));
+    }
+
+
+    private DocumentRO reviewActionDocumentVersion(DBDocument document,
+                                                   int version,
+                                                   boolean reviewEnabled,
+                                                   DocumentVersionEventType action,
+                                                   String message,
+                                                   List<DocumentPropertyRO> initialProperties) {
+
+        DBDocumentVersion documentVersion = document.getDocumentVersions().stream()
+                .filter(dv -> dv.getVersion() == version)
+                .findFirst()
+                .orElseThrow(() -> new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentVersionNotFound", "Document version not found"));
+
+        if (!reviewEnabled) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentReviewNotEnabled", "Document Review is not enabled for the document");
+        }
+
+        if (documentVersion.getStatus() != DocumentVersionStatusType.UNDER_REVIEW
+                && documentVersion.getStatus() != DocumentVersionStatusType.APPROVED) {
+            LOG.warn("Document version [{}]  action for document [{}] not allowed. Wrong status", version, initialProperties);
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentReviewActionNotAllowed", "Document Review action is not allowed for the document");
+        }
+
+        if (action == DocumentVersionEventType.APPROVE) {
+            documentVersionService.approveDocumentVersion(documentVersion, EventSourceType.UI, message);
+        } else if (action == DocumentVersionEventType.REJECT) {
+            documentVersionService.rejectDocumentVersion(documentVersion, EventSourceType.UI, message);
+        } else {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentReviewActionNotAllowed", "Document Review action is not allowed for the document");
+        }
+        // return the document with the new version
+        return convertWithVersion(document, version, initialProperties);
+    }
+
+    @Transactional
+    public DocumentRO generateDocumentForResource(Long resourceId) {
         LOG.info("generate Document For Resource");
         DBResource resource = resourceDao.find(resourceId);
+        // generate document and write to output stream
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        generateDocumentForResource(resource, bos);
+
+        String genDoc = bos.toString();
+        DocumentRO result = new DocumentRO();
+        result.setPayload(genDoc);
+        return result;
+    }
+
+    public void generateDocumentForResource(DBResource resource, OutputStream outputStream) {
+        LOG.info("generate new Document For domainResourceDef");
         DBDomainResourceDef domainResourceDef = resource.getDomainResourceDef();
+
         ResourceHandlerSpi resourceHandler = resourceHandlerService.getResourceHandler(domainResourceDef.getResourceDef());
         RequestData data = resourceHandlerService.buildRequestDataForResource(domainResourceDef.getDomain(),
                 resource, null);
 
-        return generateDocumentWithHandler(resourceHandler, data);
+        generateDocumentWithHandler(resourceHandler, data, outputStream);
     }
 
     @Transactional
-    public DocumentRO generateDocumentForSubresource(Long subresourceId, Long resourceId, DocumentRO documentRo) {
-        LOG.info("generate Document For Subresource");
+    public DocumentRO generateDocumentForSubresource(Long subresourceId, Long resourceId) {
+        LOG.info("generate Document For Subresource identifier [{}]", subresourceId);
         DBResource parentEntity = resourceDao.find(resourceId);
         DBSubresource entity = subresourceDao.find(subresourceId);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        generateDocumentForSubresource(parentEntity, entity, bos);
+
+        String genDoc = bos.toString();
+        DocumentRO result = new DocumentRO();
+        result.setPayload(genDoc);
+        return result;
+    }
+
+    public void generateDocumentForSubresource(DBResource parentEntity, DBSubresource entity, OutputStream outputStream) {
+        LOG.info("generate Document For Subresource");
         DBSubresourceDef subresourceDef = entity.getSubresourceDef();
 
         ResourceHandlerSpi resourceHandler = resourceHandlerService.getSubresourceHandler(subresourceDef, subresourceDef.getResourceDef());
         RequestData data = resourceHandlerService.buildRequestDataForSubResource(parentEntity.getDomainResourceDef().getDomain(),
                 parentEntity, entity, null);
 
-        return generateDocumentWithHandler(resourceHandler, data);
+        generateDocumentWithHandler(resourceHandler, data, outputStream);
     }
 
     /**
@@ -126,30 +327,28 @@ public class UIDocumentService {
      * @param data            request data
      * @return DocumentRo with payload
      */
-    private DocumentRO generateDocumentWithHandler(ResourceHandlerSpi resourceHandler, RequestData data) {
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        ResponseData responseData = new SpiResponseData(bos);
+    private void generateDocumentWithHandler(ResourceHandlerSpi resourceHandler, RequestData data, OutputStream outputStream) {
+
+        ResponseData responseData = new SpiResponseData(outputStream);
         try {
             resourceHandler.generateResource(data, responseData, Collections.emptyList());
         } catch (ResourceException e) {
             throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "StoreResourceValidation", ExceptionUtils.getRootCauseMessage(e));
         }
-        String genDoc = bos.toString();
-        DocumentRO result = new DocumentRO();
-        result.setPayload(genDoc);
-        return result;
     }
 
     @Transactional
-    public DocumentRO saveDocumentForResource(Long resourceId, DocumentRO documentRo) {
+    public DocumentRO saveDocumentForResource(Long resourceId, DocumentRO documentRo, Long documentReference) {
 
         final DBResource resource = resourceDao.find(resourceId);
         final DBDocument document = resource.getDocument();
 
+        int returnDocVersion = document.getCurrentVersion();
         boolean isPayloadChanged = documentRo.getPayloadStatus() != EntityROStatus.PERSISTED.getStatusNumber();
         if (isPayloadChanged) {
             LOG.debug("Store resource payload for resource [{}]", resource.getIdentifierValue());
-            storeResourcePayload(resource, document, documentRo);
+            DBDocumentVersion docVersion = storeResourcePayload(resource, document, documentRo);
+            returnDocVersion = docVersion.getVersion();
         }
 
         if (isDocumentPropertiesChanged(documentRo)) {
@@ -159,7 +358,33 @@ public class UIDocumentService {
                     .forEach(p -> persistDocumentProperty(p, document));
         }
 
-        return convertWithVersion(document, document.getCurrentVersion(), getInitialProperties(resource));
+        if (Boolean.TRUE.equals(documentRo.getSharingEnabled()) && documentReference!=null) {
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentSharingNotAllowed", "Document sharing is not allowed for the document with reference document");
+        }
+        document.setSharingEnabled(documentRo.getSharingEnabled());
+
+        DBDocument documentReferenceEntity = null;
+        if (documentReference != null) {
+            if (Objects.equals(documentReference, document.getId())) {
+                throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentReferenceNotAllowed", "Document reference cannot be the same as the document id");
+            }
+            documentReferenceEntity = documentDao.find(documentReference);
+            if (documentReferenceEntity == null) {
+                throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentReferenceNotFound", "Document reference not found");
+            }
+
+            if (documentReferenceEntity.getSharingEnabled() != null) {
+                throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentReferenceNotValid", "Can not reference to not shared document");
+            }
+
+            if (documentReferenceEntity.getReferenceDocument() != null) {
+                throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentReferenceNotValid", "Can not reference to a document that already has a reference");
+            }
+            document.setReferenceDocument(documentReferenceEntity);
+        } else {
+            document.setReferenceDocument(null);
+        }
+        return convertWithVersion(document, returnDocVersion, getInitialProperties(resource));
     }
 
 
@@ -245,14 +470,16 @@ public class UIDocumentService {
     }
 
     /**
-     * Method stores the payload for the given resource as a new payload version.
+     * Method stores the payload for the given resource. If the resource has status New then new document version is created
+     * else the existing document version is updated with the new payload.
+     * <p>
      * The method invokes the ResourceHandlerSpi to update/validate the payload before storing it to database.
      *
      * @param resource   resource to store the payload
      * @param document   the resource database document entity
      * @param documentRo document RO the with new payload
      */
-    private void storeResourcePayload(DBResource resource, DBDocument document, DocumentRO documentRo) {
+    private DBDocumentVersion storeResourcePayload(DBResource resource, DBDocument document, DocumentRO documentRo) {
         DBDomainResourceDef domainResourceDef = resource.getDomainResourceDef();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         // invoke the resource handler for the document type
@@ -266,18 +493,39 @@ public class UIDocumentService {
         } catch (ResourceException e) {
             throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "StoreResourceValidation", ExceptionUtils.getRootCauseMessage(e));
         }
+        DBDocumentVersion documentVersion = null;
+        if (documentRo.getPayloadVersion() == null) {
+            documentVersion = createNewDocumentVersionForResource(resource, document, baos.toByteArray());
+        } else {
+            documentVersion = document.getDocumentVersions().stream()
+                    .filter(dv -> dv.getVersion() == documentRo.getPayloadVersion())
+                    .findFirst().orElse(null);
+            if (documentVersion == null) {
+                throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DocumentVersionNotFound", "Document version not found");
+            }
+            documentVersion.setContent(baos.toByteArray());
+        }
+        return documentVersion;
+
+    }
+
+    public DBDocumentVersion createNewDocumentVersionForResource(DBResource resource, DBDocument document, byte[] payload) {
         // create new version to document
         int version = document.getDocumentVersions().stream().mapToInt(DBDocumentVersion::getVersion)
                 .max().orElse(0);
 
-        DBDocumentVersion documentVersion = new DBDocumentVersion();
+        DBDocumentVersion documentVersion = documentVersionService.createDocumentVersionForCreate(EventSourceType.UI, "Create and publish resource by group admin", false);
         documentVersion.setVersion(version + 1);
         documentVersion.setDocument(document);
-        documentVersion.setContent(baos.toByteArray());
+        documentVersion.setContent(payload);
+        documentVersion.setStatus(DocumentVersionStatusType.DRAFT);
         // to get the current persist time
         documentVersion.prePersist();
-        document.getDocumentVersions().add(documentVersion);
-        document.setCurrentVersion(documentVersion.getVersion());
+        document.getDocumentVersions().add(0, documentVersion);
+        if (Boolean.FALSE.equals(resource.isReviewEnabled())) {
+            document.setCurrentVersion(documentVersion.getVersion());
+        }
+        return documentVersion;
     }
 
     @Transactional
@@ -314,8 +562,6 @@ public class UIDocumentService {
     public DocumentRO getDocumentForResource(Long resourceId, int version) {
         DBResource resource = resourceDao.find(resourceId);
         DBDocument document = resource.getDocument();
-
-
         return convertWithVersion(document, version, getInitialProperties(resource));
     }
 
@@ -365,7 +611,7 @@ public class UIDocumentService {
         // to get the current persist time
         documentVersion.prePersist();
 
-        document.getDocumentVersions().add(documentVersion);
+        document.getDocumentVersions().add(0, documentVersion);
         document.setCurrentVersion(documentVersion.getVersion());
         return convert(document, documentVersion, initialProperties);
     }
@@ -388,18 +634,34 @@ public class UIDocumentService {
         return convert(document, documentVersion, initialProperties);
     }
 
+
+    /**
+     * Convert DBDocument to DocumentRo with given document version
+     *
+     * @param document          to convert to DocumentRo
+     * @param version           to set as version
+     * @param initialProperties
+     * @return
+     */
     public DocumentRO convert(DBDocument document, DBDocumentVersion version, List<DocumentPropertyRO> initialProperties) {
         DocumentRO documentRo = new DocumentRO();
         documentRo.addProperty(DOCUMENT_NAME.getPropertyName(),
                 document.getName(), "Document Name", SMPPropertyTypeEnum.STRING, true);
         documentRo.addProperty(DOCUMENT_MIMETYPE.getPropertyName(),
                 document.getMimeType(), "Document Mimetype", SMPPropertyTypeEnum.STRING, true);
-
+        documentRo.setSharingEnabled(document.getSharingEnabled());
         documentRo.getProperties().addAll(initialProperties);
 
         // set list of versions
-        document.getDocumentVersions().forEach(dv ->
-                documentRo.getAllVersions().add(dv.getVersion()));
+        document.getDocumentVersions().forEach(dv -> {
+            documentRo.getAllVersions().add(dv.getVersion());
+            documentRo.getDocumentVersions().add(conversionService.convert(dv, DocumentVersionRO.class));
+        });
+
+        documentRo.setDocumentId(SessionSecurityUtils.encryptedEntityId(document.getId()));
+        if (document.getReferenceDocument() != null) {
+            documentRo.setReferenceDocumentId(SessionSecurityUtils.encryptedEntityId(document.getReferenceDocument().getId()));
+        }
 
         documentRo.setMimeType(document.getMimeType());
         documentRo.setName(document.getName());
@@ -418,8 +680,52 @@ public class UIDocumentService {
             documentRo.setPayloadCreatedOn(version.getCreatedOn());
             documentRo.setPayloadVersion(version.getVersion());
             documentRo.setPayload(new String(version.getContent()));
+            documentRo.setDocumentVersionStatus(version.getStatus());
+            // set ven
+            version.getDocumentVersionEvents().stream().forEach(e -> {
+                DocumentVersionEventRO eventRo = new DocumentVersionEventRO();
+                eventRo.setEventType(e.getEventType());
+                eventRo.setEventOn(e.getEventOn());
+                eventRo.setUsername(e.getUsername());
+                eventRo.setEventSourceType(e.getEventSourceType());
+                eventRo.setDetails(e.getDetails());
+                documentRo.addDocumentVersionEvent(eventRo);
+            });
         }
 
         return documentRo;
     }
+
+
+    public ServiceResult<ReviewDocumentVersionRO> getDocumentReviewListForUser(
+            Long userId,
+            int page, int pageSize,
+            String sortField,
+            String sortOrder, Object filter) {
+
+        ServiceResult<ReviewDocumentVersionRO> sg = new ServiceResult<>();
+        sg.setPage(page < 0 ? 0 : page);
+        List<DBReviewDocumentVersion> listTask = documentDao.getDocumentReviewListForUser(userId);
+        long iCnt = listTask.size();
+
+        if (pageSize < 0) { // if page size iz -1 return all results and set pageSize to maxCount
+            pageSize = (int) iCnt;
+        }
+        sg.setPageSize(pageSize);
+        sg.setCount(iCnt);
+
+        if (iCnt > 0) {
+            int iStartIndex = pageSize < 0 ? -1 : page * pageSize;
+            if (iStartIndex >= iCnt && page > 0) {
+                page = page - 1;
+                sg.setPage(page); // go back for a page
+                iStartIndex = pageSize < 0 ? -1 : page * pageSize;
+            }
+        }
+
+        List<ReviewDocumentVersionRO> result = listTask.stream().map(resource -> conversionService.convert(resource, ReviewDocumentVersionRO.class))
+                .collect(Collectors.toList());
+        sg.getServiceEntities().addAll(result);
+        return sg;
+    }
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIResourceService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIResourceService.java
index b08214f1b43870f6d8aa76bfbcb1d62b3aa143d7..7441f3305774c621f2fefda9d43af6ac7ea2bee4 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIResourceService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIResourceService.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -18,13 +18,15 @@
  */
 package eu.europa.ec.edelivery.smp.services.ui;
 
-import eu.europa.ec.edelivery.smp.services.IdentifierService;
 import eu.europa.ec.edelivery.smp.data.dao.*;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+import eu.europa.ec.edelivery.smp.data.enums.EventSourceType;
 import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType;
 import eu.europa.ec.edelivery.smp.data.model.DBDomain;
 import eu.europa.ec.edelivery.smp.data.model.DBDomainResourceDef;
 import eu.europa.ec.edelivery.smp.data.model.DBGroup;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument;
+import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBResource;
 import eu.europa.ec.edelivery.smp.data.model.doc.DBResourceFilter;
 import eu.europa.ec.edelivery.smp.data.model.ext.DBResourceDef;
@@ -38,19 +40,23 @@ import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import eu.europa.ec.edelivery.smp.identifiers.Identifier;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
+import eu.europa.ec.edelivery.smp.services.IdentifierService;
 import eu.europa.ec.edelivery.smp.services.SMLIntegrationService;
+import eu.europa.ec.edelivery.smp.services.resource.DocumentVersionService;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.core.convert.ConversionService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.io.ByteArrayOutputStream;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
+import static org.apache.commons.lang3.BooleanUtils.isTrue;
+
 /**
- *
  * @author Joze Rihtarsic
  * @since 5.0
  */
@@ -75,13 +81,16 @@ public class UIResourceService {
     private final IdentifierService identifierService;
     private final ConversionService conversionService;
     private final SMLIntegrationService smlIntegrationService;
+    private final UIDocumentService uiDocumentService;
+    private final DocumentVersionService documentVersionService;
 
 
     public UIResourceService(ResourceDao resourceDao, ResourceMemberDao resourceMemberDao, ResourceDefDao resourceDefDao,
                              DomainResourceDefDao domainResourceDefDao, UserDao userDao, GroupDao groupDao,
                              IdentifierService identifierService,
                              ConversionService conversionService,
-                             SMLIntegrationService smlIntegrationService) {
+                             SMLIntegrationService smlIntegrationService,
+                             UIDocumentService uiDocumentService, DocumentVersionService documentVersionService) {
         this.resourceDao = resourceDao;
         this.resourceMemberDao = resourceMemberDao;
         this.resourceDefDao = resourceDefDao;
@@ -91,6 +100,8 @@ public class UIResourceService {
         this.identifierService = identifierService;
         this.conversionService = conversionService;
         this.smlIntegrationService = smlIntegrationService;
+        this.uiDocumentService = uiDocumentService;
+        this.documentVersionService = documentVersionService;
     }
 
 
@@ -171,7 +182,7 @@ public class UIResourceService {
             throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, ACTION_RESOURCE_DELETE, "Resource does not belong to the group!");
         }
         if (!Objects.equals(resource.getGroup().getDomain().getId(), domainId)) {
-            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, ACTION_RESOURCE_CREATE, "Group does not belong to the given domain!");
+            throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, ACTION_RESOURCE_DELETE, "Group does not belong to the given domain!");
         }
         DBDomain resourceDomain = resource.getGroup().getDomain();
         if (smlIntegrationService.isSMLIntegrationEnabled() &&
@@ -210,19 +221,20 @@ public class UIResourceService {
                 resourceRO.getIdentifierScheme(),
                 resourceRO.getIdentifierValue());
 
-        Optional<DBResource> existResource = resourceDao.getResource(resourceIdentifier.getValue(),resourceIdentifier.getScheme(), optRedef.get(), group.getDomain());
+        Optional<DBResource> existResource = resourceDao.getResource(resourceIdentifier.getValue(), resourceIdentifier.getScheme(), optRedef.get(), group.getDomain());
         if (existResource.isPresent()) {
             throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, ACTION_RESOURCE_CREATE, "Resource [val:" + resourceRO.getIdentifierValue() + " scheme:" + resourceRO.getIdentifierScheme() + "] already exists for domain!");
         }
 
-
         DBResource resource = new DBResource();
         resource.setIdentifierScheme(resourceIdentifier.getScheme());
         resource.setIdentifierValue(resourceIdentifier.getValue());
         resource.setVisibility(resourceRO.getVisibility());
         resource.setGroup(group);
         resource.setDomainResourceDef(optDoredef.get());
-        DBDocument document = createDocumentForResourceDef(optRedef.get());
+        resource.setReviewEnabled(resourceRO.isReviewEnabled());
+
+        DBDocument document = createDocumentForNewResource(resource);
         resource.setDocument(document);
         resourceDao.persist(resource);
         // create first member as admin user
@@ -242,6 +254,15 @@ public class UIResourceService {
         return conversionService.convert(resource, ResourceRO.class);
     }
 
+    /**
+     * Method allows Group admin and Resource admin to change resource visibility and enable/disable review flow.
+     *
+     * @param resourceRO
+     * @param resourceId
+     * @param groupId
+     * @param domainId
+     * @return
+     */
     @Transactional
     public ResourceRO updateResourceForGroup(ResourceRO resourceRO, Long resourceId, Long groupId, Long domainId) {
 
@@ -264,9 +285,13 @@ public class UIResourceService {
             throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, ACTION_RESOURCE_UPDATE, "Resource definition [" + resourceRO.getResourceTypeIdentifier() + "] is not registered for domain!");
         }
 
-        // at the moment only visibility can be updated for the resource
+        // at the moment only visibility and review enabled
+        // can be updated for the resource
         DBResource resource = resourceDao.find(resourceId);
         resource.setVisibility(resourceRO.getVisibility());
+        if (resourceRO.isReviewEnabled() != null) {
+            resource.setReviewEnabled(isTrue(resourceRO.isReviewEnabled()));
+        }
         return conversionService.convert(resource, ResourceRO.class);
     }
 
@@ -295,9 +320,9 @@ public class UIResourceService {
      * Add or update a member to a resource
      *
      * @param resourceId resource id to add member to
-     * @param groupId   group id to add member to
-     * @param memberRO member data
-     * @param memberId member id (optional) if null then add member, if not null then update member
+     * @param groupId    group id to add member to
+     * @param memberRO   member data
+     * @param memberId   member id (optional) if null then add member, if not null then update member
      * @return added member RO
      */
     @Transactional
@@ -312,12 +337,16 @@ public class UIResourceService {
         if (memberId != null) {
             member = resourceMemberDao.find(memberId);
             member.setRole(memberRO.getRoleType());
+            member.setHasPermissionToReview(memberRO.getHasPermissionReview());
         } else {
             DBResource resource = resourceDao.find(resourceId);
             if (resourceMemberDao.isUserResourceMember(user, resource)) {
                 throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "Add membership", "User [" + memberRO.getUsername() + "] is already a member!");
             }
-            member = resourceMemberDao.addMemberToResource(resource, user, memberRO.getRoleType());
+            member = resourceMemberDao.addMemberToResource(resource, user,
+                    memberRO.getRoleType(),
+                    isTrue(memberRO.getHasPermissionReview())
+            );
         }
         return conversionService.convert(member, MemberRO.class);
     }
@@ -349,11 +378,31 @@ public class UIResourceService {
         return resource;
     }
 
-    public DBDocument createDocumentForResourceDef(DBResourceDef resourceDef) {
+    /**
+     * Create document for new resource. Method is called when GroupAdmin creates new resource via
+     * UI.
+     *
+     * @param resource resource to create document for
+     * @return created document
+     */
+    public DBDocument createDocumentForNewResource(DBResource resource) {
+        DBResourceDef domainResourceDef = resource.getDomainResourceDef().getResourceDef();
         DBDocument document = new DBDocument();
+
         document.setCurrentVersion(1);
-        document.setMimeType(resourceDef.getMimeType());
-        document.setName(resourceDef.getName());
+        document.setMimeType(domainResourceDef.getMimeType());
+        document.setName(domainResourceDef.getName());
+        // create first version of the document
+        DBDocumentVersion version = documentVersionService.initializeDocumentVersionByGroupAdmin(EventSourceType.UI);
+        // The first version is always published.
+        version.setStatus(DocumentVersionStatusType.PUBLISHED);
+        version.setDocument(document);
+        version.setVersion(1);
+        // generate document content
+        document.addNewDocumentVersion(version);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        uiDocumentService.generateDocumentForResource(resource, baos);
+        version.setContent(baos.toByteArray());
         return document;
     }
 
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UISubresourceService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UISubresourceService.java
index e5e201640835e76093ef040734eb4052e4fc67ee..83463021ab0da5b29380b0d8853e5cbd2dba408f 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UISubresourceService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UISubresourceService.java
@@ -18,7 +18,10 @@
  */
 package eu.europa.ec.edelivery.smp.services.ui;
 
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+import eu.europa.ec.edelivery.smp.data.enums.EventSourceType;
 import eu.europa.ec.edelivery.smp.data.model.DBDomain;
+import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion;
 import eu.europa.ec.edelivery.smp.services.IdentifierService;
 import eu.europa.ec.edelivery.smp.data.dao.ResourceDao;
 import eu.europa.ec.edelivery.smp.data.dao.SubresourceDao;
@@ -33,10 +36,12 @@ import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import eu.europa.ec.edelivery.smp.identifiers.Identifier;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
+import eu.europa.ec.edelivery.smp.services.resource.DocumentVersionService;
 import org.springframework.core.convert.ConversionService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.io.ByteArrayOutputStream;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -53,26 +58,26 @@ public class UISubresourceService {
     private static final String ACTION_SUBRESOURCE_CREATE = "CreateSubresourceForResource";
     private static final String ACTION_SUBRESOURCE_DELETE = "DeleteSubresourceFromResource";
 
-    private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UISubresourceService.class);
-
     private final SubresourceDao subresourceDao;
-
     private final ResourceDao resourceDao;
     private final SubresourceDefDao subresourceDefDao;
-
-
     private final IdentifierService identifierService;
-
-
+    private final DocumentVersionService documentVersionService;
+    private final UIDocumentService uiDocumentService;
     private final ConversionService conversionService;
 
     public UISubresourceService(SubresourceDao subresourceDao, ResourceDao resourceDao,SubresourceDefDao subresourceDefDao, IdentifierService identifierService,
-                                ConversionService conversionService) {
+                                ConversionService conversionService,
+                                DocumentVersionService documentVersionService,
+                                UIDocumentService uiDocumentService
+    ) {
         this.subresourceDao = subresourceDao;
         this.resourceDao = resourceDao;
         this.subresourceDefDao = subresourceDefDao;
         this.identifierService = identifierService;
         this.conversionService = conversionService;
+        this.documentVersionService = documentVersionService;
+        this.uiDocumentService = uiDocumentService;
     }
 
 
@@ -125,18 +130,31 @@ public class UISubresourceService {
         subresource.setIdentifierValue(docId.getValue());
         subresource.setResource(resParent);
         subresource.setSubresourceDef(optRedef.get());
-        DBDocument document = createDocumentForSubresourceDef(optRedef.get());
+        DBDocument document = createDocumentForSubresourceDef(optRedef.get(), subresource, resParent);
         subresource.setDocument(document);
         subresourceDao.persist(subresource);
         // create first member as admin user
         return conversionService.convert(subresource, SubresourceRO.class);
     }
 
-    public DBDocument createDocumentForSubresourceDef(DBSubresourceDef subresourceDef) {
+    public DBDocument createDocumentForSubresourceDef(DBSubresourceDef subresourceDef, DBSubresource subresource, DBResource resource) {
         DBDocument document = new DBDocument();
         document.setCurrentVersion(1);
         document.setMimeType(subresourceDef.getMimeType());
         document.setName(subresourceDef.getName());
+
+
+        // create first version of the document
+        DBDocumentVersion version = documentVersionService.initializeDocumentVersionByGroupAdmin(EventSourceType.UI);
+
+        version.setStatus(DocumentVersionStatusType.PUBLISHED);
+        version.setDocument(document);
+        version.setVersion(1);
+        // generate document content
+        document.addNewDocumentVersion(version);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        uiDocumentService.generateDocumentForSubresource(resource,subresource, baos);
+        version.setContent(baos.toByteArray());
         return document;
     }
 }
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java
index 66e0beceafb33dbd77aff3deb9b49dcf2039ef14..d595707da01b981542f1ddaf92342cac6f6afb1f 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java
@@ -18,11 +18,14 @@
  */
 package eu.europa.ec.edelivery.smp.data.dao;
 
-import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument;
-import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentProperty;
-import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionStatusType;
+import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType;
+import eu.europa.ec.edelivery.smp.data.enums.VisibilityType;
+import eu.europa.ec.edelivery.smp.data.model.doc.*;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import javax.transaction.Transactional;
@@ -39,6 +42,7 @@ class DocumentDaoTest extends AbstractBaseDao {
     @BeforeEach
     public void prepareDatabase() {
         testUtilsDao.clearData();
+        testUtilsDao.createResourceMemberships();
         testUtilsDao.createSubresources();
     }
 
@@ -79,7 +83,8 @@ class DocumentDaoTest extends AbstractBaseDao {
         assertTrue(result.isPresent());
         // the default setup  createResources  sets two versions (0 and 1 ) with current version 1
         assertEquals(2, result.get().getVersion());
-        assertEquals(testUtilsDao.getDocumentD1G1RD1().getDocumentVersions().get(1), result.get());
+        // note that the versions are ordered by version desc
+        assertEquals(testUtilsDao.getDocumentD1G1RD1().getDocumentVersions().get(0), result.get());
     }
 
 
@@ -98,7 +103,8 @@ class DocumentDaoTest extends AbstractBaseDao {
         assertTrue(result.isPresent());
         // the default setup  createResources  sets two versions (1 and 2 ) with current version 2
         assertEquals(2, result.get().getVersion());
-        assertEquals(testUtilsDao.getDocumentD1G1RD1_S1().getDocumentVersions().get(1), result.get());
+        // note that the versions are ordered by version desc
+        assertEquals(testUtilsDao.getDocumentD1G1RD1_S1().getDocumentVersions().get(0), result.get());
     }
 
     @Test
@@ -120,4 +126,41 @@ class DocumentDaoTest extends AbstractBaseDao {
         assertEquals(2, result.getDocumentProperties().size());
         assertEquals(property1, result.getDocumentProperties().get(0));
     }
+
+    @Test
+    void testPersistUnderReviewDocument() {
+        DBResource createResourceWithStatusReview = testUtilsDao.createResource("review", "1-1-1", VisibilityType.PUBLIC,
+                DocumentVersionStatusType.UNDER_REVIEW,
+                testUtilsDao.getDomainResourceDefD1R1(), testUtilsDao.getGroupD1G1());
+
+        testUtilsDao.createResourceMembership(MembershipRoleType.ADMIN, testUtilsDao.getUser1(), createResourceWithStatusReview, true);
+
+        List<DBReviewDocumentVersion> dbDocumentVersions =  testInstance.getDocumentReviewListForUser(testUtilsDao.getUser1().getId());
+        assertEquals(1, dbDocumentVersions.size());
+
+    }
+
+    @ParameterizedTest
+    @CsvSource({
+            "UNDER_REVIEW, UNDER_REVIEW, 2",
+            "UNDER_REVIEW, DRAFT, 1",
+            "DRAFT, UNDER_REVIEW, 1",
+            "DRAFT, DRAFT, 0"
+    })
+    void testPersistUnderReviewDocumentSubresource(String resourceStatus, String subresourceStatus, int expectedSize) {
+        testUtilsDao.createResourceDefinitions();
+        DBResource resource = testUtilsDao.createResource("review", "1-1-1", VisibilityType.PUBLIC,
+                DocumentVersionStatusType.valueOf(resourceStatus),
+                testUtilsDao.getDomainResourceDefD1R1(), testUtilsDao.getGroupD1G1());
+
+        DBSubresource subres = testUtilsDao.createSubresource(resource, "1-1-1", "1-1-1",
+                DocumentVersionStatusType.valueOf(subresourceStatus),
+                testUtilsDao.getSubresourceDefSmpMetadata());
+
+        testUtilsDao.createResourceMembership(MembershipRoleType.ADMIN, testUtilsDao.getUser1(), resource, true);
+
+        List<DBReviewDocumentVersion> dbDocumentVersions =  testInstance.getDocumentReviewListForUser(testUtilsDao.getUser1().getId());
+        assertEquals(expectedSize, dbDocumentVersions.size());
+
+    }
 }
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/TestUtilsDao.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/TestUtilsDao.java
index 7eba22bdaefc782ae263f90a86061c34979e268c..e96d569a97dccfb84246ea8a1d69d3ced2f5bc85 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/TestUtilsDao.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/TestUtilsDao.java
@@ -19,10 +19,7 @@
 package eu.europa.ec.edelivery.smp.data.dao;
 
 import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum;
-import eu.europa.ec.edelivery.smp.data.enums.CredentialTargetType;
-import eu.europa.ec.edelivery.smp.data.enums.CredentialType;
-import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType;
-import eu.europa.ec.edelivery.smp.data.enums.VisibilityType;
+import eu.europa.ec.edelivery.smp.data.enums.*;
 import eu.europa.ec.edelivery.smp.data.model.DBDomain;
 import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration;
 import eu.europa.ec.edelivery.smp.data.model.DBDomainResourceDef;
@@ -109,8 +106,6 @@ public class TestUtilsDao {
     DBResourceMember resourceMemberU1R2_D2G1RD1_Viewer;
 
     DBResource resourcePrivateD1G1RD1;
-   // DBResource resourceInternalD1G1RD1;
-
     DBExtension extension;
 
     boolean searchDataCreated = false;
@@ -152,7 +147,6 @@ public class TestUtilsDao {
         resourceMemberU1R2_D2G1RD1_Viewer = null;
 
         resourcePrivateD1G1RD1 = null;
-        //resourceInternalD1G1RD1 = null;
 
         extension = null;
         searchDataCreated = false;
@@ -410,9 +404,15 @@ public class TestUtilsDao {
 
     @Transactional
     public DBResourceMember createResourceMembership(MembershipRoleType roleType, DBUser user, DBResource resource){
+        return createResourceMembership(roleType, user, resource, false);
+    }
+
+    @Transactional
+    public DBResourceMember createResourceMembership(MembershipRoleType roleType, DBUser user, DBResource resource, boolean hasPermissionToReview){
         DBResourceMember member = new DBResourceMember();
         member.setRole(roleType);
         member.setUser(user);
+        member.setHasPermissionToReview(hasPermissionToReview);
         member.setResource(resource);
         persistFlushDetach(member);
         assertNotNull(member.getId());
@@ -457,18 +457,58 @@ public class TestUtilsDao {
     }
 
     @Transactional
-    public DBResource createResource(String identifier, String schema, VisibilityType visibilityType, DBDomainResourceDef domainResourceDef, DBGroup group) {
+    public DBResource createResource(String identifier, String schema,
+                                     VisibilityType visibilityType,
+                                     DBDomainResourceDef domainResourceDef,
+                                     DBGroup group) {
+
+        return createResource(identifier, schema, visibilityType, DocumentVersionStatusType.PUBLISHED, domainResourceDef, group);
+    }
+
+    @Transactional
+    public DBResource createResource(String identifier, String schema,
+                                     VisibilityType visibilityType,
+                                     DocumentVersionStatusType status,
+                                     DBDomainResourceDef domainResourceDef,
+                                     DBGroup group) {
 
-        DBResource resource = TestDBUtils.createDBResource(identifier, schema);
+        DBResource resource = TestDBUtils.createDBResource(identifier, schema, true, status);
         resource.setVisibility(visibilityType);
         resource.setGroup(group);
         resource.setDomainResourceDef(domainResourceDef);
+        resource.setReviewEnabled(true);
 
         persistFlushDetach(resource);
         assertNotNull(resource.getId());
         return resource;
     }
 
+    @Transactional
+    public DBSubresource createSubresource(DBResource resource, String identifier, String schema,
+                                     DocumentVersionStatusType status, DBSubresourceDef subresourceDefSmp) {
+
+        DBSubresource dbSubresource = TestDBUtils.createDBSubresource(
+                resource.getIdentifierValue(),resource.getIdentifierScheme(),
+                identifier, schema);
+
+
+        dbSubresource.setSubresourceDef(subresourceDefSmp);
+
+        DBDocument doc  = createDocument(1, resourceD1G1RD1.getIdentifierValue(), resourceD1G1RD1.getIdentifierScheme(),
+                identifier, schema);
+        doc.getDocumentVersions().get(0).setStatus(status);
+
+        dbSubresource.setDocument(doc);
+        dbSubresource.setResource(resource);
+
+
+        persistFlushDetach(dbSubresource);
+        assertNotNull(dbSubresource.getId());
+        return dbSubresource;
+    }
+
+
+
 
     /**
      * Create resources with subresources  for ids:
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java
index 89425ad0d67b2410c195da88d00ac9f56d17b273..61dfed3fc8329b36c3e57a55432ee9bddad5b1c5 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java
@@ -68,7 +68,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Test
     void testGenerateDocumentForResource() {
 
-        DocumentRO result = testInstance.generateDocumentForResource(testUtilsDao.getResourceD1G1RD1().getId(), null);
+        DocumentRO result = testInstance.generateDocumentForResource(testUtilsDao.getResourceD1G1RD1().getId());
         assertNotNull(result);
         assertNotNull(result.getPayload());
     }
@@ -78,8 +78,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
         DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
 
         DocumentRO result = testInstance.generateDocumentForSubresource(subresource.getId(),
-                subresource.getResource().getId(),
-                null);
+                subresource.getResource().getId());
         assertNotNull(result);
         assertNotNull(result.getPayload());
     }
@@ -87,7 +86,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Test
     void testValidateForResource() {
         DBResource resource = testUtilsDao.getResourceD1G1RD1();
-        DocumentRO testDoc = testInstance.generateDocumentForResource(resource.getId(), null);
+        DocumentRO testDoc = testInstance.generateDocumentForResource(resource.getId());
         assertNotNull(testDoc.getPayload());
         // must not throw exception
         testInstance.validateDocumentForResource(resource.getId(), testDoc);
@@ -110,8 +109,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     void testValidateForSubresource() {
         DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
         DocumentRO testDoc = testInstance.generateDocumentForSubresource(subresource.getId(),
-                subresource.getResource().getId(),
-                null);
+                subresource.getResource().getId());
 
         assertNotNull(testDoc.getPayload());
         // must not throw exception
@@ -147,10 +145,10 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     @Test
     void testSaveDocumentForResource() {
         DBResource resource = testUtilsDao.getResourceD1G1RD1();
-        DocumentRO testDoc = testInstance.generateDocumentForResource(resource.getId(), null);
+        DocumentRO testDoc = testInstance.generateDocumentForResource(resource.getId());
         assertNotNull(testDoc.getPayload());
         //when
-        DocumentRO result = testInstance.saveDocumentForResource(resource.getId(), testDoc);
+        DocumentRO result = testInstance.saveDocumentForResource(resource.getId(), testDoc, null);
         // then
         assertNotNull(result);
     }
@@ -159,8 +157,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     void testSaveDocumentForSubresource() {
         DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
         DocumentRO testDoc = testInstance.generateDocumentForSubresource(subresource.getId(),
-                subresource.getResource().getId(),
-                null);
+                subresource.getResource().getId());
         assertNotNull(testDoc.getPayload());
 
         //when
@@ -173,8 +170,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest {
     void testTransientResolutionForSubresourceDocument() {
         DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1();
         DocumentRO testDoc = testInstance.generateDocumentForSubresource(subresource.getId(),
-                subresource.getResource().getId(),
-                null);
+                subresource.getResource().getId());
         assertNotNull(testDoc.getPayload());
         // extension used by this test is SMP example extension which generates document with placeholders
         Assertions.assertThat(testDoc.getPayload()).contains(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder());
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/sml/SmlConnectorDomainTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/sml/SmlConnectorDomainTest.java
index 11bba96179669989f77ca3d6f1f138f547eaf8ed..ce99037235b8f32afa72d2cbf9d026bada7a1feb 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/sml/SmlConnectorDomainTest.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/sml/SmlConnectorDomainTest.java
@@ -33,7 +33,6 @@ import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.boot.test.mock.mockito.SpyBean;
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java
index 5711a5601b5145bf53f0924f8e934445912088f2..3267be90acd379d713e52fe8152aacd3ba2fd4e9 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java
@@ -18,10 +18,7 @@
  */
 package eu.europa.ec.edelivery.smp.testutil;
 
-import eu.europa.ec.edelivery.smp.data.enums.ApplicationRoleType;
-import eu.europa.ec.edelivery.smp.data.enums.CredentialTargetType;
-import eu.europa.ec.edelivery.smp.data.enums.CredentialType;
-import eu.europa.ec.edelivery.smp.data.enums.VisibilityType;
+import eu.europa.ec.edelivery.smp.data.enums.*;
 import eu.europa.ec.edelivery.smp.data.model.DBAlert;
 import eu.europa.ec.edelivery.smp.data.model.DBDomain;
 import eu.europa.ec.edelivery.smp.data.model.DBGroup;
@@ -170,22 +167,26 @@ public class TestDBUtils {
         return createDBResource(id, sch, true);
     }
 
-
-    public static DBResource createDBResource(String id, String sch, boolean withExtension) {
+    public static DBResource createDBResource(String id, String sch, boolean withExtension,
+                                              DocumentVersionStatusType statusType) {
         DBResource resource = new DBResource();
         resource.setIdentifierValue(id);
         resource.setIdentifierScheme(sch);
         resource.setVisibility(VisibilityType.PUBLIC);
         if (withExtension) {
             DBDocument document = createDBDocument();
-            DBDocumentVersion documentVersion = createDBDocumentVersion(id, sch);
-            createDBDocumentVersion(id, sch).setContent(generateExtension());
+            DBDocumentVersion documentVersion = createDBDocumentVersion(id, sch, statusType);
             document.addNewDocumentVersion(documentVersion);
             resource.setDocument(document);
         }
         return resource;
     }
 
+
+    public static DBResource createDBResource(String id, String sch, boolean withExtension) {
+        return createDBResource(id, sch, withExtension, DocumentVersionStatusType.DRAFT);
+    }
+
     public static DBDocument createDBDocument() {
         DBDocument doc = new DBDocument();
         doc.setMimeType("application/xml");
@@ -194,7 +195,12 @@ public class TestDBUtils {
     }
 
     public static DBDocumentVersion createDBDocumentVersion(String id, String sch) {
+        return createDBDocumentVersion(id, sch, DocumentVersionStatusType.DRAFT);
+    }
+
+    public static DBDocumentVersion createDBDocumentVersion(String id, String sch, DocumentVersionStatusType status) {
         DBDocumentVersion docuVersion = new DBDocumentVersion();
+        docuVersion.setStatus(status);
         docuVersion.setContent(("<ServiceGroup xmlns=\"http://docs.oasis-open.org/bdxr/ns/SMP/2016/05\">" +
                 "<ParticipantIdentifier scheme=\"" + sch + "\">" + id + "</ParticipantIdentifier>" +
                 "<ServiceMetadataReferenceCollection />" +
diff --git a/smp-server-library/src/test/resources/cleanup-database.sql b/smp-server-library/src/test/resources/cleanup-database.sql
index 847d25bd63fd55ac7d4b466f84a4861211b80bb2..ea5f8adc8321046e86f48b9fcb0f9c569ae457bc 100755
--- a/smp-server-library/src/test/resources/cleanup-database.sql
+++ b/smp-server-library/src/test/resources/cleanup-database.sql
@@ -18,6 +18,7 @@ DELETE FROM SMP_SUBRESOURCE;
 DELETE FROM SMP_SUBRESOURCE_AUD;
 DELETE FROM SMP_RESOURCE;
 DELETE FROM SMP_RESOURCE_AUD;
+DELETE FROM SMP_DOCUMENT_VERSION_EVENT;
 DELETE FROM SMP_DOCUMENT_PROPERTY;
 DELETE FROM SMP_DOCUMENT_PROPERTY_AUD;
 DELETE FROM SMP_DOCUMENT_VERSION;
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java
index a93941668f529b9659d3d7e5881579041083619a..ee11d19736be8172904d8cd4450a745180e28f92 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java
@@ -114,6 +114,12 @@ public class SMPAuthorizationService {
         return resourceMemberDao.isUserResourceMemberWithRole(userDetails.getUser().getId(), resourceId, MembershipRoleType.ADMIN);
     }
 
+    public boolean isResourceReviewer(String resourceEncId) {
+        SMPUserDetails userDetails = getAndValidateUserDetails();
+        Long resourceId = getIdFromEncryptedString(resourceEncId, false);
+        return resourceMemberDao.isUserResourceMemberWithReviewPermission(userDetails.getUser().getId(), resourceId);
+    }
+
     public boolean isResourceMember(String resourceEncId) {
         SMPUserDetails userDetails = getAndValidateUserDetails();
         Long resourceId = getIdFromEncryptedString(resourceEncId, false);
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/AbstractErrorControllerAdvice.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/AbstractErrorControllerAdvice.java
index 17577ff99f661a6bd3ebc8bd0fc6326129b584a6..7f114ab42bfa7cbaf1a38325eff69d7ed7001d4f 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/AbstractErrorControllerAdvice.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/AbstractErrorControllerAdvice.java
@@ -52,7 +52,7 @@ abstract class AbstractErrorControllerAdvice {
             response = buildAndLog(UNAUTHORIZED, ErrorBusinessCode.UNAUTHORIZED, ex.getMessage(), ex);
         }else if (runtimeException instanceof AccessDeniedException){
             AccessDeniedException ex = (AccessDeniedException)runtimeException;
-            response = buildAndLog(UNAUTHORIZED, ErrorBusinessCode.UNAUTHORIZED, ex.getMessage(), ex);
+            response = buildAndLog(FORBIDDEN, ErrorBusinessCode.UNAUTHORIZED, ex.getMessage(), ex);
         }else if (runtimeException instanceof BadRequestException){
             BadRequestException ex = (BadRequestException)runtimeException;
             response = buildAndLog(UNPROCESSABLE_ENTITY, ex.getErrorBusinessCode(), ex.getMessage(), ex);
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java
index 6c94d2e7507d049d850f4d0635dd60d13eb983dc..77f20f6dfc426355588b189bb94ab6c578e008cc 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java
@@ -46,6 +46,7 @@ public class ResourceConstants {
     public static final String PATH_RESOURCE_TYPE_SUBRESOURCE = "subresource";
     public static final String PATH_RESOURCE_TYPE_DOCUMENT = "document";
     public static final String PATH_RESOURCE_TYPE_PROPERTY = "property";
+    public static final String PATH_RESOURCE_TYPE_REVIEW = "review-task";
 
     public static final String PATH_RESOURCE_TYPE_RESOURCE_DEFINITION = "res-def";
     /**
@@ -70,6 +71,11 @@ public class ResourceConstants {
     public static final String PATH_ACTION_PUT = "put";
     public static final String PATH_ACTION_VALIDATE = "validate";
     public static final String PATH_ACTION_GENERATE = "generate";
+    public static final String PATH_ACTION_PUBLISH = "publish";
+    public static final String PATH_ACTION_REVIEW_REQUEST = "review-request";
+    public static final String PATH_ACTION_REVIEW_APPROVE = "review-approve";
+    public static final String PATH_ACTION_REVIEW_REJECT = "review-reject";
+    public static final String PATH_ACTION_REVIEW_LIST = "review-list";
     public static final String PATH_ACTION_UPDATE_RESOURCE_TYPES = "update-resource-types";
     public static final String PATH_ACTION_UPDATE_SML_DATA = "update-sml-integration-data";
     public static final String PATH_ACTION_RESET_CREDENTIAL_REQUEST = "request-reset-credential";
@@ -133,14 +139,24 @@ public class ResourceConstants {
     public static final String CONTEXT_PATH_EDIT_SUBRESOURCE = CONTEXT_PATH_EDIT_RESOURCE_SHORT + URL_PATH_SEPARATOR + PATH_RESOURCE_TYPE_SUBRESOURCE;
     public static final String SUB_CONTEXT_PATH_EDIT_SUBRESOURCE_DELETE =  "{" + PATH_PARAM_ENC_SUBRESOURCE_ID + "}" + URL_PATH_SEPARATOR +  PATH_ACTION_DELETE;
 
-    public static final String CONTEXT_PATH_EDIT_DOCUMENT = CONTEXT_PATH_EDIT + URL_PATH_SEPARATOR +PATH_RESOURCE_TYPE_RESOURCE +URL_PATH_SEPARATOR + "{" + PATH_PARAM_ENC_RESOURCE_ID + "}";
-    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET =  PATH_RESOURCE_TYPE_DOCUMENT;
-    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_VALIDATE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET +  URL_PATH_SEPARATOR + PATH_ACTION_VALIDATE;
-    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_GENERATE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET +  URL_PATH_SEPARATOR + PATH_ACTION_GENERATE;
-
-    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET_SUBRESOURCE = PATH_RESOURCE_TYPE_SUBRESOURCE +  URL_PATH_SEPARATOR +  "{" + PATH_PARAM_ENC_SUBRESOURCE_ID + "}" +  URL_PATH_SEPARATOR + PATH_RESOURCE_TYPE_DOCUMENT;
-    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_VALIDATE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET_SUBRESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_VALIDATE;
-    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_GENERATE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET_SUBRESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_GENERATE;
+    public static final String CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE = CONTEXT_PATH_EDIT + URL_PATH_SEPARATOR +PATH_RESOURCE_TYPE_RESOURCE +URL_PATH_SEPARATOR + "{" + PATH_PARAM_ENC_RESOURCE_ID + "}";
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE =  PATH_RESOURCE_TYPE_DOCUMENT;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_VALIDATE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_VALIDATE;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_GENERATE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_GENERATE;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_PUBLISH =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_PUBLISH;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_REVIEW =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_REVIEW_REQUEST;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_APPROVE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_REVIEW_APPROVE;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_REJECT =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_REVIEW_REJECT;
+
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE = PATH_RESOURCE_TYPE_SUBRESOURCE +  URL_PATH_SEPARATOR +  "{" + PATH_PARAM_ENC_SUBRESOURCE_ID + "}" +  URL_PATH_SEPARATOR + PATH_RESOURCE_TYPE_DOCUMENT;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_VALIDATE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_VALIDATE;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_GENERATE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_GENERATE;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_PUBLISH =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_PUBLISH;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_REVIEW =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_REVIEW_REQUEST;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_APPROVE =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_REVIEW_APPROVE;
+    public static final String SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_REJECT =  SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE +  URL_PATH_SEPARATOR + PATH_ACTION_REVIEW_REJECT;
+
+    public static final String CONTEXT_PATH_EDIT_REVIEW =  CONTEXT_PATH_EDIT +  URL_PATH_SEPARATOR + PATH_RESOURCE_TYPE_REVIEW;
     // public
     public static final String CONTEXT_PATH_PUBLIC_SEARCH_PARTICIPANT = CONTEXT_PATH_PUBLIC + PATH_ACTION_SEARCH;
     public static final String CONTEXT_PATH_PUBLIC_SEARCH_PARTICIPANT_METADATA = CONTEXT_PATH_PUBLIC_SEARCH_PARTICIPANT + "/metadata";
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java
index 11d326265f1606c8d61e471d9fa6043f35363465..5c7e29a88d92807a0c01cbc5d863e1e4ff0b44ec 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java
@@ -19,6 +19,8 @@
 package eu.europa.ec.edelivery.smp.ui.edit;
 
 
+import eu.europa.ec.edelivery.smp.auth.SMPAuthorizationService;
+import eu.europa.ec.edelivery.smp.data.enums.DocumentVersionEventType;
 import eu.europa.ec.edelivery.smp.data.ui.DocumentRO;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
@@ -29,6 +31,8 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.util.MimeTypeUtils;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.Collections;
+
 import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.*;
 
 /**
@@ -40,20 +44,23 @@ import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.*;
  * @since 5.0
  */
 @RestController
-@RequestMapping(value = ResourceConstants.CONTEXT_PATH_EDIT_DOCUMENT)
+@RequestMapping(value = ResourceConstants.CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE)
 public class DocumentEditController {
 
     private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DocumentEditController.class);
     private final UIDocumentService uiDocumentService;
+    private final SMPAuthorizationService smpAuthorizationService;
 
-    public DocumentEditController(UIDocumentService uiDocumentService) {
+    public DocumentEditController(UIDocumentService uiDocumentService,
+                                  SMPAuthorizationService smpAuthorizationService) {
         this.uiDocumentService = uiDocumentService;
+        this.smpAuthorizationService = smpAuthorizationService;
     }
 
-
-    @GetMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @GetMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
-            "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
+            "and (@smpAuthorizationService.isResourceAdministrator(#resourceEncId)" +
+            "   or @smpAuthorizationService.isResourceReviewer(#resourceEncId))")
     public DocumentRO getDocumentForResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                              @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                              @RequestParam(value = PARAM_NAME_VERSION, defaultValue = "-1") int version) {
@@ -62,9 +69,10 @@ public class DocumentEditController {
         return uiDocumentService.getDocumentForResource(resourceId, version);
     }
 
-    @GetMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET_SUBRESOURCE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @GetMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
-            "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
+            "and (@smpAuthorizationService.isResourceAdministrator(#resourceEncId)" +
+            "   or @smpAuthorizationService.isResourceReviewer(#resourceEncId))")
     public DocumentRO getDocumentForSubResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                                 @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                                 @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
@@ -72,23 +80,32 @@ public class DocumentEditController {
         logAdminAccess("getDocumentForResource");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
-        return uiDocumentService.getDocumentForSubResource(subresourceId, resourceId, version);
+        DocumentRO document = uiDocumentService.getDocumentForSubResource(subresourceId, resourceId, version);
+        if (!smpAuthorizationService.isResourceAdministrator(userEncId)) {
+            document.setDocumentVersions(Collections.emptyList());
+        }
+        return document;
     }
 
-    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_VALIDATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_VALIDATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
-            "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
-    public void validateDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
-                                 @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
-                                 @RequestBody DocumentRO document) {
+            "and (@smpAuthorizationService.isResourceAdministrator(#resourceEncId)" +
+            "   or @smpAuthorizationService.isResourceReviewer(#resourceEncId))")
+    public void validateResourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                         @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                         @RequestBody DocumentRO document) {
         logAdminAccess("validateDocumentForResource");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         uiDocumentService.validateDocumentForResource(resourceId, document);
+        if (!smpAuthorizationService.isResourceAdministrator(userEncId)) {
+            document.setDocumentVersions(Collections.emptyList());
+        }
     }
 
     @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_VALIDATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
-            "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
+            "and (@smpAuthorizationService.isResourceAdministrator(#resourceEncId)" +
+            "   or @smpAuthorizationService.isResourceReviewer(#resourceEncId))")
     public void validateSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                             @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                             @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
@@ -100,15 +117,15 @@ public class DocumentEditController {
     }
 
 
-    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GENERATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_GENERATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
             "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
-    public DocumentRO generateDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
-                                       @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
-                                       @RequestBody(required = false) DocumentRO document) {
-        logAdminAccess("generateDocument");
+    public DocumentRO generateResourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                               @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                               @RequestBody(required = false) DocumentRO document) {
+        logAdminAccess("generateResourceDocument");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
-        return uiDocumentService.generateDocumentForResource(resourceId, document);
+        return uiDocumentService.generateDocumentForResource(resourceId);
     }
 
     @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_GENERATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
@@ -118,13 +135,132 @@ public class DocumentEditController {
                                                   @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                                   @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
                                                   @RequestBody(required = false) DocumentRO document) {
-        logAdminAccess("generateDocument");
+        logAdminAccess("generateSubresourceDocument");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
-        return uiDocumentService.generateDocumentForSubresource(subresourceId, resourceId, document);
+        return uiDocumentService.generateDocumentForSubresource(subresourceId, resourceId);
+    }
+
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_PUBLISH, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
+            "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
+    public DocumentRO publishResourceDocumentVersion(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                                     @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                                     @RequestBody DocumentRO document) {
+        logAdminAccess("publishResourceDocument");
+        Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
+        Long documentId = SessionSecurityUtils.decryptEntityId(document.getDocumentId());
+        return uiDocumentService.publishDocumentVersionForResource(resourceId, documentId, document.getPayloadVersion());
+    }
+
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_PUBLISH, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
+            "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
+    public DocumentRO publishSubresourceDocumentVersion(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                                        @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                                        @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
+                                                        @RequestBody DocumentRO document) {
+        logAdminAccess("publishSubresourceDocument");
+        Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
+        Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
+        Long documentId = SessionSecurityUtils.decryptEntityId(document.getDocumentId());
+        return uiDocumentService.publishDocumentVersionForSubresource(subresourceId, resourceId, documentId, document.getPayloadVersion());
     }
 
-    @PutMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET,
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_REVIEW, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
+            "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
+    public DocumentRO requestReviewResourceDocumentVersion(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                                           @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                                           @RequestBody DocumentRO document) {
+        logAdminAccess("requestReviewDocument");
+        Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
+        Long documentId = SessionSecurityUtils.decryptEntityId(document.getDocumentId());
+        return uiDocumentService.requestReviewDocumentVersionForResource(resourceId, documentId, document.getPayloadVersion());
+    }
+
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_REVIEW, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
+            "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
+    public DocumentRO requestReviewSubresourceDocumentVersion(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                                              @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                                              @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
+                                                              @RequestBody DocumentRO document) {
+        logAdminAccess("requestReviewDocument");
+        Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
+        Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
+        Long documentId = SessionSecurityUtils.decryptEntityId(document.getDocumentId());
+        return uiDocumentService.requestReviewDocumentVersionForSubresource(subresourceId, resourceId, documentId, document.getPayloadVersion());
+    }
+
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_APPROVE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
+            "and @smpAuthorizationService.isResourceReviewer(#resourceEncId)")
+    public DocumentRO approveResourceDocumentVersion(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                                     @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                                     @RequestBody DocumentRO document) {
+        logAdminAccess("approveResourceDocument");
+        Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
+        Long documentId = SessionSecurityUtils.decryptEntityId(document.getDocumentId());
+        return uiDocumentService.reviewActionDocumentVersionForResource(resourceId, documentId, document.getPayloadVersion(),
+                DocumentVersionEventType.APPROVE,
+                document.getActionMessage());
+    }
+
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_APPROVE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
+            "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)")
+    public DocumentRO approveSubresourceDocumentVersion(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                                        @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                                        @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
+                                                        @RequestBody DocumentRO document) {
+        logAdminAccess("approveSubresourceDocument");
+        Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
+        Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
+        Long documentId = SessionSecurityUtils.decryptEntityId(document.getDocumentId());
+        return uiDocumentService.reviewActionDocumentVersionForSubresource(subresourceId, resourceId, documentId,
+                document.getPayloadVersion(),
+                DocumentVersionEventType.APPROVE,
+                document.getActionMessage());
+    }
+
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_REJECT, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
+            "and (@smpAuthorizationService.isResourceAdministrator(#resourceEncId)" +
+            "   or @smpAuthorizationService.isResourceReviewer(#resourceEncId))")
+    public DocumentRO rejectResourceDocumentVersion(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                                    @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                                    @RequestBody DocumentRO document) {
+        logAdminAccess("rejectResourceDocument");
+        Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
+        Long documentId = SessionSecurityUtils.decryptEntityId(document.getDocumentId());
+        return uiDocumentService.reviewActionDocumentVersionForResource(resourceId, documentId, document.getPayloadVersion(),
+                DocumentVersionEventType.REJECT,
+                document.getActionMessage());
+    }
+
+    @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_REJECT, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
+            "and (@smpAuthorizationService.isResourceAdministrator(#resourceEncId)" +
+            "   or @smpAuthorizationService.isResourceReviewer(#resourceEncId))")
+    public DocumentRO rejectSubresourceDocumentVersion(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
+                                                       @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
+                                                       @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
+                                                       @RequestBody DocumentRO document) {
+        logAdminAccess("rejectSubresourceDocument");
+        Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
+        Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
+        Long documentId = SessionSecurityUtils.decryptEntityId(document.getDocumentId());
+        return uiDocumentService.reviewActionDocumentVersionForSubresource(subresourceId,
+                resourceId,
+                documentId,
+                document.getPayloadVersion(),
+                DocumentVersionEventType.REJECT,
+                document.getActionMessage());
+    }
+
+
+    @PutMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE,
             consumes = MimeTypeUtils.APPLICATION_JSON_VALUE,
             produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
@@ -132,12 +268,16 @@ public class DocumentEditController {
     public DocumentRO saveDocumentForResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                               @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                               @RequestBody DocumentRO document) {
-        logAdminAccess("validateDocumentForResource");
+        logAdminAccess("saveResourceDocument");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
-        return uiDocumentService.saveDocumentForResource(resourceId, document);
+        Long referenceDocumentId = null;
+        if (document.getReferenceDocumentId() != null) {
+            referenceDocumentId = SessionSecurityUtils.decryptEntityId(document.getReferenceDocumentId());
+        }
+        return uiDocumentService.saveDocumentForResource(resourceId, document, referenceDocumentId);
     }
 
-    @PutMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET_SUBRESOURCE,
+    @PutMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE,
             consumes = MimeTypeUtils.APPLICATION_JSON_VALUE,
             produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
@@ -146,13 +286,12 @@ public class DocumentEditController {
                                               @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                               @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId,
                                               @RequestBody DocumentRO document) {
-        logAdminAccess("validateDocumentForResource");
+        logAdminAccess("saveSubresourceDocument");
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId);
         return uiDocumentService.saveSubresourceDocumentForResource(subresourceId, resourceId, document);
     }
 
-
     protected void logAdminAccess(String action) {
         LOG.info(SMPLogger.SECURITY_MARKER, "Admin Domain action [{}] by user [{}], ", action, SessionSecurityUtils.getSessionUserDetails());
     }
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/GroupEditController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/GroupEditController.java
index 4360a4d77e93cbf625bd4c96ae62e8f157276cd7..7a7aae44932fd7907b9c3089de969527d249ad00 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/GroupEditController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/GroupEditController.java
@@ -40,7 +40,7 @@ import java.util.List;
 import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.*;
 
 /**
- * Purpose of the DomainResource is to provide search method to retrieve configured domains in SMP.
+ * Purpose of the GroupEditController is to provide search method to retrieve configured groups in SMP.
  * base path for the resource includes two variables user who is editing and domain for the group
  * /ui/edit/rest/[user-id]/domain/[domain-id]/group
  *
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditController.java
index 0ca1c4e82dfac32281cebb572ffff4a36dab8a31..d96388580a02c70faa48d05ed2e804cbc939b11f 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditController.java
@@ -39,9 +39,10 @@ import org.springframework.web.bind.annotation.*;
 import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.*;
 
 /**
- * Purpose of the DomainResource is to provide search method to retrieve configured domains in SMP.
+ * Purpose of the ResourceEditController is to provide edut methods to retrieve
+ * update  resources in the DomiSMP.
  * base path for the resource includes two variables user who is editing and domain for the group
- * /ui/edit/rest/[user-id]/domain/[domain-id]/group/[group-id]/resource
+ * /ui/edit/rest/[user-id]/domain/[domain-id]/group/[group-id]/resource\[resource-id]
  *
  * @author Joze Rihtarsic
  * @since 5.0
@@ -63,8 +64,9 @@ public class ResourceEditController {
      * resource-viewer it returns all Resources for the group where user is Resources viewer;
      * all-roles it returns all groups for the domain for user
      *
-     * @param userEncId
-     * @param groupEncId
+     * @param userEncId logged user identifier
+     * @param domainEncId domain identifier
+     * @param groupEncId group identifier
      * @param forRole
      * @return
      */
@@ -97,6 +99,14 @@ public class ResourceEditController {
         throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "ResourcesForGroups", "Unknown parameter type [" + forRole + "]!");
     }
 
+    /**
+     * Methods enables to group admin to delete resource from the group
+     * @param userEncId logged user identifier
+     * @param domainEncId domain identifier
+     * @param groupEncId group identifier
+     * @param resourceEncId resource identifier
+     * @return the deleted ResourceRO
+     */
     @DeleteMapping(path = SUB_CONTEXT_PATH_EDIT_RESOURCE_DELETE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isGroupAdministrator(#groupEncId)")
     public ResourceRO deleteResourceFromGroup(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
@@ -110,6 +120,13 @@ public class ResourceEditController {
         return uiResourceService.deleteResourceFromGroup(resourceId, groupId, domainId);
     }
 
+    /**
+     * Methods enables to group admin to create resource on the group
+     * @param userEncId logged user identifier
+     * @param domainEncId domain identifier
+     * @param groupEncId group identifier
+     * @return the created ResourceRO data
+     */
     @PutMapping(path = SUB_CONTEXT_PATH_EDIT_RESOURCE_CREATE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isGroupAdministrator(#groupEncId)")
     public ResourceRO createResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
@@ -123,20 +140,42 @@ public class ResourceEditController {
         return uiResourceService.createResourceForGroup(resourceRO, groupId, domainId, userId);
     }
 
+    /**
+     * Method allows Group admin and Resource admin to change
+     *   resource visibility and enable/disable review flow.
+     * @param userEncId logged user identifier
+     * @param domainEncId domain identifier
+     * @param groupEncId group identifier
+     * @param resourceEncId resource identifier
+     * @param resourceRO updated resource data
+     * @return the updated ResourceRO data
+     */
     @PostMapping(path = SUB_CONTEXT_PATH_EDIT_RESOURCE_UPDATE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
-    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isGroupAdministrator(#groupEncId)")
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " +
+            "and (@smpAuthorizationService.isGroupAdministrator(#groupEncId) or @smpAuthorizationService.isResourceAdministrator(#resourceEncId))")
     public ResourceRO updateResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
                                      @PathVariable(PATH_PARAM_ENC_DOMAIN_ID) String domainEncId,
                                      @PathVariable(PATH_PARAM_ENC_GROUP_ID) String groupEncId,
                                      @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                      @RequestBody ResourceRO resourceRO) {
-        logAdminAccess("createResource");
+        logAdminAccess("updateResource");
         Long domainId = SessionSecurityUtils.decryptEntityId(domainEncId);
         Long groupId = SessionSecurityUtils.decryptEntityId(groupEncId);
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         return uiResourceService.updateResourceForGroup(resourceRO, resourceId, groupId, domainId);
     }
 
+    /**
+     * Method returns list of members for the resource for group and resource
+     * @param userEncId
+     * @param domainEncId
+     * @param groupEncId
+     * @param resourceEncId
+     * @param page
+     * @param pageSize
+     * @param filter
+     * @return
+     */
 
     @GetMapping(path = SUB_CONTEXT_PATH_EDIT_RESOURCE_MEMBER, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and" +
@@ -163,7 +202,7 @@ public class ResourceEditController {
                                    @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                    @RequestBody MemberRO memberRO) {
 
-        LOG.info("add member to group");
+        LOG.debug("Add member to group");
         Long groupId = SessionSecurityUtils.decryptEntityId(groupEncId);
         Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId);
         Long memberId = memberRO.getMemberId() == null ? null : SessionSecurityUtils.decryptEntityId(memberRO.getMemberId());
@@ -193,7 +232,7 @@ public class ResourceEditController {
     }
 
     protected void logAdminAccess(String action) {
-        LOG.info(SMPLogger.SECURITY_MARKER, "Admin Domain action [{}] by user [{}], ", action, SessionSecurityUtils.getSessionUserDetails());
+        LOG.info(SMPLogger.SECURITY_MARKER, "Group/Resource admin action [{}] by user [{}], ", action, SessionSecurityUtils.getSessionUserDetails());
     }
 }
 
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ReviewEditController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ReviewEditController.java
new file mode 100644
index 0000000000000000000000000000000000000000..79b5b30e342b009b37587f0f2cfc91e69d168a5a
--- /dev/null
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ReviewEditController.java
@@ -0,0 +1,81 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+package eu.europa.ec.edelivery.smp.ui.edit;
+
+
+import eu.europa.ec.edelivery.smp.data.ui.ReviewDocumentVersionRO;
+import eu.europa.ec.edelivery.smp.data.ui.ServiceResult;
+import eu.europa.ec.edelivery.smp.logging.SMPLogger;
+import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
+import eu.europa.ec.edelivery.smp.services.ui.UIDocumentService;
+import eu.europa.ec.edelivery.smp.ui.ResourceConstants;
+import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.*;
+
+import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.*;
+
+/**
+ * Purpose of the DomainResource is to provide search method to retrieve configured domains in SMP.
+ * base path for the resource includes two variables user who is editing and domain for the group
+ * /ui/edit/rest/[user-id]/domain/[domain-id]/group/[group-id]/resource
+ *
+ * @author Joze Rihtarsic
+ * @since 5.0
+ */
+@RestController
+@RequestMapping(value = ResourceConstants.CONTEXT_PATH_EDIT_REVIEW)
+public class ReviewEditController {
+
+    private static final SMPLogger LOG = SMPLoggerFactory.getLogger(ReviewEditController.class);
+    private final UIDocumentService uiDocumentService;
+
+    public ReviewEditController(UIDocumentService uiDocumentService) {
+        this.uiDocumentService = uiDocumentService;
+    }
+
+    /**
+     * Method returns Users list of alerts. To access the list user must be logged in.
+     * <p>
+     *
+     * @param encUserId - encrypted user id (from session) - used for authorization check
+     * @param page      - page number (0..n)
+     * @param pageSize  - page size (0..n) - number of results on page/max number of returned results.
+     * @param orderBy   - order by field
+     * @param orderType - order type (asc, desc)
+     * @return ServiceResult<AlertRO> - list of alerts
+     */
+    @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#encUserId)")
+    @GetMapping(path = "/", produces = {MimeTypeUtils.APPLICATION_JSON_VALUE})
+    public ServiceResult<ReviewDocumentVersionRO> getUserReviewTasks(
+            @PathVariable("user-id") String encUserId,
+            @RequestParam(value = PARAM_PAGINATION_PAGE, defaultValue = "0") int page,
+            @RequestParam(value = PARAM_PAGINATION_PAGE_SIZE, defaultValue = "10") int pageSize,
+            @RequestParam(value = PARAM_PAGINATION_ORDER_BY, defaultValue = "id", required = false) String orderBy,
+            @RequestParam(value = PARAM_PAGINATION_ORDER_TYPE, defaultValue = "desc", required = false) String orderType,
+            @RequestParam(value = PARAM_PAGINATION_FILTER, required = false) Object filter
+    ) {
+        LOG.info("Search for page: {}, page size: {}", page, pageSize);
+        Long userId = SessionSecurityUtils.decryptEntityId(encUserId);
+        // set filter to current user
+        return uiDocumentService.getDocumentReviewListForUser(userId, page, pageSize, orderBy, orderType, filter);
+    }
+}
+
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java
index c0bb7f58f3451535fff8a724cc947c12d6884869..e46095ba427ebe7f57ae93e3168724349131e0df 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java
@@ -329,6 +329,7 @@ public class UserController {
         node.addChild(new NavigationTreeNodeRO("edit-domain", "navigation.label.edit.domains", "account_circle", "edit-domain"));
         node.addChild(new NavigationTreeNodeRO("edit-group", "navigation.label.edit.groups", "group", "edit-group"));
         node.addChild(new NavigationTreeNodeRO("edit-resource", "navigation.label.edit.resources", "article", "edit-resource"));
+        node.addChild(new NavigationTreeNodeRO("review-tasks", "navigation.label.review.tasks", "task", "review-tasks"));
         return node;
     }
 }
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl
index f33ba8d00756e7f4b4216169ebb739ca0b41400c..dec99d490e27e3e13ce08c4a6e7bc36f699a4c6c 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl
@@ -35,6 +35,10 @@
        drop 
        foreign key FKqjh6vxvb5tg0tvbkvi3k3xhe6;
 
+    alter table SMP_DOCUMENT 
+       drop 
+       foreign key FKbytp2kp8g3pj8qfp1g6a2g7p;
+
     alter table SMP_DOCUMENT_AUD 
        drop 
        foreign key FKh9epnme26i271eixtvrpqejvi;
@@ -55,6 +59,10 @@
        drop 
        foreign key FK4glqiu73939kpyyb6bhw822k3;
 
+    alter table SMP_DOCUMENT_VERSION_EVENT 
+       drop 
+       foreign key FK6es2svpoxyrnt1h05c9junmdn;
+
     alter table SMP_DOMAIN_AUD 
        drop 
        foreign key FK35qm8xmi74kfenugeonijodsg;
@@ -211,6 +219,8 @@
 
     drop table if exists SMP_DOCUMENT_VERSION_AUD;
 
+    drop table if exists SMP_DOCUMENT_VERSION_EVENT;
+
     drop table if exists SMP_DOMAIN;
 
     drop table if exists SMP_DOMAIN_AUD;
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl
index 2b82adba38fd9a546778ad24572f81c8c26605f4..ca073b2a7e19e543667c036b4c3f0f93372c540a 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl
@@ -164,6 +164,8 @@
         CURRENT_VERSION integer not null,
         MIME_TYPE varchar(128)  CHARACTER SET utf8 COLLATE utf8_bin,
         NAME varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin,
+        SHARING_ENABLED bit,
+        FK_REF_DOCUMENT_ID bigint,
         primary key (ID)
     ) comment='SMP document entity for resources and subresources' ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
@@ -176,6 +178,8 @@
         CURRENT_VERSION integer,
         MIME_TYPE varchar(128)  CHARACTER SET utf8 COLLATE utf8_bin,
         NAME varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin,
+        SHARING_ENABLED bit,
+        FK_REF_DOCUMENT_ID bigint,
         primary key (ID, REV)
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
@@ -210,6 +214,7 @@
         CREATED_ON datetime not null,
         LAST_UPDATED_ON datetime not null,
         DOCUMENT_CONTENT longblob comment 'Document content',
+        STATUS varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin not null comment 'Document version status',
         VERSION integer not null,
         FK_DOCUMENT_ID bigint,
         primary key (ID)
@@ -222,11 +227,25 @@
         CREATED_ON datetime,
         LAST_UPDATED_ON datetime,
         DOCUMENT_CONTENT longblob,
+        STATUS varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin,
         VERSION integer,
         FK_DOCUMENT_ID bigint,
         primary key (ID, REV)
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
+    create table SMP_DOCUMENT_VERSION_EVENT (
+       ID bigint not null auto_increment comment 'Unique document version event identifier',
+        CREATED_ON datetime not null,
+        LAST_UPDATED_ON datetime not null,
+        DETAILS varchar(1024)  CHARACTER SET utf8 COLLATE utf8_bin comment 'Details of the event',
+        EVENT_ON datetime comment 'Date time of the event',
+        EVENT_SOURCE varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin not null comment 'Event source UI, API',
+        EVENT_TYPE varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin not null comment 'Document version event type',
+        EVENT_BY_USERNAME varchar(64)  CHARACTER SET utf8 COLLATE utf8_bin comment 'username identifier of the user who triggered the event',
+        FK_DOCUMENT_VERSION_ID bigint,
+        primary key (ID)
+    ) comment='Document version Events.' ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
     create table SMP_DOMAIN (
        ID bigint not null auto_increment comment 'Unique domain id',
         CREATED_ON datetime not null,
@@ -411,7 +430,8 @@
         LAST_UPDATED_ON datetime not null,
         IDENTIFIER_SCHEME varchar(256)  CHARACTER SET utf8 COLLATE utf8_bin,
         IDENTIFIER_VALUE varchar(256)  CHARACTER SET utf8 COLLATE utf8_bin not null,
-        SML_REGISTERED bit not null,
+        REVIEW_ENABLED bit,
+        SML_REGISTERED bit,
         VISIBILITY varchar(128)  CHARACTER SET utf8 COLLATE utf8_bin,
         FK_DOCUMENT_ID bigint not null,
         FK_DOREDEF_ID bigint not null,
@@ -427,6 +447,7 @@
         LAST_UPDATED_ON datetime,
         IDENTIFIER_SCHEME varchar(256)  CHARACTER SET utf8 COLLATE utf8_bin,
         IDENTIFIER_VALUE varchar(256)  CHARACTER SET utf8 COLLATE utf8_bin,
+        REVIEW_ENABLED bit,
         SML_REGISTERED bit,
         VISIBILITY varchar(128)  CHARACTER SET utf8 COLLATE utf8_bin,
         FK_DOCUMENT_ID bigint,
@@ -469,6 +490,7 @@
        ID bigint not null auto_increment,
         CREATED_ON datetime not null,
         LAST_UPDATED_ON datetime not null,
+        PERMISSION_REVIEW bit comment 'User permission to review the resource document',
         MEMBERSHIP_ROLE varchar(64)  CHARACTER SET utf8 COLLATE utf8_bin,
         FK_RESOURCE_ID bigint,
         FK_USER_ID bigint,
@@ -481,6 +503,7 @@
         REVTYPE tinyint,
         CREATED_ON datetime,
         LAST_UPDATED_ON datetime,
+        PERMISSION_REVIEW bit,
         MEMBERSHIP_ROLE varchar(64)  CHARACTER SET utf8 COLLATE utf8_bin,
         FK_RESOURCE_ID bigint,
         FK_USER_ID bigint,
@@ -596,6 +619,7 @@ create index SMP_DOCVER_DOCUMENT_IDX on SMP_DOCUMENT_VERSION (FK_DOCUMENT_ID);
 
     alter table SMP_DOCUMENT_VERSION 
        add constraint SMP_DOCVER_UNIQ_VERSION_IDX unique (FK_DOCUMENT_ID, VERSION);
+create index SMP_DOCVEREVNT_DOCVER_IDX on SMP_DOCUMENT_VERSION_EVENT (FK_DOCUMENT_VERSION_ID);
 
     alter table SMP_DOMAIN 
        add constraint UK_djrwqd4luj5i7w4l7fueuaqbj unique (DOMAIN_CODE);
@@ -692,6 +716,11 @@ create index SMP_SMD_DOC_SCH_IDX on SMP_SUBRESOURCE (IDENTIFIER_SCHEME);
        foreign key (REV) 
        references SMP_REV_INFO (id);
 
+    alter table SMP_DOCUMENT 
+       add constraint FKbytp2kp8g3pj8qfp1g6a2g7p 
+       foreign key (FK_REF_DOCUMENT_ID) 
+       references SMP_DOCUMENT (ID);
+
     alter table SMP_DOCUMENT_AUD 
        add constraint FKh9epnme26i271eixtvrpqejvi 
        foreign key (REV) 
@@ -717,6 +746,11 @@ create index SMP_SMD_DOC_SCH_IDX on SMP_SUBRESOURCE (IDENTIFIER_SCHEME);
        foreign key (REV) 
        references SMP_REV_INFO (id);
 
+    alter table SMP_DOCUMENT_VERSION_EVENT 
+       add constraint FK6es2svpoxyrnt1h05c9junmdn 
+       foreign key (FK_DOCUMENT_VERSION_ID) 
+       references SMP_DOCUMENT_VERSION (ID);
+
     alter table SMP_DOMAIN_AUD 
        add constraint FK35qm8xmi74kfenugeonijodsg 
        foreign key (REV) 
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl
index 45b4fba095365730d7182e3040ceea70e09f062f..58c36e490cea75b770f94b17f440d39a49d8eaf6 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl
@@ -35,6 +35,8 @@
 
     drop table SMP_DOCUMENT_VERSION_AUD cascade constraints;
 
+    drop table SMP_DOCUMENT_VERSION_EVENT cascade constraints;
+
     drop table SMP_DOMAIN cascade constraints;
 
     drop table SMP_DOMAIN_AUD cascade constraints;
@@ -101,6 +103,8 @@
 
     drop sequence SMP_DOCUMENT_VERSION_SEQ;
 
+    drop sequence SMP_DOCVER_EVENT_SEQ;
+
     drop sequence SMP_DOMAIN_CONF_SEQ;
 
     drop sequence SMP_DOMAIN_MEMBER_SEQ;
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl
index 953bb1be081432cc584862d50ad970fbebfd56f4..f7156c6dfdcc4c29dd03ee72ced09f7e3f62506f 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl
@@ -8,6 +8,7 @@ create sequence SMP_CREDENTIAL_SEQ start with 1 increment by  1;
 create sequence SMP_DOC_PROP_SEQ start with 1 increment by  1;
 create sequence SMP_DOCUMENT_SEQ start with 1 increment by  1;
 create sequence SMP_DOCUMENT_VERSION_SEQ start with 1 increment by  1;
+create sequence SMP_DOCVER_EVENT_SEQ start with 1 increment by  1;
 create sequence SMP_DOMAIN_CONF_SEQ start with 1 increment by  1;
 create sequence SMP_DOMAIN_MEMBER_SEQ start with 1 increment by  1;
 create sequence SMP_DOMAIN_RESOURCE_DEF_SEQ start with 1 increment by  1;
@@ -283,6 +284,8 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
         CURRENT_VERSION number(10,0) not null,
         MIME_TYPE varchar2(128 char),
         NAME varchar2(255 char),
+        SHARING_ENABLED number(1,0),
+        FK_REF_DOCUMENT_ID number(19,0),
         primary key (ID)
     );
 
@@ -301,6 +304,8 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
         CURRENT_VERSION number(10,0),
         MIME_TYPE varchar2(128 char),
         NAME varchar2(255 char),
+        SHARING_ENABLED number(1,0),
+        FK_REF_DOCUMENT_ID number(19,0),
         primary key (ID, REV)
     );
 
@@ -341,6 +346,7 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
         CREATED_ON timestamp not null,
         LAST_UPDATED_ON timestamp not null,
         DOCUMENT_CONTENT blob,
+        STATUS varchar2(255 char) not null,
         VERSION number(10,0) not null,
         FK_DOCUMENT_ID number(19,0),
         primary key (ID)
@@ -355,6 +361,9 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
     comment on column SMP_DOCUMENT_VERSION.DOCUMENT_CONTENT is
         'Document content';
 
+    comment on column SMP_DOCUMENT_VERSION.STATUS is
+        'Document version status';
+
     create table SMP_DOCUMENT_VERSION_AUD (
        ID number(19,0) not null,
         REV number(19,0) not null,
@@ -362,11 +371,46 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
         CREATED_ON timestamp,
         LAST_UPDATED_ON timestamp,
         DOCUMENT_CONTENT blob,
+        STATUS varchar2(255 char),
         VERSION number(10,0),
         FK_DOCUMENT_ID number(19,0),
         primary key (ID, REV)
     );
 
+    create table SMP_DOCUMENT_VERSION_EVENT (
+       ID number(19,0) not null,
+        CREATED_ON timestamp not null,
+        LAST_UPDATED_ON timestamp not null,
+        DETAILS varchar2(1024 char),
+        EVENT_ON timestamp,
+        EVENT_SOURCE varchar2(255 char) not null,
+        EVENT_TYPE varchar2(255 char) not null,
+        EVENT_BY_USERNAME varchar2(64 char),
+        FK_DOCUMENT_VERSION_ID number(19,0),
+        primary key (ID)
+    );
+
+    comment on table SMP_DOCUMENT_VERSION_EVENT is
+        'Document version Events.';
+
+    comment on column SMP_DOCUMENT_VERSION_EVENT.ID is
+        'Unique document version event identifier';
+
+    comment on column SMP_DOCUMENT_VERSION_EVENT.DETAILS is
+        'Details of the event';
+
+    comment on column SMP_DOCUMENT_VERSION_EVENT.EVENT_ON is
+        'Date time of the event';
+
+    comment on column SMP_DOCUMENT_VERSION_EVENT.EVENT_SOURCE is
+        'Event source UI, API';
+
+    comment on column SMP_DOCUMENT_VERSION_EVENT.EVENT_TYPE is
+        'Document version event type';
+
+    comment on column SMP_DOCUMENT_VERSION_EVENT.EVENT_BY_USERNAME is
+        'username identifier of the user who triggered the event';
+
     create table SMP_DOMAIN (
        ID number(19,0) not null,
         CREATED_ON timestamp not null,
@@ -626,7 +670,8 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
         LAST_UPDATED_ON timestamp not null,
         IDENTIFIER_SCHEME varchar2(256 char),
         IDENTIFIER_VALUE varchar2(256 char) not null,
-        SML_REGISTERED number(1,0) not null,
+        REVIEW_ENABLED number(1,0),
+        SML_REGISTERED number(1,0),
         VISIBILITY varchar2(128 char),
         FK_DOCUMENT_ID number(19,0) not null,
         FK_DOREDEF_ID number(19,0) not null,
@@ -648,6 +693,7 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
         LAST_UPDATED_ON timestamp,
         IDENTIFIER_SCHEME varchar2(256 char),
         IDENTIFIER_VALUE varchar2(256 char),
+        REVIEW_ENABLED number(1,0),
         SML_REGISTERED number(1,0),
         VISIBILITY varchar2(128 char),
         FK_DOCUMENT_ID number(19,0),
@@ -699,18 +745,23 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
        ID number(19,0) not null,
         CREATED_ON timestamp not null,
         LAST_UPDATED_ON timestamp not null,
+        PERMISSION_REVIEW number(1,0),
         MEMBERSHIP_ROLE varchar2(64 char),
         FK_RESOURCE_ID number(19,0),
         FK_USER_ID number(19,0),
         primary key (ID)
     );
 
+    comment on column SMP_RESOURCE_MEMBER.PERMISSION_REVIEW is
+        'User permission to review the resource document';
+
     create table SMP_RESOURCE_MEMBER_AUD (
        ID number(19,0) not null,
         REV number(19,0) not null,
         REVTYPE number(3,0),
         CREATED_ON timestamp,
         LAST_UPDATED_ON timestamp,
+        PERMISSION_REVIEW number(1,0),
         MEMBERSHIP_ROLE varchar2(64 char),
         FK_RESOURCE_ID number(19,0),
         FK_USER_ID number(19,0),
@@ -868,6 +919,7 @@ create index SMP_DOCVER_DOCUMENT_IDX on SMP_DOCUMENT_VERSION (FK_DOCUMENT_ID);
 
     alter table SMP_DOCUMENT_VERSION 
        add constraint SMP_DOCVER_UNIQ_VERSION_IDX unique (FK_DOCUMENT_ID, VERSION);
+create index SMP_DOCVEREVNT_DOCVER_IDX on SMP_DOCUMENT_VERSION_EVENT (FK_DOCUMENT_VERSION_ID);
 
     alter table SMP_DOMAIN 
        add constraint UK_djrwqd4luj5i7w4l7fueuaqbj unique (DOMAIN_CODE);
@@ -964,6 +1016,11 @@ create index SMP_SMD_DOC_SCH_IDX on SMP_SUBRESOURCE (IDENTIFIER_SCHEME);
        foreign key (REV) 
        references SMP_REV_INFO;
 
+    alter table SMP_DOCUMENT 
+       add constraint FKbytp2kp8g3pj8qfp1g6a2g7p 
+       foreign key (FK_REF_DOCUMENT_ID) 
+       references SMP_DOCUMENT;
+
     alter table SMP_DOCUMENT_AUD 
        add constraint FKh9epnme26i271eixtvrpqejvi 
        foreign key (REV) 
@@ -989,6 +1046,11 @@ create index SMP_SMD_DOC_SCH_IDX on SMP_SUBRESOURCE (IDENTIFIER_SCHEME);
        foreign key (REV) 
        references SMP_REV_INFO;
 
+    alter table SMP_DOCUMENT_VERSION_EVENT 
+       add constraint FK6es2svpoxyrnt1h05c9junmdn 
+       foreign key (FK_DOCUMENT_VERSION_ID) 
+       references SMP_DOCUMENT_VERSION;
+
     alter table SMP_DOMAIN_AUD 
        add constraint FK35qm8xmi74kfenugeonijodsg 
        foreign key (REV) 
diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java
index 0b610f028ee3ef4937786164e1c18b52366c493a..85ce7be69abfd552daafd019a00cdfa39157c44e 100644
--- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java
+++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java
@@ -45,13 +45,13 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 class DocumentEditControllerIT extends AbstractControllerTest {
-    private static final String PATH = CONTEXT_PATH_EDIT_DOCUMENT;
+    private static final String PATH = CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE;
 
     @Autowired
     protected UIDocumentService documentService;
 
     @BeforeEach
-    public void setup() throws IOException {
+    public void setupData() throws IOException {
         super.setup();
     }
 
@@ -70,7 +70,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
         ResourceRO resourceRO = addResourceToGroup(session, domainRO, groupRO, userRO);
 
         // when
-        MvcResult result = mvc.perform(get(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET,
+        MvcResult result = mvc.perform(get(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE,
                         userRO.getUserId(), resourceRO.getResourceId())
                         .session(session)
                         .with(csrf())
@@ -79,7 +79,10 @@ class DocumentEditControllerIT extends AbstractControllerTest {
         // then
         DocumentRO documentRo = getObjectFromResponse(result, DocumentRO.class);
         assertNotNull(documentRo);
-        assertTrue(documentRo.getAllVersions().isEmpty()); // was just created without document
+        assertEquals(1, documentRo.getAllVersions().size());
+        assertEquals(1, documentRo.getAllVersions().get(0));
+        assertEquals(1, documentRo.getCurrentResourceVersion());
+        assertNotNull(documentRo.getPayload());
     }
 
     @Test
@@ -99,7 +102,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
         ResourceRO resourceRO = resources.get(0);
 
         // when
-        MvcResult result = mvc.perform(get(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET,
+        MvcResult result = mvc.perform(get(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE,
                         userRO.getUserId(), resourceRO.getResourceId())
                         .session(session)
                         .with(csrf())
@@ -133,7 +136,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
         documentRo.setPayload(TestROUtils.createSMP10ServiceGroupPayload(resourceRO.getIdentifierValue(), resourceRO.getIdentifierScheme()));
 
         // when
-        mvc.perform(post(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_VALIDATE,
+        mvc.perform(post(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_VALIDATE,
                         userRO.getUserId(), resourceRO.getResourceId())
                         .contentType(MediaType.APPLICATION_JSON)
                         .content(getObjectMapper().writeValueAsBytes(documentRo))
@@ -164,7 +167,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
         documentRo.setPayload("invalid payload");
 
         // when
-        MvcResult result = mvc.perform(post(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_VALIDATE,
+        MvcResult result = mvc.perform(post(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_VALIDATE,
                         userRO.getUserId(), resourceRO.getResourceId())
                         .contentType(MediaType.APPLICATION_JSON)
                         .content(getObjectMapper().writeValueAsBytes(documentRo))
@@ -196,7 +199,7 @@ class DocumentEditControllerIT extends AbstractControllerTest {
         ResourceRO resourceRO = resources.get(0);
 
         // when
-        MvcResult response = mvc.perform(post(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_GENERATE,
+        MvcResult response = mvc.perform(post(PATH + '/' + SUB_CONTEXT_PATH_EDIT_DOCUMENT_RESOURCE_GENERATE,
                         userRO.getUserId(), resourceRO.getResourceId())
                         .session(session)
                         .with(csrf())
diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/external/ApplicationResourceIT.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/external/ApplicationResourceIT.java
index f4b21cc73155d135d5c27449de97497cdbf9d3e5..f1bf1e9b6796b7a44d27e915ceb7c718913f3bf4 100644
--- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/external/ApplicationResourceIT.java
+++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/external/ApplicationResourceIT.java
@@ -128,7 +128,7 @@ class ApplicationResourceIT {
         // when
         mvc.perform(get(PATH + "/config")
                         .with(csrf()))
-                .andExpect(status().isUnauthorized())
+                .andExpect(status().isForbidden())
                 .andReturn()
                 .getResponse();
     }
diff --git a/smp-webapp/src/test/resources/cleanup-database.sql b/smp-webapp/src/test/resources/cleanup-database.sql
index 847d25bd63fd55ac7d4b466f84a4861211b80bb2..ea5f8adc8321046e86f48b9fcb0f9c569ae457bc 100755
--- a/smp-webapp/src/test/resources/cleanup-database.sql
+++ b/smp-webapp/src/test/resources/cleanup-database.sql
@@ -18,6 +18,7 @@ DELETE FROM SMP_SUBRESOURCE;
 DELETE FROM SMP_SUBRESOURCE_AUD;
 DELETE FROM SMP_RESOURCE;
 DELETE FROM SMP_RESOURCE_AUD;
+DELETE FROM SMP_DOCUMENT_VERSION_EVENT;
 DELETE FROM SMP_DOCUMENT_PROPERTY;
 DELETE FROM SMP_DOCUMENT_PROPERTY_AUD;
 DELETE FROM SMP_DOCUMENT_VERSION;
diff --git a/smp-webapp/src/test/resources/webapp_integration_test_data.sql b/smp-webapp/src/test/resources/webapp_integration_test_data.sql
index 9c726ef123a286caa3ee6e4df93f0a46c5b65e66..a15772c905726a694b58f0835b5255ee00c09b55 100644
--- a/smp-webapp/src/test/resources/webapp_integration_test_data.sql
+++ b/smp-webapp/src/test/resources/webapp_integration_test_data.sql
@@ -111,10 +111,10 @@ insert into SMP_DOCUMENT (ID, CURRENT_VERSION, MIME_TYPE, NAME,CREATED_ON, LAST_
 (-2, 1, 'application/xml', 'service-group', NOW(),  NOW()),
 (-3, 1, 'application/xml', 'service-metadata', NOW(),  NOW());
 
-insert into SMP_DOCUMENT_VERSION (ID, FK_DOCUMENT_ID, VERSION, DOCUMENT_CONTENT, CREATED_ON, LAST_UPDATED_ON) values
-(-1, -1, 1, '<ServiceGroup xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"><ParticipantIdentifier scheme="iso6523-actorid-upis">0088:777002abzz777</ParticipantIdentifier><ServiceMetadataReferenceCollection/></ServiceGroup>' , NOW(),  NOW()),
-(-2, -2, 1, '<ServiceGroup xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"><ParticipantIdentifier scheme="iso6523-actorid-upis">0088:777002abzz777</ParticipantIdentifier><ServiceMetadataReferenceCollection/></ServiceGroup>' , NOW(),  NOW()),
-(-3, -3, 1, FILE_READ('classpath:/input/ServiceMetadata.xml') , NOW(),  NOW());
+insert into SMP_DOCUMENT_VERSION (ID, FK_DOCUMENT_ID, VERSION, STATUS, DOCUMENT_CONTENT, CREATED_ON, LAST_UPDATED_ON) values
+(-1, -1, 1, 'PUBLISHED', '<ServiceGroup xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"><ParticipantIdentifier scheme="iso6523-actorid-upis">0088:777002abzz777</ParticipantIdentifier><ServiceMetadataReferenceCollection/></ServiceGroup>' , NOW(),  NOW()),
+(-2, -2, 1, 'PUBLISHED', '<ServiceGroup xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"><ParticipantIdentifier scheme="iso6523-actorid-upis">0088:777002abzz777</ParticipantIdentifier><ServiceMetadataReferenceCollection/></ServiceGroup>' , NOW(),  NOW()),
+(-3, -3, 1, 'PUBLISHED', FILE_READ('classpath:/input/ServiceMetadata.xml') , NOW(),  NOW());
 
 insert into SMP_RESOURCE ( ID, FK_GROUP_ID, FK_DOCUMENT_ID, FK_DOREDEF_ID,  IDENTIFIER_SCHEME, IDENTIFIER_VALUE, SML_REGISTERED, VISIBILITY, CREATED_ON, LAST_UPDATED_ON) values
 (-1, 1, -1, 1, 'ehealth-actorid-qns', 'urn:australia:ncpb', 0, 'PUBLIC', NOW(),  NOW()),