Partially overhauled content system's metadata handling, Fixed & Added tools, Added missing resources, Other minor fixes, Added index compiler

Update .gitignore, .htaccess, and 48 more files...
This commit is contained in:
2024-04-18 17:29:08 +02:00
parent be941fccca
commit 2827d6f8f6
51 changed files with 3102 additions and 220 deletions

View File

@@ -25,4 +25,7 @@ if($enable_gallery) {
if($enable_waffle_iron) {
echo('<link href="/resources/NibblePoker/css/snowflakes.min.css" rel="stylesheet"/>');
}
if($enable_debug_extras) {
echo('<link href="/resources/NibblePoker/css/debugger.min.css" rel="stylesheet"/>');
}
?>

View File

@@ -24,4 +24,7 @@ if($enable_code_highlight) {
if($enable_kitty_and_doggo_sounds) {
echo('<script src="/resources/NibblePoker/js/nibblepoker-contributors.min.js"></script>');
}
if($enable_debug_extras) {
echo('<script src="/resources/NibblePoker/js/nibblepoker-debug.min.js"></script>');
}
?>

View File

@@ -37,12 +37,11 @@ function printSidebarEntry($url, $title, $icon, $activeId) {
<?php
printSidebarEntry(l10n_url_abs('/content/?tags=application;web'), localize("sidebar.text.applications"), "fad fa-browser", "application");
printSidebarEntry(l10n_url_abs('/content/?tags=library'), localize("sidebar.text.libraries"), "fad fa-puzzle-piece", "library");
//printSidebarEntry(l10n_url_abs('/content/?tags=library'), localize("sidebar.text.libraries"), "fad fa-chart-scatter-3d", "library");
printSidebarEntry(l10n_url_abs('/content/?tags=electronic'), localize("sidebar.text.electronics"), "fad fa-microchip", "electronic");
?>
<?php
//printSidebarEntry(l10n_url_abs('/content/?tags=utility'), localize("sidebar.text.utilities"), "fad fa-toolbox", "utility");
//printSidebarEntry(l10n_url_abs('/content/?tags=3d-print'), localize("sidebar.text.3d-print"), "fad fa-print", "3d-print");
//<hr class="subtle">
//printSidebarEntry(l10n_url_abs('/tools/'), localize("sidebar.text.tools"), "fad fa-tools", "tools");
printSidebarEntry(l10n_url_abs('/tools/'), localize("sidebar.text.tools"), "fad fa-tools", "tools");
?>
<hr class="subtle">
<?php

View File

@@ -33,6 +33,7 @@ $enable_grids = false;
$enable_code_highlight = false;
$enable_gallery = false;
$enable_kitty_and_doggo_sounds = false;
$enable_debug_extras = false;
// Easter-egg optional features
// > Belgium's independence day.

View File

@@ -8,7 +8,7 @@ if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
// Including required helpers.
include_once 'commons/config.php';
include_once 'commons/langs.php';
include_once 'commons/content.php';
include_once 'commons/content/manager.php';
// Required to make headings
include_once 'commons/DOM/utils.php';
@@ -872,7 +872,7 @@ class ComposerElement {
case ComposerElementTypes::TABLE:
// Composing table.
$htmlCode .= '<div class="overflow-x-scroll">';
$htmlCode .= '<div class="overflow-x-auto">';
$htmlCode .= '<table class="' . $this->get_modifiers_classes() . '">';
if(!is_null($this->head)) {

View File

@@ -1,3 +0,0 @@
<?php
?>

View File

@@ -1,19 +0,0 @@
<?php
// Making sure the file is included and not accessed directly.
if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
header('HTTP/1.1 403 Forbidden');
die();
}
// Including required helpers.
include_once 'commons/langs.php';
// Including subclasses.
include_once 'commons/content/data/opengraph.php';
include_once 'commons/content/data/twitter_card.php';
class ContentMetadata {
}
?>

View File

@@ -1,52 +0,0 @@
<?php
// Making sure the file is included and not accessed directly.
if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
header('HTTP/1.1 403 Forbidden');
die();
}
// Including required helpers.
include_once 'commons/langs.php';
class OpenGraphData {
public string $title;
public string $description;
public string $type;
public string $url;
public string $image;
public string $image_type;
function __construct(array $title, array $description, string $type, string $url, string $image,
string $image_type) {
global $default_language;
global $user_language;
$this->title = array_key_exists($user_language, $title) ? $title[$user_language] :
(array_key_exists($default_language, $title) ? $title[$default_language] : $title[0]);
$this->description = array_key_exists($user_language, $description) ? $description[$user_language] :
(array_key_exists($default_language, $description) ? $description[$default_language] : $description[0]);
$this->type = $type;
$this->url = $url;
$this->image = $image;
$this->image_type = $image_type;
}
static function from_json(array $json_data): ?OpenGraphData {
foreach(["title", "description", "type", "url", "image", "image_type"] as $wantedKey) {
if(!key_exists($wantedKey, $json_data)) {
return null;
}
}
return new OpenGraphData(
$json_data["title"],
$json_data["description"],
$json_data["type"],
$json_data["url"],
$json_data["image"],
$json_data["image_type"]
);
}
}
?>

View File

@@ -1,20 +0,0 @@
<?php
/*enum Suit {
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}*/
// See: https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup
/*class ContentTwitterMetadata {
public string $title;
public string $description;
public string $type;
public string $url;
public string $image;
public string $image_type;
}*/
?>

View File

@@ -8,10 +8,10 @@ if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
// Importing required scripts.
include_once 'commons/langs.php';
abstract class ContentDisplayType {
enum EContentDisplayType {
const NONE = 0;
const SEARCH = 1;
const CONTENT = 2;
const DISPLAY = 2;
}
class ContentIndexEntry {
@@ -57,7 +57,7 @@ class ContentManager {
function __construct(string $contentRootPath, string $requestedUrl, ?string $urlTags) {
// Preparing default values
$this->displayType = ContentDisplayType::NONE;
$this->displayType = EContentDisplayType::NONE;
$this->hasError = false;
$this->errorMessageKey = "content.error.message.none";
$this->requestedId = NULL;
@@ -68,14 +68,21 @@ class ContentManager {
// Doing some standard things
$this->processUrl($requestedUrl, $urlTags);
if(!$this->hasError) {
if($this->displayType == ContentDisplayType::SEARCH) {
if($this->displayType == EContentDisplayType::SEARCH) {
$this->loadRootIndex(realpath($contentRootPath . "/index.json"));
} elseif($this->displayType == ContentDisplayType::CONTENT) {
} elseif($this->displayType == EContentDisplayType::DISPLAY) {
$this->prepareContentFilePath($contentRootPath);
}
}
}
/**
* Checks the given url, and determines the display type.
* If given, it also splits the tags and validates them.
* @param string $requestedUrl
* @param string|null $urlTags
* @return void
*/
function processUrl(string $requestedUrl, ?string $urlTags): void {
// Doing some dark magic whose inner workings are lost to times...
$requestedUrlPart = explode(
@@ -84,7 +91,7 @@ class ContentManager {
)[0];
if(strcmp($requestedUrlPart, "/") == 0) {
$this->displayType = ContentDisplayType::SEARCH;
$this->displayType = EContentDisplayType::SEARCH;
if(is_null($urlTags)) {
return;
@@ -110,11 +117,16 @@ class ContentManager {
}
}
} else {
$this->displayType = ContentDisplayType::CONTENT;
$this->displayType = EContentDisplayType::DISPLAY;
$this->requestedId = ltrim(rtrim($requestedUrlPart, "/"), "/");
}
}
/**
* If currently in a "EContentDisplayType::SEARCH" context, load the index into memory.
* @param string $rootIndexFilepath
* @return void
*/
function loadRootIndex(string $rootIndexFilepath): void {
// Loading the content index.
$rawJsonContent = file_get_contents($rootIndexFilepath);
@@ -162,6 +174,12 @@ class ContentManager {
});
}
/**
* If currently in a "EContentDisplayType::DISPLAY" context, we prepare the path to the content definition.
* This definition must be loaded afterward depending on the content's type. (content, tool, article, ...)
* @param string $rootIndexFilepath
* @return void
*/
function prepareContentFilePath(string $contentRootPath): void {
// Sanitizing the requested ID.
if(!ctype_alnum(str_replace("-", "", $this->requestedId))) {
@@ -190,6 +208,12 @@ function get_content_file_path(string $contentRootPath, string $contentId): ?str
}
// Functions for use in pages
/**
* Prepares a ContentManager for the current page.
* @param string $contentRootPath Root path for the relevant content ("<webRoot>/content", "<webRoot>/tools", ...)
* @return ContentManager
*/
function getContentManager(string $contentRootPath): ContentManager {
return new ContentManager(
$contentRootPath,

View File

@@ -0,0 +1,107 @@
<?php
// Making sure the file is included and not accessed directly.
if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
header('HTTP/1.1 403 Forbidden');
die();
}
// Including required helpers.
include_once 'commons/langs.php';
/**
* This class assumes that the Python-based preprocessor has
* created and pre-validated all required fields for this object.
*/
class ContentMetadata {
public string $metaTitleKey;
public string $metaDescriptionKey;
public string $metaAuthor;
public string $openGraphTitleKey;
public string $openGraphDescriptionKey;
public string $openGraphType;
public string $openGraphUrl;
public string $openGraphImage;
public string $openGraphImageMime;
public string $twitterCardType;
public string $twitterCardSite;
public string $twitterCardTitleKey;
public string $twitterCardDescriptionKey;
public string $twitterCardImageUrl;
public ?string $twitterCardImageAltKey;
public string $twitterCardCreatorHandle;
/**
* Prints all the HTML tags associated with that metadata.
* @return void - Returns nothing
*/
public function renderHtml(): void {
// Standard meta tags
echo('<title>' . localize($this->metaTitleKey) . '</title>');
echo('<meta name="description" content="' . localize($this->metaDescriptionKey) . '" />');
echo('<meta name="author" content="' . $this->metaAuthor . '" />');
// OpenGraph tags
echo('<meta property="og:title" content="' . localize($this->openGraphTitleKey) . '" />');
echo('<meta property="og:description" content="' . localize($this->openGraphDescriptionKey) . '" />');
echo('<meta property="og:type" content="' . $this->openGraphType . '" />');
echo('<meta property="og:url" content="' . $this->openGraphUrl . '" />');
echo('<meta property="og:image" content="' . $this->openGraphImage . '" />');
echo('<meta property="og:image:type" content="' . $this->openGraphImageMime . '" />');
// Twitter tags
echo('<meta name="twitter:card" content="' . $this->twitterCardType . '" />');
echo('<meta name="twitter:site" content="' . $this->twitterCardSite . '" />');
echo('<meta name="twitter:title" content="' . localize($this->twitterCardTitleKey) . '" />');
echo('<meta name="twitter:description" content="' . localize($this->twitterCardDescriptionKey) . '" />');
echo('<meta name="twitter:image" content="' . $this->twitterCardImageUrl . '" />');
if(!is_null($this->twitterCardImageAltKey)) {
echo('<meta name="twitter:image:alt" content="' . localize($this->twitterCardImageAltKey) . '" />');
}
echo('<meta name="twitter:creator" content="' . $this->twitterCardCreatorHandle . '" />');
}
static function from_json(array $json_data): ?ContentMetadata {
// Checking required fields
foreach(["metaTitleKey", "metaDescriptionKey", "metaAuthor", "openGraphTitleKey", "openGraphDescriptionKey",
"openGraphType", "openGraphUrl", "openGraphImage", "openGraphImageMime", "twitterCardType",
"twitterCardSite", "twitterCardTitleKey", "twitterCardDescriptionKey", "twitterCardImageUrl",
"twitterCardCreatorHandle"] as $wantedKey) {
if(!key_exists($wantedKey, $json_data)) {
return null;
}
}
// Preparing optional fields
foreach(["twitterCardImageAltKey"] as $optionalKey) {
if(!key_exists($optionalKey, $json_data)) {
$json_data[$optionalKey] = null;
}
}
$metadata = new ContentMetadata();
$metadata->metaTitleKey = $json_data["metaTitleKey"];
$metadata->metaDescriptionKey = $json_data["metaDescriptionKey"];
$metadata->metaAuthor = $json_data["metaAuthor"];
$metadata->openGraphTitleKey = $json_data["openGraphTitleKey"];
$metadata->openGraphDescriptionKey = $json_data["openGraphDescriptionKey"];
$metadata->openGraphType = $json_data["openGraphType"];
$metadata->openGraphUrl = $json_data["openGraphUrl"];
$metadata->openGraphImage = $json_data["openGraphImage"];
$metadata->openGraphImageMime = $json_data["openGraphImageMime"];
$metadata->twitterCardType = $json_data["twitterCardType"];
$metadata->twitterCardSite = $json_data["twitterCardSite"];
$metadata->twitterCardTitleKey = $json_data["twitterCardTitleKey"];
$metadata->twitterCardDescriptionKey = $json_data["twitterCardDescriptionKey"];
$metadata->twitterCardImageUrl = $json_data["twitterCardImageUrl"];
$metadata->twitterCardImageAltKey = $json_data["twitterCardImageAltKey"];
$metadata->twitterCardCreatorHandle = $json_data["twitterCardCreatorHandle"];
return $metadata;
}
}
?>

View File

@@ -8,10 +8,12 @@ if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
// Including required helpers.
include_once 'commons/config.php';
include_once 'commons/langs.php';
include_once 'commons/content.php';
// Required to handle opengraph data
include_once 'commons/content/opengraph.php';
// Including the "ContentManager" to shut the IDE up.
include_once 'commons/content/manager.php';
// Required to make the HTML meta-tags.
include_once 'commons/content/metadata.php';
// Required to make headings
include_once 'commons/DOM/utils.php';
@@ -26,7 +28,7 @@ class ToolInfoFile {
public string $icon;
public string $titleKey;
public ?string $subTitleKey;
public OpenGraphData $openGraphData;
//public OpenGraphData $openGraphData;
function __construct(string $domFile, ?string $langFile, ?array $codeFilesPaths, ?array $moduleFilesPaths,
?array $styleFilesPaths, ?string $icon, ?string $titleKey, ?string $subTitleKey,
@@ -39,7 +41,7 @@ class ToolInfoFile {
$this->icon = is_null($icon) ? "fad fa-question" : $icon;
$this->titleKey = is_null($titleKey) ? "unset" : $titleKey;
$this->subTitleKey = $subTitleKey;
$this->openGraphData = OpenGraphData::from_json($opengraph);
//$this->openGraphData = OpenGraphData::from_json($opengraph);
}
static function from_json(array $json_data): ?ToolInfoFile {
@@ -103,7 +105,7 @@ class ToolInfoFile {
abstract class ToolsContent {
static function loadItemIndexFile(ContentManager $contentManager, string $contentRootPath): ?ToolInfoFile {
// Preliminary check
if(!$contentManager->displayType == ContentDisplayType::CONTENT) {
if(!$contentManager->displayType == EContentDisplayType::DISPLAY) {
$contentManager->hasError = true;
$contentManager->errorMessageKey = "content.error.message.cannot.load.item.as.not.content";
return null;

View File

@@ -7,6 +7,7 @@
"sidebar.text.applications": "Applications",
"sidebar.text.libraries": "Libraries",
"sidebar.text.electronics": "Electronics",
"sidebar.text.3d-print": "3D Printing",
"sidebar.text.tools": "Tools",
"sidebar.text.links": "Links",
"sidebar.text.downloads": "Downloads",

View File

@@ -0,0 +1,7 @@
{
"test.header.title": "Test Page",
"test.controls": "Debugging options",
"test.controls.borders": "Show/Hide borders",
"test.app.card.demo": "Application card",
"test.content.card.demo": "Content card"
}

View File

@@ -7,6 +7,7 @@
"sidebar.text.applications": "Applications",
"sidebar.text.libraries": "Librairies",
"sidebar.text.electronics": "Électronique",
"sidebar.text.3d-print": "Impression 3D",
"sidebar.text.tools": "Outils",
"sidebar.text.links": "Liens",
"sidebar.text.downloads": "Téléchargements",

View File

@@ -0,0 +1,7 @@
{
"test.header.title": "Page de test",
"test.controls": "Options de débogage",
"test.controls.borders": "Afficher/Cacher les bordures",
"test.app.card.demo": "Vignette d'application",
"test.content.card.demo": "Vignette de contenu"
}