aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2023-12-04 15:00:58 +0000
committerJan-Hendrik Willms <tleilax+studip@gmail.com>2023-12-04 15:00:58 +0000
commit9ae449f94d2c3c23a272a36c486438430ca454b5 (patch)
tree7f1bf367966d809a250e8a34ff02a5119c6a76d1
parentd375c5134f04c995378fa7af6e853ef7545614a4 (diff)
use webp for avatars and convert current avatars, fixes #3183
Closes #3183 Merge request studip/studip!2326
-rw-r--r--.gitignore6
-rw-r--r--app/controllers/avatar.php25
-rw-r--r--app/controllers/privacy.php2
-rw-r--r--app/routes/User.php4
-rw-r--r--app/views/admin/licenses/edit.php4
-rw-r--r--app/views/avatar/update.php18
-rw-r--r--db/migrations/5.5.10_convert_avatars_to_webp.php116
-rw-r--r--lib/bootstrap.php4
-rw-r--r--lib/classes/Assets.class.php99
-rw-r--r--lib/classes/Avatar.class.php345
-rw-r--r--lib/classes/CourseAvatar.class.php70
-rw-r--r--lib/classes/InstituteAvatar.class.php69
-rw-r--r--lib/classes/JsonApi/Schemas/User.php2
-rw-r--r--lib/classes/LicenseAvatar.php62
-rw-r--r--lib/classes/StudygroupAvatar.class.php39
-rw-r--r--lib/models/User.class.php5
-rw-r--r--lib/phplib/Seminar_Auth.class.php1
-rw-r--r--public/assets/images/avatars/course/nobody_medium.webpbin0 -> 3354 bytes
-rw-r--r--public/assets/images/avatars/course/nobody_normal.webpbin0 -> 8726 bytes
-rw-r--r--public/assets/images/avatars/course/nobody_small.webpbin0 -> 892 bytes
-rw-r--r--public/assets/images/avatars/course/studygroup_medium.webpbin0 -> 5326 bytes
-rw-r--r--public/assets/images/avatars/course/studygroup_normal.webpbin0 -> 13310 bytes
-rw-r--r--public/assets/images/avatars/course/studygroup_small.webpbin0 -> 1232 bytes
-rw-r--r--public/assets/images/avatars/institute/nobody_medium.webpbin0 -> 1932 bytes
-rw-r--r--public/assets/images/avatars/institute/nobody_normal.webpbin0 -> 4364 bytes
-rw-r--r--public/assets/images/avatars/institute/nobody_small.webpbin0 -> 734 bytes
-rw-r--r--public/assets/images/avatars/user/nobody_medium.webpbin0 -> 2750 bytes
-rw-r--r--public/assets/images/avatars/user/nobody_normal.webpbin0 -> 8196 bytes
-rw-r--r--public/assets/images/avatars/user/nobody_small.webpbin0 -> 752 bytes
-rw-r--r--public/pictures/course/.gitkeep0
-rw-r--r--public/pictures/course/nobody_medium.pngbin2255 -> 0 bytes
-rw-r--r--public/pictures/course/nobody_medium@2x.pngbin3950 -> 0 bytes
-rw-r--r--public/pictures/course/nobody_normal.pngbin4666 -> 0 bytes
-rw-r--r--public/pictures/course/nobody_normal@2x.pngbin8408 -> 0 bytes
-rw-r--r--public/pictures/course/nobody_small.pngbin729 -> 0 bytes
-rw-r--r--public/pictures/course/nobody_small@2x.pngbin1325 -> 0 bytes
-rw-r--r--public/pictures/course/studygroup_medium.pngbin2734 -> 0 bytes
-rw-r--r--public/pictures/course/studygroup_medium2x.pngbin4761 -> 0 bytes
-rw-r--r--public/pictures/course/studygroup_normal.pngbin5733 -> 0 bytes
-rw-r--r--public/pictures/course/studygroup_normal@2x.pngbin10774 -> 0 bytes
-rw-r--r--public/pictures/course/studygroup_small.pngbin809 -> 0 bytes
-rw-r--r--public/pictures/course/studygroup_small@2x.pngbin1513 -> 0 bytes
-rw-r--r--public/pictures/institute/.gitkeep0
-rw-r--r--public/pictures/institute/nobody_medium.pngbin1568 -> 0 bytes
-rw-r--r--public/pictures/institute/nobody_medium@2x.pngbin2020 -> 0 bytes
-rw-r--r--public/pictures/institute/nobody_normal.pngbin2254 -> 0 bytes
-rw-r--r--public/pictures/institute/nobody_normal@2x.pngbin3767 -> 0 bytes
-rw-r--r--public/pictures/institute/nobody_small.pngbin968 -> 0 bytes
-rw-r--r--public/pictures/institute/nobody_small@2x.pngbin568 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-1.0_medium.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-1.0_medium.webpbin0 -> 2928 bytes
-rw-r--r--public/pictures/licenses/CC-BY-1.0_medium@2x.pngbin4098 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-1.0_normal.pngbin6548 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-1.0_normal.webpbin0 -> 6982 bytes
-rw-r--r--public/pictures/licenses/CC-BY-1.0_normal@2x.pngbin15596 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-1.0_small.pngbin1353 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-1.0_small.webpbin0 -> 1406 bytes
-rw-r--r--public/pictures/licenses/CC-BY-1.0_small@2x.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.0_medium.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.0_medium.webpbin0 -> 2928 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.0_medium@2x.pngbin4098 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.0_normal.pngbin6550 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.0_normal.webpbin0 -> 6982 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.0_normal@2x.pngbin15596 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.0_small.pngbin1352 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.0_small.webpbin0 -> 1406 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.0_small@2x.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.5_medium.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.5_medium.webpbin0 -> 2928 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.5_medium@2x.pngbin4098 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.5_normal.pngbin6548 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.5_normal.webpbin0 -> 6982 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.5_normal@2x.pngbin15596 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.5_small.pngbin1352 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.5_small.webpbin0 -> 1406 bytes
-rw-r--r--public/pictures/licenses/CC-BY-2.5_small@2x.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-3.0_medium.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-3.0_medium.webpbin0 -> 2928 bytes
-rw-r--r--public/pictures/licenses/CC-BY-3.0_medium@2x.pngbin4098 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-3.0_normal.pngbin6548 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-3.0_normal.webpbin0 -> 6982 bytes
-rw-r--r--public/pictures/licenses/CC-BY-3.0_normal@2x.pngbin15596 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-3.0_small.pngbin1352 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-3.0_small.webpbin0 -> 1406 bytes
-rw-r--r--public/pictures/licenses/CC-BY-3.0_small@2x.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-4.0_medium.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-4.0_medium.webpbin0 -> 2928 bytes
-rw-r--r--public/pictures/licenses/CC-BY-4.0_medium@2x.pngbin4098 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-4.0_normal.pngbin6550 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-4.0_normal.webpbin0 -> 6982 bytes
-rw-r--r--public/pictures/licenses/CC-BY-4.0_normal@2x.pngbin15596 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-4.0_small.pngbin1351 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-4.0_small.webpbin0 -> 1406 bytes
-rw-r--r--public/pictures/licenses/CC-BY-4.0_small@2x.pngbin2223 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-1.0_medium.pngbin2650 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-1.0_medium.webpbin0 -> 4250 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-1.0_medium@2x.pngbin5033 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-1.0_normal.pngbin6178 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-1.0_normal.webpbin0 -> 10244 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-1.0_normal@2x.pngbin13313 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-1.0_small.pngbin1526 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-1.0_small.webpbin0 -> 1984 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-1.0_small@2x.pngbin2650 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.0_medium.pngbin3504 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.0_medium.webpbin0 -> 5364 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.0_medium@2x.pngbin8657 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.0_normal.pngbin11880 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.0_normal.webpbin0 -> 13508 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.0_normal@2x.pngbin8646 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.0_small.pngbin1760 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.0_small.webpbin0 -> 2468 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.0_small@2x.pngbin3506 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.5_medium.pngbin2650 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.5_medium.webpbin0 -> 4250 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.5_medium@2x.pngbin5033 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.5_normal.pngbin6178 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.5_normal.webpbin0 -> 10244 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.5_normal@2x.pngbin13313 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.5_small.pngbin1527 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.5_small.webpbin0 -> 1984 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-2.5_small@2x.pngbin2650 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-3.0_medium.pngbin2650 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-3.0_medium.webpbin0 -> 4250 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-3.0_medium@2x.pngbin5033 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-3.0_normal.pngbin6178 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-3.0_normal.webpbin0 -> 10244 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-3.0_normal@2x.pngbin13313 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-3.0_small.pngbin1525 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-3.0_small.webpbin0 -> 1984 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-3.0_small@2x.pngbin2650 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-4.0_medium.pngbin2650 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-4.0_medium.webpbin0 -> 4250 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-4.0_medium@2x.pngbin5033 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-4.0_normal.pngbin6178 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-4.0_normal.webpbin0 -> 10244 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-4.0_normal@2x.pngbin13313 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-4.0_small.pngbin1526 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-4.0_small.webpbin0 -> 1984 bytes
-rw-r--r--public/pictures/licenses/CC-BY-SA-4.0_small@2x.pngbin2650 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC0-1.0_medium.pngbin2214 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC0-1.0_medium.webpbin0 -> 3664 bytes
-rw-r--r--public/pictures/licenses/CC0-1.0_medium@2x.pngbin4287 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC0-1.0_normal.pngbin5598 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC0-1.0_normal.webpbin0 -> 8372 bytes
-rw-r--r--public/pictures/licenses/CC0-1.0_normal@2x.pngbin13843 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC0-1.0_small.pngbin1243 -> 0 bytes
-rw-r--r--public/pictures/licenses/CC0-1.0_small.webpbin0 -> 1828 bytes
-rw-r--r--public/pictures/licenses/CC0-1.0_small@2x.pngbin2213 -> 0 bytes
-rw-r--r--public/pictures/user/.gitkeep0
-rw-r--r--public/pictures/user/nobody_medium.pngbin4833 -> 0 bytes
-rw-r--r--public/pictures/user/nobody_medium@2x.pngbin10715 -> 0 bytes
-rw-r--r--public/pictures/user/nobody_normal.pngbin13017 -> 0 bytes
-rw-r--r--public/pictures/user/nobody_normal@2x.pngbin22292 -> 0 bytes
-rw-r--r--public/pictures/user/nobody_small.pngbin920 -> 0 bytes
-rw-r--r--public/pictures/user/nobody_small@2x.pngbin2198 -> 0 bytes
-rw-r--r--templates/loginform.php2
-rw-r--r--templates/sidebar/sidebar.php2
-rw-r--r--tests/unit/lib/classes/AvatarClassTest.php176
158 files changed, 545 insertions, 506 deletions
diff --git a/.gitignore b/.gitignore
index 97ab31d..81dec99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,10 +31,10 @@ public/pictures/banner/*.gif
public/pictures/banner/*.jpeg
public/pictures/banner/*.jpg
public/pictures/banner/*.png
-public/pictures/course/[0-9a-f]*.png
-public/pictures/institute/[0-9a-f]*.png
public/pictures/stock-images/*
-public/pictures/user/[0-9a-f]*.png
+public/pictures/course/*/*.webp
+public/pictures/institute/*/*.webp
+public/pictures/user/*/*.webp
public/plugins_packages/*
tests/_log
diff --git a/app/controllers/avatar.php b/app/controllers/avatar.php
index 992c06e..c549cf0 100644
--- a/app/controllers/avatar.php
+++ b/app/controllers/avatar.php
@@ -29,13 +29,13 @@ class AvatarController extends AuthenticatedController
PageLayout::setTitle(_('Profilbild ändern'));
$has_perm = $GLOBALS['perm']->have_profile_perm('user', $id);
- $class = 'Avatar';
+ $class = Avatar::class;
$this->cancel_link = $this->url_for('profile', ['username' => User::find($id)->username]);
} else if ($type == 'institute') {
PageLayout::setTitle(Context::getHeaderLine() . ' - ' . _('Einrichtungsbild ändern'));
$has_perm = $GLOBALS['perm']->have_studip_perm('admin', $id);
- $class = 'InstituteAvatar';
+ $class = InstituteAvatar::class;
$this->cancel_link = $this->url_for('institute/basicdata/index', ['cid' => $id]);
} else {
PageLayout::setTitle(Context::getHeaderLine() . ' - ' . _('Veranstaltungsbild ändern'));
@@ -44,10 +44,10 @@ class AvatarController extends AuthenticatedController
$sem = Seminar::getInstance($id);
$studygroup_mode = $sem->getSemClass()->offsetget('studygroup_mode');
if ($studygroup_mode) {
- $class = 'StudygroupAvatar';
+ $class = StudygroupAvatar::class;
$this->cancel_link = $this->url_for('course/studygroup/edit?cid=' . $id);
} else {
- $class = 'CourseAvatar';
+ $class = CourseAvatar::class;
$this->cancel_link = $this->url_for('course/management?cid=' . $id);
}
}
@@ -56,20 +56,17 @@ class AvatarController extends AuthenticatedController
throw new AccessDeniedException(_('Sie haben keine Berechtigung, das Bild zu ändern.'));
}
- if ($type == 'user') {
+ if ($type === 'user') {
Navigation::activateItem('/profile/index');
- } else if ($type == 'institute') {
+ } else if ($type === 'institute') {
Navigation::activateItem('/admin/institute/details');
} else {
Navigation::activateItem('/course/admin/avatar');
}
- $this->customized = false;
$avatar = $class::getAvatar($id);
$this->avatar = $avatar->getURL($class::NORMAL);
- if ($avatar->is_customized()) {
- $this->customized = true;
- }
+ $this->customized = $avatar->is_customized();
$this->type = $type;
$this->id = $id;
@@ -88,21 +85,21 @@ class AvatarController extends AuthenticatedController
// Check for permission to save a new avatar.
if ($type == 'user') {
$has_perm = $GLOBALS['perm']->have_profile_perm('user', $id);
- $class = 'Avatar';
+ $class = Avatar::class;
$redirect = 'profile?username=' . User::find($id)->username;
} else if ($type == 'institute') {
$has_perm = $GLOBALS['perm']->have_studip_perm('admin', $id);
- $class = 'InstituteAvatar';
+ $class = InstituteAvatar::class;
$redirect = 'institute/basicdata/index';
} else {
$has_perm = $GLOBALS['perm']->have_studip_perm('tutor', $id);
$sem = Seminar::getInstance($id);
$studygroup_mode = $sem->getSemClass()->offsetget('studygroup_mode');
if ($studygroup_mode) {
- $class = 'StudygroupAvatar';
+ $class = StudygroupAvatar::class;
$redirect = 'course/studygroup/edit/?cid=' . $id;
} else {
- $class = 'CourseAvatar';
+ $class = CourseAvatar::class;
$redirect = 'course/management';
}
}
diff --git a/app/controllers/privacy.php b/app/controllers/privacy.php
index ee1f157..ba8e6f1 100644
--- a/app/controllers/privacy.php
+++ b/app/controllers/privacy.php
@@ -298,7 +298,7 @@ class PrivacyController extends AuthenticatedController
$avatar = Avatar::getAvatar($user_id);
if ($avatar->is_customized()) {
- $zip->addFile($avatar->getCustomAvatarPath('normal'), $user_id . '.png');
+ $zip->addFile($avatar->getCustomAvatarPath('normal'), $user_id . '.webp');
}
foreach (FileRef::findByUser_id($user_id) as $fileref) {
diff --git a/app/routes/User.php b/app/routes/User.php
index 16de204..d3cce26 100644
--- a/app/routes/User.php
+++ b/app/routes/User.php
@@ -26,7 +26,7 @@ class User extends \RESTAPI\RouteMap
'avatar_small' => $avatar->getURL(\Avatar::SMALL),
'avatar_medium' => $avatar->getURL(\Avatar::MEDIUM),
'avatar_normal' => $avatar->getURL(\Avatar::NORMAL),
- 'avatar_original' => $avatar->getURL(\Avatar::ORIGINAL)
+ 'avatar_original' => $avatar->getURL(\Avatar::NORMAL)
];
}
@@ -118,7 +118,7 @@ class User extends \RESTAPI\RouteMap
'avatar_small' => $avatar->getURL(\Avatar::SMALL),
'avatar_medium' => $avatar->getURL(\Avatar::MEDIUM),
'avatar_normal' => $avatar->getURL(\Avatar::NORMAL),
- 'avatar_original' => $avatar->getURL(\Avatar::ORIGINAL),
+ 'avatar_original' => $avatar->getURL(\Avatar::NORMAL),
'phone' => $get_field('privatnr', 'private_phone'),
'homepage' => $get_field('Home', 'homepage'),
'privadr' => strip_tags($get_field('privadr', 'privadr')),
diff --git a/app/views/admin/licenses/edit.php b/app/views/admin/licenses/edit.php
index ce722c2..63ac391 100644
--- a/app/views/admin/licenses/edit.php
+++ b/app/views/admin/licenses/edit.php
@@ -36,8 +36,8 @@
</label>
<label class="file-upload">
- <?= _('Bild hochladen (PNG, JPG, GIF)') ?>
- <input type="file" name="avatar" accept=".jpg,.png,.jpeg,.gif">
+ <?= _('Bild hochladen (PNG, JPG, GIF, WebP)') ?>
+ <input type="file" name="avatar" accept=".jpg,.png,.jpeg,.gif,.webp">
</label>
<label>
diff --git a/app/views/avatar/update.php b/app/views/avatar/update.php
index 777c0c7..f995702 100644
--- a/app/views/avatar/update.php
+++ b/app/views/avatar/update.php
@@ -1,5 +1,15 @@
+<?php
+/**
+ * @var AvatarController $controller
+ * @var string $type
+ * @var string $id
+ * @var string $avatar
+ * @var bool $customized
+ * @var string $cancel_link
+ */
+?>
<form class="default settings-avatar" enctype="multipart/form-data"
- action="<?= $controller->url_for('avatar/upload', $type, $id) ?>" method="post">
+ action="<?= $controller->link_for('avatar/upload', $type, $id) ?>" method="post">
<fieldset>
<legend>
<?= $type == 'user' ? _('Profilbild bearbeiten und zuschneiden') :
@@ -8,20 +18,20 @@
</legend>
<div class="form-group">
<div id="avatar-preview">
- <img class="avatar-normal" id="new-avatar" src="<?= $avatar ?>"
+ <img class="avatar-normal" id="new-avatar" src="<?= htmlReady($avatar) ?>"
data-message-too-small="<?= _('Das Bild ist kleiner als 250 x 250 Pixel. Wollen Sie wirklich fortfahren?') ?>">
</div>
<label class="file-upload">
<?= _('Wählen Sie ein Bild von Ihrer Festplatte aus.') ?>
- <input type="file" id="avatar-upload" accept="image/gif,image/png,image/jpeg"
+ <input type="file" id="avatar-upload" accept="image/gif,image/png,image/jpeg,image/webp"
capture="camera"
data-max-size="<?= Avatar::MAX_FILE_SIZE ?>"
data-message-too-large="<?= _('Die hochgeladene Datei ist zu groß. Bitte wählen Sie ein anderes Bild.') ?>">
<p class="form-text">
<?= sprintf(
- _('Die Bilddatei darf max. %s groß sein, es sind nur Dateien mit den Endungen .jpg, .png oder .gif erlaubt!'),
+ _('Die Bilddatei darf max. %s groß sein, es sind nur Dateien mit den Endungen .jpg, .png, .gif und .webp erlaubt!'),
relsize(Avatar::MAX_FILE_SIZE)
) ?>
</p>
diff --git a/db/migrations/5.5.10_convert_avatars_to_webp.php b/db/migrations/5.5.10_convert_avatars_to_webp.php
new file mode 100644
index 0000000..cf120a0
--- /dev/null
+++ b/db/migrations/5.5.10_convert_avatars_to_webp.php
@@ -0,0 +1,116 @@
+<?php
+final class ConvertAvatarsToWebp extends Migration
+{
+ const SIZES = [
+ 'small' => [
+ 'default' => [25 * 2, 25 * 2],
+ 'license' => [60 * 2, 20 * 2],
+ ],
+ 'medium' => [
+ 'default' => [100 * 2, 100 * 2],
+ 'license' => [120 * 2, 40 * 2],
+ ],
+ 'normal' => [
+ 'default' => [250 * 2, 250 * 2],
+ 'license' => [300 * 2, 100 * 2],
+ ],
+ ];
+
+ protected function up()
+ {
+ foreach (['user', 'course', 'institute', 'licenses'] as $type) {
+ $source_directory = $GLOBALS['DYNAMIC_CONTENT_PATH'] . '/' . $type;
+
+ // Convert original images
+ $iterator = new RegexIterator(
+ new FilesystemIterator($source_directory),
+ '/_original\.png$/i'
+ );
+ foreach ($iterator as $file) {
+ $this->convert($file, $type);
+ }
+
+ // Convert leftover images
+ $iterator = new RegexIterator(
+ new FilesystemIterator($source_directory),
+ '/_normal\.png$/i'
+ );
+ foreach ($iterator as $file) {
+ $this->convert($file, $type);
+ }
+ }
+ }
+
+ private function convert(
+ SplFileInfo $input_file,
+ string $type
+ ): void {
+ $input_image = imagecreatefromstring(file_get_contents($input_file->getPathname()));
+
+ if ($input_image === false) {
+ unlink($input_file->getPathname());
+ return;
+ }
+
+ $user_id = explode('_', $input_file->getBasename('.png'))[0];
+ $output_path = $input_file->getPath();
+ if ($type !== 'licenses') {
+ $output_path .= '/' . substr($user_id, 0, 2);
+ }
+ if (!is_dir($output_path)) {
+ mkdir($output_path);
+ }
+
+ imagepalettetotruecolor($input_image);
+ imagealphablending($input_image, false);
+ imagesavealpha($input_image, true);
+
+ $image_width = imagesx($input_image);
+ $image_height = imagesy($input_image);
+
+ foreach (array_keys(self::SIZES) as $size) {
+ [$width, $height] = self::SIZES[$size][$type] ?? self::SIZES[$size]['default'];
+ $output_file = "{$output_path}/{$user_id}_{$size}.webp";
+
+ $factor = min($width / $image_width, $height / $image_height);
+ $resized_width = round($image_width * $factor);
+ $resized_height = round($image_height * $factor);
+
+ $xpos = intval($width - $resized_width) >> 1;
+ $ypos = intval($height - $resized_height) >> 1;
+
+ $output_image = $this->createNewImage($width, $height);
+
+ imagecopyresampled(
+ $output_image, $input_image,
+ $xpos, $ypos,
+ 0, 0,
+ $resized_width, $resized_height,
+ $image_width, $image_height
+ );
+
+ imagewebp($output_image, $output_file, 90);
+ imagedestroy($output_image);
+ }
+
+ imagedestroy($input_image);
+
+ unlink($input_file->getPath() . '/' . $user_id . '_original.png');
+ foreach (array_keys(self::SIZES) as $size) {
+ unlink($input_file->getPath() . '/' . $user_id . '_' . $size . '.png');
+ unlink($input_file->getPath() . '/' . $user_id . '_' . $size . '@2x.png');
+ }
+ }
+
+ private function createNewImage(int $width, int $height)
+ {
+ $image = imagecreatetruecolor($width, $height);
+ imagealphablending($image, false);
+ imagesavealpha($image, false); // Otherwise, WebP won't store the alpha information
+
+ $transparent_color = imagecolorallocatealpha($image, 0, 0, 0, 127);
+ imagefill($image, 0, 0, $transparent_color);
+
+ return $image;
+ }
+}
diff --git a/lib/bootstrap.php b/lib/bootstrap.php
index e286e93..5690a60 100644
--- a/lib/bootstrap.php
+++ b/lib/bootstrap.php
@@ -61,8 +61,9 @@ if (isset($_SERVER['SERVER_NAME'])) {
$ABSOLUTE_URI_STUDIP .= $CANONICAL_RELATIVE_PATH_STUDIP;
}
-// default ASSETS_URL, customize if required
+// default ASSETS_URL and ASSETS_PATH, customize if required
$GLOBALS['ASSETS_URL'] = $ABSOLUTE_URI_STUDIP . 'assets/';
+$GLOBALS['ASSETS_PATH'] = $ABSOLUTE_PATH_STUDIP . 'assets/';
require __DIR__ . '/classes/StudipFileloader.php';
@@ -107,6 +108,7 @@ require_once 'lib/visual.inc.php';
// set assets url
Assets::set_assets_url($GLOBALS['ASSETS_URL']);
+Assets::set_assets_path($GLOBALS['ASSETS_PATH']);
// globale template factory anlegen
require_once 'vendor/flexi/lib/flexi.php';
diff --git a/lib/classes/Assets.class.php b/lib/classes/Assets.class.php
index b5adfcf..928c47c 100644
--- a/lib/classes/Assets.class.php
+++ b/lib/classes/Assets.class.php
@@ -41,8 +41,20 @@ class Assets
/**
* @ignore
*/
- private static $assets_url, $dynamic, $counter_cache;
+ private static $assets_url;
+ private static $assets_path;
+ private static $dynamic;
+ private static $counter_cache;
+ /**
+ * This method sets the URL to your assets.
+ *
+ * @param string $path the path to the assets
+ */
+ public static function set_assets_path(string $path): void
+ {
+ self::$assets_path = $path;
+ }
/**
* This method sets the URL to your assets.
@@ -51,14 +63,13 @@ class Assets
*
* @return void
*/
- static function set_assets_url($url)
+ public static function set_assets_url($url)
{
- Assets::$assets_url = $url;
- Assets::$counter_cache = NULL;
- Assets::$dynamic = mb_strpos($url, '%d') !== FALSE;
+ self::$assets_url = $url;
+ self::$counter_cache = NULL;
+ self::$dynamic = mb_strpos($url, '%d') !== FALSE;
}
-
/**
* This class method is an accessor to the URL "prefix" for all things "asset"
* Prepend the return value of this method to the relative path of the wanted
@@ -104,24 +115,32 @@ class Assets
*
* @return string the URL "prefix"
*/
- static function url($to = '')
+ public static function url($to = '')
{
-
- if (!Assets::$dynamic)
- return Assets::$assets_url . $to;
+ if (!self::$dynamic) {
+ return self::$assets_url . $to;
+ }
# dynamic ASSETS_URL
- return sprintf(Assets::$assets_url,
+ return sprintf(self::$assets_url,
$to == ''
- ? Assets::$counter_cache++ % Assets::NUMBER_OF_ALIASES
+ ? self::$counter_cache++ % self::NUMBER_OF_ALIASES
# alternative implementation
# : hexdec(mb_substr(sha1($to),-1)) & 3)
- : ord($to[1]) & (Assets::NUMBER_OF_ALIASES - 1))
+ : ord($to[1]) & (self::NUMBER_OF_ALIASES - 1))
. $to;
}
/**
+ * This class method is an accessor to the path "prefix" for all things "asset"
+ */
+ public static function path($to = ''): string
+ {
+ return self::$assets_path . $to;
+ }
+
+ /**
* Returns an image tag using options as html attributes on the
* tag, but with these special cases:
*
@@ -137,7 +156,7 @@ class Assets
* Do not use this to render icons. Use the more appropiate class
* Icon for this.
*/
- static function img($source, $opt = [])
+ public static function img($source, $opt = [])
{
if (!$source) {
return '';
@@ -145,9 +164,9 @@ class Assets
$size = $opt['size'] ?? null;
- $opt = Assets::parse_attributes($opt);
+ $opt = self::parse_attributes($opt);
- $opt['src'] = Assets::image_path($source);
+ $opt['src'] = self::image_path($source);
if (!isset($opt['alt'])) {
$opt['alt'] = ucfirst(current(explode('.', basename($opt['src']))));
@@ -161,7 +180,7 @@ class Assets
unset($opt['size']);
}
- return Assets::tag('img', $opt);
+ return self::tag('img', $opt);
}
@@ -179,7 +198,7 @@ class Assets
* Do not use this to render icons. Use the more appropiate class
* Icon for this.
*/
- static function input($source, $opt = [])
+ public static function input($source, $opt = [])
{
if (!$source) {
@@ -190,9 +209,9 @@ class Assets
$size = $opt['size'];
- $opt = Assets::parse_attributes($opt);
+ $opt = self::parse_attributes($opt);
- $opt['src'] = Assets::image_path($source);
+ $opt['src'] = self::image_path($source);
$opt['type'] = 'image';
if (isset($size) && !isset($opt['width'])) {
@@ -200,7 +219,7 @@ class Assets
unset($opt['size']);
}
- return Assets::tag('input', $opt);
+ return self::tag('input', $opt);
}
/**
@@ -223,9 +242,9 @@ class Assets
* scripts, as we would like to always generate the complete <image> oder
* <input> tag. Please use Assets::img or Assets::input instead.
*/
- static function image_path($source, $respect_retina = false)
+ public static function image_path($source, $respect_retina = false)
{
- $path = Assets::compute_public_path($source, 'images', 'png');
+ $path = self::compute_public_path($source, 'images', 'png');
return $path;
}
@@ -242,12 +261,12 @@ class Assets
* <script src="/js/common.javascript"></script>
* <script src="/elsewhere/cools.js"></script>
*/
- static function script($atLeastOneArgument)
+ public static function script($atLeastOneArgument)
{
$html = '';
foreach (func_get_args() as $source) {
- $source = Assets::javascript_path($source);
- $html .= Assets::content_tag('script', '',
+ $source = self::javascript_path($source);
+ $html .= self::content_tag('script', '',
['src' => $source]);
$html .= "\n";
}
@@ -263,9 +282,9 @@ class Assets
*
* Assets::javascript_path('ajax') => /javascripts/ajax.js
*/
- static function javascript_path($source)
+ public static function javascript_path($source)
{
- return Assets::compute_public_path($source, 'javascripts', 'js');
+ return self::compute_public_path($source, 'javascripts', 'js');
}
@@ -284,7 +303,7 @@ class Assets
* <link href="/stylesheets/random.styles" media="screen" rel="stylesheet">
* <link href="/css/stylish.css" media="screen" rel="stylesheet">
*/
- static function stylesheet($atLeastOneArgument)
+ public static function stylesheet($atLeastOneArgument)
{
$sources = func_get_args();
$sourceOptions = (func_num_args() > 1 &&
@@ -294,12 +313,12 @@ class Assets
$html = '';
foreach ($sources as $source) {
- $source = Assets::stylesheet_path($source);
+ $source = self::stylesheet_path($source);
$opt = array_merge(['rel' => 'stylesheet',
'media' => 'screen',
'href' => $source],
$sourceOptions);
- $html .= Assets::tag('link', $opt) . "\n";
+ $html .= self::tag('link', $opt) . "\n";
}
return $html;
@@ -313,9 +332,9 @@ class Assets
*
* stylesheet_path('style') => /stylesheets/style.css
*/
- static function stylesheet_path($source)
+ public static function stylesheet_path($source)
{
- return Assets::compute_public_path($source, 'stylesheets', 'css');
+ return self::compute_public_path($source, 'stylesheets', 'css');
}
@@ -341,7 +360,7 @@ class Assets
$source = "$dir/$source";
# consider asset host
- $source = Assets::url(ltrim($source, '/'));
+ $source = self::url(ltrim($source, '/'));
}
return $source;
@@ -373,15 +392,17 @@ class Assets
/**
* Helper function for content tags.
*
- * @param name tag name
- * @param content tag content
- * @param options tag options
+ * @param string $name tag name
+ * @param string $content tag content
+ * @param array $options tag options
*
- * @return type <description>
+ * @return string
*/
private static function content_tag($name, $content = '', $options = [])
{
- if (!$name) return '';
+ if (!$name) {
+ return '';
+ }
return '<' . $name . ' ' . arrayToHtmlAttributes($options) . '>' .
$content .
'</' . $name . '>';
diff --git a/lib/classes/Avatar.class.php b/lib/classes/Avatar.class.php
index b6165bd..959523f 100644
--- a/lib/classes/Avatar.class.php
+++ b/lib/classes/Avatar.class.php
@@ -1,62 +1,53 @@
<?php
-# Lifter007: TODO
-# Lifter003: TODO
-# Lifter010: TODO
-
-/*
- * Copyright (C) 2007 - André Klaßen (aklassen@uos.de)
- * Copyright (C) 2008 - Marcus Lunzenauer (mlunzena@uos)
- *
+/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
-
-
-/**
- * TODO
- *
- * @package studip
- * @subpackage lib
+ * the License, or (at your option) any later version. *
*
- * @author André Klaßen (aklassen@uos)
- * @author Marcus Lunzenauer (mlunzena@uos)
- * @copyright (c) Authors
- * @since 1.7
+ * @author André Klaßen (aklassen@uos)
+ * @author Marcus Lunzenauer (mlunzena@uos)
+ * @author Jan-Hendirk Willms <tleilax+studip@gmail.com>
+ * @license GPL2 or any later version
+ * @since 1.7
*/
-class Avatar {
+class Avatar
+{
+ public const AVATAR_TYPE = 'user';
+ protected const CREATE_CHUNKED_FOLDERS = true;
+
+ public const EXTENSION = 'webp';
+ public const IMAGE_QUALITY = 90;
/**
* This constant stands for the maximal size of a user picture.
*/
- const ORIGINAL = 'original';
+ public const ORIGINAL = 'original';
/**
* This constant stands for the maximal size of a user picture.
*/
- const NORMAL = 'normal';
+ public const NORMAL = 'normal';
/**
* This constant stands for a medium size of a user picture.
*/
- const MEDIUM = 'medium';
+ public const MEDIUM = 'medium';
/**
* This constant stands for an icon size of a user picture.
*/
- const SMALL = 'small';
-
+ public const SMALL = 'small';
/**
* This constant represents the maximal size of a user picture in bytes.
*/
- const MAX_FILE_SIZE = 10485760;
+ public const MAX_FILE_SIZE = 10485760;
/**
* This constant holds the username and ID of the "nobody" avatar.
*/
- const NOBODY = 'nobody';
+ protected const NOBODY = 'nobody';
/**
* Holds the user's id
@@ -65,7 +56,6 @@ class Avatar {
*/
protected $user_id;
-
/**
* Holds the user's username
*
@@ -73,24 +63,23 @@ class Avatar {
*/
protected $username;
-
/**
* Returns an avatar object of the appropriate class.
*
- * @param string the user's id
- * @param string the user's username (optional)
+ * @param string $id the user's id
+ * @param string $username the user's username (optional)
*
- * @return Avatar the user's avatar.
+ * @return static the user's avatar.
*/
public static function getAvatar($id)
{
$username = null;
- if (func_num_args() == 2) {
+ if (func_num_args() === 2) {
$username = func_get_arg(1);
}
- return new Avatar($id, $username);
+ return new static($id, $username);
}
/**
@@ -100,60 +89,113 @@ class Avatar {
*/
public static function getNobody()
{
- return new Avatar(Avatar::NOBODY, Avatar::NOBODY);
+ return new static(static::NOBODY, static::NOBODY);
}
-
+ /**
+ * Returns the url to the customized avatars
+ *
+ * @return string
+ */
public function getAvatarDirectoryUrl()
{
- return $GLOBALS['DYNAMIC_CONTENT_URL'] . "/user";
+ return sprintf(
+ '%s/%s%s',
+ $GLOBALS['DYNAMIC_CONTENT_URL'],
+ static::AVATAR_TYPE,
+ static::CREATE_CHUNKED_FOLDERS ? '/' . substr($this->user_id, 0, 2) : ''
+ );
}
-
+ /**
+ * Returns the path to the customized avatars
+ *
+ * @return string
+ */
public function getAvatarDirectoryPath()
{
- return $GLOBALS['DYNAMIC_CONTENT_PATH'] . "/user";
+ return sprintf(
+ '%s/%s%s',
+ $GLOBALS['DYNAMIC_CONTENT_PATH'],
+ static::AVATAR_TYPE,
+ static::CREATE_CHUNKED_FOLDERS ? '/' . substr($this->user_id, 0, 2) : ''
+ );
+ }
+
+ /**
+ * Returns the url to the default avatars
+ */
+ public function getDefaultAvatarDirectoryUrl(): string
+ {
+ return Assets::url('images/avatars/' . static::AVATAR_TYPE);
}
+ /**
+ * Returns the path to the default avatars
+ */
+ public function getDefaultAvatarDirectoryPath(): string
+ {
+ return Assets::path('images/avatars/' . static::AVATAR_TYPE);
+ }
- public function getCustomAvatarUrl($size, $ext = 'png')
+ /**
+ * Returns the url to a customized avatar
+ *
+ * @return string
+ */
+ public function getCustomAvatarUrl($size)
{
- $retina = isset($GLOBALS['auth']->auth['devicePixelRatio'])
- ? $GLOBALS['auth']->auth['devicePixelRatio'] > 1.2
- : false;
- $size = $retina && file_exists($this->getCustomAvatarPath($size, 'png', true))
- ? $size."@2x"
- : $size;
+ if ($this->isNobody()) {
+ return sprintf(
+ '%s/%s_%s.%s',
+ $this->getDefaultAvatarDirectoryUrl(),
+ $this->user_id,
+ $size,
+ self::EXTENSION
+ );
+ }
+
return sprintf(
'%s/%s_%s.%s?d=%s',
$this->getAvatarDirectoryUrl(),
$this->user_id,
$size,
- $ext,
+ self::EXTENSION,
@filemtime($this->getCustomAvatarPath($size)) ?: "0"
);
}
-
- public function getCustomAvatarPath($size, $ext = 'png', $retina = false)
+ /**
+ * Returns the path to a customized avatar
+ *
+ * @return string
+ */
+ public function getCustomAvatarPath($size)
{
+ if ($this->isNobody()) {
+ return sprintf(
+ '%s/%s_%s.%s',
+ $this->getDefaultAvatarDirectoryPath(),
+ $this->user_id,
+ $size,
+ self::EXTENSION
+ );
+ }
+
return sprintf(
'%s/%s_%s.%s',
$this->getAvatarDirectoryPath(),
$this->user_id,
- $retina ? $size."@2x" : $size,
- $ext
+ $size,
+ self::EXTENSION
);
}
-
/**
* Constructs a new Avatar object belonging to a user with the given id.
*
- * @param string the user's id
- * @param string the user's username (optional)
- *
- * @return void
+ * @param string $user_id the user's id
+ * @param string $username the user's username (optional)
*/
protected function __construct($user_id, $username = null)
{
@@ -163,39 +205,50 @@ class Avatar {
$this->checkAvatarVisibility();
}
-
/**
* Returns the file name of a user's avatar.
*
- * @param string one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
- * @param string an optional extension of the avatar
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
*
* @return string the absolute file path to the avatar
*/
- public function getFilename($size, $ext = 'png')
+ public function getFilename($size)
{
return $this->is_customized()
- ? $this->getCustomAvatarPath($size, $ext)
- : $this->getNobody()->getCustomAvatarPath($size, $ext);
+ ? $this->getCustomAvatarPath($size)
+ : $this->getNobody()->getCustomAvatarPath($size);
}
-
/**
* Returns the URL of a user's picture.
*
- * @param string one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
- * @param string an optional extension of the user's picture
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
*
* @return string the URL to the user's picture
*/
# TODO (mlunzena) in Url umbenennen
- public function getURL($size, $ext = 'png')
+ public function getURL($size)
{
return $this->is_customized()
- ? $this->getCustomAvatarUrl($size, $ext)
- : $this->getNobody()->getCustomAvatarUrl($size, $ext);
+ ? $this->getCustomAvatarUrl($size)
+ : $this->getNobody()->getCustomAvatarUrl($size);
+ }
+
+ /**
+ * Returns whether this avatar is a default/"nobody" avatar.
+ */
+ public function isNobody(): bool
+ {
+ return $this->user_id === static::NOBODY;
}
+ /**
+ * Returns whether a customized file exists
+ */
+ public function customizedFileExists(): bool
+ {
+ return file_exists($this->getCustomAvatarPath(static::MEDIUM));
+ }
/**
* Returns whether a user has uploaded a custom picture.
@@ -205,38 +258,40 @@ class Avatar {
*/
public function is_customized()
{
- return $this->user_id !== Avatar::NOBODY
- && file_exists($this->getCustomAvatarPath(Avatar::MEDIUM));
+ return !$this->isNobody()
+ && $this->customizedFileExists();
}
-
/**
* Returns the CSS class to use for this avatar image.
*
- * @param string one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
*
* @return string CSS class to use for the avatar
*/
protected function getCssClass($size)
{
if (!isset($this->username)) {
- $this->username = htmlReady(get_username($this->user_id));
+ $this->username = get_username($this->user_id);
}
- return sprintf('avatar-%s user-%s'.($this->is_customized() ? '' : ' recolor'), $size, $this->username);
+ return sprintf(
+ 'avatar-%s user-%s' . ($this->is_customized() ? '' : ' recolor'),
+ $size,
+ htmlReady($this->username)
+ );
}
-
/**
* Constructs a desired HTML image tag for an Avatar. Additional
* html attributes may also be specified using the $opt parameter.
*
- * @param string one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
- * @param array array of attributes to add to the HTML image tag
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
+ * @param array $opt array of attributes to add to the HTML image tag
*
* @return string returns the HTML image tag
*/
- public function getImageTag($size = Avatar::MEDIUM, $opt = [])
+ public function getImageTag($size = self::MEDIUM, $opt = [])
{
$opt['src'] = $this->getURL($size);
@@ -277,31 +332,34 @@ class Avatar {
return '<img ' . arrayToHtmlAttributes($opt) . '>';
}
-
/**
* Creates all the different sized thumbnails for an uploaded file.
*
- * @param string the key of the uploaded file,
- * see documentation about $_FILES
+ * @param string $userfile the key of the uploaded file, see documentation about $_FILES
*
* @return void
- *
- * @throws several Exceptions if the uploaded file does not satisfy the
- * requirements
*/
public function createFromUpload($userfile)
{
try {
- // Bilddatei ist zu groß
- if ($_FILES[$userfile]['size'] > self::MAX_FILE_SIZE) {
- throw new Exception(sprintf(_("Die hochgeladene Bilddatei ist %s KB groß. Die maximale Dateigröße beträgt %s KB!"),
- round($_FILES[$userfile]['size'] / 1024),
- self::MAX_FILE_SIZE / 1024));
- }
-
// keine Datei ausgewählt!
if (!$_FILES[$userfile]['name']) {
- throw new Exception(_("Sie haben keine Datei zum Hochladen ausgewählt!"));
+ throw new Exception(_('Sie haben keine Datei zum Hochladen ausgewählt!'));
+ }
+
+ // Fehler beim Hochladen
+ if ($_FILES[$userfile]['error'] !== UPLOAD_ERR_OK) {
+ throw new Exception(_('Es gab einen Fehler beim Hochladen der Datei!'));
+ }
+
+
+ // Bilddatei ist zu groß
+ if ($_FILES[$userfile]['size'] > self::MAX_FILE_SIZE) {
+ throw new Exception(sprintf(
+ _('Die hochgeladene Bilddatei ist %s KB groß. Die maximale Dateigröße beträgt %s KB!'),
+ round($_FILES[$userfile]['size'] / 1024),
+ self::MAX_FILE_SIZE / 1024)
+ );
}
// get extension
@@ -309,14 +367,15 @@ class Avatar {
$ext = mb_strtolower($pathinfo['extension']);
// passende Endung ?
- if (!in_array($ext, words('jpg jpeg gif png'))) {
- throw new Exception(sprintf(_("Der Dateityp der Bilddatei ist falsch (%s). Es sind nur die Dateiendungen .gif, .png, .jpeg und .jpg erlaubt!"), htmlReady($ext)));
+ if (!in_array($ext, words('jpg jpeg gif png webp'))) {
+ throw new Exception(sprintf(
+ _('Der Dateityp der Bilddatei ist falsch (%s). Es sind nur die Dateiendungen .gif, .png, .jpeg, .jpg oder .webp erlaubt!'),
+ $ext
+ ));
}
// na dann kopieren wir mal...
- $filename = sprintf('%s/%s.%s',
- $this->getAvatarDirectoryPath(),
- $this->user_id, $ext);
+ $filename = tempnam($GLOBALS['TMP_PATH'], 'avatar-upload');
if (!@move_uploaded_file($_FILES[$userfile]['tmp_name'], $filename)) {
throw new Exception(_("Es ist ein Fehler beim Kopieren der Datei aufgetreten. Das Bild wurde nicht hochgeladen!"));
@@ -327,20 +386,17 @@ class Avatar {
$this->sanitizeOrientation($filename);
$this->createFrom($filename);
-
- @unlink($filename);
-
- // eigentlich braucht man hier "finally"
- } catch (Exception $e) {
- @unlink($filename);
- throw $e;
+ } finally {
+ if (isset($filename)) {
+ @unlink($filename);
+ }
}
}
/**
* Creates thumbnails from an image.
*
- * @param string filename of the image to create thumbnails from
+ * @param string $filename filename of the image to create thumbnails from
*
* @return void
*/
@@ -353,13 +409,9 @@ class Avatar {
set_error_handler([__CLASS__, 'error_handler']);
NotificationCenter::postNotification('AvatarWillCreate', $this->user_id);
- copy($filename, $this->getCustomAvatarPath(Avatar::ORIGINAL));
- $this->resize(Avatar::NORMAL, $filename);
- $this->resize(Avatar::NORMAL, $filename, true);
- $this->resize(Avatar::MEDIUM, $filename);
- $this->resize(Avatar::MEDIUM, $filename, true);
- $this->resize(Avatar::SMALL, $filename);
- $this->resize(Avatar::SMALL, $filename, true);
+ $this->resize(static::NORMAL, $filename);
+ $this->resize(static::MEDIUM, $filename);
+ $this->resize(static::SMALL, $filename);
NotificationCenter::postNotification('AvatarDidCreate', $this->user_id);
restore_error_handler();
@@ -372,55 +424,50 @@ class Avatar {
{
if ($this->is_customized()) {
NotificationCenter::postNotification('AvatarWillDelete', $this->user_id);
- @unlink($this->getCustomAvatarPath(Avatar::ORIGINAL));
- @unlink($this->getCustomAvatarPath(Avatar::NORMAL));
- @unlink($this->getCustomAvatarPath(Avatar::SMALL));
- @unlink($this->getCustomAvatarPath(Avatar::MEDIUM));
- @unlink($this->getCustomAvatarPath(Avatar::NORMAL, 'png', true));
- @unlink($this->getCustomAvatarPath(Avatar::SMALL, 'png', true));
- @unlink($this->getCustomAvatarPath(Avatar::MEDIUM, 'png', true));
+ @unlink($this->getCustomAvatarPath(static::NORMAL));
+ @unlink($this->getCustomAvatarPath(static::SMALL));
+ @unlink($this->getCustomAvatarPath(static::MEDIUM));
NotificationCenter::postNotification('AvatarDidDelete', $this->user_id);
}
}
-
/**
* Return the dimension of a size
*
- * @param string the dimension of a size
- * @return array a tupel of integers [width, height]
+ * @param string $size the dimension of a size
+ * @return array{0: int, 1: int} a tupel of integers [width, height]
*/
- public static function getDimension($size) {
+ public static function getDimension($size)
+ {
$dimensions = [
- Avatar::NORMAL => [250, 250],
- Avatar::MEDIUM => [100, 100],
- Avatar::SMALL => [25, 25]
+ static::NORMAL => [250, 250],
+ static::MEDIUM => [100, 100],
+ static::SMALL => [25, 25]
];
return $dimensions[$size];
}
-
/**
* Create from an image thumbnails of a specified size.
*
- * @param string the size of the thumbnail to create
- * @param string the filename of the image to make thumbnail of
- *
- * @return void
+ * @param string $size the size of the thumbnail to create
+ * @param string $filename the filename of the image to make thumbnail of
*/
- private function resize($size, $filename, $retina = false)
+ private function resize(string $size, string $filename)
{
- list($thumb_width, $thumb_height) = static::getDimension($size);
- $thumb_width = $retina ? $thumb_width * 2 : $thumb_width;
- $thumb_height = $retina ? $thumb_height * 2 : $thumb_height;
+ [$thumb_width, $thumb_height] = static::getDimension($size);
+
+ $thumb_width = $thumb_width * 2;
+ $thumb_height = $thumb_height * 2;
- list($width, $height, $type) = getimagesize($filename);
+ [$width, $height, $type] = getimagesize($filename);
# create image resource from filename
$lookup = [
IMAGETYPE_GIF => 'imagecreatefromgif',
IMAGETYPE_JPEG => 'imagecreatefromjpeg',
IMAGETYPE_PNG => 'imagecreatefrompng',
+ IMAGETYPE_WEBP => 'imagecreatefromwebp',
];
if (!isset($lookup[$type])) {
throw new Exception(_("Der Typ des Bilds wird nicht unterstützt."));
@@ -460,9 +507,14 @@ class Avatar {
$resized_width, $resized_height
);
- imagepng($dst, $this->getCustomAvatarPath($size, 'png', $retina));
- }
+ $output_file = $this->getCustomAvatarPath($size);
+ $directory = dirname($output_file);
+ if (!is_dir($directory) && !mkdir($directory)) {
+ throw new Exception(_('Das Verzeichnis zum Speichern der Datei konnte nicht angelegt werden.'));
+ }
+ imagewebp($dst, $output_file, self::IMAGE_QUALITY);
+ }
private function imageresize($image, $current_width, $current_height, $width, $height)
{
@@ -481,7 +533,6 @@ class Avatar {
return $image_resized;
}
-
public static function error_handler($errno, $errstr, $errfile, $errline)
{
if (defined('E_RECOVERABLE_ERROR')
@@ -506,8 +557,8 @@ class Avatar {
*/
public function getDefaultTitle()
{
- if ($this->user_id === Avatar::NOBODY) {
- return Avatar::NOBODY;
+ if ($this->isNobody()) {
+ return static::NOBODY;
}
require_once 'lib/functions.php';
@@ -523,7 +574,7 @@ class Avatar {
{
$visible = Visibility::verify('picture', $this->user_id);
if (!$visible) {
- $this->user_id = 'nobody';
+ $this->user_id = self::NOBODY;
}
return $visible;
}
@@ -568,12 +619,14 @@ class Avatar {
$img = imagerotate($img, $degree, 0);
$extension = pathinfo($filename, PATHINFO_EXTENSION);
- if ($extension === 'jpg' || $extension === 'jpg') {
+ if ($extension === 'jpg' || $extension === 'jpeg') {
imagejpeg($img, $filename, 95);
} elseif ($extension === 'gif') {
imagegif($img, $filename);
- } else {
+ } elseif ($extension === 'png') {
imagepng($img, $filename, 9);
+ } else {
+ imagewebp($img, $filename, self::IMAGE_QUALITY);
}
imagedestroy($img);
diff --git a/lib/classes/CourseAvatar.class.php b/lib/classes/CourseAvatar.class.php
index 3edc9e1..8c153a8 100644
--- a/lib/classes/CourseAvatar.class.php
+++ b/lib/classes/CourseAvatar.class.php
@@ -1,71 +1,15 @@
<?php
-# Lifter010: TODO
-
-/*
- * Copyright (C) 2009 - Marcus Lunzenauer (mlunzena@uos)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
-
-
/**
* This class represents the avatar of a course.
*
- * @package studip
- * @subpackage lib
- *
* @author Marcus Lunzenauer (mlunzena@uos)
* @copyright (c) Authors
+ * @license GPL2 or any later version
* @since 1.10
*/
class CourseAvatar extends Avatar
{
-
- /**
- * Returns an avatar object of the appropriate class.
- *
- * @param string the course's id
- *
- * @return mixed the course's avatar.
- */
- static function getAvatar($id)
- {
- return new CourseAvatar($id);
- }
-
- /**
- * Returns an avatar object for "nobody".
- *
- * @return mixed the course's avatar.
- */
- static function getNobody()
- {
- return new CourseAvatar('nobody');
- }
-
- /**
- * Returns the URL to the courses' avatars.
- *
- * @return string the URL to the avatars
- */
- function getAvatarDirectoryUrl()
- {
- return $GLOBALS['DYNAMIC_CONTENT_URL'] . "/course";
- }
-
-
- /**
- * Returns the file system path to the courses' avatars
- *
- * @return string the file system path to the avatars
- */
- function getAvatarDirectoryPath()
- {
- return $GLOBALS['DYNAMIC_CONTENT_PATH'] . "/course";
- }
+ public const AVATAR_TYPE = 'course';
/**
* Returns the CSS class to use for this avatar image.
@@ -74,15 +18,16 @@ class CourseAvatar extends Avatar
*
* @return string CSS class to use for the avatar
*/
- protected function getCssClass($size) {
- return sprintf('course-avatar-%s course-%s', $size, $this->user_id);
+ protected function getCssClass($size)
+ {
+ return "course-avatar-{$size} course-{$this->user_id}";
}
/**
* Return the default title of the avatar.
* @return string the default title
*/
- function getDefaultTitle()
+ public function getDefaultTitle()
{
return Seminar::GetInstance($this->user_id)->name;
}
@@ -91,7 +36,8 @@ class CourseAvatar extends Avatar
* Return if avatar is visible to the current user.
* @return boolean: true if visible
*/
- protected function checkAvatarVisibility() {
+ protected function checkAvatarVisibility()
+ {
//no special conditions for visibility of course-avatars yet
return true;
}
diff --git a/lib/classes/InstituteAvatar.class.php b/lib/classes/InstituteAvatar.class.php
index e05c345..8adbfba 100644
--- a/lib/classes/InstituteAvatar.class.php
+++ b/lib/classes/InstituteAvatar.class.php
@@ -1,89 +1,44 @@
<?php
-# Lifter010: TODO
-
-/*
- * Copyright (C) 2009 - Marcus Lunzenauer (mlunzena@uos)
- * André Noack <noack@data-quest.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
-
/**
* This class represents the avatar of a institute.
*
- * @package studip
- * @subpackage lib
- *
* @author André Noack <noack@data-quest.de>
+ * @author Marcus Lunzenauer <mlunzena@uos>
* @copyright (c) Authors
+ * @license GPL2 or any later version
* @since 1.10
*/
class InstituteAvatar extends CourseAvatar
{
+ public const AVATAR_TYPE = 'institute';
/**
- * Returns an avatar object of the appropriate class.
- *
- * @param string the course's id
- *
- * @return mixed the course's avatar.
- */
- static function getAvatar($id)
- {
- return new InstituteAvatar($id);
- }
-
- /**
- * Returns an avatar object for "nobody".
- *
- * @return mixed the course's avatar.
- */
- static function getNobody()
- {
- return new InstituteAvatar('nobody');
- }
-
- /**
- * Returns the URL to the institute' avatars.
+ * Returns the CSS class to use for this avatar image.
*
- * @return string the URL to the avatars
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
+ * @return string CSS class to use for the avatar
*/
- function getAvatarDirectoryUrl()
+ protected function getCssClass($size)
{
- return $GLOBALS['DYNAMIC_CONTENT_URL'] . "/institute";
- }
-
-
- /**
- * Returns the file system path to the institute' avatars
- *
- * @return string the file system path to the avatars
- */
- function getAvatarDirectoryPath()
- {
- return $GLOBALS['DYNAMIC_CONTENT_PATH'] . "/institute";
+ return "institute-avatar-{$size} institute-{$this->user_id}";
}
/**
* Return the default title of the avatar.
* @return string the default title
*/
- function getDefaultTitle()
+ public function getDefaultTitle()
{
$institute = Institute::find($this->user_id);
- return $institute
- ? $institute->name
- : Avatar::NOBODY;
+ return $institute ? (string) $institute->name : self::NOBODY;
}
/**
* Return if avatar is visible to the current user.
* @return boolean: true if visible
*/
- protected function checkAvatarVisibility() {
+ protected function checkAvatarVisibility()
+ {
//no special conditions for visibility of course-avatars yet
return true;
}
diff --git a/lib/classes/JsonApi/Schemas/User.php b/lib/classes/JsonApi/Schemas/User.php
index beaeecb..9a172eb 100644
--- a/lib/classes/JsonApi/Schemas/User.php
+++ b/lib/classes/JsonApi/Schemas/User.php
@@ -109,7 +109,7 @@ class User extends SchemaProvider
'small' => $avatar->getURL(\Avatar::SMALL),
'medium' => $avatar->getURL(\Avatar::MEDIUM),
'normal' => $avatar->getURL(\Avatar::NORMAL),
- 'original' => $avatar->getURL(\Avatar::ORIGINAL),
+ 'original' => $avatar->getURL(\Avatar::NORMAL),
],
];
}
diff --git a/lib/classes/LicenseAvatar.php b/lib/classes/LicenseAvatar.php
index 27072ab..c0a429d 100644
--- a/lib/classes/LicenseAvatar.php
+++ b/lib/classes/LicenseAvatar.php
@@ -15,69 +15,31 @@
*/
class LicenseAvatar extends Avatar
{
-
- /**
- * Returns an avatar object of the appropriate class.
- *
- * @param string the course's id
- *
- * @return mixed the course's avatar.
- */
- public static function getAvatar($id)
- {
- return new self($id);
- }
-
- /**
- * Returns an avatar object for "nobody".
- *
- * @return mixed the course's avatar.
- */
- public static function getNobody()
- {
- return new self('nobody');
- }
-
- /**
- * Returns the URL to the courses' avatars.
- *
- * @return string the URL to the avatars
- */
- public function getAvatarDirectoryUrl()
- {
- return $GLOBALS['DYNAMIC_CONTENT_URL'] . "/licenses";
- }
-
-
- /**
- * Returns the file system path to the courses' avatars
- *
- * @return string the file system path to the avatars
- */
- public function getAvatarDirectoryPath()
- {
- return $GLOBALS['DYNAMIC_CONTENT_PATH'] . "/licenses";
- }
+ public const AVATAR_TYPE = 'licenses';
+ protected const CREATE_CHUNKED_FOLDERS = false;
public function getImageTag($size = Avatar::MEDIUM, $opt = [])
{
if (!$this->is_customized()) {
return "";
- } else {
- return parent::getImageTag($size, $opt);
}
+
+ return parent::getImageTag($size, $opt);
}
/**
* Returns the CSS class to use for this avatar image.
*
- * @param string one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
- *
+ * @param string $size one of the constants Avatar::(NORMAL|MEDIUM|SMALL)
* @return string CSS class to use for the avatar
*/
protected function getCssClass($size)
{
- return sprintf('license-avatar-%s license-%s', $size, $this->user_id);
+ return sprintf(
+ 'license-avatar-%s license-%s',
+ $size,
+ $this->user_id
+ );
}
/**
@@ -101,8 +63,8 @@ class LicenseAvatar extends Avatar
/**
* Return the dimension of a size
*
- * @param string the dimension of a size
- * @return array a tupel of integers [width, height]
+ * @param string $size the dimension of a size
+ * @return array a tupel of integers [width, height]
*/
public static function getDimension($size)
{
diff --git a/lib/classes/StudygroupAvatar.class.php b/lib/classes/StudygroupAvatar.class.php
index 5121c90..8e27f8e 100644
--- a/lib/classes/StudygroupAvatar.class.php
+++ b/lib/classes/StudygroupAvatar.class.php
@@ -1,49 +1,16 @@
<?php
-# Lifter010: TODO
-
-/*
- * Copyright (C) 2009 - Marcus Lunzenauer (mlunzena@uos)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
-
-
/**
* This class represents the avatar of a course.
*
- * @package studip
- * @subpackage lib
- *
* @author Marcus Lunzenauer (mlunzena@uos), Till Glöggler (tgloeggl@uos)
+ * @license GPL2 or any later version
* @copyright (c) Authors
* @since 1.10
*/
class StudygroupAvatar extends CourseAvatar
{
/**
- * Returns an avatar object of the appropriate class.
- *
- * @param string the studygroup's id
- *
- * @return mixed the studygroup's avatar.
- */
- static function getAvatar($course_id)
- {
- return new StudygroupAvatar($course_id);
- }
-
-
- /**
- * Returns an avatar object for "nobody".
- *
- * @return mixed the studygroup's avatar.
+ * This constant holds the username and ID of the "studygroup" avatar.
*/
- static function getNobody()
- {
- return new StudygroupAvatar('studygroup');
- }
-
+ protected const NOBODY = 'studygroup';
}
diff --git a/lib/models/User.class.php b/lib/models/User.class.php
index 4f9ccb7..dd3ccd7 100644
--- a/lib/models/User.class.php
+++ b/lib/models/User.class.php
@@ -1161,10 +1161,7 @@ class User extends AuthUserMd5 implements Range, PrivacyObject
$new_avatar = Avatar::getAvatar($new_id);
if ($old_avatar->is_customized()) {
if (!$new_avatar->is_customized()) {
- $avatar_file = $old_avatar->getFilename(Avatar::ORIGINAL);
- if (!file_exists($avatar_file)) {
- $avatar_file = $old_avatar->getFilename(Avatar::NORMAL);
- }
+ $avatar_file = $old_avatar->getFilename(Avatar::NORMAL);
$new_avatar->createFrom($avatar_file);
}
$old_avatar->reset();
diff --git a/lib/phplib/Seminar_Auth.class.php b/lib/phplib/Seminar_Auth.class.php
index e4f9e36..30a6d46 100644
--- a/lib/phplib/Seminar_Auth.class.php
+++ b/lib/phplib/Seminar_Auth.class.php
@@ -349,7 +349,6 @@ class Seminar_Auth
$this->auth["uname"] = Request::get('loginname'); // This provides access for "loginform.ihtml"
$this->auth["jscript"] = Request::get('resolution') != "";
- $this->auth['devicePixelRatio'] = Request::float('device_pixel_ratio');
$check_auth = StudipAuthAbstract::CheckAuthentication(Request::get('loginname'), Request::get('password'));
diff --git a/public/assets/images/avatars/course/nobody_medium.webp b/public/assets/images/avatars/course/nobody_medium.webp
new file mode 100644
index 0000000..fe3c331
--- /dev/null
+++ b/public/assets/images/avatars/course/nobody_medium.webp
Binary files differ
diff --git a/public/assets/images/avatars/course/nobody_normal.webp b/public/assets/images/avatars/course/nobody_normal.webp
new file mode 100644
index 0000000..6bc3535
--- /dev/null
+++ b/public/assets/images/avatars/course/nobody_normal.webp
Binary files differ
diff --git a/public/assets/images/avatars/course/nobody_small.webp b/public/assets/images/avatars/course/nobody_small.webp
new file mode 100644
index 0000000..2cd05a2
--- /dev/null
+++ b/public/assets/images/avatars/course/nobody_small.webp
Binary files differ
diff --git a/public/assets/images/avatars/course/studygroup_medium.webp b/public/assets/images/avatars/course/studygroup_medium.webp
new file mode 100644
index 0000000..36ac412
--- /dev/null
+++ b/public/assets/images/avatars/course/studygroup_medium.webp
Binary files differ
diff --git a/public/assets/images/avatars/course/studygroup_normal.webp b/public/assets/images/avatars/course/studygroup_normal.webp
new file mode 100644
index 0000000..25ce033
--- /dev/null
+++ b/public/assets/images/avatars/course/studygroup_normal.webp
Binary files differ
diff --git a/public/assets/images/avatars/course/studygroup_small.webp b/public/assets/images/avatars/course/studygroup_small.webp
new file mode 100644
index 0000000..e2c2fa9
--- /dev/null
+++ b/public/assets/images/avatars/course/studygroup_small.webp
Binary files differ
diff --git a/public/assets/images/avatars/institute/nobody_medium.webp b/public/assets/images/avatars/institute/nobody_medium.webp
new file mode 100644
index 0000000..af74dc8
--- /dev/null
+++ b/public/assets/images/avatars/institute/nobody_medium.webp
Binary files differ
diff --git a/public/assets/images/avatars/institute/nobody_normal.webp b/public/assets/images/avatars/institute/nobody_normal.webp
new file mode 100644
index 0000000..2394779
--- /dev/null
+++ b/public/assets/images/avatars/institute/nobody_normal.webp
Binary files differ
diff --git a/public/assets/images/avatars/institute/nobody_small.webp b/public/assets/images/avatars/institute/nobody_small.webp
new file mode 100644
index 0000000..5d2af37
--- /dev/null
+++ b/public/assets/images/avatars/institute/nobody_small.webp
Binary files differ
diff --git a/public/assets/images/avatars/user/nobody_medium.webp b/public/assets/images/avatars/user/nobody_medium.webp
new file mode 100644
index 0000000..e941ee1
--- /dev/null
+++ b/public/assets/images/avatars/user/nobody_medium.webp
Binary files differ
diff --git a/public/assets/images/avatars/user/nobody_normal.webp b/public/assets/images/avatars/user/nobody_normal.webp
new file mode 100644
index 0000000..05bea6d
--- /dev/null
+++ b/public/assets/images/avatars/user/nobody_normal.webp
Binary files differ
diff --git a/public/assets/images/avatars/user/nobody_small.webp b/public/assets/images/avatars/user/nobody_small.webp
new file mode 100644
index 0000000..c16bd4c
--- /dev/null
+++ b/public/assets/images/avatars/user/nobody_small.webp
Binary files differ
diff --git a/public/pictures/course/.gitkeep b/public/pictures/course/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/public/pictures/course/.gitkeep
diff --git a/public/pictures/course/nobody_medium.png b/public/pictures/course/nobody_medium.png
deleted file mode 100644
index 7871826..0000000
--- a/public/pictures/course/nobody_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/nobody_medium@2x.png b/public/pictures/course/nobody_medium@2x.png
deleted file mode 100644
index fb29b5c..0000000
--- a/public/pictures/course/nobody_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/nobody_normal.png b/public/pictures/course/nobody_normal.png
deleted file mode 100644
index c325a72..0000000
--- a/public/pictures/course/nobody_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/nobody_normal@2x.png b/public/pictures/course/nobody_normal@2x.png
deleted file mode 100644
index 11ee358..0000000
--- a/public/pictures/course/nobody_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/nobody_small.png b/public/pictures/course/nobody_small.png
deleted file mode 100644
index 2188f01..0000000
--- a/public/pictures/course/nobody_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/nobody_small@2x.png b/public/pictures/course/nobody_small@2x.png
deleted file mode 100644
index a4b8750..0000000
--- a/public/pictures/course/nobody_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/studygroup_medium.png b/public/pictures/course/studygroup_medium.png
deleted file mode 100644
index ed5c723..0000000
--- a/public/pictures/course/studygroup_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/studygroup_medium2x.png b/public/pictures/course/studygroup_medium2x.png
deleted file mode 100644
index 68c7579..0000000
--- a/public/pictures/course/studygroup_medium2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/studygroup_normal.png b/public/pictures/course/studygroup_normal.png
deleted file mode 100644
index 6d09371..0000000
--- a/public/pictures/course/studygroup_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/studygroup_normal@2x.png b/public/pictures/course/studygroup_normal@2x.png
deleted file mode 100644
index 6a8d4a0..0000000
--- a/public/pictures/course/studygroup_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/studygroup_small.png b/public/pictures/course/studygroup_small.png
deleted file mode 100644
index f9f05ac..0000000
--- a/public/pictures/course/studygroup_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/course/studygroup_small@2x.png b/public/pictures/course/studygroup_small@2x.png
deleted file mode 100644
index 54b9a51..0000000
--- a/public/pictures/course/studygroup_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/institute/.gitkeep b/public/pictures/institute/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/public/pictures/institute/.gitkeep
diff --git a/public/pictures/institute/nobody_medium.png b/public/pictures/institute/nobody_medium.png
deleted file mode 100644
index 10db88b..0000000
--- a/public/pictures/institute/nobody_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/institute/nobody_medium@2x.png b/public/pictures/institute/nobody_medium@2x.png
deleted file mode 100644
index c1bda52..0000000
--- a/public/pictures/institute/nobody_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/institute/nobody_normal.png b/public/pictures/institute/nobody_normal.png
deleted file mode 100644
index 592499b..0000000
--- a/public/pictures/institute/nobody_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/institute/nobody_normal@2x.png b/public/pictures/institute/nobody_normal@2x.png
deleted file mode 100644
index ff951ff..0000000
--- a/public/pictures/institute/nobody_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/institute/nobody_small.png b/public/pictures/institute/nobody_small.png
deleted file mode 100644
index 0f81f2f..0000000
--- a/public/pictures/institute/nobody_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/institute/nobody_small@2x.png b/public/pictures/institute/nobody_small@2x.png
deleted file mode 100644
index a778413..0000000
--- a/public/pictures/institute/nobody_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-1.0_medium.png b/public/pictures/licenses/CC-BY-1.0_medium.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-1.0_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-1.0_medium.webp b/public/pictures/licenses/CC-BY-1.0_medium.webp
new file mode 100644
index 0000000..cee6dc8
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-1.0_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-1.0_medium@2x.png b/public/pictures/licenses/CC-BY-1.0_medium@2x.png
deleted file mode 100644
index 3bfad3d..0000000
--- a/public/pictures/licenses/CC-BY-1.0_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-1.0_normal.png b/public/pictures/licenses/CC-BY-1.0_normal.png
deleted file mode 100644
index 9ebbfff..0000000
--- a/public/pictures/licenses/CC-BY-1.0_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-1.0_normal.webp b/public/pictures/licenses/CC-BY-1.0_normal.webp
new file mode 100644
index 0000000..16d7b88
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-1.0_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-1.0_normal@2x.png b/public/pictures/licenses/CC-BY-1.0_normal@2x.png
deleted file mode 100644
index 12a8450..0000000
--- a/public/pictures/licenses/CC-BY-1.0_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-1.0_small.png b/public/pictures/licenses/CC-BY-1.0_small.png
deleted file mode 100644
index fee6acf..0000000
--- a/public/pictures/licenses/CC-BY-1.0_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-1.0_small.webp b/public/pictures/licenses/CC-BY-1.0_small.webp
new file mode 100644
index 0000000..05d0e9a
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-1.0_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-1.0_small@2x.png b/public/pictures/licenses/CC-BY-1.0_small@2x.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-1.0_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.0_medium.png b/public/pictures/licenses/CC-BY-2.0_medium.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-2.0_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.0_medium.webp b/public/pictures/licenses/CC-BY-2.0_medium.webp
new file mode 100644
index 0000000..cee6dc8
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-2.0_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.0_medium@2x.png b/public/pictures/licenses/CC-BY-2.0_medium@2x.png
deleted file mode 100644
index 3bfad3d..0000000
--- a/public/pictures/licenses/CC-BY-2.0_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.0_normal.png b/public/pictures/licenses/CC-BY-2.0_normal.png
deleted file mode 100644
index 47090d3..0000000
--- a/public/pictures/licenses/CC-BY-2.0_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.0_normal.webp b/public/pictures/licenses/CC-BY-2.0_normal.webp
new file mode 100644
index 0000000..16d7b88
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-2.0_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.0_normal@2x.png b/public/pictures/licenses/CC-BY-2.0_normal@2x.png
deleted file mode 100644
index 12a8450..0000000
--- a/public/pictures/licenses/CC-BY-2.0_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.0_small.png b/public/pictures/licenses/CC-BY-2.0_small.png
deleted file mode 100644
index 4b3e4ad..0000000
--- a/public/pictures/licenses/CC-BY-2.0_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.0_small.webp b/public/pictures/licenses/CC-BY-2.0_small.webp
new file mode 100644
index 0000000..05d0e9a
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-2.0_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.0_small@2x.png b/public/pictures/licenses/CC-BY-2.0_small@2x.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-2.0_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.5_medium.png b/public/pictures/licenses/CC-BY-2.5_medium.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-2.5_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.5_medium.webp b/public/pictures/licenses/CC-BY-2.5_medium.webp
new file mode 100644
index 0000000..cee6dc8
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-2.5_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.5_medium@2x.png b/public/pictures/licenses/CC-BY-2.5_medium@2x.png
deleted file mode 100644
index 3bfad3d..0000000
--- a/public/pictures/licenses/CC-BY-2.5_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.5_normal.png b/public/pictures/licenses/CC-BY-2.5_normal.png
deleted file mode 100644
index 6c5f564..0000000
--- a/public/pictures/licenses/CC-BY-2.5_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.5_normal.webp b/public/pictures/licenses/CC-BY-2.5_normal.webp
new file mode 100644
index 0000000..16d7b88
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-2.5_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.5_normal@2x.png b/public/pictures/licenses/CC-BY-2.5_normal@2x.png
deleted file mode 100644
index 12a8450..0000000
--- a/public/pictures/licenses/CC-BY-2.5_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.5_small.png b/public/pictures/licenses/CC-BY-2.5_small.png
deleted file mode 100644
index dcf08cc..0000000
--- a/public/pictures/licenses/CC-BY-2.5_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.5_small.webp b/public/pictures/licenses/CC-BY-2.5_small.webp
new file mode 100644
index 0000000..05d0e9a
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-2.5_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-2.5_small@2x.png b/public/pictures/licenses/CC-BY-2.5_small@2x.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-2.5_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-3.0_medium.png b/public/pictures/licenses/CC-BY-3.0_medium.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-3.0_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-3.0_medium.webp b/public/pictures/licenses/CC-BY-3.0_medium.webp
new file mode 100644
index 0000000..cee6dc8
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-3.0_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-3.0_medium@2x.png b/public/pictures/licenses/CC-BY-3.0_medium@2x.png
deleted file mode 100644
index 3bfad3d..0000000
--- a/public/pictures/licenses/CC-BY-3.0_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-3.0_normal.png b/public/pictures/licenses/CC-BY-3.0_normal.png
deleted file mode 100644
index c5236c9..0000000
--- a/public/pictures/licenses/CC-BY-3.0_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-3.0_normal.webp b/public/pictures/licenses/CC-BY-3.0_normal.webp
new file mode 100644
index 0000000..16d7b88
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-3.0_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-3.0_normal@2x.png b/public/pictures/licenses/CC-BY-3.0_normal@2x.png
deleted file mode 100644
index 12a8450..0000000
--- a/public/pictures/licenses/CC-BY-3.0_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-3.0_small.png b/public/pictures/licenses/CC-BY-3.0_small.png
deleted file mode 100644
index 963d3a4..0000000
--- a/public/pictures/licenses/CC-BY-3.0_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-3.0_small.webp b/public/pictures/licenses/CC-BY-3.0_small.webp
new file mode 100644
index 0000000..05d0e9a
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-3.0_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-3.0_small@2x.png b/public/pictures/licenses/CC-BY-3.0_small@2x.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-3.0_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-4.0_medium.png b/public/pictures/licenses/CC-BY-4.0_medium.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-4.0_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-4.0_medium.webp b/public/pictures/licenses/CC-BY-4.0_medium.webp
new file mode 100644
index 0000000..cee6dc8
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-4.0_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-4.0_medium@2x.png b/public/pictures/licenses/CC-BY-4.0_medium@2x.png
deleted file mode 100644
index 3bfad3d..0000000
--- a/public/pictures/licenses/CC-BY-4.0_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-4.0_normal.png b/public/pictures/licenses/CC-BY-4.0_normal.png
deleted file mode 100644
index d8568aa..0000000
--- a/public/pictures/licenses/CC-BY-4.0_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-4.0_normal.webp b/public/pictures/licenses/CC-BY-4.0_normal.webp
new file mode 100644
index 0000000..16d7b88
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-4.0_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-4.0_normal@2x.png b/public/pictures/licenses/CC-BY-4.0_normal@2x.png
deleted file mode 100644
index 12a8450..0000000
--- a/public/pictures/licenses/CC-BY-4.0_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-4.0_small.png b/public/pictures/licenses/CC-BY-4.0_small.png
deleted file mode 100644
index 76a3de3..0000000
--- a/public/pictures/licenses/CC-BY-4.0_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-4.0_small.webp b/public/pictures/licenses/CC-BY-4.0_small.webp
new file mode 100644
index 0000000..05d0e9a
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-4.0_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-4.0_small@2x.png b/public/pictures/licenses/CC-BY-4.0_small@2x.png
deleted file mode 100644
index cc70c2a..0000000
--- a/public/pictures/licenses/CC-BY-4.0_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-1.0_medium.png b/public/pictures/licenses/CC-BY-SA-1.0_medium.png
deleted file mode 100644
index 725f1da..0000000
--- a/public/pictures/licenses/CC-BY-SA-1.0_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-1.0_medium.webp b/public/pictures/licenses/CC-BY-SA-1.0_medium.webp
new file mode 100644
index 0000000..3e13a4d
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-1.0_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-1.0_medium@2x.png b/public/pictures/licenses/CC-BY-SA-1.0_medium@2x.png
deleted file mode 100644
index 8e2a625..0000000
--- a/public/pictures/licenses/CC-BY-SA-1.0_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-1.0_normal.png b/public/pictures/licenses/CC-BY-SA-1.0_normal.png
deleted file mode 100644
index 9257b2d..0000000
--- a/public/pictures/licenses/CC-BY-SA-1.0_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-1.0_normal.webp b/public/pictures/licenses/CC-BY-SA-1.0_normal.webp
new file mode 100644
index 0000000..d718791
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-1.0_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-1.0_normal@2x.png b/public/pictures/licenses/CC-BY-SA-1.0_normal@2x.png
deleted file mode 100644
index 769fe78..0000000
--- a/public/pictures/licenses/CC-BY-SA-1.0_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-1.0_small.png b/public/pictures/licenses/CC-BY-SA-1.0_small.png
deleted file mode 100644
index 15e2ca3..0000000
--- a/public/pictures/licenses/CC-BY-SA-1.0_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-1.0_small.webp b/public/pictures/licenses/CC-BY-SA-1.0_small.webp
new file mode 100644
index 0000000..69bc29a
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-1.0_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-1.0_small@2x.png b/public/pictures/licenses/CC-BY-SA-1.0_small@2x.png
deleted file mode 100644
index 725f1da..0000000
--- a/public/pictures/licenses/CC-BY-SA-1.0_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.0_medium.png b/public/pictures/licenses/CC-BY-SA-2.0_medium.png
deleted file mode 100644
index 17ad272..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.0_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.0_medium.webp b/public/pictures/licenses/CC-BY-SA-2.0_medium.webp
new file mode 100644
index 0000000..5e46086
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-2.0_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.0_medium@2x.png b/public/pictures/licenses/CC-BY-SA-2.0_medium@2x.png
deleted file mode 100644
index 9769397..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.0_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.0_normal.png b/public/pictures/licenses/CC-BY-SA-2.0_normal.png
deleted file mode 100644
index 1a461d6..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.0_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.0_normal.webp b/public/pictures/licenses/CC-BY-SA-2.0_normal.webp
new file mode 100644
index 0000000..5c88fd8
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-2.0_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.0_normal@2x.png b/public/pictures/licenses/CC-BY-SA-2.0_normal@2x.png
deleted file mode 100644
index a22f351..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.0_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.0_small.png b/public/pictures/licenses/CC-BY-SA-2.0_small.png
deleted file mode 100644
index 4ef2b7c..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.0_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.0_small.webp b/public/pictures/licenses/CC-BY-SA-2.0_small.webp
new file mode 100644
index 0000000..0ec7f2d
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-2.0_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.0_small@2x.png b/public/pictures/licenses/CC-BY-SA-2.0_small@2x.png
deleted file mode 100644
index 14af68a..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.0_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.5_medium.png b/public/pictures/licenses/CC-BY-SA-2.5_medium.png
deleted file mode 100644
index 725f1da..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.5_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.5_medium.webp b/public/pictures/licenses/CC-BY-SA-2.5_medium.webp
new file mode 100644
index 0000000..3e13a4d
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-2.5_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.5_medium@2x.png b/public/pictures/licenses/CC-BY-SA-2.5_medium@2x.png
deleted file mode 100644
index 8e2a625..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.5_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.5_normal.png b/public/pictures/licenses/CC-BY-SA-2.5_normal.png
deleted file mode 100644
index 9257b2d..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.5_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.5_normal.webp b/public/pictures/licenses/CC-BY-SA-2.5_normal.webp
new file mode 100644
index 0000000..d718791
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-2.5_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.5_normal@2x.png b/public/pictures/licenses/CC-BY-SA-2.5_normal@2x.png
deleted file mode 100644
index 769fe78..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.5_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.5_small.png b/public/pictures/licenses/CC-BY-SA-2.5_small.png
deleted file mode 100644
index 39a017d..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.5_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.5_small.webp b/public/pictures/licenses/CC-BY-SA-2.5_small.webp
new file mode 100644
index 0000000..69bc29a
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-2.5_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-2.5_small@2x.png b/public/pictures/licenses/CC-BY-SA-2.5_small@2x.png
deleted file mode 100644
index 725f1da..0000000
--- a/public/pictures/licenses/CC-BY-SA-2.5_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-3.0_medium.png b/public/pictures/licenses/CC-BY-SA-3.0_medium.png
deleted file mode 100644
index 725f1da..0000000
--- a/public/pictures/licenses/CC-BY-SA-3.0_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-3.0_medium.webp b/public/pictures/licenses/CC-BY-SA-3.0_medium.webp
new file mode 100644
index 0000000..3e13a4d
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-3.0_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-3.0_medium@2x.png b/public/pictures/licenses/CC-BY-SA-3.0_medium@2x.png
deleted file mode 100644
index 8e2a625..0000000
--- a/public/pictures/licenses/CC-BY-SA-3.0_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-3.0_normal.png b/public/pictures/licenses/CC-BY-SA-3.0_normal.png
deleted file mode 100644
index 9257b2d..0000000
--- a/public/pictures/licenses/CC-BY-SA-3.0_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-3.0_normal.webp b/public/pictures/licenses/CC-BY-SA-3.0_normal.webp
new file mode 100644
index 0000000..d718791
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-3.0_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-3.0_normal@2x.png b/public/pictures/licenses/CC-BY-SA-3.0_normal@2x.png
deleted file mode 100644
index 769fe78..0000000
--- a/public/pictures/licenses/CC-BY-SA-3.0_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-3.0_small.png b/public/pictures/licenses/CC-BY-SA-3.0_small.png
deleted file mode 100644
index 4fb11a0..0000000
--- a/public/pictures/licenses/CC-BY-SA-3.0_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-3.0_small.webp b/public/pictures/licenses/CC-BY-SA-3.0_small.webp
new file mode 100644
index 0000000..69bc29a
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-3.0_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-3.0_small@2x.png b/public/pictures/licenses/CC-BY-SA-3.0_small@2x.png
deleted file mode 100644
index 725f1da..0000000
--- a/public/pictures/licenses/CC-BY-SA-3.0_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-4.0_medium.png b/public/pictures/licenses/CC-BY-SA-4.0_medium.png
deleted file mode 100644
index 725f1da..0000000
--- a/public/pictures/licenses/CC-BY-SA-4.0_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-4.0_medium.webp b/public/pictures/licenses/CC-BY-SA-4.0_medium.webp
new file mode 100644
index 0000000..3e13a4d
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-4.0_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-4.0_medium@2x.png b/public/pictures/licenses/CC-BY-SA-4.0_medium@2x.png
deleted file mode 100644
index 8e2a625..0000000
--- a/public/pictures/licenses/CC-BY-SA-4.0_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-4.0_normal.png b/public/pictures/licenses/CC-BY-SA-4.0_normal.png
deleted file mode 100644
index 9257b2d..0000000
--- a/public/pictures/licenses/CC-BY-SA-4.0_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-4.0_normal.webp b/public/pictures/licenses/CC-BY-SA-4.0_normal.webp
new file mode 100644
index 0000000..d718791
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-4.0_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-4.0_normal@2x.png b/public/pictures/licenses/CC-BY-SA-4.0_normal@2x.png
deleted file mode 100644
index 769fe78..0000000
--- a/public/pictures/licenses/CC-BY-SA-4.0_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-4.0_small.png b/public/pictures/licenses/CC-BY-SA-4.0_small.png
deleted file mode 100644
index 3d718e9..0000000
--- a/public/pictures/licenses/CC-BY-SA-4.0_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-4.0_small.webp b/public/pictures/licenses/CC-BY-SA-4.0_small.webp
new file mode 100644
index 0000000..69bc29a
--- /dev/null
+++ b/public/pictures/licenses/CC-BY-SA-4.0_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC-BY-SA-4.0_small@2x.png b/public/pictures/licenses/CC-BY-SA-4.0_small@2x.png
deleted file mode 100644
index 725f1da..0000000
--- a/public/pictures/licenses/CC-BY-SA-4.0_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC0-1.0_medium.png b/public/pictures/licenses/CC0-1.0_medium.png
deleted file mode 100644
index 6b33ed0..0000000
--- a/public/pictures/licenses/CC0-1.0_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC0-1.0_medium.webp b/public/pictures/licenses/CC0-1.0_medium.webp
new file mode 100644
index 0000000..c4894db
--- /dev/null
+++ b/public/pictures/licenses/CC0-1.0_medium.webp
Binary files differ
diff --git a/public/pictures/licenses/CC0-1.0_medium@2x.png b/public/pictures/licenses/CC0-1.0_medium@2x.png
deleted file mode 100644
index bef4729..0000000
--- a/public/pictures/licenses/CC0-1.0_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC0-1.0_normal.png b/public/pictures/licenses/CC0-1.0_normal.png
deleted file mode 100644
index 70c7b8e..0000000
--- a/public/pictures/licenses/CC0-1.0_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC0-1.0_normal.webp b/public/pictures/licenses/CC0-1.0_normal.webp
new file mode 100644
index 0000000..6066916
--- /dev/null
+++ b/public/pictures/licenses/CC0-1.0_normal.webp
Binary files differ
diff --git a/public/pictures/licenses/CC0-1.0_normal@2x.png b/public/pictures/licenses/CC0-1.0_normal@2x.png
deleted file mode 100644
index 67c7bf1..0000000
--- a/public/pictures/licenses/CC0-1.0_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC0-1.0_small.png b/public/pictures/licenses/CC0-1.0_small.png
deleted file mode 100644
index e911016..0000000
--- a/public/pictures/licenses/CC0-1.0_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/licenses/CC0-1.0_small.webp b/public/pictures/licenses/CC0-1.0_small.webp
new file mode 100644
index 0000000..d6ebf81
--- /dev/null
+++ b/public/pictures/licenses/CC0-1.0_small.webp
Binary files differ
diff --git a/public/pictures/licenses/CC0-1.0_small@2x.png b/public/pictures/licenses/CC0-1.0_small@2x.png
deleted file mode 100644
index 8bbd5f0..0000000
--- a/public/pictures/licenses/CC0-1.0_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/user/.gitkeep b/public/pictures/user/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/public/pictures/user/.gitkeep
diff --git a/public/pictures/user/nobody_medium.png b/public/pictures/user/nobody_medium.png
deleted file mode 100644
index 73f6adc..0000000
--- a/public/pictures/user/nobody_medium.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/user/nobody_medium@2x.png b/public/pictures/user/nobody_medium@2x.png
deleted file mode 100644
index 726a9c9..0000000
--- a/public/pictures/user/nobody_medium@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/user/nobody_normal.png b/public/pictures/user/nobody_normal.png
deleted file mode 100644
index 57e29d9..0000000
--- a/public/pictures/user/nobody_normal.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/user/nobody_normal@2x.png b/public/pictures/user/nobody_normal@2x.png
deleted file mode 100644
index 9b926db..0000000
--- a/public/pictures/user/nobody_normal@2x.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/user/nobody_small.png b/public/pictures/user/nobody_small.png
deleted file mode 100644
index a7ebfe8..0000000
--- a/public/pictures/user/nobody_small.png
+++ /dev/null
Binary files differ
diff --git a/public/pictures/user/nobody_small@2x.png b/public/pictures/user/nobody_small@2x.png
deleted file mode 100644
index 1e0034b..0000000
--- a/public/pictures/user/nobody_small@2x.png
+++ /dev/null
Binary files differ
diff --git a/templates/loginform.php b/templates/loginform.php
index edbd565..348ccee 100644
--- a/templates/loginform.php
+++ b/templates/loginform.php
@@ -65,7 +65,6 @@ if (!match_route('web_migrate.php')) {
<?= CSRFProtection::tokenTag() ?>
<input type="hidden" name="login_ticket" value="<?=Seminar_Session::get_ticket();?>">
<input type="hidden" name="resolution" value="">
- <input type="hidden" name="device_pixel_ratio" value="1">
<?= Button::createAccept(_('Anmelden'), _('Login')); ?>
<?= LinkButton::create(_('Abbrechen'), URLHelper::getURL('index.php', ['cancel_login' => 1], true)) ?>
</form>
@@ -93,7 +92,6 @@ if (!match_route('web_migrate.php')) {
$(function () {
$('form[name=login]').submit(function () {
$('input[name=resolution]', this).val( screen.width + 'x' + screen.height );
- $('input[name=device_pixel_ratio]').val(window.devicePixelRatio || 1);
});
});
// -->
diff --git a/templates/sidebar/sidebar.php b/templates/sidebar/sidebar.php
index fa8ee57..9aa72de 100644
--- a/templates/sidebar/sidebar.php
+++ b/templates/sidebar/sidebar.php
@@ -4,7 +4,7 @@
<? if ($avatar) : ?>
<div class="sidebar-context">
<? if ($avatar->is_customized()) : ?>
- <a href="<?= htmlReady($avatar->getURL(file_exists($avatar->getFilename(Avatar::ORIGINAL)) ? Avatar::ORIGINAL : Avatar::NORMAL)) ?>"
+ <a href="<?= htmlReady($avatar->getURL(Avatar::NORMAL)) ?>"
data-lightbox="sidebar-avatar"
data-title="<?= htmlReady(PageLayout::getTitle()) ?>">
<? endif ?>
diff --git a/tests/unit/lib/classes/AvatarClassTest.php b/tests/unit/lib/classes/AvatarClassTest.php
index 88b24bd..8172e20 100644
--- a/tests/unit/lib/classes/AvatarClassTest.php
+++ b/tests/unit/lib/classes/AvatarClassTest.php
@@ -11,136 +11,152 @@
require_once 'lib/phplib/Seminar_Perm.class.php';
-/**
- * Testcase for Avatar class.
- *
- * @package studip
- * @subpackage test
- *
- * @author mlunzena
- * @copyright (c) Authors
- */
-class AvatarTestCase extends \Codeception\Test\Unit
+abstract class AvatarTest extends \Codeception\Test\Unit
{
- private $avatar_id;
- private $avatar;
+ protected $avatar_id;
+ protected $avatar;
- public function setUp(): void
+ abstract protected function getType(): string;
+
+ protected function createPath(string $avatar_id, ?string $size, bool $subdir): string
{
- $stub = $this->createMock('Seminar_Perm');
- // Configure the stub.
- $stub->expects($this->any())
- ->method('have_perm')
- ->will($this->returnValue(true));
+ $result = $avatar_id;
- $GLOBALS['perm'] = $stub;
- $GLOBALS['DYNAMIC_CONTENT_URL'] = "/dynamic";
- $GLOBALS['DYNAMIC_CONTENT_PATH'] = "/dynamic";
- $this->avatar_id = "123456789";
- $this->avatar = Avatar::getAvatar($this->avatar_id);
+ if ($size) {
+ $result .= "_{$size}";
+ }
+
+ $result .= '.' . Avatar::EXTENSION;
+
+ if ($subdir) {
+ $result = substr($avatar_id, 0, 2) . '/' . $result;
+ }
+ return "/dynamic/{$this->getType()}/{$result}";
}
- public function tearDown(): void
+ protected function createFixedPath(?string $size): string
+ {
+ $result = 'nobody';
+
+ if ($size) {
+ $result .= "_{$size}";
+ }
+
+ $result .= '.' . Avatar::EXTENSION;
+
+ return "/fixed/images/avatars/{$this->getType()}/{$result}";
+ }
+
+ protected function setUp(): void
{
- unset($GLOBALS['DYNAMIC_CONTENT_PATH'], $GLOBALS['DYNAMIC_CONTENT_URL']);
+ $GLOBALS['DYNAMIC_CONTENT_URL'] = "/dynamic";
+ $GLOBALS['DYNAMIC_CONTENT_PATH'] = "/dynamic";
+ $GLOBALS['ASSETS_URL'] = "/fixed/";
+ $GLOBALS['ASSETS_PATH'] = "/fixed/";
+
+ Assets::set_assets_url($GLOBALS['ASSETS_URL']);
+ Assets::set_assets_path($GLOBALS['ASSETS_PATH']);
}
- public function test_class_should_exist()
+ public function tearDown(): void
{
- $this->assertTrue(class_exists('Avatar'));
+ unset(
+ $GLOBALS['DYNAMIC_CONTENT_PATH'],
+ $GLOBALS['DYNAMIC_CONTENT_URL'],
+ $GLOBALS['ASSETS_URL'],
+ $GLOBALS['ASSETS_PATH']
+ );
}
public function test_avatar_url()
{
- $url = $this->avatar->getCustomAvatarUrl(Avatar::NORMAL);
- $this->assertEquals("/dynamic/user/" . $this->avatar_id . "_normal.png?d=0", $url);
+ $this->assertEquals(
+ $this->createPath($this->avatar_id, Avatar::NORMAL, true) . '?d=0',
+ $this->avatar->getCustomAvatarUrl(Avatar::NORMAL)
+ );
}
public function test_avatar_path()
{
- $path = $this->avatar->getCustomAvatarPath(Avatar::NORMAL);
- $this->assertEquals("/dynamic/user/" . $this->avatar_id . "_normal.png", $path);
+ $this->assertEquals(
+ $this->createPath($this->avatar_id, Avatar::NORMAL, true),
+ $this->avatar->getCustomAvatarPath(Avatar::NORMAL)
+ );
}
public function test_nobody_url()
{
- $url = Avatar::getNobody()->getUrl(Avatar::NORMAL);
- $this->assertEquals("/dynamic/user/nobody_normal.png?d=0", $url);
+ $this->assertEquals(
+ $this->createFixedPath(Avatar::NORMAL),
+ $this->avatar->getNobody()->getURL(Avatar::NORMAL)
+ );
}
public function test_nobody_path()
{
- $path = Avatar::getNobody()->getCustomAvatarPath(Avatar::NORMAL);
- $this->assertEquals("/dynamic/user/nobody_normal.png", $path);
+ $this->assertEquals(
+ $this->createFixedPath(Avatar::NORMAL),
+ $this->avatar->getNobody()->getFilename(Avatar::NORMAL)
+ );
}
}
-
-class CourseAvatarTestCase extends \Codeception\Test\Unit
+/**
+ * Testcase for Avatar class.
+ *
+ * @package studip
+ * @subpackage test
+ *
+ * @author mlunzena
+ * @copyright (c) Authors
+ */
+class AvatarTestCase extends AvatarTest
{
- private $avatar_id;
- private $avatar;
-
public function setUp(): void
{
- $this->avatar_id = "123456789";
- $this->avatar = CourseAvatar::getAvatar($this->avatar_id);
-
- $this->setUpFS();
+ parent::setUp();
- $GLOBALS['DYNAMIC_CONTENT_URL'] = "/dynamic";
- $GLOBALS['DYNAMIC_CONTENT_PATH'] = "/dynamic";
- }
+ $stub = $this->createMock('Seminar_Perm');
+ // Configure the stub.
+ $stub->expects($this->any())
+ ->method('have_perm')
+ ->will($this->returnValue(true));
- private function setUpFS()
- {
- ArrayFileStream::set_filesystem([
- 'dynamic' => [
- 'course' => [
- $this->avatar_id . '_normal.png' => '',
- $this->avatar_id . '_medium.png' => '',
- $this->avatar_id . '_small.png' => '',
- ],
- ],
- ]);
-
- if (!stream_wrapper_register("var", "ArrayFileStream")) {
- throw new Exception("Failed to register protocol");
- }
- }
+ $GLOBALS['perm'] = $stub;
- public function tearDown(): void
- {
- stream_wrapper_unregister("var");
- unset($GLOBALS['DYNAMIC_CONTENT_PATH'], $GLOBALS['DYNAMIC_CONTENT_URL']);
+ $this->avatar_id = "123456789";
+ $this->avatar = Avatar::getAvatar($this->avatar_id);
}
public function test_class_should_exist()
{
- $this->assertTrue(class_exists('CourseAvatar'));
+ $this->assertTrue(class_exists(Avatar::class));
}
- public function test_avatar_url()
+ protected function getType(): string
{
- $url = $this->avatar->getCustomAvatarUrl(Avatar::NORMAL);
- $this->assertEquals("/dynamic/course/". $this->avatar_id . "_normal.png?d=0", $url);
+ return 'user';
}
+}
- public function test_avatar_path()
+
+class CourseAvatarTestCase extends AvatarTest
+{
+ public function setUp(): void
{
- $path = $this->avatar->getCustomAvatarPath(Avatar::NORMAL);
- $this->assertEquals("/dynamic/course/". $this->avatar_id . "_normal.png", $path);
+ parent::setUp();
+
+ $this->avatar_id = "123456789";
+ $this->avatar = CourseAvatar::getAvatar($this->avatar_id);
}
- public function test_nobody_url()
+ public function test_class_should_exist()
{
- $url = CourseAvatar::getNobody()->getUrl(Avatar::NORMAL);
- $this->assertEquals("/dynamic/course/nobody_normal.png?d=0", $url);
+ $this->assertTrue(class_exists(CourseAvatar::class));
}
- public function test_nobody_path()
+ protected function getType(): string
{
- $path = CourseAvatar::getNobody()->getCustomAvatarPath(Avatar::NORMAL);
- $this->assertEquals("/dynamic/course/nobody_normal.png", $path);
+ return 'course';
}
}