diff --git a/Frontend Angular 4/src/app/layout/grupos/grupos.component.html b/Frontend Angular 4/src/app/layout/grupos/grupos.component.html
index 9c374cb2cfce899f4c636960c79925addbcb49c4..eee2b4dd5b170b2faa87f01e6ecf47d70334d463 100755
--- a/Frontend Angular 4/src/app/layout/grupos/grupos.component.html	
+++ b/Frontend Angular 4/src/app/layout/grupos/grupos.component.html	
@@ -60,7 +60,7 @@
         [destroyOnHide]="false"
       >
         <li [ngbNavItem]="1">
-          <a ngbNavLink>Alumnos</a>
+          <a ngbNavLink>Users</a>
           <ng-template ngbNavContent>
             <div class="card">
               <div>
@@ -90,7 +90,6 @@
                   <i class="fa fa-users"></i>
                 </button>
                 <button
-                  *ngIf="roleCanDestroyGroup()"
                   class="btn btn-sm btn-secondary pull-right"
                   style="cursor: pointer; margin-top: -35px; margin-right: 71px"
                   (click)="openDestroyGroupModal()"
@@ -136,11 +135,11 @@
           <a ngbNavLink>Archivos</a>
           <ng-template ngbNavContent>
             <div class="card">
-              <div>
+              <div class="relative pull-right right-0">
                 <button
                   class="btn btn-sm btn-secondary pull-right"
                   style="cursor: pointer; margin-top: -35px; margin-right: 1px"
-                  (click)="desseleccionarGrupo()"
+                  (click)="navBack()"
                   ngbPopover="{{
                     'i18n.action.goBack' | translate | titleCase
                   }}"
@@ -149,9 +148,31 @@
                 >
                   <i class="fa fa-arrow-up"></i>
                 </button>
+                <button
+                  ngbPopover="{{ 'i18n.action.new' | translate | titleCase }}"
+                  triggers="mouseenter:mouseleave"
+                  placement="bottom"
+                  type="button"
+                  data-toggle="dropdown"
+                  class="btn btn-sm btn-secondary pull-right"
+                  style="cursor: pointer; margin-top: -35px; margin-right: 36px"
+                  aria-haspopup="true"
+                  aria-expanded="false"
+                >
+                  <i aria-hidden="true" class="fa fa-plus"></i>
+                </button>
+                <div class="dropdown-menu right-0" style="left: unset">
+                  <a class="dropdown-item" (click)="mkFile(true)">
+                    {{ "i18n.object.file" | translate | titleCase }}
+                  </a>
+                  <div role="separator" class="dropdown-divider"></div>
+                  <a class="dropdown-item" (click)="mkFile(false)">
+                    {{ "i18n.object.folder" | translate | titleCase }}
+                  </a>
+                </div>
                 <p
                   class="pull-right"
-                  style="margin-top: -34px; margin-right: 60px"
+                  style="margin-top: -34px; margin-right: 95px"
                 >
                   {{ grupoSeleccionado.name }}
                 </p>
@@ -162,12 +183,19 @@
                   style="min-height: 100px; overflow-y: scroll"
                 >
                   <div
-                    *ngFor="let archivo of grupoSeleccionado.archivos"
+                    *ngFor="let archivo of directorioActual.archivos"
                     (click)="seleccionarArchivo(archivo)"
                     class="col-sm-3 col-4 matefun-group-wrapper"
                   >
                     <i
+                      *ngIf="archivo.directorio"
+                      class="fa fa-folder matefun-fa-folder"
+                      aria-hidden="true"
+                    ></i>
+                    <i
+                      *ngIf="!archivo.directorio"
                       class="fa fa-file-text matefun-fa-file"
+                      [class.text-blue-400]="archivo.feedbackRequested"
                       aria-hidden="true"
                     ></i>
                     <p>{{ archivo.nombre }}</p>
@@ -182,14 +210,14 @@
       <div [ngbNavOutlet]="nav" class="mt-2" *ngIf="grupoSeleccionado"></div>
     </div>
     <div class="col-lg-7">
-      <div class="card" *ngIf="selectedUser">
+      <div class="card" *ngIf="selectedUser && !archivoSeleccionado">
         <div class="card-block">
           <div
             class="row listadoEntregasAlumnoGrupos"
             style="min-height: 100px; overflow-y: scroll"
           >
             <div
-              *ngFor="let entrega of selectedUser.archivos"
+              *ngFor="let entrega of selectedUserDirectorioActual"
               (click)="seleccionarEntrega(entrega)"
               class="col-sm-3 col-4 matefun-file-wrapper"
             >
@@ -201,7 +229,7 @@
               <p>{{ entrega.nombre }}</p>
             </div>
             <div
-              *ngIf="selectedUser.archivos.length == 0"
+              *ngIf="selectedUser && selectedUserDirectorioActual.length == 0"
               style="width: 100%; text-align: center"
             >
               <i
@@ -253,14 +281,46 @@
             Calificar
           </button>
           <button
-            *ngIf="esArchivoGrupo()"
-            ngbPopover="Cargar/Editar"
+            ngbPopover="{{ 'i18n.action.load' | translate | titleCase }}/{{
+              'i18n.action.edit' | translate | titleCase
+            }}"
             placement="bottom"
             triggers="mouseenter:mouseleave"
             class="btn btn-sm btn-secondary pull-left mr-2"
-            (click)="cargarArchivoCompartido()"
+            (click)="cargarArchivo()"
           >
-            <i class="fa fa-pencil"></i>
+            <i aria-hidden="true" class="fa fa-pencil"></i>
+          </button>
+          <button
+            ngbPopover="{{ 'i18n.action.delete' | translate | titleCase }}"
+            placement="bottom"
+            triggers="mouseenter:mouseleave"
+            class="btn btn-sm btn-secondary pull-left mr-2"
+            (click)="mostrarEliminarDialogo()"
+          >
+            <i aria-hidden="true" class="fa fa-remove"></i>
+          </button>
+          <button
+            ngbPopover="{{ 'i18n.action.move' | translate | titleCase }} {{
+              'i18n.object.file' | translate | titleCase
+            }}"
+            placement="bottom"
+            triggers="mouseenter:mouseleave"
+            class="btn btn-sm btn-secondary pull-left mr-2"
+            (click)="seleccionarDirectorioAMover()"
+          >
+            <i aria-hidden="true" class="fa fa-cut"></i>
+          </button>
+          <button
+            ngbPopover="{{
+              'i18n.action.requestFeedback' | translate | titleCase
+            }} {{ 'i18n.object.file' | translate | titleCase }}"
+            placement="bottom"
+            triggers="mouseenter:mouseleave"
+            class="btn btn-sm btn-secondary pull-left mr-2"
+            (click)="requestFeedback()"
+          >
+            <i aria-hidden="true" class="fa fa-exchange"></i>
           </button>
           <div class="pull-left">
             Nombre: {{ archivoSeleccionado?.nombre }} - Creado:
@@ -296,3 +356,45 @@
   *ngIf="modalQualifyDelivery"
 >
 </matefun-modal-calificar-entrega>
+
+<matefun-modal-nuevo-archivo
+  confirmLabel="{{ 'i18n.action.create' | translate | titleCase }}"
+  fileDescriptionLabel="{{ 'i18n.object.descr' | translate | titleCase }}:"
+  fileNameLabel="{{ 'i18n.object.name' | translate | titleCase }}:"
+  header="{{ 'i18n.action.new' | translate | titleCase }}
+  {{
+    (modalTypeIsFile ? 'i18n.object.file' : 'i18n.object.folder')
+      | translate
+      | titleCase
+  }} "
+  [opened]="modalCreateFileOpened"
+  [typeOfFile]="modalTypeIsFile ? 'file' : 'directory'"
+  (close)="modalCreateFile = false"
+  (confirmFileCreation)="
+    modalTypeIsFile
+      ? confirmDocumentCreation($event)
+      : confirmFileCreation($event)
+  "
+  *ngIf="modalCreateFile"
+>
+</matefun-modal-nuevo-archivo>
+
+<matefun-modal-seleccionar-directorio
+  confirmLabel="{{ 'i18n.action.move' | translate | titleCase }} {{
+    'i18n.object.here' | translate
+  }}"
+  [currentDirectory]="currentDirOfFileToMove"
+  [fileIdToMove]="archivoSeleccionado ? archivoSeleccionado.id : -1"
+  fileNameLabel="{{ 'i18n.object.name' | translate | titleCase }}"
+  header="{{ 'i18n.msg.file.move' | translate }}"
+  [initialPath]="currentPath"
+  navigateBackLabel="{{ 'i18n.action.goBack' | translate | titleCase }}"
+  [opened]="modalMoveFileOpened"
+  type-of-modal="move"
+  (close)="modalMoveFile = false"
+  (confirmFileCreation)="confirmFileMove($event)"
+  (navBack)="navigateBackModal($event)"
+  (navTo)="currentDirOfFileToMove = $event.detail"
+  *ngIf="modalMoveFile"
+>
+</matefun-modal-seleccionar-directorio>
diff --git a/Frontend Angular 4/src/app/layout/grupos/grupos.component.ts b/Frontend Angular 4/src/app/layout/grupos/grupos.component.ts
index d4802809c148c09f6fd8e43a423077e1fa6824c2..6e9b47134467b6e4e25d4e11082b52f8ddee7965 100755
--- a/Frontend Angular 4/src/app/layout/grupos/grupos.component.ts	
+++ b/Frontend Angular 4/src/app/layout/grupos/grupos.component.ts	
@@ -1,7 +1,11 @@
 import { ChangeDetectorRef, Component } from "@angular/core";
 import { Router } from "@angular/router";
 
-import { Archivo, Evaluacion } from "../../shared/objects/archivo-types";
+import {
+  Archivo,
+  Evaluacion,
+  MDocument,
+} from "../../shared/objects/archivo-types";
 import { Group } from "../../shared/objects/grupo";
 import { Usuario } from "../../shared/objects/usuario";
 import { AuthenticationService } from "../../shared/services/authentication.service";
@@ -11,11 +15,23 @@ import { NgbPopoverConfig, NgbPopover } from "@ng-bootstrap/ng-bootstrap";
 import { NotificacionService } from "../../shared/services/notificacion.service";
 import { TranslateService } from "@ngx-translate/core";
 import { GroupService } from "../../shared/services/group.service";
-import { roleCanManageGroupUsers, roleCanDestroyGroup } from "app/utils";
+import { GroupFileService } from "../../shared/services/group-file.service";
+import { FileService } from "../../shared/services/file.service";
+import { DocumentService } from "../../shared/services/document.service";
+import { GroupDocumentService } from "../../shared/services/group-document.service";
+import {
+  roleCanManageGroupUsers,
+  roleCanDestroyGroup,
+  findInTree,
+} from "app/utils";
 import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
 import { CreateGroupModal } from "../../shared/components/create-group-modal/create-group-modal.component";
 import { ManageGroupUsersModal } from "app/shared/components/manage-group-users-modal/manage-group-users-modal.component";
