diff --git a/commons/composer.php b/commons/composer.php
new file mode 100644
index 0000000..f63dbae
--- /dev/null
+++ b/commons/composer.php
@@ -0,0 +1,656 @@
+getConstants();
+ }
+}
+
+// Defining the different types of elements available.
+abstract class ComposerElementTypes {
+ const UNSET = "unset";
+ const H1 = "h1";
+ const H2 = "h2";
+ const H3 = "h3";
+ const PARAGRAPH = "paragraph";
+ const BUTTON = "button";
+ const CODE = "code";
+ const HR = "hr";
+ const CONTAINER = "container";
+ const COLLAPSE = "collapse";
+ const SPACER = "spacer";
+ const IMAGE = "image";
+ const TABLE = "table";
+ const GRID = "grid";
+ const GLIDER = "glider";
+
+ /**
+ * Returns all the constants present in the class.
+ * @return array All the constants in as "[[k, v], [k, v], ...]".
+ * @see https://www.php.net/manual/en/reflectionclass.getconstants.php
+ */
+ static function getConstants(): array {
+ $oClass = new ReflectionClass(__CLASS__);
+ return $oClass->getConstants();
+ }
+}
+
+// Defining modifiers.
+abstract class ComposerElementModifiersTest {
+ // Generic > Margin
+ const GENERIC_MARGIN_NO_TOP = ["no-top-margin", "mt-0"];
+ const GENERIC_MARGIN_NO_BOTTOM = ["no-bottom-margin", "mb-0"];
+ const GENERIC_MARGIN_NO_LEFT = ["no-left-margin", "ml-0"];
+ const GENERIC_MARGIN_NO_RIGHT = ["no-right-margin", "mr-0"];
+ const GENERIC_MARGIN_NO_X = ["no-y-margin", "mx-0"];
+ const GENERIC_MARGIN_NO_Y = ["no-x-margin", "my-0"];
+ const GENERIC_MARGIN_NONE = ["no-margin", "m-0" ];
+
+ // Generic > Padding
+ const GENERIC_PADDING_NO_TOP = ["no-top-padding", "pt-0"];
+ const GENERIC_PADDING_NO_BOTTOM = ["no-bottom-padding", "pb-0"];
+ const GENERIC_PADDING_NO_LEFT = ["no-left-padding", "pl-0"];
+ const GENERIC_PADDING_NO_RIGHT = ["no-right-padding", "pr-0"];
+ const GENERIC_PADDING_NO_X = ["no-y-padding", "px-0"];
+ const GENERIC_PADDING_NO_Y = ["no-x-padding", "py-0"];
+ const GENERIC_PADDING_NONE = ["no-padding", "p-0" ];
+
+ // Containers
+ const CONTAINER_SCROLL_HORIZONTAL = ["h-scroll", "overflow-x-scroll hide-scrollbar"];
+ const CONTAINER_CARD = ["card", "card"];
+
+ // Buttons
+ const BUTTON_THIN = ["thin", "?thin"];
+ const BUTTON_THICK = ["thick", "?thick"];
+
+ // Horizontal ruler
+ const HR_SUBTLE = ["subtle", "subtle"];
+
+ // Other internal constants
+ const _INDEX_KEY = 0;
+ const _INDEX_CLASSES = 1;
+
+ /**
+ * Returns all the constants present in the class.
+ * @return array All the constants in as "[[k, v], [k, v], ...]".
+ * @see https://www.php.net/manual/en/reflectionclass.getconstants.php
+ */
+ static function getConstants(): array {
+ $oClass = new ReflectionClass(__CLASS__);
+ return $oClass->getConstants();
+ }
+
+ /**
+ * Returns the given modifier's constant's key
+ * @param array $modifier_data A modifier constant defined in "ComposerElementModifiersTest".
+ * @return string The modifier's key or an empty string if an error was encountered.
+ */
+ static function get_modifier_key(array $modifier_data) : string {
+ return sizeof($modifier_data) >= 1 ? $modifier_data[ComposerElementModifiersTest::_INDEX_KEY] : '';
+ }
+
+ /**
+ * Returns the given modifier's constant's classes
+ * @param array $modifier_data A modifier constant defined in "ComposerElementModifiersTest".
+ * @return string The modifier's classes or an empty string if an error was encountered.
+ */
+ static function get_modifier_classes(array $modifier_data) : string {
+ return sizeof($modifier_data) >= 2 ? $modifier_data[ComposerElementModifiersTest::_INDEX_CLASSES] : '';
+ }
+
+ /**
+ * @param string $modifier_key
+ * @return string The resolved DOM classes, or an empty string if the given modifier is unknown.
+ */
+ static function getClassesFromKey(string $modifier_key) : string {
+ foreach(ComposerElementModifiersTest::getConstants() as $constant_values) {
+ if(!is_array($constant_values)) {
+ continue;
+ }
+ if($modifier_key == $constant_values[ComposerElementModifiersTest::_INDEX_KEY]) {
+ return $constant_values[ComposerElementModifiersTest::_INDEX_CLASSES];
+ }
+ }
+ return "";
+ }
+
+ static function is_modifier_in_modifiers(array $modifier_data, array $modifiers) : bool {
+ foreach($modifiers as $modifier) {
+ if($modifier_data[ComposerElementModifiersTest::_INDEX_KEY] == $modifier) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+// Data classes
+class ComposerContent {
+ public array $strings;
+ public ComposerContentMetadata $metadata;
+ public array $elements;
+
+ function __construct(array $strings, ComposerContentMetadata $metadata, array $elements) {
+ $this->strings = $strings;
+ $this->metadata = $metadata;
+ $this->elements = $elements;
+ }
+
+ static function from_json(array $json_data) : ComposerContent {
+ global $default_language;
+ return new ComposerContent(
+ key_exists("strings", $json_data) ? $json_data["strings"] : array($default_language=>[]),
+ ComposerContentMetadata::from_json(
+ key_exists("metadata", $json_data) ? $json_data["metadata"] : array()
+ ),
+ key_exists("elements", $json_data) ?
+ ComposerElement::from_json_array($json_data["elements"]) : array()
+ );
+ }
+
+ public function get_html() : string {
+ $htmlCode = "";
+
+ // FIXME: Check for the template after the loop
+
+ foreach($this->elements as $element) {
+ /** @var ComposerElement $element */
+ $htmlCode .= $element->get_html($this);
+ }
+
+ return $this->metadata->apply_template($htmlCode);
+ }
+}
+
+class ComposerContentMetadata {
+ public string $title;
+ public string $description;
+ public string $template;
+ public ComposerContentMetadataOpengraph $opengraph;
+ public ?ComposerContentMetadataArticle $article;
+
+ function __construct(string $title, string $description, string $template, ComposerContentMetadataOpengraph $opengraph,
+ ?ComposerContentMetadataArticle $article) {
+ $this->title = $title;
+ $this->description = $description;
+ $this->template = $template;
+ $this->opengraph = $opengraph;
+ $this->article = $article;
+
+ // Safety checks.
+ if($this->template == ComposerTemplates::ARTICLE && is_null($this->article)) {
+ $this->article = ComposerContentMetadataArticle::from_json([]);
+ }
+ }
+
+ static function from_json(array $json_data) : ComposerContentMetadata {
+ return new ComposerContentMetadata(
+ key_exists("title", $json_data) ? $json_data["title"] : "",
+ key_exists("description", $json_data) ? $json_data["description"] : "",
+ key_exists("template", $json_data) ? $json_data["template"] : "",
+ ComposerContentMetadataOpengraph::from_json(
+ key_exists("opengraph", $json_data) ? $json_data["opengraph"] : array()
+ ),
+ key_exists("article", $json_data) ?
+ ComposerContentMetadataArticle::from_json($json_data["article"]) : null,
+ );
+ }
+
+ function apply_template(string $inner_html) : string {
+ switch($this->template) {
+ case ComposerTemplates::ARTICLE:
+ $inner_html = '
' .
+ '
' .
+ ' ' . localize($this->article->title) .
+ '' .
+ '\$subTitle' . '
' .
+ '
' .
+ $inner_html . '' .
+ '
' .
+ '
';
+
+ if(sizeof($this->article->tags) > 0) {
+ foreach($this->article->tags as $tag) {
+ $inner_html .= '
#' . $tag . '');
+ }
+ } else {
+ $inner_html .= '
' . localize("error.content.data.no.tags") . '';
+ }
+
+ $inner_html .= '
';
+ break;
+ case ComposerTemplates::RAW:
+ default:
+ break;
+ }
+ return $inner_html;
+ }
+
+ /*function start_content_card($iconClasses, $title, $subTitle) {
+ echo('');
+ echo('
'.localize($title));
+ echo(''.$subTitle.'
');
+ echo('');
+}
+
+ function end_content_card() {
+ echo('
');
+ }*/
+}
+
+class ComposerContentMetadataOpengraph {
+ private ?string $title;
+ private ?string $description;
+ private ?string $type;
+ private ?string $url;
+ private ?string $image;
+ private ?string $imageType;
+
+ function __construct(?string $title, ?string $description, ?string $type, ?string $url, ?string $image,
+ ?string $imageType) {
+ $this->title = $title;
+ $this->description = $description;
+ $this->type = $type;
+ $this->url = $url;
+ $this->image = $image;
+ $this->imageType = $imageType;
+ }
+
+ static function from_json(array $json_data) : ComposerContentMetadataOpengraph {
+ return new ComposerContentMetadataOpengraph(
+ key_exists("title", $json_data) ? $json_data["title"] : null,
+ key_exists("description", $json_data) ? $json_data["description"] : null,
+ key_exists("type", $json_data) ? $json_data["type"] : null,
+ key_exists("url", $json_data) ? $json_data["url"] : null,
+ key_exists("image", $json_data) ? $json_data["image"] : null,
+ key_exists("imageType", $json_data) ? $json_data["imageType"] : null,
+ );
+ }
+
+ public function __get($property) {
+ return is_null($this->$property) ? "" : $this->$property;
+ }
+}
+
+class ComposerContentMetadataArticle {
+ public string $icon;
+ public string $title;
+ public array $tags;
+
+ function __construct(string $icon, string $title, array $tags) {
+ $this->icon = $icon;
+ $this->title = $title;
+ $this->tags = $tags;
+ }
+
+ static function from_json(array $json_data) : ComposerContentMetadataArticle {
+ return new ComposerContentMetadataArticle(
+ key_exists("icon", $json_data) ? $json_data["icon"] : "fad fa-question",
+ key_exists("title", $json_data) ?
+ $json_data["title"] : ''.localize("error.content.data.no.title").'',
+ key_exists("tags", $json_data) ? $json_data["tags"] : [],
+ );
+ }
+
+ public function __get($property) {
+ return is_null($this->$property) ? "" : $this->$property;
+ }
+}
+
+class ComposerElement {
+ // Global parameters
+ private string $type;
+ private ?array $modifiers;
+
+ // Any direct element-container
+ private ?array $parts;
+
+ // Any direct text-container
+ private ?string $content;
+ private bool $localize;
+
+ // Generic modifier values
+ private ?int $padding;
+ private ?int $margin;
+
+ // Spacer's size
+ private ?int $size;
+
+ // Table's parameters
+ private ?array $head;
+ private ?array $body;
+ private ?int $colspan;
+ private ?int $rowspan;
+
+ // Paragraph and code's parameters
+ private ?int $indent;
+
+ // Code's parameters
+ private ?array $code;
+
+ function __construct(string $type, ?array $modifiers, ?array $parts, ?string $content, bool $localize,
+ ?int $padding, ?int $margin, ?int $size, ?array $head, ?array $body, ?int $colspan,
+ ?int $rowspan, ?int $indent, ?array $code) {
+ $this->type = $type;
+ $this->modifiers = $modifiers;
+ $this->parts = $parts;
+ $this->content = $content;
+ $this->localize = $localize;
+ $this->padding = $padding;
+ $this->margin = $margin;
+ $this->size = $size;
+ $this->head = $head;
+ $this->body = $body;
+ $this->colspan = $colspan;
+ $this->rowspan = $rowspan;
+ $this->indent = $indent;
+ $this->code = $code;
+ }
+
+ static function from_json_array(array $json_dataArray) : array {
+ $parts = array();
+ foreach($json_dataArray as $part) {
+ $parts[] = ComposerElement::from_json($part);
+ }
+ return $parts;
+ }
+
+ static function from_json(array $json_data) : ComposerElement {
+ return new ComposerElement(
+ key_exists("type", $json_data) ? $json_data["type"] : ComposerElementTypes::UNSET,
+ key_exists("modifiers", $json_data) ? $json_data["modifiers"] : null,
+ key_exists("parts", $json_data) ? ComposerElement::from_json_array($json_data["parts"]) : null,
+ key_exists("content", $json_data) ? $json_data["content"] : null,
+ key_exists("localize", $json_data) ? $json_data["localize"] : true,
+ key_exists("padding", $json_data) ? $json_data["padding"] : null,
+ key_exists("margin", $json_data) ? $json_data["margin"] : null,
+ key_exists("size", $json_data) ? $json_data["size"] : null,
+ key_exists("head", $json_data) ? $json_data["head"] : null,
+ key_exists("body", $json_data) ? $json_data["body"] : null,
+ key_exists("colspan", $json_data) ? $json_data["colspan"] : null,
+ key_exists("rowspan", $json_data) ? $json_data["rowspan"] : null,
+ key_exists("indent", $json_data) ? $json_data["indent"] : null,
+ key_exists("code", $json_data) ? $json_data["code"] : null,
+ );
+ }
+
+ /**
+ * Processes the "content" and "parts" class' variables and returns their interpreted content as HTML.
+ * @param ComposerContent $contentRoot The content in which this element is contained.
+ * @param bool $doLocalization Whether the "content" variable should be processed to return localized text.
+ * @param bool $doSubElements Whether the "parts" variable should be processed to return localized text.
+ * @param bool $stopIfLocalized Whether the process should return if some text was in the "content" variable.
+ * @return string The interpreted content as HTML.
+ */
+ private function get_inner_html(ComposerContent $contentRoot, bool $doLocalization = true,
+ bool $doSubElements = true, bool $stopIfLocalized = true) : string {
+ global $LANG_FALLBACK_KEY_PREFIX;
+
+ $htmlCode = "";
+ $wasTextLocalized = false;
+
+ if($doLocalization) {
+ // Checking if "content" was declared.
+ if(is_null($this->content) && !$doSubElements) {
+ return "error.no.inner.content
";
+ }
+
+ // Checking if there is something to process.
+ if(!empty($this->content)) {
+ $wasTextLocalized = true;
+
+ if(!$this->localize) {
+ $htmlCode .= $this->content;
+ } else {
+ // We can now localize the content key.
+ $htmlCode .= localize_private($this->content, $contentRoot->strings, true,
+ $LANG_FALLBACK_KEY_PREFIX);
+ }
+ }
+
+ // Checking for early stop.
+ if($wasTextLocalized && $stopIfLocalized) {
+ return $htmlCode;
+ }
+ }
+
+ if($doSubElements) {
+ // Checking if "parts" was declared.
+ if(is_null($this->parts)) {
+ if(!$wasTextLocalized) {
+ $htmlCode = "error.no.inner.parts
";
+ }
+ return $htmlCode;
+ }
+
+ // Appending each sub-element.
+ foreach($this->parts as $subElement) {
+ /** @var ComposerElement $subElement */
+ $htmlCode .= $subElement->get_html($contentRoot);
+ }
+ }
+
+ return $htmlCode;
+ }
+
+ private function get_inner_html_elements(ComposerContent $contentRoot) : string {
+ return $this->get_inner_html($contentRoot, false, true, false);
+ }
+
+ private function get_inner_html_text(ComposerContent $contentRoot) : string {
+ return $this->get_inner_html($contentRoot, true, false, false);
+ }
+
+ private function get_modifiers_classes() : string {
+ if(!is_null($this->modifiers)) {
+ $classes = "";
+
+ // Combining classes.
+ foreach($this->modifiers as $modifier) {
+ /** @var string $modifier */
+ $classes .= ComposerElementModifiersTest::getClassesFromKey($modifier) . ' ';
+ }
+
+ // Removing redundant and useless spaces.
+ return preg_replace('/\s+/', ' ', trim($classes));
+ }
+
+ return "";
+ }
+
+ /**
+ * Processes the element and returns its interpreted form as HTML.
+ * @param ComposerContent $contentRoot The content in which this element is contained.
+ * @return string The interpreted element as HTML.
+ */
+ public function get_html(ComposerContent $contentRoot) : string {
+ $htmlCode = "";
+
+ switch($this->type) {
+ case ComposerElementTypes::UNSET:
+ $htmlCode .= "error.unset !
";
+ break;
+
+ case ComposerElementTypes::H1:
+ case ComposerElementTypes::H2:
+ case ComposerElementTypes::H3:
+ // Defining the text's size.
+ $_headingFontSize = ($this->type == ComposerElementTypes::H3 ? '18' : (
+ $this->type == ComposerElementTypes::H2 ? '20' : '22'
+ ));
+
+ // Composing heading.
+ $htmlCode .= '<' . strtolower($this->type) . ' class="font-weight-semi-bold font-size-' .
+ $_headingFontSize . ' m-0">' . $this->get_inner_html($contentRoot) . '' . strtolower($this->type) .
+ '>';
+
+ break;
+
+ case ComposerElementTypes::PARAGRAPH:
+ // Defining the text's indent level.
+ $_paragraph_ident_level = is_null($this->indent) ? 0 : $this->indent;
+
+ // Composing the paragraph
+ $htmlCode .= '' .
+ $this->get_inner_html($contentRoot) . '
';
+
+ break;
+
+ case ComposerElementTypes::BUTTON:
+ break;
+
+ case ComposerElementTypes::CODE:
+ // Defining the code's indent level.
+ $_paragraph_ident_level = is_null($this->indent) ? 0 : $this->indent;
+
+ // Opening the code element.
+ $htmlCode .= '';
+
+ // Adding code lines.
+ if(!is_null($this->code)) {
+ foreach($this->code as $code_line) {
+ $htmlCode .= htmlspecialchars($code_line) . '
';
+ }
+ }
+
+ // Closing code element.
+ $htmlCode .= '
';
+
+ break;
+
+ case ComposerElementTypes::HR:
+ // Getting the modifiers' classes
+ $_hr_classes = $this->get_modifiers_classes();
+
+ // Composing the element.
+ if(empty($_hr_classes)) {
+ $htmlCode .= '';
+ } else {
+ $htmlCode .= '
';
+ }
+
+ break;
+
+ case ComposerElementTypes::CONTAINER:
+ // Defining the padding's size.
+ $_container_padding = is_null($this->padding) ? 10 : $this->padding;
+
+ // Getting the modifiers' classes
+ $_container_classes = $this->get_modifiers_classes();
+
+ // Composing the container.
+ $htmlCode .= '' .
+ $this->get_inner_html($contentRoot) . '
';
+
+ break;
+
+ case ComposerElementTypes::COLLAPSE:
+ break;
+
+ case ComposerElementTypes::SPACER:
+ // Defining the spacer's size.
+ $_spacer_size = is_null($this->size) ? 1 : $this->size;
+
+ // Composing spacer.
+ $htmlCode .= '';
+
+ break;
+
+ case ComposerElementTypes::IMAGE:
+ break;
+ case ComposerElementTypes::TABLE:
+ break;
+ case ComposerElementTypes::GRID:
+ break;
+ case ComposerElementTypes::GLIDER:
+ break;
+
+ default:
+ $htmlCode .= "error.unknown !
";
+ break;
+ }
+
+ return $htmlCode;
+ }
+}
+
+
+// Generic functions
+function get_content_error(string $error_title_key, string $error_description_key) : ?ComposerContent {
+ // FIXME: Make this non-nullable !!!
+ return null;
+}
+
+function get_content_file_path(string $content_id) : ?string {
+ global $dir_content;
+
+ if(ctype_alnum(str_replace("-", "", $content_id))) {
+ return realpath($dir_content . "/items/" . $content_id . ".json");
+ }
+
+ return null;
+}
+
+function load_content_by_file_path(string $file_path) : ?ComposerContent {
+ // FIXME: Add handling for JSON errors !
+ $content_json_data = json_decode(file_get_contents($file_path), true);
+ return ComposerContent::from_json($content_json_data);
+}
+
+function load_content_by_id(string $content_id) : ?ComposerContent {
+ $content_file_path = get_content_file_path($content_id);
+
+ if(is_null($content_file_path)) {
+ return null;
+ } else {
+ return load_content_by_file_path($content_file_path);
+ }
+}
+
+// Test
+if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
+ $content = load_content_by_id("test2");
+
+ if(!is_null($content)) {
+ echo "";
+ print_r(htmlspecialchars($content->get_html()));
+ echo "
";
+
+ echo "";
+ print_r($content);
+ echo "
";
+ }
+
+ echo("
");
+}
+?>
\ No newline at end of file
diff --git a/commons/content.php b/commons/content.php
index 6f7e70b..58bf658 100644
--- a/commons/content.php
+++ b/commons/content.php
@@ -5,12 +5,11 @@ if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
die();
}
-// Importing required scripts
+// Importing required scripts.
include_once 'langs.php';
+include_once 'composer.php';
-// This helper requires PHP 8 or newer !
-
-$SHOW_CONTENT_DEBUG_CARD = false;
+// Defining some options.
$PRINT_CONTENT_DEBUG_INFO_TEXT_ELEMENTS = true;
$PRINT_CONTENT_DEBUG_ERROR_TEXT_ELEMENTS = true;
@@ -30,6 +29,7 @@ $requested_tags = array();
$raw_additional_tags = "";
$filtered_content_index_data = NULL;
$requested_item_data = NULL;
+$content = null;
// Detecting content display type requested.
$content_requested_url_part = explode("?", explode("#", preg_replace("^\/(content)^", "", l10n_url_switch(NULL)))[0])[0];
@@ -93,7 +93,7 @@ if($requested_content_display_type == ContentDisplayType::SEARCH) {
$content_error_message_key = "error.content.detect.empty";
goto content_end;
}
-} elseif($requested_content_display_type == ContentDisplayType::CONTENT) {
+} else if($requested_content_display_type == ContentDisplayType::CONTENT) {
// Sanitizing the requested ID.
if(!ctype_alnum(str_replace("-", "", $content_requested_url_part))) {
$content_has_error = true;
@@ -101,37 +101,46 @@ if($requested_content_display_type == ContentDisplayType::SEARCH) {
goto content_end;
}
- // Loading the content's data.
- $content_file_path = realpath($dir_content . "/items/".$content_requested_url_part.".json");
- if($content_file_path) {
- // FIXME: Handle JSON errors cleanly
- $content_json = file_get_contents($content_file_path);
- $requested_item_data = json_decode($content_json, true);
- unset($content_json);
- } else {
+ // Loading the content's data
+ $content_file_path = get_content_file_path($content_requested_url_part);
+
+ if(is_null($content_file_path)) {
+ // File doesn't exist !
$content_has_error = true;
$content_error_message_key = "error.content.data.not.exist";
+ unset($content_file_path);
goto content_end;
+ } else {
+ $content = load_content_by_file_path($content_file_path);
+
+ if(is_null($content)) {
+ $content_has_error = true;
+ $content_error_message_key = "error.content.cannot.load";
+ unset($content_file_path);
+ goto content_end;
+ }
}
+
unset($content_file_path);
}
content_end:
+// TODO: Create error thingy
$content_error_message = localize($content_error_message_key);
// These functions are placed here to prevent the main file from becoming impossible to read.
-function startMainCard($iconClasses, $title, $subTitle) {
+function start_content_card($iconClasses, $title, $subTitle) {
echo('');
echo('
'.localize($title));
echo(''.$subTitle.'
');
echo('');
}
-function endMainCard() {
+function end_content_card() {
echo('
');
}
-function getContentItemText(array $contentNode, bool $italicOnError = true, bool $returnMissingAsEmpty = false) : string {
+/*function getContentItemText(array $contentNode, bool $italicOnError = true, bool $returnMissingAsEmpty = false) : string {
global $user_language, $default_language;
if(array_key_exists("key", $contentNode)) {
return localize($contentNode["key"]);
@@ -607,6 +616,6 @@ function createElementNode(mixed $elementNode, string $prepend="", string $appen
printErrorTextElement(sprintf(localize("error.content.data.part.unknown"), $elementNode["type"]));
break;
}
-}
+}*/
?>
diff --git a/commons/langs.php b/commons/langs.php
index 33183eb..6e5142a 100644
--- a/commons/langs.php
+++ b/commons/langs.php
@@ -33,26 +33,41 @@ if(str_starts_with($_SERVER['REQUEST_URI'], "/en/")) {
$lang_json = file_get_contents(realpath($dir_commons . "/strings.json"));
$lang_data = json_decode($lang_json, true);
-// Localizer function
-function localize($stringKey) {
+// Localization functions
+function localize_private(string $string_key, array $private_lang_data, bool $fallback_to_common = false,
+ string $fallback_prefix = "fallback.unknown") : string {
global $user_language, $default_language, $lang_data;
- if(array_key_exists($stringKey, $lang_data[$user_language])) {
- return $lang_data[$user_language][$stringKey];
- } else {
- if(array_key_exists($stringKey, $lang_data[$default_language])) {
- return $lang_data[$default_language][$stringKey];
- } else {
- return $stringKey;
+
+ if(array_key_exists($user_language, $private_lang_data)) {
+ if(array_key_exists($string_key, $private_lang_data[$user_language])) {
+ // If found in direct array in user's language.
+ return $private_lang_data[$user_language][$string_key];
}
+ } else if(array_key_exists($default_language, $private_lang_data)) {
+ if(array_key_exists($string_key, $private_lang_data[$default_language])) {
+ // If found in direct array in default language.
+ return $private_lang_data[$default_language][$string_key];
+ }
+ } else if($fallback_to_common) {
+ // If we can attempt to fallback on the common lang file.
+ return localize_private($fallback_prefix . "." . $string_key, $lang_data, false);
}
+
+ // If nothing could be done, we simply return the key.
+ return $string_key;
}
-function l10n_url_abs($url) {
+function localize($string_key) : string {
+ global $lang_data;
+ return localize_private($string_key, $lang_data, false);
+}
+
+function l10n_url_abs($url) : string {
global $user_uri_language;
return $user_uri_language . $url;
}
-function l10n_url_switch($lang) {
+function l10n_url_switch($lang) : string {
if(is_null($lang)) {
return preg_replace("^\/(lb|lu|fr|en)^", "", $_SERVER['REQUEST_URI']);
} else {
diff --git a/content/index.json b/content/index.json
index 39ed634..453b984 100644
--- a/content/index.json
+++ b/content/index.json
@@ -23,7 +23,7 @@
"fr": "Conteneur et application Python hautement configurable qui permet d'automatiquement archiver et télécharger des livestreams et vidéos depuis YouTube."
},
"image": "/resources/Azias/imgs/yaa/icon-final.png",
- "tags": ["docker", "web", "python"]
+ "tags": ["docker", "application", "web", "python"]
},
{
"id": "excel-worksheet-password-remover",
diff --git a/content/index.php b/content/index.php
index 7050133..37a5e22 100644
--- a/content/index.php
+++ b/content/index.php
@@ -1,5 +1,5 @@
metadata->title
// Printing the title, meta and opengraph tags.
echo(''.$_metaTitle.' - Nibble Poker');
@@ -71,21 +72,15 @@ if($content_has_error) {
');
if($content_has_error) {
- if(isset($_SERVER['HTTP_REFERER'])) {
- echo(''.localize("content.title.content").'');
- } else {
- //echo(''.localize("content.title.content").'');
- echo(localize("content.title.content"));
- }
- echo('❱'.localize("content.title.error"));
+ echo(localize("content.title.content").'❱'.localize("content.title.error"));
} elseif($requested_content_display_type == ContentDisplayType::SEARCH) {
echo(localize("content.title.content").'❱'.localize("content.title.search.header"));
} elseif($requested_content_display_type == ContentDisplayType::CONTENT) {
$_nav_title_text = '' . localize("error.content.data.no.title") . '';
- if (array_key_exists("page", $requested_item_data["title"])) {
- $_nav_title_text = getContentItemText($requested_item_data["title"]["page"]);
- }
+ //if (array_key_exists("page", $requested_item_data["title"])) {
+ // $_nav_title_text = getContentItemText($requested_item_data["title"]["page"]);
+ //}
echo(localize("content.title.content").'❱'.$_nav_title_text);
}
@@ -95,47 +90,12 @@ if($content_has_error) {
-
-
');
- echo('
REQUEST_URI: '.$_SERVER['REQUEST_URI'].'
');
- echo('
$requested_content_display_type: '.$requested_content_display_type.'
');
- echo('
$requested_tags: ['.implode(", ", $requested_tags).']
');
- echo('
count($requested_tags): '.count($requested_tags).'
');
- echo('
$content_has_error: '.$content_has_error.'
');
- echo('
$content_error_message_key: '.$content_error_message_key.'
');
- echo('
localize($content_error_message_key): '.localize($content_error_message_key).'
');
- echo('
$content_error_message: '.$content_error_message.'
');
- echo('
$raw_additional_tags: '.$raw_additional_tags.'
');
- echo('
$filtered_content_index_data: ');
- print_r($filtered_content_index_data);
- echo('
');
- echo('
$content_requested_url_part: '.$content_requested_url_part.'
');
- if($requested_content_display_type == ContentDisplayType::CONTENT) {
- echo('
');
- echo('
$requested_item_data: ');
- print_r($requested_item_data);
- echo('
');
- }
- echo('
');
- }
-
// Checking if an error occurred.
if($content_error_code != 200) {
// #############
// Error card
// #############
- startMainCard("fad fa-exclamation-triangle", localize('error.content.title.generic'), "");
+ start_content_card("fad fa-exclamation-triangle", localize('error.content.title.generic'), "");
echo('');
echo('
'.$content_error_message.'
');
echo('');
@@ -143,7 +103,7 @@ if($content_has_error) {
''.
'Card footer here.'.
'
');
- endMainCard();
+ end_content_card();
goto content_printing_end;
}
@@ -152,9 +112,7 @@ if($content_has_error) {
// #############
// Search page
// #############
-
- // Creating the start of the card, only a "" should be required afterward.
- startMainCard(
+ start_content_card(
"fad fa-file-search",
localize("content.title.search.card"),
strval(count($filtered_content_index_data)) . " " .
@@ -198,70 +156,12 @@ if($content_has_error) {
''.
'Card footer here.'.
'
');
- endMainCard();
+ end_content_card();
} elseif($requested_content_display_type == ContentDisplayType::CONTENT) {
// ##############
// Content page
// ##############
-
- // Preparing soma variables for the icon, title and subtitle.
- $_title_icon = "fad fa-question";
- $_title_text_main = ''.localize("error.content.data.no.title").'';
- $_title_text_sub = NULL;
-
- // Attempting to read the card's icon, title and subtitle.
- if (array_key_exists("title", $requested_item_data)) {
- if (array_key_exists("icon", $requested_item_data["title"])) {
- $_title_icon = $requested_item_data["title"]["icon"];
- }
- if (array_key_exists("card", $requested_item_data["title"])) {
- if (array_key_exists("main", $requested_item_data["title"]["card"])) {
- $_title_text_main = getContentItemText($requested_item_data["title"]["card"]["main"], true);
- }
- if (array_key_exists("sub", $requested_item_data["title"]["card"])) {
- $_title_text_sub = getContentItemText($requested_item_data["title"]["card"]["sub"], true);
- }
- }
- }
-
- // Opening the card.
- startMainCard($_title_icon, $_title_text_main, (is_null($_title_text_sub) ? "" : $_title_text_sub));
-
- // Opening the content container.
- echo('');
-
- // Adding elements defined in the JSON file.
- if(array_key_exists("parts", $requested_item_data)) {
- for ($i = 0; $i < count($requested_item_data["parts"]); $i++) {
- createElementNode($requested_item_data["parts"][$i]);
- }
- } else {
- echo('');
- echo(localize("error.content.data.no.parts").'
');
- }
-
- // New elements test zone. - START
-
- // New elements test zone. - END
-
- // Closing the content container.
- echo('');
-
- // Printing the tags' section at the end of the card
- echo('');
- echo('
');
- if(array_key_exists("tags", $requested_item_data)) {
- for($i = 0; $i < count($requested_item_data["tags"]); $i++) {
- echo('
#');
- echo($requested_item_data["tags"][$i].'');
- }
- } else {
- echo('
'.localize("error.content.data.no.tags").'');
- }
- echo('
');
-
- // Closing the card.
- endMainCard();
+ echo($content->get_html());
}
content_printing_end:
diff --git a/content/items/lscom-cli.json b/content/items/lscom-cli.json
index 7100acb..1450bae 100644
--- a/content/items/lscom-cli.json
+++ b/content/items/lscom-cli.json
@@ -4,7 +4,7 @@
"page": {"en": "PB-ListComPort", "fr": "PB-ListComPort"},
"card": {
"main": {"en": "PB-ListComPort", "fr": "PB-ListComPort"},
- "sub": {"en": "CLI COM port enumerator", "fr": "Enumérateur de port COM pour invité de commande"}
+ "sub": {"en": ""}
}
},
"meta": {
@@ -123,7 +123,7 @@
"type": "spacer", "size": 1
},{
"type": "container", "padding": 0,
- "modifiers": ["no-bottom-padding", "no-top-margin", "card"],
+ "modifiers": ["no-bottom-padding", "no-top-margin", "card", "horizontal-scroll"],
"content": {
"parts": [{
"type": "table",
@@ -193,7 +193,7 @@
"type": "spacer", "size": 1
},{
"type": "container", "padding": 0,
- "modifiers": ["no-bottom-padding", "no-top-margin", "card"],
+ "modifiers": ["no-bottom-padding", "no-top-margin", "card", "horizontal-scroll"],
"content": {
"parts": [{
"type": "table",
@@ -307,7 +307,7 @@
"type": "spacer", "size": 1
},{
"type": "container", "padding": 0,
- "modifiers": ["no-bottom-padding", "no-top-margin", "card"],
+ "modifiers": ["no-bottom-padding", "no-top-margin", "card", "horizontal-scroll"],
"content": {
"parts": [{
"type": "table", "modifiers": ["striped", "inner-bordered"],
diff --git a/content/items/test2.json b/content/items/test2.json
new file mode 100644
index 0000000..ed9a990
--- /dev/null
+++ b/content/items/test2.json
@@ -0,0 +1,56 @@
+{
+ "strings": {
+ "en": {
+ "title.meta": "Test content",
+ "title.og": "Test content",
+ "title.article": "Test content",
+ "heading.1": "t1",
+ "text.1": "p1",
+ "hello.world": "Hello World !"
+ },
+ "fr": {
+ "title.meta": "Test content fr",
+ "title.og": "Test content fr",
+ "title.article": "Test content fr",
+ "heading.1": "t1 fr",
+ "text.1": "p1 fr",
+ "hello.world": "Bonjour le monde !"
+ }
+ },
+ "metadata": {
+ "title": "title.meta",
+ "template": "article",
+ "opengraph": {
+ "title": "title.og",
+ "description": "",
+ "type": "",
+ "url": null,
+ "image": "",
+ "imageType": ""
+ },
+ "article": {
+ "icon": "fad fa-terminal",
+ "title": "title.article",
+ "tags": ["fuck", "you"]
+ }
+ },
+ "elements": [
+ {
+ "type": "container",
+ "padding": 10,
+ "modifiers": ["no-bottom-padding", "no-top-margin"],
+ "parts": [
+ {
+ "type": "paragraph",
+ "modifiers": ["no-top-margin"],
+ "content": "hello.world"
+ },{
+ "type": "paragraph",
+ "modifiers": [],
+ "content": "Joe mama !",
+ "localize": false
+ }
+ ]
+ }
+ ]
+}
diff --git a/content/items/youtube-auto-archiver.json b/content/items/youtube-auto-archiver.json
index ade0701..05e6c89 100644
--- a/content/items/youtube-auto-archiver.json
+++ b/content/items/youtube-auto-archiver.json
@@ -23,6 +23,6 @@
}
},
"tags": [
- "docker", "web", "python"
+ "docker", "application", "web", "python"
]
}