🔀 merge pull request 'v1.0.0' (#8) from dev into main

Reviewed-on: #8
This commit is contained in:
DrMaxNix 2024-03-01 17:00:10 +01:00
commit b414648015
18 changed files with 806 additions and 6 deletions

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2023 Kim Endisch Copyright (c) 2024 Kim Endisch
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

BIN
asset/logo-1024.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
asset/logo-2048.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
asset/logo-256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
asset/logo-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
asset/logo-bg-1024.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
asset/logo-bg-2048.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
asset/logo-bg-256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
asset/logo-bg-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

12
asset/logo.svg Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32" height="32" version="1.1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="#c5cad3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path d="m4 5v8a1 1 45 0 0 1 1h8a1 1 135 0 0 1-1v-8a1 1 45 0 0-1-1h-8a1 1 135 0 0-1 1z"/>
<path transform="translate(-.1099 13.704)" d="m4 5.2965v8a1 1 45 0 0 1 1h8.1099a1 1 135 0 0 1-1v-8a1 1 45 0 0-1-1h-8.1099a1 1 135 0 0-1 1z"/>
<path transform="translate(15.048 -.2728)" d="m2.952 5.2728v22a1 1 45 0 0 1 1h8a1 1 135 0 0 1-1v-22a1 1 45 0 0-1-1h-8a1 1 135 0 0-1 1z"/>
<path d="m7 9h4"/>
<path d="m9 7v4"/>
<path d="m23 16v4"/>
<path d="m23 12v1e-3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 715 B

35
init.php Normal file
View File

@ -0,0 +1,35 @@
<?php
declare(strict_types = 1);
namespace Kimendisch\Thi_Hub;
use Flake\Lang;
use Flake\Lang_Dict;
// DATA CLASS FOR STORING GLOBAL DATA //
class Data {
/**
* @var string $lang Lang code for current client.
*/
public static string $lang;
/**
* @var Lang $lang_object Lang object for current client.
*/
public static Lang $lang_object;
/**
* @var Lang_Dict $dict Dict object for current client.
*/
public static Lang_Dict $dict;
/**
* @var array $alternate Urls for this page in alternate language.
*/
public static array $alternate = [];
}
?>

View File

@ -1,11 +1,16 @@
<?php <?php
// VERSION // // VERSION //
static::$version = "0.1.0"; static::$version = "1.0.0";
// DEPENDENCIES // // DEPENDENCIES //
// used extensions // used extensions
static::$ext[] = "excuse"; static::$ext[] = "project";
static::$ext[] = "lang";
static::$ext[] = "request";
static::$ext[] = "url";
static::$ext[] = "page";
static::$ext[] = "file";
// ROUTES // // ROUTES //
@ -16,6 +21,7 @@
// pages // pages
static::$route["thi-hub.de"] = [ static::$route["thi-hub.de"] = [
["path" => "", "target" => "page/start"] ["path" => "", "target" => "page/start"],
["path" => ":lang", "target" => "page/start"],
]; ];
?> ?>

34
page/footer.php Normal file
View File

@ -0,0 +1,34 @@
<?php
declare(strict_types = 1);
namespace Kimendisch\Thi_Hub;
use Flake\File;
use Flake\Project;
?>
<div class="footer">
<div class="brand">
<img src="<?= File::file("./asset/logo-256.png") ?>" alt="logo" />
<span>THI-Hub.de</span>
<a href="https://git.tjdev.de/kimendisch/thi-hub.de" target="_blank"><?= Data::$dict->get("text_sourcecode") ?> <i class="ti ti-external-link"></i></a>
<span class="version">v<?= Project::version() ?></span>
</div>
<div class="lang">
<span><i class="ti ti-world"></i></span>
<a <?= (Data::$lang === "de" ? "class=\"selected\"" : "") ?> href="<?= Data::$alternate["de"] ?>">DE</a>
<span class="delimiter">|</span>
<a <?= (Data::$lang === "en" ? "class=\"selected\"" : "") ?> href="<?= Data::$alternate["en"] ?>">EN</a>
</div>
<div class="legal">
<span>&copy; 2024 Kim Endisch</span>
<span class="delimiter">|</span>
<a href="<?= Data::$dict->get("link_imprint") ?>" target="_blank"><?= Data::$dict->get("text_imprint") ?> <i class="ti ti-external-link"></i></a>
<span class="delimiter">|</span>
<a href="<?= Data::$dict->get("link_privacy_policy") ?>" target="_blank"><?= Data::$dict->get("text_privacy_policy") ?> <i class="ti ti-external-link"></i></a>
</div>
</div>

55
page/lang_autoselect.php Normal file
View File

@ -0,0 +1,55 @@
<?php
declare(strict_types = 1);
namespace Kimendisch\Thi_Hub;
use Flake\Project;
use Flake\Lang;
use Flake\Lang_Dict;
use Flake\Request;
use Flake\Url;
use Flake\Url_redirect;
// MAYBE REDIRECT //
// get selected language
$param_lang = Project::param("lang");
// validate
if(!in_array($param_lang, ["de", "en"])){
// try to find best language
unset($_GET["lang"]);
if(isset($_COOKIE[Lang::COOKIE_NAME])) unset($_COOKIE[Lang::COOKIE_NAME]);
$lang = new Lang(list: ["de", "en"], default: "de");
// redirect
$path_raw = Request::path_raw();
if($param_lang !== null) $path_raw = array_splice($path_raw, 1);
array_unshift($path_raw, $lang->get());
$path_raw_full = implode("/", $path_raw);
Url_redirect::path_modify($path_raw_full);
}
// LANGUAGE MANAGER //
// hack: fake get param from url path
$_GET["lang"] = $param_lang;
// initialize
Data::$lang_object = new Lang(list: ["de", "en"], default: "de");
Data::$lang = Data::$lang_object->get();
// load dict
Data::$dict = new Lang_Dict(Data::$lang_object);
require("./page/strings.php");
// BUILD ALTERNATE LANGUAGE URLS //
// get content path
$content_path_raw = Request::path_raw();
$content_path_raw = array_splice($content_path_raw, 1);
// prepend lang codes
foreach(["de", "en"] as $one_lang_code){
$path_raw = $content_path_raw;
array_unshift($path_raw, $one_lang_code);
Data::$alternate[$one_lang_code] = Url::path_modify(implode("/", $path_raw));
}
?>

21
page/page_base.php Normal file
View File

@ -0,0 +1,21 @@
<?php
declare(strict_types = 1);
namespace Kimendisch\Thi_Hub;
use Flake\Page;
// PAGE INIT //
Page::start();
Page::icon("./asset/logo-256.png");
Page::lang(Data::$lang);
Page::viewport(scale: 1, zoom: true);
Page::$head["alternate_de"] = '<link rel="alternate" hreflang="de" href="' . Data::$alternate["de"] . '" />';
Page::$head["alternate_en"] = '<link rel="alternate" hreflang="en" href="' . Data::$alternate["en"] . '" />';
Page::author("Kim Endisch");
Page::$head["analytics"] = '<script defer data-domain="thi-hub.de" src="https://analytics.tjdev.de/js/script.js"></script>';
Page::css("./page/style.css");
Page::font("ubuntu");
Page::font("tabler");
?>

View File

@ -1,7 +1,65 @@
<?php <?php
declare(strict_types = 1); declare(strict_types = 1);
namespace Kimendisch\Thi_Hub; namespace Kimendisch\Thi_Hub;
use Flake\Excuse; use Flake\Page;
Excuse::show("service_unavailable"); // AUTOSELECT LANGUAGE //
require("./page/lang_autoselect.php");
// PAGE INIT //
require("./page/page_base.php");
Page::title(Data::$dict->get("title"));
Page::description(Data::$dict->get("description"));
Page::robots(index: true, follow: true);
Page::keywords("thi", "thi hub", "thi links", "hochschule", "ingolstadt", "hochschule ingolstadt");
?>
<div class="page-container">
<div class="page">
<div class="section quicklinks">
<div class="content">
<div class="button-list">
<?php foreach(Data::$dict->get("quicklink_list") as $one_quicklink){ ?>
<a href="<?= $one_quicklink["url"] ?>" target="_blank" class="button big <?= $one_quicklink["color"] ?>">
<span class="icon big ti ti-<?= $one_quicklink["icon"] ?>"></span>
<div class="text">
<span><?= $one_quicklink["name"] ?></span>
<span class="gray-dark"><?= $one_quicklink["extra"] ?></span>
</div>
</a>
<?php } ?>
</div>
</div>
</div>
<div class="section about">
<div class="header">
<span class="icon ti ti-info-circle"></span>
<span class="text"><?= Data::$dict->get("about_title") ?></span>
</div>
<div class="content">
<div class="box">
<?php
$about_text = Data::$dict->get("about_text");
foreach($about_text as $one_about_text_line){
echo("<span>" . $one_about_text_line . "</span>");
}
?>
</div>
</div>
</div>
</div>
</div>
<?php
require("./page/footer.php");
?> ?>

136
page/strings.php Normal file
View File

@ -0,0 +1,136 @@
<?php
declare(strict_types = 1);
namespace Kimendisch\Thi_Hub;
Data::$dict->define([
"title" => [
"de" => "THI-Hub.de",
"en" => "THI-Hub.de",
],
"description" => [
"de" => "Ein übersichtlicher Hub für Student*innen der THI",
"en" => "A simple hub for THI students",
],
"quicklink_list" => [
"de" => [
[
"color" => "blue",
"icon" => "school",
"name" => "Moodle",
"extra" => "",
"url" => "https://moodle.thi.de/my",
],[
"color" => "red",
"icon" => "settings",
"name" => "Primuss",
"extra" => "Für Studierende",
"url" => "https://www3.primuss.de/cgi-bin/login/index.pl?FH=fhin",
],[
"color" => "green",
"icon" => "calendar-time",
"name" => "Stundenplan",
"extra" => "",
"url" => "https://www3.primuss.de/stpl/index.php?FH=fhin",
],[
"color" => "yellow",
"icon" => "books",
"name" => "Bücherei",
"extra" => "OPAC",
"url" => "https://thi.idm.oclc.org/login?url=https://opac.ku.de/index-hi.html",
],[
"color" => "orange",
"icon" => "mail",
"name" => "Webmail",
"extra" => "Outlook",
"url" => "https://outlook.thi.de",
],
],
"en" => [
[
"color" => "blue",
"icon" => "school",
"name" => "Moodle",
"extra" => "",
"url" => "https://moodle.thi.de/my",
],[
"color" => "red",
"icon" => "settings",
"name" => "Primuss",
"extra" => "For Students",
"url" => "https://www3.primuss.de/cgi-bin/login/index.pl?FH=fhin",
],[
"color" => "green",
"icon" => "calendar-time",
"name" => "Timetable",
"extra" => "",
"url" => "https://www3.primuss.de/stpl/index.php?FH=fhin",
],[
"color" => "yellow",
"icon" => "books",
"name" => "Library",
"extra" => "OPAC",
"url" => "https://thi.idm.oclc.org/login?url=https://opac.ku.de/index-hi.html",
],[
"color" => "orange",
"icon" => "mail",
"name" => "Webmail",
"extra" => "Outlook",
"url" => "https://outlook.thi.de",
],
],
],
"about_title" => [
"de" => "Über diese Seite",
"en" => "About this Page",
],
"about_text" => [
"de" => [
"Eine Webseite nur für THI-Links? Im Ernst? &ndash; Ja, im Ernst!",
"Die Onlineangebote der Technischen Hochschule Ingolstadt sind über eine Vielzahl von Seiten verteilt; Um diese zu erreichen musst Du dich jedes Mal durch komplexe Untermenüs klicken &ndash; Das muss nicht sein!",
"Auf <i>THI-Hub.de</i> hast Du alle wichtigen Links für Dein Studium immer griffbereit! Die großen farbigen Buttons mit Icons helfen Dir dabei, den Richtigen Link blitzschnell zu finden; Ohne Untermenüs oder schreckliche Ladezeiten. Ganz einfach.",
],
"en" => [
"A website just for THI links? Seriously? &ndash; Yes, seriously!",
"The online portals of the Ingolstadt Technical University are distributed over a variety of pages; To reach them, you have to click through complex sub menus every time &ndash; This should not be necessary!",
"On <i>THI-Hub.de</i> you always have all the important links for your studies at hand! The large colored buttons with icons help you to find the right link at lightning speed; Without sub menus or terrible loading times. Very easy.",
],
],
"text_sourcecode" => [
"de" => "Quellcode",
"en" => "Source Code",
],
"text_imprint" => [
"de" => "Impressum",
"en" => "Imprint",
],
"link_imprint" => [
"de" => "https://www.tjdev.de/impressum",
"en" => "https://www.tjdev.de/imprint",
],
"text_privacy_policy" => [
"de" => "Datenschutz&shy;erklärung",
"en" => "Privacy Policy",
],
"link_privacy_policy" => [
"de" => "https://www.tjdev.de/datenschutz",
"en" => "https://www.tjdev.de/privacy",
],
]);
?>

443
page/style.css Normal file
View File

@ -0,0 +1,443 @@
:root {
--color-bg: #21252b;
--color-bg-light: #2c313a;
--color-white: #c5cad3;
--color-gray-light: #acb0b9;
--color-gray: #828997;
--color-gray-dark: #5c6370;
--color-gray-dark-dark: #454b54;
--color-red: #e06c75;
--color-red-light: #e9969d;
--color-red-dark: #cb4d58;
--color-orange: #d19a66;
--color-orange-light: #ddb48d;
--color-orange-dark: #b9804b;
--color-yellow: #dace71;
--color-yellow-light: #e5dc9a;
--color-yellow-dark: #c4b754;
--color-green: #98c379;
--color-green-light: #b3d39c;
--color-green-dark: #7fa762;
--color-cyan: #71d6bc;
--color-cyan-light: #98e1cf;
--color-cyan-dark: #55bea4;
--color-blue: #61afef;
--color-blue-light: #90c7f4;
--color-blue-dark: #3e97e0;
--color-purple: #c678dd;
--color-purple-light: #d7a1e8;
--color-purple-dark: #af5bc8;
--theme: var(--color-cyan);
--theme-light: var(--color-cyan-light);
--theme-dark: var(--color-cyan-dark);
}
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
overflow-x: hidden;
color: var(--color-white);
background-color: var(--color-bg);
}
html {
font-size: 16px;
}
@media only screen and (max-width: 1000px) {
html {
font-size: 14px;
}
}
body {
min-height: 100vh;
min-height: 100dvh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 0;
font-family: "Ubuntu", sans-serif;
}
span {
display: block;
font-size: 1rem;
}
a {
text-decoration: none;
}
span a {
color: var(--theme);
}
span a:hover {
text-decoration: underline;
cursor: pointer;
}
span.gray a {
color: var(--color-gray);
text-decoration: underline;
text-decoration-style: dotted;
}
span.gray a:hover {
text-decoration-style: solid;
}
button {
all: unset;
}
button:focus {
outline: revert;
}
.colored {
color: var(--theme);
}
.gray {
color: var(--color-gray);
}
.gray-dark {
color: var(--color-gray-dark-dark);
}
.white {
color: var(--color-white);
}
.hidden {
visibility: hidden !important;
}
.gone {
display: none !important;
}
.select-none {
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* PAGE LAYOUT */
.page-container {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
.page {
max-width: 60vw;
padding: 6rem 2rem;
display: flex;
flex-direction: column;
gap: 6rem;
text-align: center;
}
@media only screen and (max-width: 1600px) {
.page {
max-width: 75vw;
}
}
@media only screen and (max-width: 1300px) {
.page {
max-width: 85vw;
}
}
@media only screen and (max-width: 1000px) {
.page {
max-width: 100vw;
padding: 6rem 1rem;
}
}
/* PAGE TITLE */
.page > .title {
display: flex;
flex-direction: column;
gap: 1rem;
margin: 2rem 0;
}
@media only screen and (max-width: 1000px) {
.page > .title {
margin: 0 0;
}
}
.page > .title h1, .page > .title h2 {
align-self: center;
margin: 0;
}
.page > .title h1 {
font-size: 3rem;
background-image: linear-gradient(to right, var(--color-red), var(--color-orange), var(--color-yellow), var(--color-green), var(--color-blue), var(--color-purple));
-webkit-background-clip: text;
color: transparent;
}
.page > .title h2 {
font-size: 1.5rem;
}
/* SECTIONS */
.section {
display: flex;
flex-direction: column;
width: 100%;
}
.section > .header {
align-self: flex-start;
position: relative;
padding-right: 0.5rem;
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 0.5rem;
color: var(--theme);
}
.section > .header .extra {
color: var(--color-gray);
}
.section > .content {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem 2rem;
}
.section > .content.rows {
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
justify-content: center;
}
/* BOX */
.box {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
padding: 2rem;
border-radius: 2rem;
background-color: var(--color-bg-light);
}
.box.shrink {
display: inline-flex;
}
.box .title, .box .extra {
display: inline-flex;
flex-direction: row;
justify-content: center;
align-items: baseline;
gap: 0.5rem;
}
.box .title {
position: relative;
left: -0.5rem;
padding-left: 0.5rem;
color: var(--theme);
font-size: 1.5rem;
}
.box .extra {
color: var(--color-gray);
}
/* BUTTONS */
.button-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 1rem;
}
.button, .button.disabled:hover {
align-items: center;
display: inline-flex;
gap: 0.5rem;
width: fit-content;
padding: 1rem 1.5rem;
color: var(--color-white);
border-radius: 1rem;
background-color: var(--color-gray-dark-dark);
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.button.big {
gap: 1rem;
}
.button.on-bg {
background-color: var(--color-bg-light);
}
.button:hover {
cursor: pointer;
}
.button:hover {
background-color: var(--color-gray-dark);
}
.button.on-bg:hover {
background-color: var(--color-gray-dark-dark);
}
.button.disabled {
opacity: 0.3;
}
.button.disabled:hover {
cursor: not-allowed;
}
.button.primary, .button.primary.disabled:hover {
color: var(--color-bg);
background-color: var(--theme);
}
.button.primary:hover {
background-color: var(--theme-dark);
}
.button .icon.big {
font-size: 1.75rem;
}
.button .text {
display: inline-flex;
flex-flow: column;
align-items: flex-start;
}
.button.red, .button.red.disabled:hover, .button.orange, .button.orange.disabled:hover, .button.yellow, .button.yellow.disabled:hover, .button.green, .button.green.disabled:hover, .button.cyan, .button.cyan.disabled:hover, .button.blue, .button.blue.disabled:hover, .button.purple, .button.purple.disabled:hover {
color: var(--color-bg);
}
.button.red, .button.red.disabled:hover { background-color: var(--color-red); }
.button.red:hover { background-color: var(--color-red-dark); }
.button.orange, .button.orange.disabled:hover { background-color: var(--color-orange); }
.button.orange:hover { background-color: var(--color-orange-dark); }
.button.yellow, .button.yellow.disabled:hover { background-color: var(--color-yellow); }
.button.yellow:hover { background-color: var(--color-yellow-dark); }
.button.green, .button.green.disabled:hover { background-color: var(--color-green); }
.button.green:hover { background-color: var(--color-green-dark); }
.button.cyan, .button.cyan.disabled:hover { background-color: var(--color-cyan); }
.button.cyan:hover { background-color: var(--color-cyan-dark); }
.button.blue, .button.blue.disabled:hover { background-color: var(--color-blue); }
.button.blue:hover { background-color: var(--color-blue-dark); }
.button.purple, .button.purple.disabled:hover { background-color: var(--color-purple); }
.button.purple:hover { background-color: var(--color-purple-dark); }
/* FOOTER */
.footer {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: wrap;
gap: 1rem;
padding: 1rem;
background-color: var(--color-bg-light);
}
.footer > div {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 0.5rem;
text-align: center;
}
@media only screen and (max-width: 1000px) {
.footer > div {
flex-basis: 100%;
justify-content: center !important;
}
}
.footer > div a {
font-size: 1rem;
color: var(--theme);
}
.footer > div a:hover {
text-decoration: underline;
}
.footer .delimiter {
color: var(--color-gray);
}
.footer .brand {
justify-content: flex-start;
}
.footer .legal {
justify-content: flex-end;
}
.footer .brand img {
height: 2rem;
}
.footer .brand .version {
color: var(--color-gray);
}
.footer .lang .selected {
font-weight: bold;
color: inherit;
}