-import { DestroyGroupModal } from "app/shared";
+import { DestroyGroupModal, RequestFeedbackModal } from "app/shared";
+
+const STARTS_WITH_CAPITAL_LETTER_REGEX = /^[A-Z]/;
+
+const ID_ROOT_DIR = -1;
 
 @Component({
   selector: "grupos",
@@ -23,12 +39,14 @@ import { DestroyGroupModal } from "app/shared";
 })
 export class GruposComponent {
   archivos: Archivo[] = [];
+  selectedUserArchivos: Archivo[] = [];
   grupos: Group[] = [];
   grupoSeleccionado: Group = undefined;
 
   selectedUser: Usuario = undefined;
 
   archivoSeleccionado: Archivo = undefined;
+  selectedFile: Archivo = undefined;
 
   tipoArchivo: string = undefined;
 
@@ -39,8 +57,12 @@ export class GruposComponent {
   configCodeMirror = JSON.parse(localStorage.getItem("codeMirrorConfig"));
   translateService: any;
 
+  selectedUserFileArray: any;
+  selectedUserDirectorioActual: any;
+
   createModalRef: any;
 
+  preview: string = "";
   // - - - - - - - - - - - -  Modal show confirm  - - - - - - - - - - - -
   /**
    * Con `true` se renderiza el modal de calificar entrega.
@@ -68,6 +90,51 @@ export class GruposComponent {
     },
   ];
 
+  // - - - - - - - - - - - - - Modal create file - - - - - - - - - - - - -
+  /**
+   * Con `true` se renderiza el modal de crear un archivo
+   */
+  modalCreateFile = false;
+
+  /**
+   * Con `true` se configura el modal para que se muestre la interfaz de
+   * agregar/borrar archivo. De otro modo, se muestra la interfaz de
+   * agregar/borrar directorio
+   */
+  modalTypeIsFile = true;
+
+  /**
+   * Con `true` se indica que el modal -de crear un archivo- se quiere abrir.
+   * Útil para avisar al modal que anime el dismiss antes de que se elimine del
+   * DOM
+   */
+  modalCreateFileOpened = true;
+
+  /**
+   * Directorio actual sobre el cual será desplegada la lista de directorios
+   * del modal mover archivo.
+   */
+  currentDirOfFileToMove: Archivo;
+
+  /**
+   * Determina la ruta en donde se encuentra ubicado el archivo/directorio
+   * seleccionado actualmente.
+   */
+  currentPath: string = "/";
+
+  // - - - - - - - - - - - - -  Modal move file  - - - - - - - - - - - - -
+  /**
+   * Con `true` se renderiza el modal de mover un archivo
+   */
+  modalMoveFile = false;
+
+  /**
+   * Con `true` se indica que el modal -de mover un archivo- se quiere abrir.
+   * Útil para avisar al modal que anime el dismiss antes de que se elimine del
+   * DOM
+   */
+  modalMoveFileOpened = true;
+
   constructor(
     private router: Router,
     private authService: AuthenticationService,
@@ -76,6 +143,10 @@ export class GruposComponent {
     private sessionService: SessionService,
     public translate: TranslateService,
     public groupService: GroupService,
+    public groupFileService: GroupFileService,
+    public documentService: DocumentService,
+    public groupDocumentService: GroupDocumentService,
+    public fileService: FileService,
     private modalService: NgbModal,
     private changeDetectorRef: ChangeDetectorRef
   ) {
@@ -144,9 +215,12 @@ export class GruposComponent {
   }
 
   ordenarArchivos() {
-    // this.grupoSeleccionado.archivos = this.grupoSeleccionado.archivos.sort(
-    //   this.ordenarAlph
-    // );
+    // var tipo = this.sortFunction;
+    // if (tipo === "tipo") {
+    //   this.ordenarMixto();
+    // } else if (tipo === "fecha") {
+    //   this.ordenarFechaCreacion();
+    // }
   }
 
   //ordeno los archivos del alumno (los archivos entregados.)
@@ -183,37 +257,181 @@ export class GruposComponent {
     // );
   }
 
+  /**
+   * Renderiza en pantalla el modal de agregar archivos o directorios,
+   * dependiendo del valor de `modalTypeIsFile`.
+   * @param modalTypeIsFile Con `true` se indica que el modal a renderizar es el de agregar archivo. De otro modo, se renderiza el de agregar directorio.
+   */
+  mkFile(modalTypeIsFile: boolean) {
+    this.modalTypeIsFile = modalTypeIsFile;
+
+    // Mostrar el modal
+    this.modalCreateFile = true;
+    this.modalCreateFileOpened = true;
+  }
+
   seleccionarGrupo(grupo) {
     this.grupoSeleccionado = grupo;
     this.ordenarAlumnos();
-    this.ordenarArchivos();
-    this.archivoSeleccionado = undefined;
+    this.loadFilesAndFolders(grupo.id);
     this.selectedUser = undefined;
   }
 
+  loadFilesAndFolders(grupoId: number, directorioActualId = null) {
+    this.groupFileService.getGroupFiles(grupoId).subscribe(
+      (files) => {
+        this.tree = this.fileService.fileToArchivo(files["files"]);
+
+        this.archivos = [this.tree];
+        this.loading = false;
+
+        if (directorioActualId) {
+          this.directorioActual = findInTree(this.archivos, directorioActualId);
+        } else {
+          this.directorioActual = this.tree;
+        }
+        this.ordenarArchivos();
+        this.sessionService.setArchivosTree(this.tree);
+
+        this.currentDirOfFileToMove = this.directorioActual;
+      },
+      (error) => console.log(error)
+    );
+  }
+
   desseleccionarGrupo() {
     this.grupoSeleccionado = undefined;
     this.archivoSeleccionado = undefined;
     this.selectedUser = undefined;
   }
 
-  seleccionarUser(alumno) {
-    if (!(typeof alumno === "undefined")) {
-      this.selectedUser = alumno;
-      this.ordenarArchivosAlumno();
+  seleccionarUser(user) {
+    this.groupFileService
+      .getFeedbackRequestedGroupFiles(this.grupoSeleccionado.id, user.id)
+      .subscribe(
+        (files) => {
+          this.selectedUserFileArray = files["files"].map((file) =>
+            this.fileService.fileToArchivo(file)
+          );
+          this.selectedUserArchivos = [this.selectedUserFileArray];
+          this.loading = false;
+
+          this.selectedUserDirectorioActual = this.selectedUserFileArray;
+          this.ordenarArchivos();
+          this.sessionService.setArchivosTree(this.selectedUserFileArray);
+
+          this.selectedUser = user;
+
+          this.archivoSeleccionado = undefined;
+        },
+        (error) => console.log(error)
+      );
+  }
+
+  canRequestFeedback() {
+    return (
+      !!this.grupoSeleccionado.id &&
+      !!this.selectedFile &&
+      this.selectedFile.feedbackRequested !== undefined &&
+      !this.selectedFile.feedbackRequested
+    );
+  }
+
+  requestFeedback() {
+    this.openRequestFeedbackModal();
+  }
+
+  seleccionarArchivo(archivo) {
+    this.selectedFile = archivo;
+    if (archivo.directorio) {
+      this.directorioActual = archivo;
+      // this.loadFilesAndFolders(this.grupoSeleccionado.id, archivo.id);
       this.archivoSeleccionado = undefined;
+      // this.selectedUser = undefined;
+      this.currentPath += `${archivo.nombre}/`;
+      this.preview = "";
+    } else {
+      this.selectedUser = undefined;
+      this.tipoArchivo = "compartido";
+      this.groupDocumentService
+        .getGroupDocument(
+          this.grupoSeleccionado.id,
+          archivo.documentId,
+          this.selectedUser?.id
+        )
+        .subscribe(
+          (data) => {
+            const document = data["document"];
+            const archivoDocument =
+              this.documentService.documentToArchivo(document);
+            this.actualizarArchivoSeleccionado(archivoDocument);
+          },
+          (error) => console.log(error)
+        );
     }
   }
 
-  seleccionarArchivo(archivo) {
+  /**
+   * Actualiza el estado del archivo seleccionado, así como la preview del
+   * contenido de dicho archivo.
+   * @param archivo Archivo a seleccionar
+   */
+  actualizarArchivoSeleccionado(archivo: Archivo) {
     this.archivoSeleccionado = archivo;
-    this.selectedUser = undefined;
-    this.tipoArchivo = "compartido";
+    this.preview = !!archivo?.contenido ? archivo.contenido : "";
+  }
+
+  /**
+   * Navega hacia el directorio padre en la vista principal de directorios.
+   * @param shouldUpdateSelectedFile Determina si se debe actualizar el archivo seleccionado cuando se navega hacia atrás
+   */
+  navBack(shouldUpdateSelectedFile = true) {
+    const { padreId } = this.directorioActual;
+
+    if (padreId === ID_ROOT_DIR) {
+      return;
+    }
+
+    // Actualiza el current path
+    const lastDirectoryIndex = this.currentPath.lastIndexOf(
+      `${this.directorioActual.nombre}`
+    );
+
+    this.currentPath = this.currentPath.substring(0, lastDirectoryIndex);
+
+    const padre = findInTree(this.archivos, padreId);
+
+    if (shouldUpdateSelectedFile) {
+      // Cuando se selecciona un directorio cuyo id es el root, significa que ese
+      // es el último archivo de la rama de directorios. En otras palabras, el
+      // root se identifica porque su padreId es ID_ROOT_DIR.
+      const archivoSeleccionado =
+        padre.padreId == ID_ROOT_DIR ? undefined : padre;
+      this.actualizarArchivoSeleccionado(archivoSeleccionado);
+    }
+
+    // Actualiza la vista de directorios y archivos
+    this.directorioActual = padre;
   }
 
   seleccionarEntrega(entrega) {
-    this.archivoSeleccionado = entrega;
-    this.selectedUser = undefined;
+    this.groupDocumentService
+      .getGroupDocument(
+        this.grupoSeleccionado.id,
+        entrega.documentId,
+        this.selectedUser.id
+      )
+      .subscribe(
+        (data) => {
+          // this.archivoSeleccionado = entrega;
+          const document = data["document"];
+          const archivoDocument =
+            this.documentService.documentToArchivo(document);
+          this.actualizarArchivoSeleccionado(archivoDocument);
+        },
+        (error) => console.log(error)
+      );
+    // this.selectedUser = undefined;
     this.tipoArchivo = "entrega";
   }
 
@@ -231,14 +449,35 @@ export class GruposComponent {
     );
   }
 
-  openDestroyGroupModal() {
-    if (!this.grupoSeleccionado) {
+  openRequestFeedbackModal() {
+    if (!this.grupoSeleccionado.id) {
       this.showNotification("warning", "i18n.warning.group.noSelected");
       return;
     }
 
-    if (!roleCanDestroyGroup(this.grupoSeleccionado.role)) {
-      this.showNotification("error", "i18n.warning.group.noPermission");
+    if (!this.selectedFile.id) {
+      this.showNotification("warning", "i18n.warning.file.noSelected");
+      return;
+    }
+
+    this.createModalRef = this.modalService.open(RequestFeedbackModal);
+
+    this.createModalRef.componentInstance.groupName =
+      this.grupoSeleccionado.name;
+    this.createModalRef.componentInstance.groupId = this.grupoSeleccionado.id;
+    this.createModalRef.componentInstance.fileName =
+      this.archivoSeleccionado.nombre;
+    this.createModalRef.componentInstance.fileId =
+      this.archivoSeleccionado.fileId;
+    this.createModalRef.componentInstance.confirmFeedbackRequested =
+      this.confirmFeedbackRequsted;
+  }
+
+  confirmFeedbackRequsted() {}
+
+  openDestroyGroupModal() {
+    if (!this.grupoSeleccionado) {
+      this.showNotification("warning", "i18n.warning.group.noSelected");
       return;
     }
 
@@ -249,6 +488,8 @@ export class GruposComponent {
     this.createModalRef.componentInstance.groupId = this.grupoSeleccionado.id;
     this.createModalRef.componentInstance.confirmGroupDestroyed =
       this.confirmGroupDestroyed;
+    this.createModalRef.componentInstance.userRole =
+      this.grupoSeleccionado.role;
 
     // this.translateService
     //   .get("i18n.warning.group.destroy", {
@@ -382,14 +623,131 @@ export class GruposComponent {
     // } else {
     //   return false;
     // }
+    return true;
   }
 
-  cargarArchivoCompartido() {
+  cargarArchivo() {
     if (!this.archivoSeleccionado || this.archivoSeleccionado.directorio) {
       this.showNotification("warning", "i18n.warning.file.noSelected");
       return;
     }
     this.sessionService.setArchivo(this.archivoSeleccionado);
-    this.router.navigate(["/matefun"]);
+    const userId = !!this.selectedUser
+      ? this.selectedUser.id
+      : JSON.parse(localStorage.getItem("currentUser"))["id"];
+
+    this.router.navigate([
+      `/grupos/${this.grupoSeleccionado.id}/users/${userId}/matefun/${this.archivoSeleccionado.id}`,
+    ]);
+  }
+
+  seleccionarDirectorioAMover() {
+    if (!this.archivoSeleccionado) {
+      this.showNotification("warning", "i18n.warning.file.noSelected");
+      return;
+    }
+
+    // Si el archivo es del alumno, se puede mover.
+    // No se controla por creador, dado que los compartidos mantienen este atributo
+    // if (!this.archivos.some((arch) => arch.id == this.archivoSeleccionado.id)) {
+    //   this.showNotification("warning", "i18n.warning.file.noPermissionMove");
+    //   return;
+    // }
+
+    // this.navBack(false);
+    if (this.archivoSeleccionado.directorio) {
+      this.currentDirOfFileToMove = this.directorioActual.parent;
+    } else {
+      this.currentDirOfFileToMove = this.directorioActual;
+    }
+
+    // Mostrar el modal
+    this.modalMoveFile = true;
+    this.modalMoveFileOpened = true;
+  }
+
+  /**
+   * Valida y confirma la creación del archivo.
+   * @param event Evento devuelto por el modal asociado para crear el archivo. En el detalle contiene el `nombre` y `descripcion` del archivo que se desea agregar.
+   */
+  confirmFileCreation(event: CustomEvent) {
+    const { nombre, descripcion } = event.detail;
+
+    // Antes que nada, se chequea que empiece con mayúscula
+    if (!STARTS_WITH_CAPITAL_LETTER_REGEX.test(nombre)) {
+      this.showNotification("warning", "i18n.warning.file.capitalLetter");
+      return;
+    }
+
+    const archivo = new Archivo();
+    // archivo.cedulaCreador = this.directorioActual.cedulaCreador; // cedula
+    archivo.contenido = this.modalTypeIsFile ? "" : descripcion || "";
+    archivo.directorio = !this.modalTypeIsFile;
+    archivo.editable = true;
+    archivo.fechaCreacion = new Date();
+    archivo.nombre = nombre;
+    archivo.padreId = this.directorioActual.id;
+    archivo.archivos = [];
+
+    const that = this;
+    const idDirectorioActual = this.directorioActual.id;
+
+    const file = this.fileService.archivoToFile(archivo);
+    this.groupFileService
+      .createGroupFile(this.grupoSeleccionado.id, file)
+      .subscribe(
+        () => {
+          that.loadFilesAndFolders(idDirectorioActual);
+
+          // Cerrar el modal en caso de éxito
+          that.modalCreateFileOpened = false;
+        },
+        (error) => {
+          that.notifService.error(error.text());
+
+          // Cerrar el modal en caso de error
+          that.modalCreateFileOpened = false;
+        }
+      );
+  }
+
+  /**
+   * Valida y confirma la creación del archivo.
+   * @param event Evento devuelto por el modal asociado para crear el archivo. En el detalle contiene el `nombre` y `descripcion` del archivo que se desea agregar.
+   */
+  confirmDocumentCreation(event: CustomEvent) {
+    const { nombre } = event.detail;
+
+    // Antes que nada, se chequea que empiece con mayúscula
+    if (!STARTS_WITH_CAPITAL_LETTER_REGEX.test(nombre)) {
+      this.showNotification("warning", "i18n.warning.file.capitalLetter");
+      return;
+    }
+
+    const document = new MDocument();
+    document.text_doc = "";
+    document.title = nombre;
+    document.parent_id = this.directorioActual.id;
+
+    const that = this;
+    const idDirectorioActual = this.directorioActual.id;
+
+    this.documentService
+      .createDocument(document, this.grupoSeleccionado.id)
+      .subscribe(
+        () => {
+          that.loadFilesAndFolders(
+            this.grupoSeleccionado.id,
+            idDirectorioActual
+          );
+
+          that.modalCreateFileOpened = false;
+        },
+        (error) => {
+          that.notifService.error(error.text());
+
+          that.modalCreateFileOpened = false;
+        }
+      );
   }
 }
diff --git a/Frontend Angular 4/src/app/layout/layout-routing.module.ts b/Frontend Angular 4/src/app/layout/layout-routing.module.ts
index 0e510bbbf313e7941ae83f5ab80d39bb75aec17a..9a4cbb8b60560d497c24f760a49b9c0211be4e5b 100755
--- a/Frontend Angular 4/src/app/layout/layout-routing.module.ts	
+++ b/Frontend Angular 4/src/app/layout/layout-routing.module.ts	
@@ -17,6 +17,11 @@ const routes: Routes = [
         loadChildren: () =>
           import("./matefun/matefun.module").then((m) => m.MateFunModule),
       },
+      {
+        path: "grupos/:groupId/users/:groupUserId/matefun/:id",
+        loadChildren: () =>
+          import("./matefun/matefun.module").then((m) => m.MateFunModule),
+      },
       {
         path: "archivos",
         loadChildren: () =>
diff --git a/Frontend Angular 4/src/app/layout/layout.module.ts b/Frontend Angular 4/src/app/layout/layout.module.ts
index 3e4f59afcb81f54694c11933b9e2fe1d143290a2..ca4ed8ee8e05b73190189f9a7e3167c4368d5c22 100755
--- a/Frontend Angular 4/src/app/layout/layout.module.ts	
+++ b/Frontend Angular 4/src/app/layout/layout.module.ts	
@@ -10,6 +10,8 @@ import { AuthenticationService } from "../shared/services/authentication.service
 import { HaskellService } from "../shared/services/haskell.service";
 import { DocumentService } from "../shared/services/document.service";
 import { FileService } from "../shared/services/file.service";
+import { GroupDocumentService } from "../shared/services/group-document.service";
+import { GroupFileService } from "../shared/services/group-file.service";
 import { GroupService } from "../shared/services/group.service";
 import { LtCodemirrorModule } from "lt-codemirror";
 import { CodemirrorModule } from "@ctrl/ngx-codemirror";
@@ -35,6 +37,8 @@ import { TitleCaseModule } from "../shared/modules/titlecase.module";
     HaskellService,
     DocumentService,
     FileService,
+    GroupDocumentService,
+    GroupFileService,
     GroupService,
   ],
 })
diff --git a/Frontend Angular 4/src/app/layout/matefun/matefun.component.html b/Frontend Angular 4/src/app/layout/matefun/matefun.component.html
index 15d5a3eae914138397edff6f99518365dce6bd2c..7dbe99cc4aab0c17d980977ac55f9c886dc7a311 100755
--- a/Frontend Angular 4/src/app/layout/matefun/matefun.component.html	
+++ b/Frontend Angular 4/src/app/layout/matefun/matefun.component.html	
@@ -169,6 +169,32 @@
                   >
                     <i class="fa fa-plus"></i>
                   </button>
+                  <button
+                    *ngIf="canRequestFeedback()"
+                    style="float: right"
+                    (click)="requestFeedback()"
+                    class="btn btn-sm btn-secondary"
+                    ngbPopover="{{
+                      'i18n.action.requestFeedback' | translate | titleCase
+                    }} {{ 'i18n.object.file' | translate | titleCase }}"
+                    triggers="mouseenter:mouseleave"
+                    placement="bottom"
+                  >
+                    <i class="fa fa-exchange"></i>
+                  </button>
+                  <button
+                    *ngIf="canReturnFeedback()"
+                    style="float: right"
+                    (click)="returnFeedback()"
+                    class="btn btn-sm btn-secondary"
+                    ngbPopover="{{
+                      'i18n.action.returnFeedback' | translate | titleCase
+                    }} {{ 'i18n.object.file' | translate | titleCase }}"
+                    triggers="mouseenter:mouseleave"
+                    placement="bottom"
+                  >
+                    <i class="fa fa-exchange"></i>
+                  </button>
                   <ng-template #popoverContent style="width: 15em">
                     <div style="width: 12em">
                       <div class="form-group">
diff --git a/Frontend Angular 4/src/app/layout/matefun/matefun.component.ts b/Frontend Angular 4/src/app/layout/matefun/matefun.component.ts
index 5f65fc1d10c4175b71b93455462a47062f386b04..20238240c3a020e9a8f775769d5ef343483f03c6 100755
--- a/Frontend Angular 4/src/app/layout/matefun/matefun.component.ts	
+++ b/Frontend Angular 4/src/app/layout/matefun/matefun.component.ts	
@@ -14,6 +14,7 @@ import { HaskellService } from "../../shared/services/haskell.service";
 import { DocumentService } from "../../shared/services/document.service";
 import { UserService } from "../../shared/services/user.service";
 import { FileService } from "../../shared/services/file.service";
+import { GroupFileService } from "../../shared/services/group-file.service";
 import { WebsocketService } from "../../shared/services/websocket.service";
 import { ActionCableService } from "app/shared/services/actioncable.service";
 import { UsuarioService } from "../../shared/services/usuario.service";
@@ -41,7 +42,12 @@ import { CodeMirrorBinding } from "y-codemirror";
 import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
 import { CreateFileModal } from "../../shared/components/create-file-modal/create-file-modal.component";
 import { ImportFileModal } from "../../shared/components/import-file-modal/import-file-modal.component";
-import { ShareFileModal } from "app/shared";
+import {
+  ShareFileModal,
+  RequestFeedbackModal,
+  ReturnFeedbackModal,
+} from "app/shared";
+import { GroupDocumentService } from "../../shared/services/group-document.service";
 
 import * as Y from "yjs";
 
@@ -67,7 +73,8 @@ import "./codemirror/addons/functions_definition_EN.js";
 import "./codemirror/addons/functions_definition_ES.js";
 import { Action } from "rxjs/internal/scheduler/Action";
 import { findInTree, roleCanShareFile } from "app/utils";
-import { filter } from "rxjs";
+import { filter, Observable } from "rxjs";
+import { GroupService } from "app/shared/services/group.service";
 
 var codeMirrorRef: any;
 var componentRef: any;
@@ -80,6 +87,8 @@ var focus: any;
   providers: [WebsocketService, NgbPopoverConfig, UsuarioService],
 })
 export class MateFunComponent implements OnInit, OnChanges {
+  currentUserGroupRole: string;
+  documentBelongsToCurrentUser: boolean;
   titlecasePipe: any;
   translateService: any;
   consoleDisable: boolean = false;
@@ -180,6 +189,8 @@ export class MateFunComponent implements OnInit, OnChanges {
   activeTabId = 1;
   ydoc: null | Y.Doc = null;
   documentId: number | null = null;
+  groupId: number | null = null;
+  groupUserId: number | null = null;
   editorContainer: Element;
   editor: CodeMirror.Editor;
   yUndoManager: Y.UndoManager;
@@ -224,6 +235,7 @@ export class MateFunComponent implements OnInit, OnChanges {
     private documentService: DocumentService,
     private userService: UserService,
     private fileService: FileService,
+    private groupFileService: GroupFileService,
     private authService: AuthenticationService,
     private ghciService: GHCIService,
     private notifService: NotificacionService,
@@ -231,6 +243,8 @@ export class MateFunComponent implements OnInit, OnChanges {
     private usuarioService: UsuarioService,
     public translate: TranslateService,
     private actionCableService: ActionCableService,
+    public groupDocumentService: GroupDocumentService,
+    public groupService: GroupService,
     private router: Router,
     private route: ActivatedRoute,
     private modalService: NgbModal,
@@ -399,10 +413,51 @@ export class MateFunComponent implements OnInit, OnChanges {
 
   getCurrentDocument(nextFunction: () => void = null) {
     this.documentId = parseInt(this.route.snapshot.paramMap.get("id"));
-    this.documentService.getDocument(this.documentId).subscribe({
+    this.groupId = parseInt(this.route.snapshot.paramMap.get("groupId"));
+    this.groupUserId = parseInt(
+      this.route.snapshot.paramMap.get("groupUserId")
+    );
+    let documentSubscription: Observable<{
+      document: MDocument;
+    }> = null;
+    let fileSubscriptionService: {
+      getFile: (id: number) => Observable<{
+        file: MFile;
+      }>;
+    } = null;
+    // TODO: Do something similar to this but for files,
+    // also the redirect to here when choosing a file in the groups page
+    // is setting the wrong user id
+    if (!!this.groupId && !!this.groupUserId) {
+      documentSubscription = this.groupDocumentService.getGroupDocument(
+        this.groupId,
+        this.documentId,
+        this.groupUserId
+      );
+
+      fileSubscriptionService =
+        this.groupFileService.getSpecificUserFileWithSetGroup(
+          this.groupId,
+          this.groupUserId
+        );
+    } else {
+      documentSubscription = this.documentService.getDocument(this.documentId);
+      fileSubscriptionService = this.fileService;
+    }
+    documentSubscription.subscribe({
       next: (data) => {
         this.archivo = this.documentService.documentToArchivo(data.document);
-        if (!!nextFunction) {
+        if (this.router.url.includes("grupos")) {
+          fileSubscriptionService
+            .getFile(this.archivo.fileId)
+            .subscribe((file) => {
+              this.archivo.feedbackRequested = file["file"].feedback_requested;
+
+              if (!!nextFunction) {
+                nextFunction();
+              }
+            });
+        } else if (!!nextFunction) {
           nextFunction();
         }
       },
@@ -412,6 +467,83 @@ export class MateFunComponent implements OnInit, OnChanges {
     });
   }
 
+  canRequestFeedback() {
+    // TODO: Quiero que un moderator pueda pedir feedback de un archivo que no es suyo?
+    return (
+      !!this.groupId &&
+      !!this.archivo &&
+      this.archivo.feedbackRequested !== undefined &&
+      !this.archivo.feedbackRequested &&
+      this.documentBelongsToCurrentUser
+    );
+  }
+
+  canReturnFeedback() {
+    return (
+      !!this.groupId &&
+      !!this.archivo &&
+      this.archivo.feedbackRequested !== undefined &&
+      this.archivo.feedbackRequested &&
+      this.groupService.userCanReturnFeedback(this.currentUserGroupRole)
+    );
+    // TODO: Do this and the other function
+  }
+
+  requestFeedback() {
+    this.openRequestFeedbackModal();
+  }
+
+  returnFeedback() {
+    this.openReturnFeedbackModal();
+  }
+
+  openRequestFeedbackModal() {
+    if (!this.groupId) {
+      this.showNotification("warning", "i18n.warning.group.noSelected");
+      return;
+    }
+
+    if (!this.archivo.fileId) {
+      this.showNotification("warning", "i18n.warning.file.noSelected");
+      return;
+    }
+
+    this.createModalRef = this.modalService.open(RequestFeedbackModal);
+
+    this.createModalRef.componentInstance.groupName = null;
+    this.createModalRef.componentInstance.groupId = this.groupId;
+    this.createModalRef.componentInstance.fileName = this.archivo.nombre;
+    this.createModalRef.componentInstance.fileId = this.archivo.fileId;
+    this.createModalRef.componentInstance.confirmFeedbackRequested =
+      this.confirmFeedbackRequsted;
+  }
+
+  confirmFeedbackRequsted() {}
+
+  openReturnFeedbackModal() {
+    if (!this.groupId) {
+      this.showNotification("warning", "i18n.warning.group.noSelected");
+      return;
+    }
+
+    if (!this.archivo.fileId) {
+      this.showNotification("warning", "i18n.warning.file.noSelected");
+      return;
+    }
+
+    this.createModalRef = this.modalService.open(ReturnFeedbackModal);
+
+    this.createModalRef.componentInstance.groupName = null;
+    this.createModalRef.componentInstance.groupId = this.groupId;
+    this.createModalRef.componentInstance.fileName = this.archivo.nombre;
+    this.createModalRef.componentInstance.fileId = this.archivo.fileId;
+    this.createModalRef.componentInstance.userId = this.groupUserId;
+    this.createModalRef.componentInstance.confirmFeedbackReturned =
+      this.confirmFeedbackReturned;
+  }
+
+  confirmFeedbackReturned() {}
+
   ngOnChanges() {
     // const docId = this.route.snapshot.paramMap.get("id");
     // this.confirmFileCreation = this.confirmFileCreation.bind(this);
@@ -458,10 +590,34 @@ export class MateFunComponent implements OnInit, OnChanges {
     this.configCodeMirrorDefinicion["readOnly"] = true;
 
     // This was always on ngOnInit
+
+    this.groupId = !!this.route.snapshot.paramMap.get("groupId")
+      ? parseInt(this.route.snapshot.paramMap.get("groupId"))
+      : null;
+    this.groupUserId = !!this.route.snapshot.paramMap.get("groupUserId")
+      ? parseInt(this.route.snapshot.paramMap.get("groupUserId"))
+      : null;
+
     if (!!this.route.snapshot.paramMap.get("id")) {
       this.getCurrentDocument(() => {
         this.copiaContenidoArchivo = this.archivo.contenido;
         this.copiaNombreArchivo = this.archivo.nombre;
+
+        if (
+          this.router.url.includes("grupos") &&
+          !!this.groupId &&
+          !!this.groupUserId
+        ) {
+          this.groupFileService
+            .getSpecificUserGroupFiles(this.groupId, this.groupUserId)
+            .subscribe((data) => {
+              this.importFilesData(data);
+            });
+        } else {
+          this.fileService.getFiles().subscribe((data) => {
+            this.importFilesData(data);
+          });
+        }
       });
     } else {
       this.newFile();
@@ -588,38 +744,42 @@ export class MateFunComponent implements OnInit, OnChanges {
       this.configCodeMirror.mode.name = "matefun-ES";
     }
 
-    this.fileService.getFiles().subscribe((files) => {
-      let matefun_files = files["files"];
-      matefun_files = this.fileService.completeFiles(matefun_files);
-      this.sessionService.setUserFiles(matefun_files);
-      let fileArchivos = this.fileService.fileToArchivo(matefun_files);
-
-      this.archivosTree = fileArchivos;
-      if (!!this.editor) {
-        (this.editor as any).options.files = this.archivosTree;
-      }
-      this.sessionService.setArchivosTree(this.archivosTree);
-    });
+    // this.fileService.getFiles().subscribe((files) => {
+    //   let matefun_files = files["files"];
+    //   matefun_files = this.fileService.completeFiles(matefun_files);
+    //   this.sessionService.setUserFiles(matefun_files);
+    //   let fileArchivos = this.fileService.fileToArchivo(matefun_files);
+
+    //   this.archivosTree = fileArchivos;
+    //   if (!!this.editor) {
+    //     (this.editor as any).options.files = this.archivosTree;
+    //   }
+    //   this.sessionService.setArchivosTree(this.archivosTree);
+    // });
+  }
 
-    this.fileService.getFiles().subscribe((data) => {
-      let matefun_files = data["files"];
-      matefun_files = this.fileService.completeFiles(matefun_files);
-      const tree = this.fileService.fileToArchivo(matefun_files);
-      this.archivosTree = tree;
-      this.sessionService.setArchivosTree(tree);
-      const file = findInTree([tree], this.archivo.fileId);
-      const directorioActual = findInTree([tree], file.padreId);
-      this.sessionService.setDirectorioActual(directorioActual);
-      this.documentService
-        .getDocuments(
-          directorioActual.archivos.map((archivo) => {
-            return archivo.documentId;
-          })
-        )
-        .subscribe((data) => {
-          this.sessionService.setCurrentDirectoryDocuments(data["documents"]);
-        });
-    });
+  importFilesData(data) {
+    let matefun_files = data["files"];
+    matefun_files = this.fileService.completeFiles(matefun_files);
+    this.sessionService.setUserFiles(matefun_files);
+    const tree = this.fileService.fileToArchivo(matefun_files);
+    this.archivosTree = tree;
+    if (!!this.editor) {
+      (this.editor as any).options.files = this.archivosTree;
+    }
+    this.sessionService.setArchivosTree(tree);
+    const file = findInTree([tree], this.archivo.fileId);
+    const directorioActual = findInTree([tree], file.padreId);
+    this.sessionService.setDirectorioActual(directorioActual);
+    this.documentService
+      .getDocuments(
+        directorioActual.archivos.map((archivo) => {
+          return archivo.documentId;
+        })
+      )
+      .subscribe((data) => {
+        this.sessionService.setCurrentDirectoryDocuments(data["documents"]);
+      });
   }
 
   ngAfterViewInit() {
@@ -767,15 +927,35 @@ export class MateFunComponent implements OnInit, OnChanges {
     if (!!this.archivo.users) {
       if (this.archivo.users.length > 0) {
         const id = JSON.parse(localStorage.getItem("currentUser"))["id"];
-        readOnly =
-          this.archivo.users.find((user) => {
-            return user.id === id;
-          }).role === "viewer";
+        const user = this.archivo.users.find((user) => {
+          return user.id === id;
+        });
+        this.documentBelongsToCurrentUser = !!user;
 
-        this.editor.setOption("readOnly", readOnly);
+        if (!!user) {
+          readOnly = user.role === "viewer";
+        }
+        if ((!user || readOnly == true) && !!this.groupId) {
+          const id = JSON.parse(localStorage.getItem("currentUser"))["id"];
+          this.groupService.getUserRole(this.groupId, id).subscribe((data) => {
+            this.currentUserGroupRole = data.role.role;
+
+            this.editor.setOption(
+              "readOnly",
+              this.groupService.userCanEditAnyGroupFile(
+                this.currentUserGroupRole
+              )
+            );
+          });
+        } else {
+          this.editor.setOption("readOnly", readOnly);
+        }
       } else {
+        this.documentBelongsToCurrentUser = true;
         this.editor.setOption("readOnly", false);
       }
+    } else {
+      this.documentBelongsToCurrentUser = true;
     }
 
     if (!!this.binding) {
@@ -802,11 +982,22 @@ export class MateFunComponent implements OnInit, OnChanges {
 
     this.ydoc = yDocument;
 
+    let params: Record<string, string> = {
+      doc_id: this.documentId?.toString(),
+    };
+    if (!!this.groupId && !!this.groupUserId) {
+      params = {
+        ...params,
+        user_id: this.groupUserId.toString(),
+        group_id: this.groupId.toString(),
+      };
+    }
+
     this.provider = new WebsocketProvider(
       this.ydoc,
       this.actionCableService.consumer,
       "ApplicationCable::DocumentChannel",
-      { doc_id: this.documentId?.toString() }
+      params
     );
 
     let awareness = undefined;
diff --git a/Frontend Angular 4/src/app/shared/components/destroy-group-modal/destroy-group-modal.component.ts b/Frontend Angular 4/src/app/shared/components/destroy-group-modal/destroy-group-modal.component.ts
index 62e1962aebeea8aecfe4f8067f3f78784cc48798..f4b4582fbb8c65df9b93ed204a3030dd4f8ed79a 100644
--- a/Frontend Angular 4/src/app/shared/components/destroy-group-modal/destroy-group-modal.component.ts	
+++ b/Frontend Angular 4/src/app/shared/components/destroy-group-modal/destroy-group-modal.component.ts	
@@ -17,11 +17,12 @@ import { GroupService } from "../../services/group.service";
 export class DestroyGroupModal implements OnInit {
   @Input() groupName: string;
   @Input() groupId: number;
+  @Input() userRole: string;
   /**
    * Se dispara cuando se quiere administrar los usuarios del grupo
    */
   @Input() confirmGroupDestroyed: () => void;
-  title = "Destruir Grupo";
+  title: string;
   message: string;
 
   constructor(
@@ -32,7 +33,13 @@ export class DestroyGroupModal implements OnInit {
   ngOnInit(): void {
     this.groupName = `${this.groupName}`;
 
-    this.message = `¿Estás seguro de que deseas destruir el grupo ${this.groupName}?`;
+    if (this.userRole === "owner") {
+      this.title = "Destruir Grupo";
+      this.message = `¿Estás seguro de que deseas destruir el grupo ${this.groupName}?`;
+    } else {
+      this.title = "Abandonar Grupo";
+      this.message = `¿Estás seguro de que desea abandonar el grupo ${this.groupName}?`;
+    }
     this.destroyGroup = this.destroyGroup.bind(this);
     this.cancel = this.cancel.bind(this);
   }
diff --git a/Frontend Angular 4/src/app/shared/components/index.ts b/Frontend Angular 4/src/app/shared/components/index.ts
index d7aadd0fa72ba8c689acfccbb9d9728da622c91e..ebf34903707e3fb4f16ff194ba22aa18a7a7c733 100755
--- a/Frontend Angular 4/src/app/shared/components/index.ts	
+++ b/Frontend Angular 4/src/app/shared/components/index.ts	
@@ -12,3 +12,5 @@ export * from "./create-group-modal/create-group-modal.component";
 export * from "./manage-group-users-modal/manage-group-users-modal.component";
 export * from "./action-confirmation-modal/action-confirmation-modal.component";
 export * from "./destroy-group-modal/destroy-group-modal.component";
+export * from "./request-feedback-modal/request-feedback-modal.component";
+export * from "./return-feedback-modal/return-feedback-modal.component";
diff --git a/Frontend Angular 4/src/app/shared/components/request-feedback-modal/request-feedback-modal.component.html b/Frontend Angular 4/src/app/shared/components/request-feedback-modal/request-feedback-modal.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..626e7c6ab99157c67b1b6d00cae37ac92ae214e1
--- /dev/null
+++ b/Frontend Angular 4/src/app/shared/components/request-feedback-modal/request-feedback-modal.component.html	
@@ -0,0 +1,6 @@
+<app-action-confirmation-modal
+  [title]="title"
+  [message]="message"
+  [confirmAction]="requestFeedback"
+  [cancelAction]="cancel"
+></app-action-confirmation-modal>
diff --git a/Frontend Angular 4/src/app/shared/components/request-feedback-modal/request-feedback-modal.component.ts b/Frontend Angular 4/src/app/shared/components/request-feedback-modal/request-feedback-modal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ee43f0938544d359f620f2166ab362a053af9a33
--- /dev/null
+++ b/Frontend Angular 4/src/app/shared/components/request-feedback-modal/request-feedback-modal.component.ts	
@@ -0,0 +1,65 @@
+import {
+  Component,
+  ChangeDetectionStrategy,
+  Input,
+  OnInit,
+} from "@angular/core";
+
+import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
+
+import { GroupFileService } from "../../services/group-file.service";
+
+@Component({
+  selector: "app-request-feedback-modal",
+  templateUrl: "./request-feedback-modal.component.html",
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class RequestFeedbackModal implements OnInit {
+  // TODO: Hacer todo este modal basándome en lo que ya está que viene del DestroyGroupModal,
+  //       y luego terminar de hacer la llamada y todo en el GroupComponent
+  @Input() fileName: string;
+  @Input() groupName: string | null;
+  @Input() groupId: number;
+  @Input() fileId: number;
+
+  /**
+   * Se dispara cuando se quiere administrar los usuarios del grupo
+   */
+  @Input() confirmFeedbackRequested: () => void;
+  title = "Pedir retroalimentación";
+  message: string;
+
+  constructor(
+    public modal: NgbActiveModal,
+    private groupFileService: GroupFileService
+  ) {}
+
+  ngOnInit(): void {
+    this.fileName = `${this.fileName}`;
+
+    if (!!this.groupName) {
+      this.groupName = `${this.groupName}`;
+      this.message = `¿Desea pedir retroalimentación del archivo ${this.fileName} en el grupo ${this.groupName}?`;
+    } else {
+      this.message = `¿Desea pedir retroalimentación del archivo ${this.fileName}?`;
+    }
+
+    this.requestFeedback = this.requestFeedback.bind(this);
+    this.cancel = this.cancel.bind(this);
+  }
+
+  requestFeedback(): void {
+    this.groupFileService.requestFeedback(this.groupId, this.fileId).subscribe({
+      next: (_) => {
+        this.confirmFeedbackRequested();
+
+        this.modal.close();
+      },
+      error: (error) => console.log(error),
+    });
+  }
+
+  cancel(): void {
+    this.modal.dismiss();
+  }
+}
diff --git a/Frontend Angular 4/src/app/shared/components/request-group-file-feedback-modal/request-group-file-feedback-modal.component.html b/Frontend Angular 4/src/app/shared/components/request-group-file-feedback-modal/request-group-file-feedback-modal.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..0451b0ce81c5184153c2fcf24d5de5b2b621d11e
--- /dev/null
+++ b/Frontend Angular 4/src/app/shared/components/request-group-file-feedback-modal/request-group-file-feedback-modal.component.html	
@@ -0,0 +1,6 @@
+<app-action-confirmation-modal
+  [title]="title"
+  [message]="message"
+  [confirmAction]="destroyGroup"
+  [cancelAction]="cancel"
+></app-action-confirmation-modal>
diff --git a/Frontend Angular 4/src/app/shared/components/request-group-file-feedback-modal/request-group-file-feedback-modal.component.ts b/Frontend Angular 4/src/app/shared/components/request-group-file-feedback-modal/request-group-file-feedback-modal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1f81dd51a21b8122c9ac87550ed15ba5a994741c
--- /dev/null
+++ b/Frontend Angular 4/src/app/shared/components/request-group-file-feedback-modal/request-group-file-feedback-modal.component.ts	
@@ -0,0 +1,54 @@
+import {
+  Component,
+  ChangeDetectionStrategy,
+  Input,
+  OnInit,
+} from "@angular/core";
+
+import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
+
+import { GroupService } from "../../services/group.service";
+
+@Component({
+  selector: "app-destroy-group-modal",
+  templateUrl: "./destroy-group-modal.component.html",
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class RequestGroupFileModal implements OnInit {
+  @Input() groupName: string;
+  @Input() fileName: string;
+  @Input() groupId: number;
+  @Input() fileId: number;
+  /**
+   * Se dispara cuando se quiere administrar los usuarios del grupo
+   */
+  title: string;
+  message: string;
+
+  constructor(
+    public modal: NgbActiveModal,
+    private groupService: GroupService
+  ) {}
+
+  ngOnInit(): void {
+    this.groupName = `${this.groupName}`;
+
+    this.title = "Pedir Retroalimentación";
+    this.message = `¿Estás seguro de que desea pedir retroalimentación para el archivo ${this.fileName}? \n Esta decisión no se puede deshacer.`;
+    this.requestFeedback = this.requestFeedback.bind(this);
+    this.cancel = this.cancel.bind(this);
+  }
+
+  requestFeedback(): void {
+    this.groupService.requestFeedback(this.groupId, this.fileId).subscribe({
+      next: (_) => {
+        this.modal.close();
+      },
+      error: (error) => console.log(error),
+    });
+  }
+
+  cancel(): void {
+    this.modal.dismiss();
+  }
+}
diff --git a/Frontend Angular 4/src/app/shared/components/return-feedback-modal/return-feedback-modal.component.html b/Frontend Angular 4/src/app/shared/components/return-feedback-modal/return-feedback-modal.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..8f8bd9d54121b24dccc760a9b5bb966afc823b0e
--- /dev/null
+++ b/Frontend Angular 4/src/app/shared/components/return-feedback-modal/return-feedback-modal.component.html	
@@ -0,0 +1,6 @@
+<app-action-confirmation-modal
+  [title]="title"
+  [message]="message"
+  [confirmAction]="returnFeedback"
+  [cancelAction]="cancel"
+></app-action-confirmation-modal>
diff --git a/Frontend Angular 4/src/app/shared/components/return-feedback-modal/return-feedback-modal.component.ts b/Frontend Angular 4/src/app/shared/components/return-feedback-modal/return-feedback-modal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..550dbe3c6e5716b2ae73d597cb24d78362f224b1
--- /dev/null
+++ b/Frontend Angular 4/src/app/shared/components/return-feedback-modal/return-feedback-modal.component.ts	
@@ -0,0 +1,68 @@
+import {
+  Component,
+  ChangeDetectionStrategy,
+  Input,
+  OnInit,
+} from "@angular/core";
+
+import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
+
+import { GroupFileService } from "../../services/group-file.service";
+
+@Component({
+  selector: "app-return-feedback-modal",
+  templateUrl: "./return-feedback-modal.component.html",
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ReturnFeedbackModal implements OnInit {
+  // TODO: Hacer todo este modal basándome en lo que ya está que viene del DestroyGroupModal,
+  //       y luego terminar de hacer la llamada y todo en el GroupComponent
+  @Input() fileName: string;
+  @Input() groupName: string | null;
+  @Input() groupId: number;
+  @Input() fileId: number;
+  @Input() userId: number;
+
+  /**
+   * Se dispara cuando se quiere administrar los usuarios del grupo
+   */
+  @Input() confirmFeedbackReturned: () => void;
+  title = "Dar retroalimentación";
+  message: string;
+
+  constructor(
+    public modal: NgbActiveModal,
+    private groupFileService: GroupFileService
+  ) {}
+
+  ngOnInit(): void {
+    this.fileName = `${this.fileName}`;
+
+    if (!!this.groupName) {
+      this.groupName = `${this.groupName}`;
+      this.message = `¿Desea dar retroalimentación del archivo ${this.fileName} en el grupo ${this.groupName}?`;
+    } else {
+      this.message = `¿Desea dar retroalimentación del archivo ${this.fileName}?`;
+    }
+
+    this.returnFeedback = this.returnFeedback.bind(this);
+    this.cancel = this.cancel.bind(this);
+  }
+
+  returnFeedback(): void {
+    this.groupFileService
+      .returnFeedback(this.groupId, this.fileId, this.userId)
+      .subscribe({
+        next: (_) => {
+          this.confirmFeedbackReturned();
+
+          this.modal.close();
+        },
+        error: (error) => console.log(error),
+      });
+  }
+
+  cancel(): void {
+    this.modal.dismiss();
+  }
+}
diff --git a/Frontend Angular 4/src/app/shared/config.ts b/Frontend Angular 4/src/app/shared/config.ts
index 1a9848d10b0b3ef51f1f2beddd77ddb3fb1a18bf..953ce88817c005602e655c4bbc6ae5bbebc78082 100755
--- a/Frontend Angular 4/src/app/shared/config.ts	
+++ b/Frontend Angular 4/src/app/shared/config.ts	
@@ -56,3 +56,19 @@ export const GET_GROUPS = SERVER + "/api/v2/groups";
 export const CREATE_GROUP = SERVER + "/api/v2/groups";
 export const UPDATE_GROUP = SERVER + "/api/v2/groups";
 export const DELETE_GROUP = SERVER + "/api/v2/groups";
+
+export const GET_GROUP_DOCUMENT =
+  SERVER + "/api/v2/groups/:group_id/documents/:id";
+export const GET_GROUP_FILE = SERVER + "/api/v2/groups/:group_id/files/:id";
+export const GET_GROUP_FILES = SERVER + "/api/v2/groups/:group_id/files";
+export const GET_GROUP_FILES_LISTS =
+  SERVER + "/api/v2/groups/:group_id/files/lists";
+export const CREATE_GROUP_FILE = SERVER + "/api/v2/groups/:group_id/files";
+export const UPDATE_GROUP_FILE = SERVER + "/api/v2/groups/:group_id/files";
+export const DELETE_GROUP_FILE = SERVER + "/api/v2/groups/:group_id/files";
+export const GET_GROUP_FEEDBACK_REQUESTED_FILES =
+  SERVER + "/api/v2/groups/:group_id/feedback_requested_files";
+export const CREATE_GROUP_FILE_FEEDBACK_REQUEST =
+  SERVER + "/api/v2/groups/:group_id/feedback_requests";
+export const GET_GROUP_USER_ROLE =
+  SERVER + "/api/v2/groups/:group_id/users/:user_id/role";
diff --git a/Frontend Angular 4/src/app/shared/objects/archivo-types.ts b/Frontend Angular 4/src/app/shared/objects/archivo-types.ts
index 15e62bc2f682a5f41e282ee7d6e732866bf6587e..111b577baf3a0f16921894b8ec93f07c77cc21eb 100755
--- a/Frontend Angular 4/src/app/shared/objects/archivo-types.ts	
+++ b/Frontend Angular 4/src/app/shared/objects/archivo-types.ts	
@@ -22,9 +22,11 @@ export class Archivo {
   eliminado: boolean;
   evaluacion: Evaluacion;
   documentId: number;
+  groupId?: number;
   fileId?: number;
   users?: User[];
   role?: string;
+  feedbackRequested?: boolean;
 
   constructor() {}
 }
@@ -39,8 +41,10 @@ export class MFile {
   parent_id?: number;
   directory: boolean;
   children?: MFile[];
+  group_id?: number;
   users?: User[];
   role?: string;
+  feedback_requested?: boolean;
 
   constructor() {}
 }
diff --git a/Frontend Angular 4/src/app/shared/services/document.service.ts b/Frontend Angular 4/src/app/shared/services/document.service.ts
index 9883de73421cd71259c668c9433b8d0ad9508c65..4978249cbead78eb5e44ea96f47142d61e193f52 100644
--- a/Frontend Angular 4/src/app/shared/services/document.service.ts	
+++ b/Frontend Angular 4/src/app/shared/services/document.service.ts	
@@ -114,10 +114,17 @@ export class DocumentService {
     };
   }
 
-  createDocument(document: MDocument): Observable<HttpResponse<MDocument>> {
+  createDocument(
+    document: MDocument,
+    groupId?: number
+  ): Observable<HttpResponse<MDocument>> {
+    const params = { ...document };
+    if (groupId) {
+      params["group_id"] = groupId;
+    }
     return this.http.post<MDocument>(
       CREATE_DOCUMENT,
-      { document },
+      { document: params },
       {
         observe: "response",
         headers: this.postHeaders(),
diff --git a/Frontend Angular 4/src/app/shared/services/file.service.ts b/Frontend Angular 4/src/app/shared/services/file.service.ts
index f1d56c3f52e1cac2da5e7877e38cf5eed2724a31..e67468b7ceaa65a788bb6adeb9c07a9ac6fdf2a4 100644
--- a/Frontend Angular 4/src/app/shared/services/file.service.ts	
+++ b/Frontend Angular 4/src/app/shared/services/file.service.ts	
@@ -107,6 +107,8 @@ export class FileService {
     archivo.evaluacion = null;
     archivo.users = file.users;
     archivo.role = file.role;
+    archivo.groupId = file.group_id;
+    archivo.feedbackRequested = file.feedback_requested;
 
     if (!!file.children) {
       archivo.archivos = file.children.map<Archivo>((child) => {
@@ -145,6 +147,8 @@ export class FileService {
       children: [],
       directory: archivo.directorio,
       role: archivo.role,
+      group_id: archivo.groupId,
+      feedback_requested: archivo.feedbackRequested,
     };
 
     file.children = archivo.archivos.map<MFile>((archivoHijo) => {
diff --git a/Frontend Angular 4/src/app/shared/services/group-document.service.ts b/Frontend Angular 4/src/app/shared/services/group-document.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4aca2791df5c2b99d9359c41a6246348ffbd492
--- /dev/null
+++ b/Frontend Angular 4/src/app/shared/services/group-document.service.ts	
@@ -0,0 +1,307 @@
+import { throwError as observableThrowError, Observable } from "rxjs";
+import { Injectable } from "@angular/core";
+import { Router } from "@angular/router";
+import {
+  HttpClient,
+  HttpHeaders,
+  HttpParams,
+  HttpResponse,
+} from "@angular/common/http";
+import {
+  Archivo,
+  Evaluacion,
+  MFile,
+  MDocument,
+} from "../objects/archivo-types";
+import { Group } from "../objects/grupo";
+
+import { SERVER } from "../config";
+import { TranslateService } from "@ngx-translate/core";
+import { AuthenticationService } from "./authentication.service";
+import { catchError } from "rxjs/operators";
+import { GET_GROUP_DOCUMENT } from "../config";
+
+@Injectable()
+export class GroupDocumentService {
+  translateService: any;
+
+  /**
+   * Creates a new DocumentService with the injected HttpClient.
+   * @param {HttpClient} http - The injected HttpClient.
+   * @constructor
+   */
+  constructor(
+    private http: HttpClient,
+    private router: Router,
+    private authService: AuthenticationService,
+    public translate: TranslateService
+  ) {
+    this.translateService = translate;
+  }
+
+  private getHeaders() {
+    return new HttpHeaders({
+      "Content-Type": "application/json",
+      Authorization: "Bearer " + this.authService.getToken(),
+    });
+  }
+
+  private postHeaders() {
+    return new HttpHeaders({
+      "Content-Type": "application/json",
+      Accept: "application/json",
+      "Access-Control-Allow-Headers": "Content-Type",
+    });
+  }
+
+  // getDocuments(ids: number[]): Observable<{ documents: MDocument[] }> {
+  //   const params = new HttpParams({
+  //     fromObject: { "document_ids[]": ids },
+  //   });
+  //   return this.http
+  //     .get<{ documents: MDocument[] }>(GET_DOCUMENTS, {
+  //       headers: this.getHeaders(),
+  //       params: params,
+  //     })
+  //     .pipe(catchError(this.handleError));
+  // }
+
+  getGroupDocument(
+    groupId: number,
+    id: number,
+    userId: number
+  ): Observable<{ document: MDocument }> {
+    return this.http
+      .get<{ document: MDocument }>(
+        GET_GROUP_DOCUMENT.replace(/:group_id/g, String(groupId)).replace(
+          /:id/g,
+          String(id)
+        ),
+        {
+          headers: this.getHeaders(),
+          params: new HttpParams().set("user_id", userId),
+        }
+      )
+      .pipe(catchError(this.handleError));
+  }
+
+  // documentToArchivo(document: MDocument): Archivo {
+  //   return {
+  //     id: document.id,
+  //     documentId: document.id,
+  //     nombre: document.title,
+  //     contenido: document.text_doc,
+  //     fechaCreacion: document.created_at,
+  //     cedulaCreador: document.users[0]?.email,
+  //     editable: true,
+  //     padreId: document.parent_id ? document.parent_id : -1,
+  //     fileId: document.file_id,
+  //     archivoOrigenId: null,
+  //     archivos: [],
+  //     directorio: false,
+  //     estado: "activo",
+  //     eliminado: false,
+  //     evaluacion: null,
+  //     users: document.users,
+  //     role: document.role,
+  //   };
+  // }
+
+  // archivoToDocument(archivo: Archivo): MDocument {
+  //   return {
+  //     id: archivo.id,
+  //     title: archivo.nombre,
+  //     text_doc: archivo.contenido,
+  //     created_at: archivo.fechaCreacion,
+  //     parent_id: archivo.padreId,
+  //     role: archivo.role,
+  //     users: [],
+  //   };
+  // }
+
+  // createDocument(
+  //   document: MDocument,
+  //   groupId?: number
+  // ): Observable<HttpResponse<MDocument>> {
+  //   const params = { ...document };
+  //   if (groupId) {
+  //     params["group_id"] = groupId;
+  //   }
+  //   return this.http.post<MDocument>(
+  //     CREATE_DOCUMENT,
+  //     { document: params },
+  //     {
+  //       observe: "response",
+  //       headers: this.postHeaders(),
+  //     }
+  //   );
+  // }
+
+  // // Legacy method, need to check if it's still used
+  // getArchivos(cedula: string): Observable<Archivo[]> {
+  //   let headers = this.getHeaders();
+  //   let params: HttpParams = new HttpParams();
+  //   params = params.set("cedula", cedula);
+  //   let httpOptions = { headers: headers, params: params };
+
+  //   return this.http
+  //     .get<Archivo[]>(SERVER + "/servicios/archivo", httpOptions)
+  //     .pipe(catchError(this.handleError));
+  // }
+
+  // // Legacy method, need to check if it's still used
+  // getArchivosCompartidosAlumno(cedula: string): Observable<Archivo[]> {
+  //   let headers = this.getHeaders();
+  //   let params: HttpParams = new HttpParams();
+  //   params = params.set("cedula", cedula);
+  //   params = params.set("compartidos", "true");
+  //   let httpOptions = { headers: headers, params: params };
+
+  //   return this.http
+  //     .get<Archivo[]>(SERVER + "/servicios/archivo", httpOptions)
+  //     .pipe(catchError(this.handleError));
+  // }
+
+  // createFile(
+  //   file_title,
+  //   file_binary_doc_text
+  // ): Observable<HttpResponse<Object>> {
+  //   return this.http.post<Object>(
+  //     CREATE_DOCUMENT,
+  //     {
+  //       document: { title: file_title, text_doc: file_binary_doc_text },
+  //     },
+  //     {
+  //       observe: "response",
+  //       headers: this.postHeaders(),
+  //     }
+  //   );
+  // }
+
+  // updateDocument(
+  //   file_id,
+  //   file_title,
+  //   save_document_content = false
+  // ): Observable<HttpResponse<Object>> {
+  //   return this.http.patch<Object>(
+  //     `${UPDATE_DOCUMENT}/${file_id}`,
+  //     {
+  //       document: {
+  //         title: file_title,
+  //         save_document_content: save_document_content,
+  //       },
+  //     },
+  //     {
+  //       observe: "response",
+  //       headers: this.postHeaders(),
+  //     }
+  //   );
+  // }
+
+  // destroyAuxDocument(): Observable<HttpResponse<Object>> {
+  //   return this.http.delete<Object>(DESTROY_AUX_DOCUMENT, {
+  //     observe: "response",
+  //     headers: this.postHeaders(),
+  //   });
+  // }
+
+  // // Legacy method, need to check if it's still used
+  // eliminarArchivo(archivoId): Observable<Response> {
+  //   let headers = this.getHeaders();
+  //   let httpOptions = { headers: headers };
+
+  //   return this.http
+  //     .delete<Response>(SERVER + "/servicios/archivo/" + archivoId, httpOptions)
+  //     .pipe(catchError(this.handleError));
+  // }
+
+  // // Legacy method, need to check if it's still used
+  // getCopiaArchivoCompartidoGrupo(cedula, archivoId): Observable<Archivo> {
+  //   let headers = this.getHeaders();
+  //   let params: HttpParams = new HttpParams();
+  //   params = params.set("cedula", cedula);
+  //   let httpOptions = { headers: headers, params: params };
+
+  //   return this.http
+  //     .get<Archivo>(
+  //       SERVER + "/servicios/archivo/compartido/" + archivoId,
+  //       httpOptions
+  //     )
+  //     .pipe(catchError(this.handleError));
+  // }
+
+  // // Legacy method, need to check if it's still used
+  // compartirArchivoGrupo(grupo, archivoId): Observable<Archivo> {
+  //   let headers = this.getHeaders();
+  //   let httpOptions = { headers: headers };
+  //   var archId = {
+  //     id: archivoId,
+  //   };
+  //   return this.http
+  //     .post<Archivo>(
+  //       SERVER +
+  //         "/servicios/grupo/" +
+  //         grupo.liceoId +
+  //         "/" +
+  //         grupo.anio +
+  //         "/" +
+  //         grupo.grado +
+  //         "/" +
+  //         grupo.grupo +
+  //         "/archivo",
+  //       archId,
+  //       httpOptions
+  //     )
+  //     .pipe(catchError(this.handleError));
+  // }
+
+  // // Legacy method, need to check if it's still used
+  // calificarArchivo(archivoId, estado, evaluacion): Observable<Evaluacion> {
+  //   let headers = this.getHeaders();
+  //   let httpOptions = { headers: headers };
+  //   return this.http
+  //     .post<Evaluacion>(
+  //       SERVER +
+  //         "/servicios/archivo/" +
+  //         archivoId +
+  //         "/" +
+  //         estado +
+  //         "/evaluacion",
+  //       evaluacion,
+  //       httpOptions
+  //     )
+  //     .pipe(catchError(this.handleError));
+  // }
+
+  // // Legacy method, need to check if it's still used
+  // getGrupos(cedula: string): Observable<Group[]> {
+  //   let headers = this.getHeaders();
+  //   let params: HttpParams = new HttpParams();
+  //   params = params.set("cedula", cedula);
+  //   let httpOptions = { headers: headers, params: params };
+
+  //   return this.http
+  //     .get<Group[]>(SERVER + "/servicios/grupo", httpOptions)
+  //     .pipe(catchError(this.handleError));
+  // }
+
+  /**
+   * Handle HTTP error
+   */
+  private handleError(error: any) {
+    if (error.status == 401) {
+      this.translateService
+        .get("i18n.code")
+        .subscribe((res) => this.router.navigate(["/" + res + "/login"]));
+    }
+    // In a real world app, we might use a remote logging infrastructure
+    // We'd also dig deeper into the error to get a better message
+    let errMsg = error.message
+      ? error.message
+      : error.status
+      ? `${error.status} - ${error.statusText}`
+      : "Server error";
+    console.error(errMsg); // log to console instead
+    return observableThrowError(errMsg);
+  }
+}
diff --git a/Frontend Angular 4/src/app/shared/services/group-file.service.ts b/Frontend Angular 4/src/app/shared/services/group-file.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..210aef59375a912b97975a9a60ed058a71d466a4
--- /dev/null
+++ b/Frontend Angular 4/src/app/shared/services/group-file.service.ts	
@@ -0,0 +1,264 @@
+import { throwError as observableThrowError, Observable } from "rxjs";
+import { Injectable } from "@angular/core";
+import { Router } from "@angular/router";
+import {
+  HttpClient,
+  HttpHeaders,
+  HttpParams,
+  HttpResponse,
+} from "@angular/common/http";
+import { Archivo, Evaluacion, MFile } from "../objects/archivo-types";
+
+import { TranslateService } from "@ngx-translate/core";
+import { AuthenticationService } from "./authentication.service";
+import { catchError } from "rxjs/operators";
+import {
+  CREATE_GROUP_FILE,
+  UPDATE_GROUP_FILE,
+  DELETE_GROUP_FILE,
+  GET_GROUP_FILE,
+  GET_GROUP_FILES,
+  GET_GROUP_FILES_LISTS,
+  GET_GROUP_FEEDBACK_REQUESTED_FILES,
+} from "../config";
+
+@Injectable()
+export class GroupFileService {
+  translateService: any;
+
+  /**
+   * Creates a new FileService with the injected HttpClient.
+   * @param {HttpClient} http - The injected HttpClient.
+   * @constructor
+   */
+  constructor(
+    private http: HttpClient,
+    private router: Router,
+    private authService: AuthenticationService,
+    public translate: TranslateService
+  ) {
+    this.translateService = translate;
+  }
+
+  private getHeaders() {
+    return new HttpHeaders({
+      "Content-Type": "application/json",
+      Authorization: "Bearer " + this.authService.getToken(),
+    });
+  }
+
+  private postHeaders() {
+    return new HttpHeaders({
+      "Content-Type": "application/json",
+      Accept: "application/json",
+      "Access-Control-Allow-Headers": "Content-Type",
+    });
+  }
+
+  getGroupFile(groupId: number, id: number): Observable<{ file: MFile }> {
+    return this.http
+      .get<{ file: MFile }>(
+        GET_GROUP_FILE.replace(/:group_id/g, String(groupId)).replace(
+          /:id/g,
+          String(id)
+        ),
+        {
+          headers: this.getHeaders(),
+        }
+      )
+      .pipe(catchError(this.handleError));
+  }
+
+  getSpecificUserFileWithSetGroup(groupId: number, userId: number) {
+    return {
+      getFile: (id: number) => {
+        return this.getSpecificUserGroupFile(groupId, userId, id);
+      },
+    };
+  }
+
+  getGroupFiles(groupId: number): Observable<MFile[]> {
+    return this.http
+      .get<MFile[]>(GET_GROUP_FILES.replace(/:group_id/g, String(groupId)), {
+        headers: this.getHeaders(),
+      })
+      .pipe(catchError(this.handleError));
+  }
+
+  getSpecificUserGroupFiles(
+    groupId: number,
+    userId: number
+  ): Observable<MFile[]> {
+    return this.http
+      .get<MFile[]>(GET_GROUP_FILES.replace(/:group_id/g, String(groupId)), {
+        headers: this.getHeaders(),
+        params: new HttpParams().set("user_id", userId),
+      })
+      .pipe(catchError(this.handleError));
+  }
+
+  getSpecificUserGroupFile(
+    groupId: number,
+    userId: number,
+    fileId: number
+  ): Observable<{ file: MFile }> {
+    return this.http
+      .get<{ file: MFile }>(
+        GET_GROUP_FILE.replace(/:group_id/g, String(groupId)).replace(
+          /:id/g,
+          String(fileId)
+        ),
+        {
+          headers: this.getHeaders(),
+          params: new HttpParams().set("user_id", userId),
+        }
+      )
+      .pipe(catchError(this.handleError));
+  }
+
+  getGroupFilesList(
+    groupId: number,
+    options = {}
+  ): Observable<{ files: MFile[] }> {
+    const siblingsOfId = options["siblingsOfId"];
+    return this.http
+      .get<{ files: MFile[] }>(
+        GET_GROUP_FILES_LISTS.replace(/:group_id/g, String(groupId)),
+        {
+          headers: this.getHeaders(),
+          params: siblingsOfId
+            ? new HttpParams().set("siblings_of_id", siblingsOfId)
+            : null,
+        }
+      )
+      .pipe(catchError(this.handleError));
+  }
+
+  getFeedbackRequestedGroupFiles(
+    groupId: number,
+    userId?: number
+  ): Observable<MFile[]> {
+    let params = {};
+    if (userId) {
+      params["user_id"] = userId;
+    }
+    return this.http
+      .get<MFile[]>(
+        GET_GROUP_FEEDBACK_REQUESTED_FILES.replace(
+          /:group_id/g,
+          String(groupId)
+        ),
+        {
+          headers: this.getHeaders(),
+          params: params,
+        }
+      )
+      .pipe(catchError(this.handleError));
+  }
+
+  createGroupFile(
+    groupId: number,
+    file: MFile
+  ): Observable<HttpResponse<Object>> {
+    return this.http.post<Object>(
+      CREATE_GROUP_FILE.replace(/:group_id/g, String(groupId)),
+      {
+        file: {
+          title: file.title,
+          parent_id: file.parent_id,
+          directory: file.directory,
+        },
+      },
+      {
+        observe: "response",
+        headers: this.postHeaders(),
+      }
+    );
+  }
+
+  updateGroupFile(
+    groupId: number,
+    file_id,
+    file_parent_id,
+    users
+  ): Observable<HttpResponse<Object>> {
+    const update_params = {};
+    if (file_parent_id) {
+      update_params["parent_id"] = file_parent_id;
+    }
+    if (users) {
+      users = users.map((user) => {
+        return { ...user, role: user.role.toLowerCase() };
+      });
+      update_params["users"] = users;
+    }
+
+    return this.http.patch<Object>(
+      `${UPDATE_GROUP_FILE.replace(/:group_id/g, String(groupId))}/${file_id}`,
+      { file: update_params },
+      {
+        observe: "response",
+        headers: this.postHeaders(),
+      }
+    );
+  }
+
+  requestFeedback(
+    groupId: number,
+    fileId: number
+  ): Observable<HttpResponse<Object>> {
+    return this.http.patch<Object>(
+      `${UPDATE_GROUP_FILE.replace(/:group_id/g, String(groupId))}/${fileId}`,
+      { file: { feedback_requested: true } },
+      {
+        observe: "response",
+        headers: this.postHeaders(),
+      }
+    );
+  }
+
+  returnFeedback(
+    groupId: number,
+    fileId: number,
+    userId: number
+  ): Observable<HttpResponse<Object>> {
+    return this.http.patch<Object>(
+      `${UPDATE_GROUP_FILE.replace(/:group_id/g, String(groupId))}/${fileId}`,
+      { file: { feedback_requested: false }, user_id: userId },
+      {
+        observe: "response",
+        headers: this.postHeaders(),
+      }
+    );
+  }
+
+  deleteGroupFile(groupId: number, file_id): Observable<HttpResponse<Object>> {
+    return this.http.delete<Object>(
+      `${DELETE_GROUP_FILE.replace(/:group_id/g, String(groupId))}/${file_id}`,
+      {
+        observe: "response",
+        headers: this.postHeaders(),
+      }
+    );
+  }
+
+  /**
+   * Handle HTTP error
+   */
+  private handleError(error: any) {
+    if (error.status == 401) {
+      this.translateService
+        .get("i18n.code")
+        .subscribe((res) => this.router.navigate(["/" + res + "/login"]));
+    }
+    // In a real world app, we might use a remote logging infrastructure
+    // We'd also dig deeper into the error to get a better message
+    let errMsg = error.message
+      ? error.message
+      : error.status
+      ? `${error.status} - ${error.statusText}`
+      : "Server error";
+    console.error(errMsg); // log to console instead
+    return observableThrowError(errMsg);
+  }
+}
diff --git a/Frontend Angular 4/src/app/shared/services/group.service.ts b/Frontend Angular 4/src/app/shared/services/group.service.ts
index f1335f842dc8c7ead62c6330e59e948272de55fe..bf3e8d87add435c9e1707ba0deef7919be7fb965 100644
--- a/Frontend Angular 4/src/app/shared/services/group.service.ts	
+++ b/Frontend Angular 4/src/app/shared/services/group.service.ts	
@@ -19,6 +19,8 @@ import {
   DELETE_GROUP,
   GET_GROUP,
   GET_GROUPS,
+  CREATE_GROUP_FILE_FEEDBACK_REQUEST,
+  GET_GROUP_USER_ROLE,
 } from "../config";
 
 @Injectable()
@@ -210,6 +212,48 @@ export class GroupService {
   //   });
   // }
 
+  requestFeedback(
+    groupId: number,
+    fileId: number
+  ): Observable<HttpResponse<Object>> {
+    return this.http.patch<Object>(
+      `${CREATE_GROUP_FILE_FEEDBACK_REQUEST.replace(
+        /:group_id/g,
+        String(groupId)
+      )}`,
+      { feedback_request: { file_id: fileId } },
+      {
+        observe: "response",
+        headers: this.postHeaders(),
+      }
+    );
+  }
+
+  getUserRole(
+    groupId: number,
+    userId: number
+  ): Observable<{ role: { role: string } }> {
+    return this.http
+      .get<{ role: { role: string } }>(
+        GET_GROUP_USER_ROLE.replace(/:group_id/g, String(groupId)).replace(
+          /:user_id/g,
+          String(userId)
+        ),
+        {
+          headers: this.getHeaders(),
+        }
+      )
+      .pipe(catchError(this.handleError));
+  }
+
+  userCanEditAnyGroupFile(role: string): boolean {
+    return role === "owner" || role === "moderator" || role === "manager";
+  }
+
+  userCanReturnFeedback(role: string): boolean {
+    return this.userCanEditAnyGroupFile(role);
+  }
+
   /**
    * Handle HTTP error
    */
diff --git a/Frontend Angular 4/src/app/shared/shared.module.ts b/Frontend Angular 4/src/app/shared/shared.module.ts
index 06214deb85b5d6f3b19cd45883bbfa2bbf2582ec..8012c632e46723e9ef40e341cb6eb6fc6f19c08f 100644
--- a/Frontend Angular 4/src/app/shared/shared.module.ts	
+++ b/Frontend Angular 4/src/app/shared/shared.module.ts	
@@ -3,22 +3,26 @@ import { MainButtonComponent } from "./components/main-button/main-button.compon
 import { TitleCaseModule } from "../shared/modules/titlecase.module";
 import { I18nModule } from "../shared/modules/translate/i18n.module";
 import { CommonModule } from "@angular/common";
-import { CreateFileModal } from "./components";
-import { CreateGroupModal } from "./components";
-import { ImportFileModal } from "./components";
-import { ShareFileModal } from "./components";
-import { ManageGroupUsersModal } from "./components";
 import { FormsModule } from "@angular/forms";
-import { FolderInput } from "./components";
-import { SelectComponent } from "./components";
-import { ChipInputComponent } from "./components";
 import { MatFormFieldModule } from "@angular/material/form-field";
 import { MatChipsModule } from "@angular/material/chips";
 import { MatIconModule } from "@angular/material/icon";
 import { NgbDropdownModule } from "@ng-bootstrap/ng-bootstrap";
-import { AddUsersModal } from "./components";
-import { ActionConfirmationModal } from "./components";
-import { DestroyGroupModal } from "./components";
+import {
+  ActionConfirmationModal,
+  AddUsersModal,
+  ChipInputComponent,
+  CreateFileModal,
+  CreateGroupModal,
+  DestroyGroupModal,
+  FolderInput,
+  ImportFileModal,
+  ManageGroupUsersModal,
+  RequestFeedbackModal,
+  ReturnFeedbackModal,
+  SelectComponent,
+  ShareFileModal,
+} from "./components";
 @NgModule({
   imports: [
     TitleCaseModule,
@@ -43,6 +47,8 @@ import { DestroyGroupModal } from "./components";
     AddUsersModal,
     ActionConfirmationModal,
     DestroyGroupModal,
+    RequestFeedbackModal,
+    ReturnFeedbackModal,
   ],
   exports: [MainButtonComponent, CreateFileModal],
 })
diff --git a/Frontend Angular 4/src/assets/i18n/en.json b/Frontend Angular 4/src/assets/i18n/en.json
index 246f22045bac4c673a3075c574829b78305c8cfa..8b9a351bd282c48a5c02a31c580df3b5460da1b8 100755
--- a/Frontend Angular 4/src/assets/i18n/en.json	
+++ b/Frontend Angular 4/src/assets/i18n/en.json	
@@ -33,7 +33,8 @@
       "close": "close",
       "qualify": "qualify",
       "confirm": "confirm",
-      "signup": "sign up"
+      "signup": "sign up",
+      "requestFeedback": "Request feedback"
     },
     "links": {
       "register": "Create account",
diff --git a/Frontend Angular 4/src/assets/i18n/es.json b/Frontend Angular 4/src/assets/i18n/es.json
index 44ac256f4b2bfbc7da3ed5d38419d1c1f36e4e70..440668e93b2cc57517e4e8f8d3255d292a0f3f07 100755
--- a/Frontend Angular 4/src/assets/i18n/es.json	
+++ b/Frontend Angular 4/src/assets/i18n/es.json	
@@ -33,7 +33,8 @@
       "close": "cerrar",
       "qualify": "calificar",
       "confirm": "confirmar",
-      "signup": "registrarse"
+      "signup": "registrarse",
+      "requestFeedback": "Solicitar retroalimentación"
     },
     "links": {
       "register": "Registrarse",
diff --git a/Frontend Angular 4/src/styles/console.css b/Frontend Angular 4/src/styles/console.css
index 4a44020d73acfef4efbe3d6f7c30a74e7da9ad84..6441af37415a27b6a41e2ccdf74d9c43630779a4 100755
--- a/Frontend Angular 4/src/styles/console.css	
+++ b/Frontend Angular 4/src/styles/console.css	
@@ -70,7 +70,7 @@
 
 .nomArchivoInp {
   /*width: 55% !important;*/
-  width: calc(100% - 315px);
+  width: calc(100% - 345px);
   float: left;
 }