From 4c277cd685a3acc728296e949adb85feb2da4632 Mon Sep 17 00:00:00 2001
From: Inga Kirschnick <inga.kirschnick@uni-bielefeld.de>
Date: Tue, 6 Jun 2023 15:39:47 +0200
Subject: [PATCH] Corpus List update

---
 app/static/js/ResourceLists/CorpusFileList.js |  2 +-
 app/static/js/ResourceLists/CorpusList.js     | 83 +++++++++++--------
 .../DetailledPublicCorpusList.js              | 71 ++++++++++++++++
 .../js/ResourceLists/PublicCorpusList.js      | 22 +++--
 app/templates/_scripts.html.j2                |  3 +-
 app/templates/corpora/public_corpus.html.j2   |  2 +-
 app/templates/main/dashboard.html.j2          |  2 +-
 app/templates/main/social_area.html.j2        |  4 +-
 app/templates/users/user.html.j2              |  4 +-
 9 files changed, 142 insertions(+), 51 deletions(-)
 create mode 100644 app/static/js/ResourceLists/DetailledPublicCorpusList.js

diff --git a/app/static/js/ResourceLists/CorpusFileList.js b/app/static/js/ResourceLists/CorpusFileList.js
index 07a2eaca..5957a140 100644
--- a/app/static/js/ResourceLists/CorpusFileList.js
+++ b/app/static/js/ResourceLists/CorpusFileList.js
@@ -158,7 +158,7 @@ class CorpusFileList extends ResourceList {
               window.location.reload();
             });
           } else {
-            Requests.corpora.entity.followers.entity.delete(this.corpusId, itemId);
+            Requests.corpora.entity.files.ent.delete(this.corpusId, itemId)
           }
         });
         modal.open();
