From 1231022837beceedef376e4bb8084ff38fbc7d93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michaela=20Br=C3=BCckner?= <brueckner@data-quest.de>
Date: Thu, 24 Nov 2022 10:00:25 +0000
Subject: [PATCH] Resolve "Visuelle Kennzeichnung von barrierefreien Dateien in
 den Dateibereichen", closes #1540

Closes #1540

Merge request studip/studip!1027
---
 .../admin/accessibility_info_text.php         | 33 +++++++++++++
 app/controllers/file.php                      | 19 +++++++-
 .../admin/accessibility_info_text/index.php   | 19 ++++++++
 app/views/file/_file_aside.php                |  6 +++
 app/views/file/_terms_of_use_select.php       |  3 ++
 app/views/file/edit.php                       | 12 ++++-
 app/views/file/edit_license.php               | 29 ++++++++---
 ....3.8_add_accessibility_field_for_files.php | 48 +++++++++++++++++++
 lib/filesystem/FileType.php                   |  6 ++-
 lib/filesystem/FilesystemVueDataManager.php   |  1 +
 lib/filesystem/StandardFile.php               |  4 ++
 lib/filesystem/UnknownFileType.php            |  8 ++++
 lib/models/FileRef.php                        |  9 ++++
 lib/navigation/AdminNavigation.php            |  8 ++++
 .../images/icons/black/accessibility.svg      |  1 +
 resources/vue/components/FilesTable.vue       | 11 ++++-
 16 files changed, 205 insertions(+), 12 deletions(-)
 create mode 100644 app/controllers/admin/accessibility_info_text.php
 create mode 100644 app/views/admin/accessibility_info_text/index.php
 create mode 100644 db/migrations/5.3.8_add_accessibility_field_for_files.php
 create mode 100644 public/assets/images/icons/black/accessibility.svg

diff --git a/app/controllers/admin/accessibility_info_text.php b/app/controllers/admin/accessibility_info_text.php
new file mode 100644
index 00000000000..9a1828bcc73
--- /dev/null
+++ b/app/controllers/admin/accessibility_info_text.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * accessibility_info_text.php - controller class for administrating additional information text to accessible files
+ * in file upload/edit dialogs
+ *
+ * @author    Michaela Brückner <brueckner@data-quest.de>
+ * @license   GPL2 or any later version
+ * @category  Stud.IP
+ * @package   admin
+ * @since     5.3
+ */
+class Admin_AccessibilityInfoTextController extends AuthenticatedController
+{
+    public function before_filter(&$action, &$args)
+    {
+        parent::before_filter($action, $args);
+        $GLOBALS['perm']->check('root');
+        PageLayout::setTitle(_('Infotext zu barrierefreien Dateien'));
+        Navigation::activateItem('/admin/locations/accessibility_info_text');
+    }
+
+    public function index_action()
+    {
+    }
+
+    public function edit_action()
+    {
+        CSRFProtection::verifyUnsafeRequest();
+        Config::get()->store('ACCESSIBILITY_INFO_TEXT', Request::i18n('accessbility_info_text'));
+        PageLayout::postSuccess(_('Die Einstellungen wurden gespeichert.'));
+        $this->relocate('admin/accessibility_info_text/index');
+    }
+}
diff --git a/app/controllers/file.php b/app/controllers/file.php
index f418109d352..b1ed35a0b8d 100644
--- a/app/controllers/file.php
+++ b/app/controllers/file.php
@@ -378,6 +378,8 @@ class FileController extends AuthenticatedController
             $force_save = Request::submitted('force_save');
             $this->name = trim(Request::get('name'));
             $this->description = Request::get('description');
+            $this->store_accessibility_flag($file_ref_id);
+
             $this->content_terms_of_use_id = Request::get('content_terms_of_use_id');
 
             //Check if the FileRef is unmodified:
@@ -1541,6 +1543,12 @@ class FileController extends AuthenticatedController
 
         if (Request::isPost()) {
             CSRFProtection::verifyUnsafeRequest();
+
+            if (count($file_ref_ids) === 1) {
+                // store flag if file is an accessible file
+                $this->store_accessibility_flag($file_ref_ids[0]);
+            }
+
             if (($folder_id == 'bulk') && !Request::submitted('accept')) {
                 $file_ref_ids = Request::getArray('ids');
                 $this->file_refs = FileRef::findMany($file_ref_ids);
@@ -1662,8 +1670,8 @@ class FileController extends AuthenticatedController
 
         PageLayout::setTitle(sprintf(
             ngettext(
-                'Lizenz auswählen',
-                'Lizenz auswählen: %s Dateien',
+                'Zusatzangaben und Lizenz wählen',
+                'Zusatzangaben und Lizenz auswählen: %s Dateien',
                 count($this->file_refs)
             ),
             count($this->file_refs)
@@ -2159,4 +2167,11 @@ class FileController extends AuthenticatedController
         }
         return [$folder_size, $folder_file_amount];
     }
+
+    private function store_accessibility_flag($file_ref_id)
+    {
+        $file_ref = FileRef::find($file_ref_id);
+        $file_ref->file['is_accessible'] = Request::get('is_accessible');
+        $file_ref->file->store();
+    }
 }
diff --git a/app/views/admin/accessibility_info_text/index.php b/app/views/admin/accessibility_info_text/index.php
new file mode 100644
index 00000000000..c236099ccbe
--- /dev/null
+++ b/app/views/admin/accessibility_info_text/index.php
@@ -0,0 +1,19 @@
+<form class="default a11y-settings" action="<?= $controller->url_for('admin/accessibility_info_text/edit') ?>" method="post">
+    <?= CSRFProtection::tokenTag() ?>
+
+    <fieldset>
+        <legend><?= _('Infotext zu barrierefreien Dateien') ?></legend>
+        <section>
+            <label for="accessbility_info_text">
+                <?= _('Die angegebene Information wird im Datei-Hochladen-Dialog unter der Checkbox angezeigt.') ?>
+            </label>
+            <?= I18N::textarea('accessbility_info_text', Config::get()->ACCESSIBILITY_INFO_TEXT,
+                ['class' => 'add_toolbar wysiwyg', 'data-editor' => 'toolbar=small']) ?>
+        </section>
+    </fieldset>
+
+    <footer data-dialog-button>
+        <?= CSRFProtection::tokenTag() ?>
+        <?= Studip\Button::createAccept(_('Speichern'), 'store') ?>
+    </footer>
+</form>
diff --git a/app/views/file/_file_aside.php b/app/views/file/_file_aside.php
index 47f33f38e29..e626f324f4e 100644
--- a/app/views/file/_file_aside.php
+++ b/app/views/file/_file_aside.php
@@ -36,6 +36,12 @@
                     <? endif ?>
                 </td>
             </tr>
+            <tr>
+                <td><?= _('Barrierefrei') ?></td>
+                <td>
+                    <?= $file->getAccessibility() ? _('ja') : _('nein') ?>
+                </td>
+            </tr>
 
             <? $content_terms_of_use = $file->getTermsOfUse() ?>
 
diff --git a/app/views/file/_terms_of_use_select.php b/app/views/file/_terms_of_use_select.php
index c1dd994e95e..59cb5079b20 100644
--- a/app/views/file/_terms_of_use_select.php
+++ b/app/views/file/_terms_of_use_select.php
@@ -4,6 +4,8 @@ if (!$selected_terms_of_use_id) {
 }
 ?>
 <? if ($content_terms_of_use_entries) : ?>
+<fieldset>
+    <legend><?= _('Lizenzauswahl') ?></legend>
     <div style="margin-bottom: 1ex;">
         <?= _('Bereitgestellte Dateien können heruntergeladen und ggf. weiterverbreitet werden.
                Dabei ist das Urheberrecht sowohl beim Hochladen der Datei als auch bei der Nutzung
@@ -47,4 +49,5 @@ if (!$selected_terms_of_use_id) {
         <? endif ?>
     <? endforeach ?>
     </fieldset>
+</fieldset>
 <? endif; ?>
diff --git a/app/views/file/edit.php b/app/views/file/edit.php
index 737d2414b6e..3e36abb312a 100644
--- a/app/views/file/edit.php
+++ b/app/views/file/edit.php
@@ -7,8 +7,7 @@
 
             <?= CSRFProtection::tokenTag() ?>
             <fieldset>
-                <legend><?= _('Datei bearbeiten') ?></legend>
-
+                <legend><?= _('Zusatzangaben') ?></legend>
                 <label>
                     <?= _('Name') ?>
                     <input id="edit_file_name" type="text" name="name"
@@ -20,6 +19,15 @@
                 </label>
             </fieldset>
 
+            <fieldset>
+                <legend><?= _('Barrierefreiheit') ?></legend>
+                <label>
+                    <input type="checkbox" name="is_accessible" value="1" <?= $file->getAccessibility() ? "checked" : "" ?>>
+                    <?= _('Diese Datei ist barrierefrei.') ?>
+                </label>
+                <?= formatReady((string)Config::get()->ACCESSIBILITY_INFO_TEXT ?: '') ?>
+            </fieldset>
+
             <?= $this->render_partial('file/_terms_of_use_select.php', [
                 'content_terms_of_use_entries' => $content_terms_of_use_entries,
                 'selected_terms_of_use_id'     => $content_terms_of_use->id,
diff --git a/app/views/file/edit_license.php b/app/views/file/edit_license.php
index ebd5ff01de4..ddd031d153c 100644
--- a/app/views/file/edit_license.php
+++ b/app/views/file/edit_license.php
@@ -5,21 +5,38 @@
     <input type="hidden" name="file_refs[]" value="<?= htmlReady($file_ref->id) ?>">
 <? endforeach ?>
 
+
     <? if ($show_description_field): ?>
+    <fieldset>
+        <legend><?= _('Zusatzangaben') ?></legend>
         <label>
-            <?= _('Beschreibung') ?>
+            <b><?= _('Beschreibung') ?></b>
             <textarea name="description" placeholder="<?= _('Optionale Beschreibung') ?>"></textarea>
         </label>
+    </fieldset>
+
     <? endif ?>
-    <?= $this->render_partial('file/_terms_of_use_select.php', [
-        'content_terms_of_use_entries' => $licenses,
-        'selected_terms_of_use_id'     => $file_ref->content_terms_of_use_id
-    ]) ?>
+
+    <? if (count($file_refs) === 1) : ?>
+        <fieldset>
+            <legend><?= _('Barrierefreiheit') ?></legend>
+            <label>
+                <input type="checkbox" name="is_accessible" value="1">
+                <?= _('Diese Datei ist barrierefrei.') ?>
+            </label>
+            <?= formatReady((string)Config::get()->ACCESSIBILITY_INFO_TEXT ?: '') ?>
+        </fieldset>
+    <? endif ?>
+
+        <?= $this->render_partial('file/_terms_of_use_select.php', [
+            'content_terms_of_use_entries' => $licenses,
+            'selected_terms_of_use_id'     => $file_ref->content_terms_of_use_id
+        ]) ?>
 
     <footer data-dialog-button>
         <?= Studip\Button::createAccept(_('Speichern')) ?>
         <?= Studip\LinkButton::createCancel(
-            _('Lizenzauswahl abbrechen'),
+            _('Abbrechen'),
             $controller->url_for((in_array($folder->range_type, ['course', 'institute']) ? $folder->range_type . '/' : '') . 'files/index/' . $folder->id)
         ) ?>
     </footer>
diff --git a/db/migrations/5.3.8_add_accessibility_field_for_files.php b/db/migrations/5.3.8_add_accessibility_field_for_files.php
new file mode 100644
index 00000000000..e9ee2475f96
--- /dev/null
+++ b/db/migrations/5.3.8_add_accessibility_field_for_files.php
@@ -0,0 +1,48 @@
+<?php
+
+
+class AddAccessibilityFieldForFiles extends Migration
+{
+    public function description()
+    {
+        return 'Add field is_accessible to file table; creates config for accessibility info text';
+    }
+
+    public function up()
+    {
+        $db = DBManager::get();
+
+        $db->exec(
+            "ALTER TABLE `files`
+            ADD `is_accessible` TINYINT(1) NULL AFTER `author_name`"
+        );
+
+
+        $query = 'INSERT INTO `config` (`field`, `value`, `type`, `section`, `range`, `description`, `mkdate`, `chdate`)
+                  VALUES (:name, :value, :type, :section, :range, :description, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())';
+        $statement = DBManager::get()->prepare($query);
+        $statement->execute([
+            'name'        => 'ACCESSIBILITY_INFO_TEXT',
+            'value'       => '',
+            'type'        => 'i18n',
+            'section'     => 'accessibility',
+            'range' => 'global',
+            'description' => 'Diese Konfiguration bitte unter Admin -> Standort -> Infotext zu barrierefreien Dateien anpassen!'
+        ]);
+
+    }
+
+    public function down()
+    {
+        $db = DBManager::get();
+
+        $db->exec(
+            "ALTER TABLE `files` DROP `is_accessible`"
+        );
+
+        $query = "DELETE `config`, `config_values`
+                  FROM `config` LEFT JOIN `config_values` USING (`field`)
+                  WHERE `field` = 'ACCESSIBILITY_INFO_TEXT'";
+        DBManager::get()->exec($query);
+    }
+}
diff --git a/lib/filesystem/FileType.php b/lib/filesystem/FileType.php
index 003ee856a9a..5a53896fdc9 100644
--- a/lib/filesystem/FileType.php
+++ b/lib/filesystem/FileType.php
@@ -35,13 +35,17 @@ interface FileType
      */
     public function getUserName();
 
+    /**
+     * Returns if a file is accessible or not.
+     * @return bool
+     */
+    public function getAccessibility(): bool;
 
     /**
      * @returns The User object representing the author.
      */
     public function getUser();
 
-
     /**
      * Returns the size of the file in bytes. If this is null, the file doesn't exist
      * physically - is probably only a weblink or a request for libraries.
diff --git a/lib/filesystem/FilesystemVueDataManager.php b/lib/filesystem/FilesystemVueDataManager.php
index 0714356f721..06ea27313c2 100644
--- a/lib/filesystem/FilesystemVueDataManager.php
+++ b/lib/filesystem/FilesystemVueDataManager.php
@@ -44,6 +44,7 @@ class FilesystemVueDataManager
             'actions' => $actionMenu ? (is_string($actionMenu) ? $actionMenu : $actionMenu->render()) : "",
             'new' => isset($last_visitdate) && $file->getLastChangeDate() > $last_visitdate && $file->getUserId() !== $GLOBALS['user']->id,
             'isEditable' => $file->isEditable(),
+            'isAccessible' => $file->getAccessibility(),
         ];
     }
 
diff --git a/lib/filesystem/StandardFile.php b/lib/filesystem/StandardFile.php
index 5a4271ab5ff..c11541066be 100644
--- a/lib/filesystem/StandardFile.php
+++ b/lib/filesystem/StandardFile.php
@@ -174,6 +174,10 @@ class StandardFile implements FileType, ArrayAccess, StandardFileInterface
         return $this->fileref['author_name'];
     }
 
+    public function getAccessibility(): bool
+    {
+        return (bool) $this->fileref['is_accessible'];
+    }
 
     public function getUser()
     {
diff --git a/lib/filesystem/UnknownFileType.php b/lib/filesystem/UnknownFileType.php
index effe75178de..15c2b92e13f 100644
--- a/lib/filesystem/UnknownFileType.php
+++ b/lib/filesystem/UnknownFileType.php
@@ -284,4 +284,12 @@ class UnknownFileType implements FileType, ArrayAccess
     {
         return '';
     }
+
+    /**
+     * @inheritDoc
+     */
+    public function getAccessibility() : bool
+    {
+        return false;
+    }
 }
diff --git a/lib/models/FileRef.php b/lib/models/FileRef.php
index 48c64dc6150..9cc24388141 100644
--- a/lib/models/FileRef.php
+++ b/lib/models/FileRef.php
@@ -60,6 +60,7 @@ class FileRef extends SimpleORMap implements PrivacyObject, FeedbackRange
         $config['additional_fields']['download_url']['set'] = 'setDownloadURL';
         $config['additional_fields']['download_url']['get'] = 'getDownloadURL';
         $config['additional_fields']['author_name']['get'] = 'getAuthorName';
+        $config['additional_fields']['is_accessible']['get'] = 'getAccessibility';
         $config['additional_fields']['is_link']['get'] = 'isLink';
         $config['additional_fields']['foldertype']['set'] = 'setFolderType';
         $config['additional_fields']['foldertype']['get'] = 'getFolderType';
@@ -189,6 +190,14 @@ class FileRef extends SimpleORMap implements PrivacyObject, FeedbackRange
         return $this->file->author_name;
     }
 
+    /**
+     * Returns true if the file is accessible
+     */
+    public function getAccessibility() : bool
+    {
+        return (bool) $this->file->is_accessible;
+    }
+
     /**
      * This method increments the download counter of the FileRef.
      *
diff --git a/lib/navigation/AdminNavigation.php b/lib/navigation/AdminNavigation.php
index 2bb24189e00..b45859c59c7 100644
--- a/lib/navigation/AdminNavigation.php
+++ b/lib/navigation/AdminNavigation.php
@@ -153,6 +153,14 @@ class AdminNavigation extends Navigation
                 );
             }
 
+            $navigation->addSubNavigation(
+                'accessibility_info_text',
+                new Navigation(
+                    _('Infotext zu barrierefreien Dateien'),
+                    'dispatch.php/admin/accessibility_info_text/index'
+                )
+            );
+
         }
         $this->addSubNavigation('locations', $navigation);
 
diff --git a/public/assets/images/icons/black/accessibility.svg b/public/assets/images/icons/black/accessibility.svg
new file mode 100644
index 00000000000..2c1f0fe03eb
--- /dev/null
+++ b/public/assets/images/icons/black/accessibility.svg
@@ -0,0 +1 @@
+<svg viewBox="0 0 54 54" xmlns="http://www.w3.org/2000/svg"><g fill="#000000"><path d="m27 8c10.5 0 19 8.5 19 19s-8.5 19-19 19-19-8.6-19-19 8.5-19 19-19m0-5c-13.3 0-24 10.7-24 24s10.7 24 24 24 24-10.7 24-24-10.7-24-24-24z"/><circle cx="27" cy="15" r="4"/><path d="m38.2 19.1c-.6-.9-1.7-1.1-2.7-.8l-5.8 2.1c-.8.4-1.8.6-2.7.6h-.2c-.9 0-1.9-.2-2.8-.5l-5.8-2.1c-1-.4-2.1-.1-2.7.8-.8 1.2-.2 2.8 1.1 3.3l7.4 2.7c.3.1.5.4.5.7v3.7c0 .3-.1.6-.2.9l-4.5 7.9c-.5.9-.4 2.1.3 2.8 1.1 1 2.7.7 3.4-.5l3.5-6.1 3.6 6.2c.6 1 1.9 1.4 3 .8 1-.6 1.4-2 .8-3.1l-4.6-7.9c-.2-.3-.2-.6-.2-.9v-3.8c0-.3.2-.6.5-.7l7.2-2.6c1.2-.6 1.8-2.2.9-3.5z"/></g></svg>
diff --git a/resources/vue/components/FilesTable.vue b/resources/vue/components/FilesTable.vue
index 875b07635bb..c694a024f6f 100644
--- a/resources/vue/components/FilesTable.vue
+++ b/resources/vue/components/FilesTable.vue
@@ -180,8 +180,15 @@
                            data-lightbox="gallery"></a>
                     </td>
                     <td :class="{'filter-match': valueMatchesFilter(file.name)}">
+
                         <a :href="file.details_url" data-dialog>
                             <span v-html="highlightString(file.name)"></span>
+                            <studip-icon v-if="file.isAccessible"
+                                         shape="accessibility"
+                                         role="info"
+                                         size="16"
+                                         style="vertical-align: text-bottom"
+                                         :title="$gettext('Diese Datei ist barrierefrei.')"></studip-icon>
                         </a>
 
                         <studip-icon v-if="file.restrictedTermsOfUse"
@@ -189,7 +196,9 @@
                                      role="info"
                                      size="16"
                                      :title="$gettext('Das Herunterladen dieser Datei ist nur eingeschränkt möglich.')"></studip-icon>
+
                     </td>
+
                     <td :data-sort-value="file.size"
                         class="responsive-hidden">
                         <studip-file-size v-if="file.size !== null" :size="parseInt(file.size, 10)"></studip-file-size>
@@ -388,7 +397,7 @@ export default {
     },
     computed: {
         numberOfColumns () {
-            return 7
+            return 8
                 + (this.showdownloads ? 1 : 0)
                 + Object.keys(this.topfolder.additionalColumns).length;
         },
-- 
GitLab