aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/oer/mymaterial.php344
-rw-r--r--app/views/oer/mymaterial/edit.php344
-rw-r--r--lib/classes/TwilloConnector.php2
-rw-r--r--lib/models/OERMaterial.php3
-rw-r--r--resources/assets/javascripts/bootstrap/dialog.js15
-rw-r--r--resources/assets/javascripts/bootstrap/oer.js92
-rw-r--r--resources/assets/stylesheets/scss/quicksearch.scss44
-rw-r--r--resources/vue/components/OERMaterialEditor.vue62
-rw-r--r--resources/vue/components/Quicksearch.vue46
-rw-r--r--resources/vue/components/StudipLevelSlider.vue21
10 files changed, 297 insertions, 676 deletions
diff --git a/app/controllers/oer/mymaterial.php b/app/controllers/oer/mymaterial.php
index ef45722..4cc0920 100644
--- a/app/controllers/oer/mymaterial.php
+++ b/app/controllers/oer/mymaterial.php
@@ -9,8 +9,7 @@ class Oer_MymaterialController extends AuthenticatedController
parent::before_filter($action, $args);
if (
!Config::get()->OERCAMPUS_ENABLED
- ||
- !$GLOBALS['perm']->have_perm(Config::get()->OER_PUBLIC_STATUS)
+ || !$GLOBALS['perm']->have_perm(Config::get()->OER_PUBLIC_STATUS)
) {
throw new AccessDeniedException();
}
@@ -28,176 +27,199 @@ class Oer_MymaterialController extends AuthenticatedController
public function edit_action(OERMaterial $material = null)
{
+ $this->validateMaterial($material);
+
PageLayout::setTitle($material->isNew() ? _('Neues Material hochladen') : _('Material bearbeiten'));
- if ($material->id && !$material->isMine() && !$GLOBALS['perm']->have_perm('root')) {
- throw new AccessDeniedException();
- }
+
+ $tagsearch = new SQLSearch(
+ "SELECT oer_tags.name, oer_tags.name
+ FROM oer_tags
+ WHERE name LIKE :input
+ ORDER BY oer_tags.name",
+ _('Thema suchen')
+ );
+
+ $this->render_vue_app(
+ Studip\VueApp::create('OERMaterialEditor')
+ ->withProps([
+ 'store-url' => $this->storeURL($material),
+ 'material' => [
+ ...$material->toArray(),
+
+ 'filesize' => $material->getFilePath() && file_exists($material->getFilePath()) ? filesize($material->getFilePath()) : null,
+ 'logoUrl' => $material->getLogoURL(),
+ 'tags' => array_column($material->getTopics(), 'name'),
+ 'users' => $material->users->map(function (OERMaterialUser $user) {
+ if ($user->external_contact) {
+ return [
+ 'id' => $user->oeruser->id,
+ 'name' => $user->oeruser->name,
+ 'avatar' => $user->oeruser->avatar_url,
+ 'external' => true,
+ ];
+ }
+
+ $u = User::find($user->user_id);
+ return [
+ 'id' => $u->user_id,
+ 'avatar' => Avatar::getAvatar($u->id)->getURL(Avatar::SMALL),
+ 'name' => $u ? $u->getFullName() : _('unbekannt'),
+ 'external' => false,
+ ];
+ }),
+ ],
+ 'template' => $_SESSION['NEW_OER'] ?? null,
+ 'tag-search' => (string) $tagsearch,
+ 'licenses-enabled' => !Config::get()->getValue('OER_DISABLE_LICENSE'),
+ 'licenses' => License::findAndMapBySQL(
+ function (License $license) {
+ return [
+ 'id' => $license->id,
+ 'name' => $license->name,
+ ];
+ },
+ '1 ORDER BY name'
+ ),
+ 'enable-twillo' => $this->isTwilloEnabled(),
+ ])
+ );
+
+ }
+
+ public function store_action(OERMaterial $material = null)
+ {
+ $material = $this->validateMaterial($material);
+
+ CSRFProtection::verifyUnsafeRequest();
+
$content_types = ['application/x-zip-compressed', 'application/zip', 'application/x-zip'];
$tmp_folder = $GLOBALS['TMP_PATH'] . '/temp_folder_' . md5(uniqid());
- if (Request::submitted('delete') && Request::isPost()) {
- CSRFProtection::verifyUnsafeRequest();
- $material->pushDataToIndexServers('delete');
- $material->delete();
- PageLayout::postSuccess(_('Das Material wurde gelöscht.'));
- $this->redirect('oer/market/index');
- return;
- } elseif (Request::isPost()) {
- CSRFProtection::verifyUnsafeRequest();
- $was_new = $material->isNew();
- $was_on_twillo = (bool) $material['published_id_on_twillo'];
- $data = Request::getArray('data');
- $material->setData($data);
- if ($data['player_url'] && !$material->hasValidPreviewUrl()) {
- PageLayout::postWarning(_('Die angegebene URL muss mit http(s) beginnen.'));
- $material->player_url = '';
+
+ $was_new = $material->isNew();
+ $was_on_twillo = (bool) $material->published_id_on_twillo;
+ $data = Request::getArray('data');
+ $material->setData($data);
+ if ($data['player_url'] && !$material->hasValidPreviewUrl()) {
+ PageLayout::postWarning(_('Die angegebene URL muss mit http(s) beginnen.'));
+ $material->player_url = '';
+ }
+ $material->host_id = null;
+ if (!empty($_FILES['file']['tmp_name'])) {
+ $material->content_type = get_mime_type($_FILES['file']['name']);
+ if (in_array($material->content_type, $content_types)) {
+ mkdir($tmp_folder);
+ \Studip\ZipArchive::extractToPath($_FILES['file']['tmp_name'], $tmp_folder);
+ $material->structure = $this->getFolderStructure($tmp_folder);
+ rmdirr($tmp_folder);
+ } else {
+ $material->structure = null;
}
- $material['host_id'] = null;
- $material['license_identifier'] = Request::get('license', 'CC-BY-SA-4.0');
- if (!empty($_FILES['file']['tmp_name'])) {
- $material['content_type'] = get_mime_type($_FILES['file']['name']);
- if (in_array($material['content_type'], $content_types)) {
- mkdir($tmp_folder);
- \Studip\ZipArchive::extractToPath($_FILES['file']['tmp_name'], $tmp_folder);
- $material['structure'] = $this->getFolderStructure($tmp_folder);
- rmdirr($tmp_folder);
- } else {
- $material['structure'] = null;
- }
- $material['filename'] = $_FILES['file']['name'];
- move_uploaded_file($_FILES['file']['tmp_name'], $material->getFilePath());
- } elseif (!empty($_SESSION['NEW_OER']['tmp_name'])) {
- $material['content_type'] = $_SESSION['NEW_OER']['content_type'] ?: get_mime_type($_SESSION['NEW_OER']['tmp_name']);
- if (in_array($material['content_type'], $content_types)) {
- mkdir($tmp_folder);
- \Studip\ZipArchive::extractToPath($_SESSION['NEW_OER']['tmp_name'], $tmp_folder);
- $material['structure'] = $this->getFolderStructure($tmp_folder);
- rmdirr($tmp_folder);
- } else {
- $material['structure'] = null;
- }
- $material['filename'] = $_SESSION['NEW_OER']['filename'];
- copy($_SESSION['NEW_OER']['tmp_name'], $material->getFilePath());
+ $material->filename = $_FILES['file']['name'];
+ move_uploaded_file($_FILES['file']['tmp_name'], $material->getFilePath());
+ } elseif (!empty($_SESSION['NEW_OER']['tmp_name'])) {
+ $material->content_type = $_SESSION['NEW_OER']['content_type'] ?: get_mime_type($_SESSION['NEW_OER']['tmp_name']);
+ if (in_array($material->content_type, $content_types)) {
+ mkdir($tmp_folder);
+ \Studip\ZipArchive::extractToPath($_SESSION['NEW_OER']['tmp_name'], $tmp_folder);
+ $material->structure = $this->getFolderStructure($tmp_folder);
+ rmdirr($tmp_folder);
+ } else {
+ $material->structure = null;
}
+ $material->filename = $_SESSION['NEW_OER']['filename'];
+ copy($_SESSION['NEW_OER']['tmp_name'], $material->getFilePath());
+ }
- if (!empty($_FILES['image']['tmp_name']) && is_array(getimagesize($_FILES['image']['tmp_name']))) {
- $material['front_image_content_type'] = get_mime_type($_FILES['image']['name']);
- move_uploaded_file($_FILES['image']['tmp_name'], $material->getFrontImageFilePath());
- } elseif (!empty($_SESSION['NEW_OER']['image_tmp_name'])) {
- $material['front_image_content_type'] = get_mime_type($_SESSION['NEW_OER']['image_tmp_name']);
- copy($_SESSION['NEW_OER']['image_tmp_name'], $material->getFrontImageFilePath());
- }
- if (Request::get('delete_front_image')) {
- $material['front_image_content_type'] = null;
- }
- if ($material->isNew() && $material['category'] === 'auto') {
- $material['category'] = $material->autoDetectCategory();
- }
- $material->store();
-
- if ($was_new) {
- OERMaterialUser::create(
- [
- 'material_id' => $material->getId(),
- 'user_id' => $GLOBALS['user']->id,
- 'external_contact' => 0,
- 'position' => 1
- ]
- );
- $material->notifyFollowersAboutNewMaterial();
- }
- $removed_users = Request::getArray('remove_users');
- if (!empty($removed_users)) {
- foreach (Request::getArray('remove_users') as $index => $user) {
- if (!$index && count($removed_users) === count($material->users)) {
- continue;
- }
- [$external, $user_id] = array_map('trim', explode('_', $user));
- OERMaterialUser::deleteBySQL(
- 'user_id = ? AND material_id = ? AND external_contact = ?',
- [$user_id, $material->getId(), $external]
- );
- }
- }
- if (Request::get('new_user')) {
- [$external, $user_id] = array_map('trim', explode('_', Request::get('new_user')));
-
- OERMaterialUser::create(
- [
- 'material_id' => $material->getId(),
- 'user_id' => $user_id,
- 'external_contact' => $external,
- 'position' => count($material->users) + 1
- ]
- );
- }
+ if (
+ !empty($_FILES['image']['tmp_name'])
+ && getimagesize($_FILES['image']['tmp_name']) !== false
+ ) {
+ $material->front_image_content_type = get_mime_type($_FILES['image']['name']);
+ move_uploaded_file($_FILES['image']['tmp_name'], $material->getFrontImageFilePath());
+ } elseif (!empty($_SESSION['NEW_OER']['image_tmp_name'])) {
+ $material->front_image_content_type = get_mime_type($_SESSION['NEW_OER']['image_tmp_name']);
+ copy($_SESSION['NEW_OER']['image_tmp_name'], $material->getFrontImageFilePath());
+ }
+ if (Request::get('delete_front_image')) {
+ $material->front_image_content_type = null;
+ }
+ if ($material->isNew() && $material->category === 'auto') {
+ $material->category = $material->autoDetectCategory();
+ }
+ $material->store();
- //Topics:
- $tags = Request::getArray('tags');
- if (!empty($tags)) {
- foreach ($tags as $key => $tag) {
- if (!trim($tag)) {
- unset($tags[$key]);
- }
- }
- }
- $material->setTopics($tags);
-
- $material->pushDataToIndexServers();
-
- if (Config::get()->OERCAMPUS_ENABLE_TWILLO && TwilloConnector::getTwilloUserID()) {
- if (Request::bool('publish_on_twillo') || $_SESSION['NEW_OER']['publish_on_twillo']) {
- //upload it to twillo.de
- $succes_or_error = $material->uploadToTwillo($material);
- if (is_string($succes_or_error)) {
- PageLayout::postWarning(_('Konnte Material nicht zu twillo.de hochladen.'), [$succes_or_error]);
- }
- } elseif ($was_on_twillo) {
- //remove it from twillo.de if able
- $material->deleteFromTwillo();
- }
+ if ($was_new) {
+ OERMaterialUser::create([
+ 'material_id' => $material->id,
+ 'user_id' => User::findCurrent()->id,
+ 'external_contact' => false,
+ 'position' => 1,
+ ]);
+ $material->notifyFollowersAboutNewMaterial();
+ }
+ $removed_users = Request::getArray('remove_users');
+ foreach (Request::getArray('remove_users') as $index => $user) {
+ if (!$index && count($removed_users) === count($material->users)) {
+ continue;
}
+ [$external, $user_id] = array_map('trim', explode('_', $user));
+ OERMaterialUser::deleteBySQL(
+ 'user_id = ? AND material_id = ? AND external_contact = ?',
+ [$user_id, $material->getId(), $external]
+ );
+ }
- unset($_SESSION['NEW_OER']);
- PageLayout::postSuccess(_('Lernmaterial erfolgreich gespeichert.'));
+ //Topics:
+ $tags = Request::getArray('tags');
+ $material->setTopics($tags);
- if (Request::get('redirect_url')) {
- if (Request::get('redirect_url') === 'files') {
- $this->redirect(
- URLHelper::getURL(
- 'dispatch.php/course/files/index/' . Request::get('dir'),
- ['cid' => Request::get('cid')]
- )
+ $material->pushDataToIndexServers();
+
+ if ($this->isTwilloEnabled()) {
+ if (
+ Request::bool('publish_on_twillo')
+ || !empty($_SESSION['NEW_OER']['publish_on_twillo'])
+ ) {
+ //upload it to twillo.de
+ $succes_or_error = $material->uploadToTwillo();
+ if (is_string($succes_or_error)) {
+ PageLayout::postWarning(
+ _('Konnte Material nicht zu twillo.de hochladen.'),
+ [htmlReady($succes_or_error)]
);
- } else {
- $this->redirect(URLHelper::getURL(Request::get('redirect_url'), [
- 'material_id' => $material->getId(),
- 'url' => $this->url_for('oer/market/details/' . $material->id)
- ]));
}
- } else {
- $this->redirect('oer/market/details/' . $material->id);
+ } elseif ($was_on_twillo) {
+ //remove it from twillo.de if able
+ $material->deleteFromTwillo();
}
+ }
+ unset($_SESSION['NEW_OER']);
+ PageLayout::postSuccess(_('Lernmaterial erfolgreich gespeichert.'));
+ $redirect_url = Request::get('redirect_url');
+ if (!$redirect_url) {
+ $this->redirect('oer/market/details/' . $material->id);
+ } elseif ($redirect_url === 'files') {
+ $this->redirect(
+ URLHelper::getURL(
+ 'dispatch.php/course/files/index/' . Request::get('dir'),
+ ['cid' => Request::get('cid')]
+ )
+ );
+ } else {
+ $this->redirect(URLHelper::getURL($redirect_url, [
+ 'material_id' => $material->id,
+ 'url' => $this->url_for('oer/market/details/' . $material->id)
+ ]));
}
- if (isset($_SESSION['NEW_OER'])) {
- $this->template = $_SESSION['NEW_OER'];
- }
-
- $this->tagsearch = new SQLSearch("
- SELECT oer_tags.name, oer_tags.name
- FROM oer_tags
- WHERE name LIKE :input
- ORDER BY oer_tags.name
- ", _("Thema suchen"));
}
public function delete_action(OERMaterial $material)
{
- if ($material->id && !$material->isMine() && !$GLOBALS['perm']->have_perm('root')) {
- throw new AccessDeniedException();
- }
+ $material = $this->validateMaterial($material);
+
if (Request::isPost()) {
$material->pushDataToIndexServers('delete');
$material->delete();
@@ -211,13 +233,12 @@ class Oer_MymaterialController extends AuthenticatedController
public function statistics_action(OERMaterial $material)
{
+ $material = $this->validateMaterial($material);
+
PageLayout::setTitle(sprintf(
_('Zugriffszahlen für %s'),
$material->name
));
- if (!$GLOBALS['perm']->have_perm('root') && !$material->isMine()) {
- throw new AccessDeniedException();
- }
if (Request::get("export")) {
$this->counter = OERDownloadcounter::findBySQL(
'material_id = ? ORDER BY mkdate DESC',
@@ -299,4 +320,19 @@ class Oer_MymaterialController extends AuthenticatedController
Sidebar::Get()->addWidget($actions);
}
+
+ private function isTwilloEnabled(): bool
+ {
+ return Config::get()->getValue('OERCAMPUS_ENABLE_TWILLO')
+ && TwilloConnector::getTwilloUserID();
+ }
+
+ private function validateMaterial(OERMaterial $material): OERMaterial
+ {
+ if (!$material->isNew() && !$material->isMine() && !$GLOBALS['perm']->have_perm('root')) {
+ throw new AccessDeniedException();
+ }
+
+ return $material;
+ }
}
diff --git a/app/views/oer/mymaterial/edit.php b/app/views/oer/mymaterial/edit.php
deleted file mode 100644
index 1838b75..0000000
--- a/app/views/oer/mymaterial/edit.php
+++ /dev/null
@@ -1,344 +0,0 @@
-<?php
-/**
- * @var OERMaterial $material
- * @var Oer_MymaterialController $controller
- * @var SQLSearch $usersearch
- * @var SQLSearch $tagsearch,
- */
-?>
-
-
-<?= Studip\VueApp::create('OERMaterialEditor')
- ->withProps([
- 'store-url' => $controller->edit($material),
- 'material' => [
- ...$material->toArray(),
-
- 'filesize' => $material->getFilePath() && file_exists($material->getFilePath()) ? filesize($material->getFilePath()) : null,
- 'logoUrl' => $material->getLogoURL(),
- 'tags' => array_map(
- fn($tag) => $tag['name'],
- $material->getTopics()
- ),
- 'users' => $material->users->map(function (OERMaterialUser $user) {
- if ($user->external_contact) {
- return [
- 'id' => $user->oeruser->id,
- 'name' => $user->oeruser->name,
- 'avatar' => $user->oeruser->avatar_url,
- 'external' => true,
- ];
- }
-
- $u = User::find($user->user_id);
- return [
- 'id' => $u->user_id,
- 'avatar' => Avatar::getAvatar($u->id)->getURL(Avatar::SMALL),
- 'name' => $u ? $u->getFullName() : _('unbekannt'),
- 'external' => false,
- ];
- }),
- ],
- 'template' => $template ?? null,
- 'tag-search' => (string) $tagsearch,
- 'licenses-enabled' => !Config::get()->getValue('OER_DISABLE_LICENSE'),
- 'licenses' => License::findAndMapBySQL(
- function (License $license) {
- return [
- 'id' => $license->id,
- 'name' => $license->name,
- ];
- },
- '1 ORDER BY name'
- ),
- 'enable-twillo' => Config::get()->getValue('OERCAMPUS_ENABLE_TWILLO') && TwilloConnector::getTwilloUserID(),
- ]) ?>
-
-<form action="<?= $controller->edit($material->isNew() ? '' : $material) ?>"
- method="post"
- class="default"
- onsubmit="$(window).off('beforeunload')"
- data-secure
- enctype="multipart/form-data">
- <?= CSRFProtection::tokenTag() ?>
- <div class="oercampus_editmaterial">
- <fieldset>
- <legend><?= _('Grunddaten') ?></legend>
-
- <label>
- <span class="required"><?= _('Name') ?></span>
- <input type="text"
- name="data[name]"
- class="oername"
- required
- value="<?= htmlReady($material['name'] ?: $template['name'] ?? '') ?>"
- @keyup="editName"
- maxlength="64">
- </label>
-
-
- <div>
- <?= _('Vorschau') ?>
- </div>
-
- <div class="hgroup" @drop.prevent="dropImage">
- <label for="oer_logo_uploader">
- <article class="contentbox" :title="name">
- <header>
- <h1>
- <studip-icon shape="file"
- role="clickable"
- :size="20"
- class="text-bottom"></studip-icon>
- <div class="title">{{ name }}</div>
- </h1>
- </header>
- <div class="image"
- :style="'background-image: url(' + logo_url + ');' + (!customlogo ? ' background-size: 60% auto;': '')"></div>
- </article>
- </label>
-
- <div>
- <label class="file-upload logo_file"
- data-oldurl="<?= htmlReady(!empty($_SESSION['NEW_OER']['image_tmp_name']) ? URLHelper::getURL("dispatch.php/oer/mymaterial/show_tmp_image") : $material->getLogoURL()) ?>"
- data-customlogo="<?= !empty($_SESSION['NEW_OER']['image_tmp_name']) || $material['front_image_content_type'] ? 1 : 0 ?>">
- <?= _('Vorschau-Bilddatei (optional)') ?>
- <input type="file"
- name="image"
- id="oer_logo_uploader"
- accept="image/*"
- @change="editImage">
- </label>
-
- <? if ($material['front_image_content_type']) : ?>
- <label>
- <input type="checkbox" name="delete_front_image" value="1">
- <?= _('Vorschaubild löschen') ?>
- </label>
- <? endif ?>
- </div>
-
- </div>
-
-
-
-
- <? if (empty($_SESSION['NEW_OER']['tmp_name'])) : ?>
- <label class="file drag-and-drop"
- data-filename="<?= htmlReady($material['filename']) ?>"
- data-filesize="<?= htmlReady(!$material->isNew() && file_exists($material->getFilePath()) ? filesize($material->getFilePath()) : "") ?>"
- @drop.prevent="dropFile">
- <?= _('Datei (gerne auch eine ZIP-Datei) auswählen') ?>
- <input type="file" name="file" id="oer_file" @change="editFile">
- <div v-if="filename">
- <span>{{ filename }}</span>
- <span>{{ filesize }}</span>
- </div>
- </label>
- <? endif ?>
-
- <label>
- <?= _('Beschreibung') ?>
- <textarea
- name="data[description]"><?= htmlReady($material['description'] ?: $template['description'] ?? '') ?></textarea>
- </label>
-
- <label>
- <input type="hidden" name="data[draft]" value="0">
- <input type="checkbox" name="data[draft]" value="1"<?= $material['draft'] ? " checked" : "" ?>>
- <?= _('Entwurf (nicht veröffentlicht)') ?>
- </label>
-
- <label>
- <?= _('Kategorie') ?>
- <select name="data[category]">
- <? if ($material->isNew()) : ?>
- <option value="auto"><?= _('Automatisch erkennen') ?></option>
- <? endif ?>
- <option value="audio"<?= $material['category'] === "audio" ? " selected" : "" ?>>
- <?= _('Audio') ?>
- </option>
- <option value="video"<?= $material['category'] === "video" ? " selected" : "" ?>>
- <?= _('Video') ?>
- </option>
- <option value="presentation"<?= $material['category'] === "presentation" ? " selected" : "" ?>>
- <?= _('Folien') ?>
- </option>
- <option value="elearning"<?= $material['category'] === "elearning" ? " selected" : "" ?>>
- <?= _('Lernmodule') ?>
- </option>
- <option value=""<?= !$material['category'] && !$material->isNew() ? " selected" : "" ?>
- title="<?= _('Fehlt eine Kategorie? Kein Problem, arbeiten Sie stattdessen mit Schlagwörtern. Die sind viel flexibler.') ?>">
- <?= _('Ohne Kategorie') ?>
- </option>
- </select>
- </label>
-
- <label>
- <?= _('Vorschau-URL (optional)') ?>
- <input type="url" name="data[player_url]" pattern="^https?://.*"
- value="<?= htmlReady($material['player_url'] ?: $template['player_url'] ?? '') ?>">
- </label>
-
- <? if (!$material->isNew()) : ?>
- <div>
- <h4><?= _('Autoren') ?></h4>
- <ul class="clean autoren<?= count($material->users) > 1 ? " multiple" : "" ?>">
- <? foreach ($material->users as $materialuser) : ?>
- <li>
- <? if ($materialuser['external_contact']) : ?>
- <? $user = $materialuser['oeruser'] ?>
- <? $image = $user['avatar'] ?>
- <label>
- <? if (count($material->users) > 1) : ?>
- <input type="checkbox" name="remove_users[]"
- value="1_<?= htmlReady($user->getId()) ?>">
- <? endif ?>
- <div>
- <span class="avatar" style="background-image: url('<?= $image ?>');"></span>
- <span class="author_name">
- <?= htmlReady($user['name']) ?>
- </span>
- <? if (count($material->users) > 1) : ?>
- <?= Icon::create('trash')->asImg(['class' => "text-bottom", 'title' => _('Person als Autor entfernen.')]) ?>
- <? endif ?>
- </div>
- </label>
- <? else : ?>
- <? $user = User::find($materialuser['user_id']) ?>
- <? $image = Avatar::getAvatar($materialuser['user_id'])->getURL(Avatar::SMALL) ?>
- <label>
- <? if (count($material->users) > 1) : ?>
- <input type="checkbox" name="remove_users[]"
- value="0_<?= htmlReady($user->getId()) ?>">
- <? endif ?>
- <div>
- <span class="avatar" style="background-image: url('<?= $image ?>');"></span>
- <span class="author_name">
- <?= htmlReady($user ? $user->getFullName() : _('unbekannt')) ?>
- </span>
- <? if (count($material->users) > 1) : ?>
- <?= Icon::create('trash')->asImg(['class' => "text-bottom", 'title' => _('Person als Autor/Autorin entfernen.')]) ?>
- <? endif ?>
- </div>
- </label>
- <? endif ?>
- </li>
- <? endforeach ?>
- <li>
- <quicksearch name="new_user"
- searchtype="<?= htmlReady($usersearch) ?>"
- placeholder="<?= _('Person hinzufügen') ?>"></quicksearch>
- </li>
- </ul>
- </div>
- <? endif ?>
-
-
- <div class="oer_tags_container">
- <?= _('Themen (am besten mindestens 5)') ?>
- <?
- $tags = [];
- foreach ($material->getTopics() as $tag) {
- $tags[] = $tag['name'];
- }
- if (!empty($template['tags'])) {
- foreach ((array)$template['tags'] as $tag) {
- $tags[] = $tag;
- }
- }
- ?>
-
- <ul class="clean oer_tags" data-defaulttags="<?= htmlReady(json_encode($tags)) ?>">
- <li v-for="(tag, index) in displayTags" :key="index">
- #
- <quicksearch name="tags[]"
- searchtype="<?= htmlReady($tagsearch) ?>"
- v-model="tags[index]"
- :autocomplete="true"
- ></quicksearch>
- <a href="#"
- @click.prevent="removeTag(index)"
- title="<?= _('Thema aus der Liste streichen') ?>">
- <studip-icon shape="trash" role="clickable" :size="20" class="text-bottom"></studip-icon>
- </a>
-
- </li>
- </ul>
- <a href="#" @click.prevent="addTag">
- <studip-icon shape="add" role="clickable" :size="20" class="text-bottom"></studip-icon>
- <?= _('Thema hinzufügen') ?>
- </a>
- </div>
-
- <div class="level_filter" style="margin-top: 13px; max-width: 682px;">
- <?= _('Niveau') ?>
-
- <input type="hidden" id="difficulty_start" name="data[difficulty_start]"
- value="<?= htmlReady($material['difficulty_start']) ?>">
- <input type="hidden" id="difficulty_end" name="data[difficulty_end]"
- value="<?= htmlReady($material['difficulty_end']) ?>">
-
- <div class="level_labels">
- <div><?= _('Leicht') ?></div>
- <div><?= _('Schwer') ?></div>
- </div>
- <div style="display: flex; justify-content: space-between;">
- <? for ($i = 1; $i <= 12; $i++) : ?>
- <div><?= ($i < 10 ? "&nbsp;" : "") . $i ?></div>
- <? endfor ?>
- </div>
- <div id="difficulty_slider_edit" style="margin-left: 5px; margin-right: 9px;"></div>
- </div>
-
- <? if (!empty($template['module_id'])) : ?>
- <input type="hidden"
- name="module_id"
- value="<?= htmlReady($template['module_id']) ?>">
- <? endif ?>
-
- <? if (Config::get()->OERCAMPUS_ENABLE_TWILLO && TwilloConnector::getTwilloUserID()) : ?>
- <input type="hidden" name="publish_on_twillo" value="0">
- <label style="margin-top: 20px;">
- <input type="checkbox"
- name="publish_on_twillo"
- value="1"<?= $material['published_id_on_twillo'] ? " checked" : "" ?>>
- <?= _('Auf twillo.de veröffentlichen') ?>
- </label>
- <? endif ?>
- </fieldset>
-
- <? if (!Config::get()->OER_DISABLE_LICENSE) : ?>
- <? $license = $material->isNew()
- ? License::findDefault()
- : $material->license;
- ?>
- <fieldset class="oer_license_selector">
- <legend><?= _('Lizenz') ?></legend>
- <?=
- _('Ich erkläre mich bereit, dass meine Lernmaterialien unter der angegebenen Lizenz an alle Nutzenden freigegeben werden. Ich bestätige zudem, dass ich das Recht habe, diese Dateien frei zu veröffentlichen, weil entweder ich selbst sie angefertigt habe, oder sie von anderen Quellen mit kompatibler Lizenz stammen.')
- ?>
-
- <div>
- <select class="licenses_selector" name="data[license_identifier]">
- <? foreach (License::findBySQL("1 ORDER BY name ASC") as $l) : ?>
- <option value="<?= htmlReady($l->id) ?>" <?= $l->id === $license->id ? " selected" : "" ?>>
- <?= htmlReady($l['name']) ?>
- </option>
- <? endforeach ?>
- </select>
- </div>
- </fieldset>
-
- <? endif ?>
- <? if (!empty($template['redirect_url'])) : ?>
- <input type="hidden"
- name="redirect_url"
- value="<?= htmlReady($template['redirect_url']) ?>">
- <? endif ?>
- </div>
-
- <div data-dialog-button>
- <?= \Studip\Button::create($material->isNew() ? _('Hochladen') : _('Speichern'), "save") ?>
- </div>
-</form>
diff --git a/lib/classes/TwilloConnector.php b/lib/classes/TwilloConnector.php
index 40ceff8..e7dd8b4 100644
--- a/lib/classes/TwilloConnector.php
+++ b/lib/classes/TwilloConnector.php
@@ -49,7 +49,7 @@ class TwilloConnector
*/
public static function uploadMaterial(OERMaterial $material, $user_id = null)
{
- $user_id || $user_id = User::findCurrent()->id;
+ $user_id = $user_id ?? User::findCurrent()->id;
$base = new \EduSharingApiClient\EduSharingHelperBase(
self::$twillo_base_url,
file_get_contents($GLOBALS['STUDIP_BASE_PATH']."/config/twillo-private.key"),
diff --git a/lib/models/OERMaterial.php b/lib/models/OERMaterial.php
index 224c860..2048457 100644
--- a/lib/models/OERMaterial.php
+++ b/lib/models/OERMaterial.php
@@ -257,6 +257,9 @@ class OERMaterial extends SimpleORMap
public function setTopics($tags)
{
+ $tags = array_map('trim', $tags);
+ $tags = array_filter($tags);
+
$statement = DBManager::get()->prepare("
DELETE FROM oer_tags_material
WHERE material_id = :material_id
diff --git a/resources/assets/javascripts/bootstrap/dialog.js b/resources/assets/javascripts/bootstrap/dialog.js
index 58d01fd..58857cb 100644
--- a/resources/assets/javascripts/bootstrap/dialog.js
+++ b/resources/assets/javascripts/bootstrap/dialog.js
@@ -2,7 +2,14 @@ STUDIP.domReady(function () {
STUDIP.Dialog.initialize();
});
-$(document).on('click', '[data-vue-app] [data-dialog-button] .cancel.button', () => {
- STUDIP.Dialog.close();
- return false;
-});
+document.addEventListener(
+ 'click',
+ (event) => {
+ if (event.target.matches('.studip-dialog [data-vue-app] [data-dialog-button] .cancel.button')) {
+ STUDIP.Dialog.close();
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ },
+ true
+);
diff --git a/resources/assets/javascripts/bootstrap/oer.js b/resources/assets/javascripts/bootstrap/oer.js
index 3adecbc..ed472b5 100644
--- a/resources/assets/javascripts/bootstrap/oer.js
+++ b/resources/assets/javascripts/bootstrap/oer.js
@@ -47,95 +47,3 @@ STUDIP.domReady(() => {
});
});
-
-STUDIP.ready(() => {
- if ($('.oercampus_editmaterial').length) {
-
- STUDIP.Vue.load().then(({createApp}) => {
- STUDIP.OER.EditApp = createApp({
- el: '.oercampus_editmaterial',
- data() {
- return {
- name: $('.oercampus_editmaterial input.oername').val(),
- logo_url: $('.oercampus_editmaterial .logo_file').data("oldurl"),
- customlogo: $('.oercampus_editmaterial .logo_file').data("customlogo"),
- filename: $('.oercampus_editmaterial .file.drag-and-drop').data("filename"),
- filesize: $('.oercampus_editmaterial .file.drag-and-drop').data("filesize"),
- tags: $('.oercampus_editmaterial .oer_tags').data("defaulttags"),
- minimumTags: 5
- };
- },
- mounted: function () {
- jQuery("#difficulty_slider_edit").slider({
- range: true,
- min: 1,
- max: 12,
- values: [jQuery("#difficulty_start").val(), jQuery("#difficulty_end").val()],
- change: function (event, ui) {
- jQuery("#difficulty_start").val(ui.values[0]);
- jQuery("#difficulty_end").val(ui.values[1]);
- }
- });
- jQuery('.oercampus_editmaterial').find(':focusable').first().focus();
- },
- methods: {
- editName: function () {
- this.name = $('.oername').val();
- },
- editImage: function (event) {
- let reader = new FileReader();
- let vue = this;
- reader.addEventListener("load", function () {
- vue.logo_url = reader.result;
- vue.customlogo = true;
- }, false);
- reader.readAsDataURL(
- event.target.files.length > 0
- ? event.target.files[0]
- : event.dataTransfer.files[0]
- );
- },
- dropImage: function (event) {
- window.document.getElementById("oer_logo_uploader").files = event.dataTransfer.files;
- this.editImage(event);
- },
- editFile: function (event) {
- this.filename = event.target.files[0].name;
- this.filesize = event.target.files[0].size;
- if (!this.name) {
- this.name = this.filename;
- $('.oername').val(this.name);
- }
- },
- dropFile: function (event) {
- window.document.getElementById("oer_file").files = event.dataTransfer.files;
- this.editFile(event);
- },
- addTag: function () {
- if (this.minimumTags < this.tags.length) {
- this.minimumTags = this.tags.length + 1;
- } else {
- this.minimumTags++;
- }
- },
- removeTag: function (i) {
- this.$delete(this.tags, i);
- if ((this.minimumTags > this.tags.length) && (this.minimumTags > 5)) {
- this.minimumTags--;
- }
- }
- },
- computed: {
- displayTags () {
- const result = this.tags.concat([]);
- while (result.length < this.minimumTags) {
- result.push('');
- }
- return result;
- }
- },
- components: { Quicksearch }
- });
- });
- }
-});
diff --git a/resources/assets/stylesheets/scss/quicksearch.scss b/resources/assets/stylesheets/scss/quicksearch.scss
index ef6c372..6687450 100644
--- a/resources/assets/stylesheets/scss/quicksearch.scss
+++ b/resources/assets/stylesheets/scss/quicksearch.scss
@@ -89,47 +89,3 @@ div.quicksearch_frame {
background-position: center center;
}
}
-
-.quicksearch_container {
- display: inline-flex;
- flex-direction: row-reverse;
- width: 100%;
-
- .dropdownmenu {
- max-width: 0;
- max-height: 0;
- overflow: visible;
- position: relative;
- top: 31px;
- z-index: 99999;
-
- .autocomplete__results {
- list-style-type: none;
- padding: 1px;
- border: 1px solid var(--light-gray-color-40);
- background-color: var(--white);
- max-height: 275px;
- width: 600px;
- overflow-x: auto;
- overflow-y: hidden;
-
- > li {
- padding: 5px;
- cursor: pointer;
- display: flex;
- align-items: flex-start;
-
- &:hover, &.autocomplete__result--selected {
- background-color: var(--base-color);
- color: var(--white);
- }
-
- img {
- max-width: 40px;
- max-height: 40px;
- margin-right: 5px;
- }
- }
- }
- }
-}
diff --git a/resources/vue/components/OERMaterialEditor.vue b/resources/vue/components/OERMaterialEditor.vue
index 88b9232..1ad1f86 100644
--- a/resources/vue/components/OERMaterialEditor.vue
+++ b/resources/vue/components/OERMaterialEditor.vue
@@ -6,7 +6,7 @@
data-secure
enctype="multipart/form-data"
>
- <input type="hidden" :name="csrf.name" :value="csrf.token">
+ <input type="hidden" :name="csrf.name" :value="csrf.value">
<input v-if="template?.redirect_url"
type="hidden"
name="redirect_url"
@@ -136,18 +136,18 @@
<div class="oer_tags_container">
<h4>{{ $gettext('Themen (am besten mindestens 5)') }}</h4>
<ul class="clean oer_tags">
- <li v-for="(tag, index) in displayTags" :key="index">
+ <li v-for="(tag, index) in tags" :key="index">
#
<quicksearch name="tags[]"
:searchtype="tagSearch"
v-model="tags[index]"
:autocomplete="true"
+ v-focus-on-create
></quicksearch>
- <a href="#"
- @click.prevent="removeTag(index)"
+ <button class="as-link" @click.prevent="removeTag(index)"
:title="$gettext('Thema aus der Liste streichen')">
<studip-icon shape="trash" :size="20" class="text-bottom"></studip-icon>
- </a>
+ </button>
</li>
</ul>
@@ -196,9 +196,12 @@
</fieldset>
<footer data-dialog-button>
- <button type="button" name="save">
- {{ material.id ? $gettext('Hochladen') : $gettext('Speichern') }}
+ <button type="submit" name="save" class="button">
+ {{ material.id ? $gettext('Speichern') : $gettext('Hochladen') }}
</button>
+ <a :href="URLHelper.getURL('dispatch.php/oer/mymaterial')" class="button cancel">
+ {{ $gettext('Abbrechen') }}
+ </a>
</footer>
</form>
</template>
@@ -206,10 +209,25 @@
import Quicksearch from "./Quicksearch.vue";
import StudipIcon from "./StudipIcon.vue";
import StudipLevelSlider from "./StudipLevelSlider.vue";
+import Vue from "vue";
+
+let mounted = false;
export default {
name: "OERMaterialEditor",
components: {StudipLevelSlider, StudipIcon, Quicksearch },
+ directives: {
+ ['focus-on-create']: {
+ bind(el) {
+ if (mounted) {
+ Vue.nextTick(() => el.querySelector('input')?.focus());
+ }
+ }
+ }
+ },
+ mounted () {
+ mounted = true;
+ },
props: {
material: {
type: Object,
@@ -287,11 +305,6 @@ export default {
return this.template?.image_tmp_name?.length > 0
|| this.material.front_image_content_type?.length > 0;
},
- displayTags () {
- return this.material.tags.concat(
- Array(this.minimumTags).fill('')
- ).slice(0, this.minimumTags);
- },
logoUrl() {
return this.template?.image_tmp_name ? STUDIP.URLHelper.getURL('dispatch.php/oer/mymaterial/show_tmp_image') : this.material.logoUrl;
},
@@ -299,9 +312,6 @@ export default {
return STUDIP.URLHelper;
}
},
- mounted() {
- jQuery('.oercampus_editmaterial').find(':focusable').first().focus();
- },
methods: {
editImage(event) {
let reader = new FileReader();
@@ -336,12 +346,13 @@ export default {
this.tags.push('');
},
removeTag(i) {
- this.$delete(this.tags, i);
- if (
- this.minimumTags > this.tags.length
- && this.minimumTags > 5
- ) {
- this.minimumTags--;
+ this.tags = this.tags.filter((element, index) => index !== i);
+ }
+ },
+ watch: {
+ tags(current) {
+ if (current.length === 0) {
+ this.tags.push('');
}
}
}
@@ -383,14 +394,5 @@ export default {
.oer_tags_container {
margin-top: 10px;
}
-
- .level_labels {
- display: flex;
- justify-content: space-between;
- font-size: 0.8em;
- color: var(--black);
- margin-top: 20px;
- }
-
}
</style>
diff --git a/resources/vue/components/Quicksearch.vue b/resources/vue/components/Quicksearch.vue
index 0f37ae0..cdfdeb0 100644
--- a/resources/vue/components/Quicksearch.vue
+++ b/resources/vue/components/Quicksearch.vue
@@ -181,3 +181,49 @@ export default {
}
}
</script>
+<style lang="scss">
+.quicksearch_container {
+ align-items: center;
+ display: inline-flex;
+ flex-direction: row-reverse;
+ width: 100%;
+
+ .dropdownmenu {
+ max-width: 0;
+ max-height: 0;
+ overflow: visible;
+ position: relative;
+ top: 31px;
+ z-index: 99999;
+
+ .autocomplete__results {
+ list-style-type: none;
+ padding: 1px;
+ border: 1px solid var(--light-gray-color-40);
+ background-color: var(--white);
+ max-height: 275px;
+ width: 600px;
+ overflow-x: auto;
+ overflow-y: hidden;
+
+ > li {
+ padding: 5px;
+ cursor: pointer;
+ display: flex;
+ align-items: flex-start;
+
+ &:hover, &.autocomplete__result--selected {
+ background-color: var(--base-color);
+ color: var(--white);
+ }
+
+ img {
+ max-width: 40px;
+ max-height: 40px;
+ margin-right: 5px;
+ }
+ }
+ }
+ }
+}
+</style>
diff --git a/resources/vue/components/StudipLevelSlider.vue b/resources/vue/components/StudipLevelSlider.vue
index a1b91e0..ee362e6 100644
--- a/resources/vue/components/StudipLevelSlider.vue
+++ b/resources/vue/components/StudipLevelSlider.vue
@@ -1,11 +1,16 @@
<template>
<div class="level-slider" :style="{width: this.width}">
<div class="level-labels">
- <div>{{ lowerLabel }}</div>
- <div>{{ upperLabel }}</div>
+ <div class="level-label">{{ lowerLabel }}</div>
+ <div class="level-label">{{ upperLabel }}</div>
</div>
<div class="level-numbers">
- <div v-for="i in this.maxValue" :key="`level-${i}`">{{ i }}</div>
+ <div v-for="i in this.maxValue"
+ :key="`level-${i}`"
+ class="level-number"
+ >
+ {{ i }}
+ </div>
</div>
<div ref="slider" class="slider-element"></div>
</div>
@@ -48,8 +53,6 @@ export default {
max: this.maxValue,
values: [this.lowerValue, this.upperValue],
change: (event, ui) => {
- console.log('Updating', ui.values[0], ui.values[1]);
-
this.$emit('update:lowerValue', ui.values[0]);
this.$emit('update:upperValue', ui.values[1]);
}
@@ -71,10 +74,14 @@ export default {
display: flex;
justify-content: space-between;
}
+ .level-number {
+ flex: 0 2ex;
+ text-align: right;
+ }
.slider-element {
- margin-left: 5px;
- margin-right: 9px;
margin-top: 5px;
+ margin-left: 0.5ex;
+ margin-right: 0.5ex;
}
}
</style>