diff --git a/app/static/js/ResourceLists/CorpusList.js b/app/static/js/ResourceLists/CorpusList.js
index a95533fe..7988cb85 100644
--- a/app/static/js/ResourceLists/CorpusList.js
+++ b/app/static/js/ResourceLists/CorpusList.js
@@ -26,9 +26,9 @@ class CorpusList extends ResourceList {
   get item() {
     return (values) => {
       return `
-        <tr class="${values['is-owner'] ? '' : 'deep-purple lighten-5'} list-item">
+        <tr class="list-item">
           <td>
-            <label class="list-action-trigger ${values['is-owner'] ? '' : 'hide'}" data-list-action="select">
+            <label class="list-action-trigger" data-list-action="select">
               <input class="select-checkbox" type="checkbox">
               <span class="disable-on-click"></span>
             </label>
@@ -36,8 +36,9 @@ class CorpusList extends ResourceList {
           <td><b class="title"></b><br><i class="description"></i></td>
           <td><span class="owner"></span></td>
           <td><span class="status badge new corpus-status-color corpus-status-text" data-badge-caption=""></span></td>
+          <td>${values['current-user-is-following'] ? '<span><i class="left material-icons">visibility</i>Following</span>' : ''}</td>
           <td class="right-align">
-            <a class="list-action-trigger btn-floating red waves-effect waves-light ${values['is-owner'] ? '' : 'hide'}" data-list-action="delete-request"><i class="material-icons">delete</i></a>
+            <a class="list-action-trigger btn-floating red waves-effect waves-light" data-list-action="delete-request"><i class="material-icons">delete</i></a>
             <a class="list-action-trigger btn-floating service-color darken waves-effect waves-light" data-list-action="view" data-service="corpus-analysis"><i class="material-icons">send</i></a>
           </td>
         </tr>
@@ -53,6 +54,7 @@ class CorpusList extends ResourceList {
       'description',
       'title',
       'owner',
+      'current-user-is-following'
     ];
   }
 
@@ -79,6 +81,7 @@ class CorpusList extends ResourceList {
             <th>Title and Description</th>
             <th>Owner</th>
             <th>Status</th>
+            <th></th>
             <th class="right-align">
               <a class="corpus-list-selection-action-trigger btn-floating red waves-effect waves-light hide" data-selection-action="delete"><i class="material-icons">delete</i></a>
             </th>
@@ -98,7 +101,8 @@ class CorpusList extends ResourceList {
       'status': corpus.status,
       'title': corpus.title,
       'owner': corpus.user.username,
-      'is-owner': corpus.user.id === this.userId ? true : false
+      'is-owner': corpus.user.id === this.userId ? true : false,
+      'current-user-is-following': Object.values(corpus.corpus_follower_associations).some(association => association.follower.id === currentUserId)
     };
   }
 
@@ -120,7 +124,7 @@ class CorpusList extends ResourceList {
             <div class="modal">
               <div class="modal-content">
                 <h4>Confirm Corpus deletion</h4>
-                <p>Do you really want to delete the Corpus <b>${values.title}</b>? All files will be permanently deleted!</p>
+                <p>Do you really want to ${values['is-owner'] ? 'delete' : 'unfollow'} the Corpus <b>${values.title}</b>? ${values['is-owner'] ? 'All files will be permanently deleted!' : ''}</p>
               </div>
               <div class="modal-footer">
                 <a class="btn modal-close waves-effect waves-light">Cancel</a>
@@ -142,7 +146,14 @@ class CorpusList extends ResourceList {
         );
         let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
         confirmElement.addEventListener('click', (event) => {
-          Requests.corpora.entity.delete(itemId);
+          if (!values['is-owner']) {
+            Requests.corpora.entity.followers.entity.delete(itemId, currentUserId)
+              .then((response) => {
+                window.location.reload();
+              });
+          } else {
+            Requests.corpora.entity.delete(itemId);
+          }
         });
         modal.open();
         break;
@@ -152,7 +163,6 @@ class CorpusList extends ResourceList {
         break;
       }
       case 'select': {
-
         if (event.target.checked) {
           this.selectedItemIds.add(itemId);
         } else {
@@ -169,7 +179,7 @@ class CorpusList extends ResourceList {
   onSelectionAction(event) {
     let selectionActionElement = event.target.closest('.corpus-list-selection-action-trigger[data-selection-action]');
     let selectionAction = selectionActionElement.dataset.selectionAction;
-    let items = Array.from(this.listjs.items).filter(item => item._values["is-owner"] === true);
+    let items = Array.from(this.listjs.items);
     let selectableItems = Array.from(items)
       .filter(item => item.elm)
       .map(item => item.elm.querySelector('.select-checkbox[type="checkbox"]'));
@@ -195,10 +205,11 @@ class CorpusList extends ResourceList {
           `
             <div class="modal">
               <div class="modal-content">
-                <h4>Confirm Corpus File deletion</h4>
-                <p>Do you really want to delete this Corpora?</p>
-                  <ul id="selected-items-list"></ul>
-                <p>All corpora will be permanently deleted!</p>
+                <h4>Confirm Corpus deletion</h4>
+                <p>Do you really want to delete this Corpora? <i>All corpora will be permanently deleted!</i></p>
+                  <ul id="selected-deletion-items-list"></ul>
+                <p>Do you really want to unfollow this Corpora?</p>
+                  <ul id="selected-unfollow-items-list"></ul>
               </div>
               <div class="modal-footer">
                 <a class="btn modal-close waves-effect waves-light">Cancel</a>
@@ -208,12 +219,17 @@ class CorpusList extends ResourceList {
           `
         );
         document.querySelector('#modals').appendChild(modalElement);
-        let itemList = document.querySelector('#selected-items-list');
+        let itemDeletionList = document.querySelector('#selected-deletion-items-list');
+        let itemUnfollowList = document.querySelector('#selected-unfollow-items-list');
         this.selectedItemIds.forEach(selectedItemId => {
           let listItem = this.listjs.get('id', selectedItemId)[0].elm;
           let values = this.listjs.get('id', listItem.dataset.id)[0].values();
           let itemElement = Utils.HTMLToElement(`<li> - ${values.title}</li>`);
-          itemList.appendChild(itemElement);
+          if (!values['is-owner']) { 
+            itemUnfollowList.appendChild(itemElement);
+          } else {
+            itemDeletionList.appendChild(itemElement);
+          }
         });
         let modal = M.Modal.init(
           modalElement,
@@ -228,7 +244,13 @@ class CorpusList extends ResourceList {
         let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
         confirmElement.addEventListener('click', (event) => {
           this.selectedItemIds.forEach(selectedItemId => {
-            Requests.corpora.entity.delete(selectedItemId);
+            let listItem = this.listjs.get('id', selectedItemId)[0].elm;
+            let values = this.listjs.get('id', listItem.dataset.id)[0].values();
+            if (!values['is-owner']) {
+              Requests.corpora.entity.followers.entity.delete(selectedItemId, currentUserId);
+            } else {
+              Requests.corpora.entity.delete(selectedItemId);
+            }
           });
           this.selectedItemIds.clear();
           this.renderingItemSelection();
@@ -248,23 +270,17 @@ class CorpusList extends ResourceList {
     let actionButtons = [];
 
     Object.values(selectableItems).forEach(selectableItem => {
-      if (selectableItem._values["is-owner"] === false && this.selectedItemIds.size > 0) {
-        selectableItem.elm.classList.add('hide');
-      } else if (selectableItem._values["is-owner"] === false && this.selectedItemIds.size === 0) {
-        selectableItem.elm.classList.remove('hide');
-      } else {
-        if (selectableItem.elm) {
-          let checkbox = selectableItem.elm.querySelector('.select-checkbox[type="checkbox"]');
-          if (checkbox.checked) {
-            selectableItem.elm.classList.add('grey', 'lighten-3');
-          } else {
-            selectableItem.elm.classList.remove('grey', 'lighten-3');
-          }
-          let itemActionButtons = selectableItem.elm.querySelectorAll('.list-action-trigger:not([data-list-action="select"])');
-          itemActionButtons.forEach(itemActionButton => {
-            actionButtons.push(itemActionButton);
-          });
+      if (selectableItem.elm) {
+        let checkbox = selectableItem.elm.querySelector('.select-checkbox[type="checkbox"]');
+        if (checkbox.checked) {
+          selectableItem.elm.classList.add('grey', 'lighten-3');
+        } else {
+          selectableItem.elm.classList.remove('grey', 'lighten-3');
         }
+        let itemActionButtons = selectableItem.elm.querySelectorAll('.list-action-trigger:not([data-list-action="select"])');
+        itemActionButtons.forEach(itemActionButton => {
+          actionButtons.push(itemActionButton);
+        });
       }
     });
     // Hide item action buttons if > 0 item is selected and show selection action buttons
@@ -285,11 +301,10 @@ class CorpusList extends ResourceList {
     }
 
     // Check select all checkbox if all items are selected
-    let filteredItems = Object.values(selectableItems).filter(item => item._values["is-owner"] === true)
     let selectAllCheckbox = document.querySelector('.corpus-list-select-all-checkbox[type="checkbox"]');
-    if (filteredItems.length === this.selectedItemIds.size && selectAllCheckbox.checked === false) {
+    if (selectableItems.length === this.selectedItemIds.size && selectAllCheckbox.checked === false) {
       selectAllCheckbox.checked = true;
-    } else if (filteredItems.length !== this.selectedItemIds.size && selectAllCheckbox.checked === true) {
+    } else if (selectableItems.length !== this.selectedItemIds.size && selectAllCheckbox.checked === true) {
       selectAllCheckbox.checked = false;
     }
   }
diff --git a/app/static/js/ResourceLists/DetailledPublicCorpusList.js b/app/static/js/ResourceLists/DetailledPublicCorpusList.js
new file mode 100644
index 00000000..5bfc59ff
--- /dev/null
+++ b/app/static/js/ResourceLists/DetailledPublicCorpusList.js
@@ -0,0 +1,71 @@
+class DetailledPublicCorpusList extends CorpusList {
+  get item() {
+    return (values) => {
+      return `
+        <tr class="list-item clickable hoverable">
+          <td></td>
+          <td><b class="title"></b><br><i class="description"></i></td>
+          <td><span class="owner"></span></td>
+          <td><span class="status badge new corpus-status-color corpus-status-text" data-badge-caption=""></span></td>
+          <td>${values['current-user-is-following'] ? '<span><i class="left material-icons">visibility</i>Following</span>' : ''}</td>
+          <td class="right-align">
+            <a class="list-action-trigger btn-floating service-color darken waves-effect waves-light" data-list-action="view" data-service="corpus-analysis"><i class="material-icons">send</i></a>
+          </td>
+        </tr>
+      `.trim();
+    };
+  }
+
+  get valueNames() {
+    return [
+      {data: ['id']},
+      {data: ['creation-date']},
+      {name: 'status', attr: 'data-status'},
+      'description',
+      'title',
+      'owner',
+      'current-user-is-following'
+    ];
+  }
+
+  initListContainerElement() {
+    if (!this.listContainerElement.hasAttribute('id')) {
+      this.listContainerElement.id = Utils.generateElementId('corpus-list-');
+    }
+    let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`);
+    this.listContainerElement.innerHTML = `
+      <div class="input-field">
+        <i class="material-icons prefix">search</i>
+        <input id="${listSearchElementId}" class="search" type="text"></input>
+        <label for="${listSearchElementId}">Search Corpus</label>
+      </div>
+      <table>
+        <thead>
+          <tr>
+            <th></th>
+            <th>Title and Description</th>
+            <th>Owner</th>
+            <th>Status</th>
+            <th></th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody class="list"></tbody>
+      </table>
+      <ul class="pagination"></ul>
+    `.trim();
+  }
+
+  mapResourceToValue(corpus) {
+    return {
+      'id': corpus.id,
+      'creation-date': corpus.creation_date,
+      'description': corpus.description,
+      'status': corpus.status,
+      'title': corpus.title,
+      'owner': corpus.user.username,
+      'is-owner': corpus.user.id === this.userId,
+      'current-user-is-following': Object.values(corpus.corpus_follower_associations).some(association => association.follower.id === currentUserId)
+    };
+  }
+}
diff --git a/app/static/js/ResourceLists/PublicCorpusList.js b/app/static/js/ResourceLists/PublicCorpusList.js
index 4b5f5ecb..ce2c9cbd 100644
--- a/app/static/js/ResourceLists/PublicCorpusList.js
+++ b/app/static/js/ResourceLists/PublicCorpusList.js
@@ -1,14 +1,17 @@
 class PublicCorpusList extends CorpusList {
   get item() {
-    return `
-      <tr class="list-item clickable hoverable">
-        <td><b class="title"></b><br><i class="description"></i></td>
-        <td><span class="owner"></span></td>
-        <td class="right-align">
-          <a class="list-action-trigger btn-floating service-color darken waves-effect waves-light" data-list-action="view" data-service="corpus-analysis"><i class="material-icons">send</i></a>
-        </td>
-      </tr>
-    `.trim();
+    return (values) => {    
+      return `
+        <tr class="list-item clickable hoverable">
+          <td><b class="title"></b><br><i class="description"></i></td>
+          <td><span class="owner"></span></td>
+          <td>${values['current-user-is-following'] ? '<span><i class="left material-icons">visibility</i></span>' : ''}</td>
+          <td class="right-align">
+            <a class="list-action-trigger btn-floating service-color darken waves-effect waves-light" data-list-action="view" data-service="corpus-analysis"><i class="material-icons">send</i></a>
+          </td>
+        </tr>
+      `.trim();
+    };
   }
 
   initListContainerElement() {
@@ -28,6 +31,7 @@ class PublicCorpusList extends CorpusList {
             <th>Title and Description</th>
             <th>Owner</th>
             <th></th>
+            <th></th>
           </tr>
         </thead>
         <tbody class="list"></tbody>
diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2
index f3b18003..88167db7 100644
--- a/app/templates/_scripts.html.j2
+++ b/app/templates/_scripts.html.j2
@@ -48,7 +48,8 @@
   'js/ResourceLists/TesseractOCRPipelineModelList.js',
   'js/ResourceLists/UserList.js',
   'js/ResourceLists/AdminUserList.js',
-  'js/ResourceLists/CorpusFollowerList.js'
+  'js/ResourceLists/CorpusFollowerList.js',
+  'js/ResourceLists/DetailledPublicCorpusList.js'
 %}
 <script src="{{ ASSET_URL }}"></script>
 {%- endassets %}
diff --git a/app/templates/corpora/public_corpus.html.j2 b/app/templates/corpora/public_corpus.html.j2
index 6ace0f7a..61efca3a 100644
--- a/app/templates/corpora/public_corpus.html.j2
+++ b/app/templates/corpora/public_corpus.html.j2
@@ -270,7 +270,7 @@ publicCorpusFollowerList.add(
 {% endif %}
 
 // #region Build Corpus Request
-{% if cfr.has_permission('MANAGE_CORPUS') %}
+{% if cfr.has_permission('MANAGE_FILES') %}
 let followerBuildRequest = document.querySelector('#follower-build-request');
 followerBuildRequest.addEventListener('click', () => {
   Requests.corpora.entity.build({{ corpus.hashid|tojson }})
diff --git a/app/templates/main/dashboard.html.j2 b/app/templates/main/dashboard.html.j2
index 89f9121d..7c36ff3a 100644
--- a/app/templates/main/dashboard.html.j2
+++ b/app/templates/main/dashboard.html.j2
@@ -143,7 +143,7 @@
   corpusList.add(
     [
       {% for corpus in corpora %}
-      {{ corpus.to_json_serializeable(backrefs=True)|tojson }},
+      {{ corpus.to_json_serializeable(backrefs=True, relationships=True)|tojson }},
       {% endfor %}
     ]
   );
diff --git a/app/templates/main/social_area.html.j2 b/app/templates/main/social_area.html.j2
index ff57d371..8b8528fc 100644
--- a/app/templates/main/social_area.html.j2
+++ b/app/templates/main/social_area.html.j2
@@ -70,11 +70,11 @@
       {% endfor %}
     ]
   );
-  let publicCorpusList = new PublicCorpusList(document.querySelector('.public-corpus-list'));
+  let publicCorpusList = new DetailledPublicCorpusList(document.querySelector('.public-corpus-list'));
   publicCorpusList.add(
     [
       {% for corpus in corpora %}
-      {{ corpus.to_json_serializeable(backrefs=True)|tojson }},
+      {{ corpus.to_json_serializeable(backrefs=True, relationships=True)|tojson }},
       {% endfor %}
     ]
   );
diff --git a/app/templates/users/user.html.j2 b/app/templates/users/user.html.j2
index 238edd7e..d75ffc7e 100644
--- a/app/templates/users/user.html.j2
+++ b/app/templates/users/user.html.j2
@@ -127,7 +127,7 @@ followedCorpusList.add(
   [
     {% for corpus in user.followed_corpora %}
       {% if (corpus.is_public or corpus.user == current_user or user == current_user or current_user.is_following_corpus(corpus)) %}
-      {{ corpus.to_json_serializeable(backrefs=True)|tojson }},
+      {{ corpus.to_json_serializeable(backrefs=True, relationships=True)|tojson }},
       {% endif %}
     {% endfor %}
   ]
@@ -137,7 +137,7 @@ publicCorpusList.add(
   [
     {% for corpus in user.corpora %}
       {% if corpus.is_public %}
-      {{ corpus.to_json_serializeable(backrefs=True)|tojson }},
+      {{ corpus.to_json_serializeable(backrefs=True, relationships=True)|tojson }},
       {% endif %}
     {% endfor %}
   ]
-- 
GitLab