Improved content system, Removed trash, Fixed sitemap
Update .gitignore, .htaccess, and 11 more files...
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,5 @@ resources/FontAwesomePro/
|
||||
resources/GoogleFonts/
|
||||
|
||||
# Other folders (Will be removed once the content system is finished !)
|
||||
content/page/
|
||||
# /files/*/
|
||||
files/
|
||||
|
17
.htaccess
17
.htaccess
@@ -49,9 +49,6 @@ Header always set Access-Control-Allow-Origin "*"
|
||||
Header unset X-Powered-By
|
||||
Header always set X-Powered-By "Amiga 1200, Kickstart 3.1"
|
||||
|
||||
# Removed the condition to prevent silent errors since the module is required for the website
|
||||
# <IfModule mod_rewrite.c></IfModule>
|
||||
|
||||
# Handling all other redirections.
|
||||
RewriteEngine On
|
||||
|
||||
@@ -67,19 +64,6 @@ RewriteRule ^en/(.*)$ /$1 [QSA]
|
||||
RewriteRule ^fr/(.*)$ /$1 [QSA]
|
||||
RewriteRule ^lb/(.*)$ /$1 [QSA]
|
||||
|
||||
# Content categories. - Should be removed ?
|
||||
#RewriteRule ^((en|fr|lb)/)?blog/article/(.*)$ /content/page/$1 [QSA]
|
||||
#RewriteRule ^((en|fr|lb)/)?programming/(applications|tutorials|tools)/(.*)$ /content/page/$1 [QSA]
|
||||
##RewriteRule ^((en|fr|lb)/)?electronics/ham/(.*)$ /content/page/$1 [QSA]
|
||||
|
||||
# Content root pages. - Should be removed ?
|
||||
#RewriteRule ^((en|fr|lb)/)?programming/(purebasic|python|others)/(.*)$ /content/$1 [QSA]
|
||||
#RewriteRule ^((en|fr|lb)/)?electronics/(iot|experiments)/(.*)$ /content/$1 [QSA]
|
||||
|
||||
# Content pages. (Old regex are taken care of by the "content/index.php" page)
|
||||
#RewriteRule ^((en|fr|lb)/)?(blog|programming|electronics|projects)/(.*)$ /content/$1 [QSA]
|
||||
#RewriteRule ^((en|fr|lb)/)?(content)/(.*)$ /content/$1 [QSA]
|
||||
|
||||
# Internal redirections for scanning and exploit attempts.
|
||||
# These rules are here since they're easier to implement in the .htaccess.
|
||||
#RewriteRule ^.*(install|xmlrpc)\.php.*$ /honeypot/file-php.php [QSA]
|
||||
@@ -94,4 +78,3 @@ RewriteRule ^lb/(.*)$ /$1 [QSA]
|
||||
# * //vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
|
||||
|
||||
# TODO: Implement bee-movie themed tarpit once I have a rate-limiting solution in place !
|
||||
|
||||
|
@@ -10,7 +10,7 @@ include_once 'langs.php';
|
||||
|
||||
// This helper requires PHP 8 or newer !
|
||||
|
||||
$SHOW_CONTENT_DEBUG_CARD = true;
|
||||
$SHOW_CONTENT_DEBUG_CARD = false;
|
||||
|
||||
// Defining constants and enums.
|
||||
abstract class ContentDisplayType {
|
||||
@@ -27,6 +27,7 @@ $content_error_message = "";
|
||||
$requested_tags = array();
|
||||
$raw_additional_tags = "";
|
||||
$filtered_content_index_data = NULL;
|
||||
$requested_item_data = NULL;
|
||||
|
||||
// Detecting content display type requested.
|
||||
$content_requested_url_part = explode("?", explode("#", preg_replace("^\/(content)^", "", l10n_url_switch(NULL)))[0])[0];
|
||||
@@ -91,7 +92,26 @@ if($requested_content_display_type == ContentDisplayType::SEARCH) {
|
||||
goto content_end;
|
||||
}
|
||||
} elseif($requested_content_display_type == ContentDisplayType::CONTENT) {
|
||||
// Attempting to get the requested ID.
|
||||
// Sanitizing the requested ID.
|
||||
if(!ctype_alnum(str_replace("-", "", $content_requested_url_part))) {
|
||||
$content_has_error = true;
|
||||
$content_error_message_key = "error.content.id.alphanumeric";
|
||||
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 {
|
||||
$content_has_error = true;
|
||||
$content_error_message_key = "error.content.data.not.exist";
|
||||
goto content_end;
|
||||
}
|
||||
unset($content_file_path);
|
||||
}
|
||||
|
||||
content_end:
|
||||
@@ -100,9 +120,9 @@ $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) {
|
||||
echo('<div class="card p-0 mx-0"><div class="px-card py-10 border-bottom px-20"><div class="container-fluid">');
|
||||
echo('<div class="row"><div class="col-4"><h2 class="card-title font-size-18 m-0">');
|
||||
echo('<div class="row"><div class="col-8"><h2 class="card-title font-size-18 m-0">');
|
||||
echo('<i class="'.$iconClasses.'"></i> '.localize($title).'</h2></div>');
|
||||
echo('<div class="col-8 text-right font-italic"><h2 class="card-title font-size-18 m-0 text-super-muted">'.$subTitle.'</h2>');
|
||||
echo('<div class="col-4 text-right font-italic"><h2 class="card-title font-size-18 m-0 text-super-muted">'.$subTitle.'</h2>');
|
||||
echo('</div></div></div></div>');
|
||||
}
|
||||
|
||||
|
@@ -105,11 +105,15 @@
|
||||
"error.content.tags.length": "The \"tags\" URL parameter is too long.",
|
||||
"error.content.tags.alphanumeric": "One of the tags given in the \"tags\" URL parameter is not a valid alphanumeric string.",
|
||||
"error.content.detect.type": "The type of requested content couldn't be determined.",
|
||||
"error.content.id.alphanumeric": "The requested resource's ID isn't valid.",
|
||||
"error.content.data.not.exist": "The requested resource's doesn't have an associated item file.",
|
||||
"error.content.data.no.title": "No title found !",
|
||||
|
||||
"content.title.error": "$content.title.error",
|
||||
"content.title.search": "Projects search",
|
||||
"content.title.article": "$content.title.article",
|
||||
"content.title.application": "$content.title.application",
|
||||
"content.title.error": "Error",
|
||||
"content.title.content": "Content",
|
||||
"content.title.search": "Search",
|
||||
"content.title.search.card.single": "Search result",
|
||||
"content.title.search.card.multiple": "Search results",
|
||||
"content.tags.requested": "Requested tags",
|
||||
"content.search.count.single": "result",
|
||||
"content.search.count.multiple": "results",
|
||||
@@ -219,7 +223,7 @@
|
||||
"error.404.title": "Erreur 404",
|
||||
"error.404.description": "La ressource demandée est introuvable sur le serveur !",
|
||||
|
||||
"error.content.title.generic": "Erreur de contenu",
|
||||
"error.content.title.generic": "Erreur",
|
||||
"error.content.title.empty": "Aucun contenu trouvé",
|
||||
"error.content.none": "Aucune erreur n'a été détectée.",
|
||||
"error.content.detect.category": "Impossibilité de détecter la catégorie de contenu demandée.",
|
||||
@@ -230,11 +234,15 @@
|
||||
"error.content.tags.length": "Le paramètre d'URL \"tags\" est trop long.",
|
||||
"error.content.tags.alphanumeric": "Un des tags donné dans le paramètre d'URL \"tags\" n'est pas une chaîne de texte alphanumérique valide.",
|
||||
"error.content.detect.type": "Le type de contenu désiré n'as pas pu être détecté.",
|
||||
"error.content.id.alphanumeric": "L'ID de la ressource demandée n'est pas valide.",
|
||||
"error.content.data.not.exist": "La ressource demandée n'a pas de fichier de données associé.",
|
||||
"error.content.data.no.title": "Aucun titre trouvé !",
|
||||
|
||||
"content.title.error": "$content.title.error",
|
||||
"content.title.search": "Recherche de projets",
|
||||
"content.title.article": "$content.title.article",
|
||||
"content.title.application": "$content.title.application",
|
||||
"content.title.error": "Erreur de contenu",
|
||||
"content.title.content": "Content",
|
||||
"content.title.search": "Recherche de contenu",
|
||||
"content.title.search.card.single": "Résultat de recherche",
|
||||
"content.title.search.card.multiple": "Résultats de recherche",
|
||||
"content.tags.requested": "Tags demandés",
|
||||
"content.search.count.single": "résultat",
|
||||
"content.search.count.multiple": "résultats",
|
||||
|
@@ -1,113 +0,0 @@
|
||||
<?php if (basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) { header('HTTP/1.1 403 Forbidden'); die(); } ?>
|
||||
<div class="content"><?php
|
||||
|
||||
function handleError($errorMessage) {
|
||||
|
||||
}
|
||||
|
||||
function printBlogPreviewCard($id, $title, $previewPreface, $previewText, $previewImageUrl, $previewImageAlt, $tags, $faIcon) {
|
||||
echo('<div class="card p-0 mx-0"><div class="px-card py-10 border-bottom px-20"><h2 class="card-title font-size-18 m-0 d-inline-block">
|
||||
<span class="text-left"><i class="'.$faIcon.'"></i> '.$title.'</span><span class="text-right"></span></h2></div>');
|
||||
|
||||
echo('<div class="d-flex"><div class="w-100 h-100 m-10 align-self-center"><div class="w-100 h-100 rounded d-flex align-items-center justify-content-center" style="background-color: #5352ed;">
|
||||
<img src="'.$previewImageUrl.'" class="d-block w-100 h-100 rounded" loading="lazy" alt="'.$previewImageAlt.'"></div></div><div class="flex-grow-1 overflow-y-hidden d-flex align-items-center position-relative h-120">
|
||||
<div class="p-10 w-full m-auto">');
|
||||
|
||||
if(!empty($previewPreface)) {
|
||||
echo('<p class="font-size-10 text-dark-lm text-light-dm m-0 mb-5 text-truncate font-weight-medium">'.$previewPreface.'</p>');
|
||||
}
|
||||
|
||||
if(!empty($previewText)) {
|
||||
echo('<p class="font-size-12 mt-5 mb-0">'.$previewText.'<br><span class="text-primary text-smoothing-auto-dm d-inline-block">Click here <i class="fa fa-angle-right" aria-hidden="true"></i></span></p>');
|
||||
} else {
|
||||
echo('<p class="font-size-12 mt-5 mb-0"><span class="text-primary text-smoothing-auto-dm d-inline-block">Click here <i class="fa fa-angle-right" aria-hidden="true"></i></span></p>');
|
||||
}
|
||||
|
||||
echo('</div><div class="sponsor-section-card-scroll-block"></div></div></div>');
|
||||
|
||||
echo('<div class="px-card py-10 bg-light-lm bg-very-dark-dm rounded-bottom px-20"><p class="font-size-12 m-0"><i class="fad fa-tags"></i>');
|
||||
foreach($tags as &$tagValue) {
|
||||
echo(' <a href="?c=t&t='.$tagValue.'">#'.$tagValue.'</a>');
|
||||
}
|
||||
echo('</p></div></div>');
|
||||
}
|
||||
|
||||
/* Defining globals and constants */
|
||||
const BLOG_ACTION_NONE = "n";
|
||||
const BLOG_ACTION_SEARCH_TAGS = "t";
|
||||
const BLOG_ACTION_SEARCH_AUTHOR = "a";
|
||||
const BLOG_ACTION_SEARCH_DATE = "d";
|
||||
//const BLOG_ACTION_ERROR_SEARCH_TYPE_INVALID = "e";
|
||||
|
||||
const BLOG_PARAM_ACTION = "c";
|
||||
const BLOG_PARAM_ARTICLE_PER_PAGE = "c";
|
||||
const BLOG_PARAM_PAGE = "p";
|
||||
const BLOG_PARAM_SEARCH_PARAM = "s";
|
||||
|
||||
$blog_action = BLOG_ACTION_NONE;
|
||||
$blog_search_parameter = "";
|
||||
$current_page = 0;
|
||||
$article_per_page = 10;
|
||||
|
||||
/* Parsing and verifying arguments */
|
||||
if(count($_GET) > 0) {
|
||||
if(array_key_exists(BLOG_PARAM_ACTION, $_GET)) {
|
||||
$blog_action = $_GET[BLOG_PARAM_ACTION];
|
||||
}
|
||||
if(array_key_exists(BLOG_PARAM_ARTICLE_PER_PAGE, $_GET)) {
|
||||
$article_per_page = $_GET[BLOG_PARAM_ARTICLE_PER_PAGE];
|
||||
}
|
||||
if(array_key_exists(BLOG_PARAM_PAGE, $_GET)) {
|
||||
$current_page = $_GET[BLOG_PARAM_PAGE];
|
||||
}
|
||||
if(array_key_exists(BLOG_PARAM_SEARCH_PARAM, $_GET)) {
|
||||
$blog_search_parameter = $_GET[BLOG_PARAM_SEARCH_PARAM];
|
||||
}
|
||||
}
|
||||
|
||||
if(!($blog_action == BLOG_ACTION_NONE || $blog_action == BLOG_ACTION_SEARCH_TAGS ||
|
||||
$blog_action == BLOG_ACTION_SEARCH_AUTHOR || $blog_action == BLOG_ACTION_SEARCH_DATE)) {
|
||||
$blog_action = BLOG_ACTION_NONE;
|
||||
}
|
||||
|
||||
if(!is_numeric($article_per_page)) {
|
||||
$article_per_page = 10;
|
||||
}
|
||||
|
||||
if(!is_numeric($current_page)) {
|
||||
$current_page = 0;
|
||||
}
|
||||
|
||||
/* Loading blog entries */
|
||||
$jsonArticles = null;
|
||||
|
||||
try {
|
||||
$jsonArticles = file_get_contents("articles.json");
|
||||
$jsonArticles = json_decode($jsonArticles, true);
|
||||
} catch (Exception $e) {
|
||||
echo 'Caught exception: ', $e->getMessage(), "\n";
|
||||
$jsonArticles = null;
|
||||
}
|
||||
|
||||
if($jsonArticles == null) {
|
||||
exit("An error occured during the JSON parsing process...");
|
||||
}
|
||||
|
||||
/* Printing the articles... */
|
||||
$currentArticleId = 0;
|
||||
$articlesLeft = $article_per_page;
|
||||
|
||||
//TODO: Max value is not right or checked yet !
|
||||
for($currentArticleId = ($article_per_page * $current_page); $currentArticleId < count($jsonArticles); $currentArticleId++) {
|
||||
//echo($jsonArticles[$currentArticleId]["id"]."<br>");
|
||||
printBlogPreviewCard(
|
||||
$jsonArticles[$currentArticleId]["id"],
|
||||
$jsonArticles[$currentArticleId]["title"],
|
||||
$jsonArticles[$currentArticleId]["preview"]["preface"],
|
||||
$jsonArticles[$currentArticleId]["preview"]["text"],
|
||||
$jsonArticles[$currentArticleId]["preview"]["image"],
|
||||
$jsonArticles[$currentArticleId]["preview"]["imageAlt"],
|
||||
$jsonArticles[$currentArticleId]["tags"],
|
||||
$jsonArticles[$currentArticleId]["preview"]["icon"]);
|
||||
}
|
||||
?></div>
|
@@ -41,7 +41,7 @@
|
||||
},
|
||||
"image": "/resources/Azias/imgs/lscom-v2-text-01-bkgd-cli.png",
|
||||
"tags": [
|
||||
"application", "lscom", "purebasic"
|
||||
"application", "lscom", "purebasic", "windows"
|
||||
]
|
||||
}
|
||||
]
|
@@ -44,12 +44,35 @@ if($content_has_error) {
|
||||
<div class="container-fluid">
|
||||
<div id="page-title-bar" class="card p-0 pl-20 m-0 square-corners bg-very-dark title-bkgd navbar">
|
||||
<h2 class="content-title font-size-24 mt-20 text-truncate">
|
||||
<i class="fad fa-mailbox"></i> ${TODO}
|
||||
<i class="fad fa-briefcase"></i> <?php
|
||||
if($content_has_error) {
|
||||
if(isset($_SERVER['HTTP_REFERER'])) {
|
||||
echo('<a href="'.$_SERVER['HTTP_REFERER'].'">'.localize("content.title.content").'</a>');
|
||||
} else {
|
||||
//echo('<a class="js-set-previous-url" href="#">'.localize("content.title.content").'</a>');
|
||||
echo(localize("content.title.content"));
|
||||
}
|
||||
echo('<span class="mx-10">❱</span>'.localize("content.title.error"));
|
||||
} elseif($requested_content_display_type == ContentDisplayType::SEARCH) {
|
||||
echo(localize("content.title.content").'<span class="mx-10">❱</span>'.localize("content.title.search"));
|
||||
} elseif($requested_content_display_type == ContentDisplayType::CONTENT) {
|
||||
echo(localize("content.title.content").'<span class="mx-10">❱</span>$TODO');
|
||||
}
|
||||
?>
|
||||
</h2>
|
||||
<?php include 'header-lang.php'; ?>
|
||||
</div>
|
||||
<div class="content mx-auto w-lg-p90">
|
||||
<?php
|
||||
startMainCard("fad fa-construction", "Under construction", "");
|
||||
echo('<div class="py-20 bg-light-lm rounded-bottom px-0 bg-very-dark title-bkgd">');
|
||||
echo('<div class="content m-0 mx-20">');
|
||||
echo('<h3 class="font-size-18 mb-5 font-weight-semi-bold content-search-title">');
|
||||
echo('<span class="font-weight-bold">15/04/2021</span> - This section is under construction...</h3>');
|
||||
echo('</div>');
|
||||
echo('</div>');
|
||||
endMainCard();
|
||||
|
||||
if($SHOW_CONTENT_DEBUG_CARD) {
|
||||
echo('<div class="card p-0 mx-0">
|
||||
<div class="px-card py-10 border-bottom px-20">
|
||||
@@ -74,6 +97,12 @@ if($content_has_error) {
|
||||
print_r($filtered_content_index_data);
|
||||
echo('</p>');
|
||||
echo('<p class="m-0 mb-5">$content_requested_url_part: '.$content_requested_url_part.'</p>');
|
||||
if($requested_content_display_type == ContentDisplayType::CONTENT) {
|
||||
echo('<hr class="subtle mb-10 mt-15">');
|
||||
echo('<p class="m-0 mb-5">$requested_item_data: ');
|
||||
print_r($requested_item_data);
|
||||
echo('</p>');
|
||||
}
|
||||
echo('</div></div>');
|
||||
}
|
||||
|
||||
@@ -139,8 +168,34 @@ if($content_has_error) {
|
||||
'</p></div>');
|
||||
endMainCard();
|
||||
} elseif($requested_content_display_type == ContentDisplayType::CONTENT) {
|
||||
startMainCard("fad fa-file-alt", localize("content.title.content"), "subtitle");
|
||||
endMainCard();
|
||||
$_title_icon = "fad fa-question";
|
||||
$_title_text = '<i>' . localize("error.content.data.no.title") . '</i>';
|
||||
|
||||
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($user_language, $requested_item_data["title"])) {
|
||||
$_title_text = $requested_item_data["title"][$user_language];
|
||||
} elseif (array_key_exists($default_language, $requested_item_data["title"])) {
|
||||
$_title_text = $requested_item_data["title"][$user_language];
|
||||
}
|
||||
}
|
||||
|
||||
startMainCard($_title_icon, $_title_text, "");
|
||||
|
||||
echo('<div class="py-20 bg-light-lm rounded-bottom px-0 bg-very-dark title-bkgd">');
|
||||
echo('<div class="content m-0 mx-20">');
|
||||
echo('<p class="my-0">Text will go here...</p>');
|
||||
echo('</div>');
|
||||
echo('</div>');
|
||||
|
||||
echo('<div class="px-card py-10 bg-light-lm bg-dark-dm rounded-bottom border-top">' .
|
||||
'<p class="font-size-12 m-0">' .
|
||||
'Card footer here.' .
|
||||
'</p></div>');
|
||||
endMainCard();
|
||||
}
|
||||
|
||||
content_printing_end:
|
||||
|
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"title": {
|
||||
"icon": "fad fa-terminal",
|
||||
"en": "PB-ListComPort - CLI COM port enumerator",
|
||||
"fr": "PB-ListComPort - Enumérateur de port COM pour invité de commande"
|
||||
},
|
||||
"tags": [
|
||||
"programming", "lscom"
|
||||
]
|
||||
],
|
||||
"toggles": {
|
||||
"downloads": true
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"_": "Is this file used anywhere ? - Referenced as the id in the article index"
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
* Add bottom margin to the last container in the content container for small screens
|
||||
* Maybe for big ones too ?
|
||||
* Does not happen on the privacy one, probably because all of them are shown.
|
||||
* Small is done !
|
@@ -3,6 +3,14 @@ document.getElementById('button-sidebar').addEventListener("click", () => {
|
||||
halfmoon.toggleSidebar();
|
||||
});
|
||||
|
||||
// Adding the last URL to every a element with the 'js-set-previous-url' class.
|
||||
/*document.querySelectorAll("a.js-set-previous-url").forEach(element => {
|
||||
element.href = document.referrer;
|
||||
element.addEventListener('click', function(e) {
|
||||
window.history.go(-2);
|
||||
});
|
||||
});*/
|
||||
|
||||
// TOX ID copiers. (Contact page)
|
||||
if(document.getElementById('button-copy-tox-id-main') != null) {
|
||||
document.getElementById('button-copy-tox-id-main').addEventListener("click", () => {
|
||||
|
30
sitemap.txt
30
sitemap.txt
@@ -1,41 +1,17 @@
|
||||
https://nibblepoker.lu/
|
||||
https://nibblepoker.lu/blog/
|
||||
https://nibblepoker.lu/programming/
|
||||
https://nibblepoker.lu/programming/applications/
|
||||
https://nibblepoker.lu/programming/tutorials/
|
||||
https://nibblepoker.lu/programming/tools/
|
||||
https://nibblepoker.lu/programming/purebasic/
|
||||
https://nibblepoker.lu/programming/python/
|
||||
https://nibblepoker.lu/programming/docker/
|
||||
https://nibblepoker.lu/programming/java/
|
||||
https://nibblepoker.lu/content/
|
||||
https://nibblepoker.lu/links/
|
||||
https://nibblepoker.lu/about/
|
||||
https://nibblepoker.lu/contact/
|
||||
https://nibblepoker.lu/privacy/
|
||||
https://nibblepoker.lu/en/
|
||||
https://nibblepoker.lu/en/blog/
|
||||
https://nibblepoker.lu/en/programming/
|
||||
https://nibblepoker.lu/en/programming/applications/
|
||||
https://nibblepoker.lu/en/programming/tutorials/
|
||||
https://nibblepoker.lu/en/programming/tools/
|
||||
https://nibblepoker.lu/en/programming/purebasic/
|
||||
https://nibblepoker.lu/en/programming/python/
|
||||
https://nibblepoker.lu/en/programming/docker/
|
||||
https://nibblepoker.lu/en/programming/java/
|
||||
https://nibblepoker.lu/en/content/
|
||||
https://nibblepoker.lu/en/links/
|
||||
https://nibblepoker.lu/en/about/
|
||||
https://nibblepoker.lu/en/contact/
|
||||
https://nibblepoker.lu/en/privacy/
|
||||
https://nibblepoker.lu/fr/
|
||||
https://nibblepoker.lu/fr/blog/
|
||||
https://nibblepoker.lu/fr/programming/
|
||||
https://nibblepoker.lu/fr/programming/applications/
|
||||
https://nibblepoker.lu/fr/programming/tutorials/
|
||||
https://nibblepoker.lu/fr/programming/tools/
|
||||
https://nibblepoker.lu/fr/programming/purebasic/
|
||||
https://nibblepoker.lu/fr/programming/python/
|
||||
https://nibblepoker.lu/fr/programming/docker/
|
||||
https://nibblepoker.lu/fr/programming/java/
|
||||
https://nibblepoker.lu/fr/content/
|
||||
https://nibblepoker.lu/fr/links/
|
||||
https://nibblepoker.lu/fr/about/
|
||||
https://nibblepoker.lu/fr/contact/
|
||||
|
Reference in New Issue
Block a user