Browse Source

initial commit after server failure

Léo 1 year ago
commit
a14390f8f5
100 changed files with 13973 additions and 0 deletions
  1. 5 0
      .gitignore
  2. 9 0
      .htaccess
  3. 58 0
      controllers/d.admin.php
  4. 204 0
      controllers/d.blog.php
  5. 77 0
      controllers/d.contact.php
  6. 20 0
      controllers/d.map.php
  7. 237 0
      controllers/d.users.php
  8. 77 0
      controllers/d.wiki.php
  9. 65 0
      includes/config.example.php
  10. 42 0
      includes/images.php
  11. 71 0
      includes/routes.php
  12. 23 0
      includes/session.php
  13. 6 0
      index.php
  14. 6 0
      info.php
  15. 487 0
      models/d.blog.php
  16. 410 0
      models/d.users.php
  17. 215 0
      models/d.wiki.php
  18. 48 0
      src/Mercator-Mountain1.svg
  19. 119 0
      src/exportpgsql.backup
  20. 6 0
      src/logo.min.svg
  21. 217 0
      src/logo.svg
  22. 102 0
      src/logo2.svg
  23. 104 0
      src/logo3.svg
  24. BIN
      src/logo3_black.png
  25. 1 0
      src/wiki.backup
  26. 10 0
      third/Md/Markdown.inc.php
  27. 1616 0
      third/Md/Markdown.php
  28. 11 0
      third/Md/MarkdownExtra.inc.php
  29. 1625 0
      third/Md/MarkdownExtra.php
  30. 9 0
      third/Md/MarkdownInterface.inc.php
  31. 34 0
      third/Md/MarkdownInterface.php
  32. 7 0
      views/blocks/d.footer.html
  33. 46 0
      views/blocks/d.head.html
  34. 52 0
      views/blocks/d.nav.html
  35. 240 0
      views/css/d.blog.css
  36. 341 0
      views/css/d.index.css
  37. 173 0
      views/css/d.map.css
  38. 219 0
      views/css/d.user.css
  39. 66 0
      views/css/d.wiki.css
  40. 23 0
      views/d.admin.git-pull.html
  41. 24 0
      views/d.admin.html
  42. 41 0
      views/d.admin.logs.html
  43. 72 0
      views/d.blog.edit.html
  44. 55 0
      views/d.blog.list.html
  45. 45 0
      views/d.blog.list.rss
  46. 125 0
      views/d.blog.view.html
  47. 58 0
      views/d.contact.html
  48. 33 0
      views/d.index.html
  49. 25 0
      views/d.map.html
  50. 34 0
      views/d.user.login.html
  51. 70 0
      views/d.user.member_list.html
  52. 24 0
      views/d.user.password_lost.html
  53. 74 0
      views/d.user.profile.edit.html
  54. 66 0
      views/d.user.profile.html
  55. 41 0
      views/d.user.signin.html
  56. 30 0
      views/d.wiki.edit.html
  57. 56 0
      views/d.wiki.view.html
  58. BIN
      views/fonts/FiraMono-Bold.eot
  59. BIN
      views/fonts/FiraMono-Bold.otf
  60. BIN
      views/fonts/FiraSans-Bold.ttf
  61. BIN
      views/fonts/FiraSans-ExtraLight.eot
  62. BIN
      views/fonts/FiraSans-ExtraLight.otf
  63. BIN
      views/fonts/FiraSans-ExtraLight.ttf
  64. BIN
      views/fonts/FiraSans-Light.eot
  65. BIN
      views/fonts/FiraSans-Light.otf
  66. BIN
      views/fonts/FiraSans-Light.ttf
  67. BIN
      views/fonts/FiraSans-Medium.eot
  68. BIN
      views/fonts/FiraSans-Medium.otf
  69. BIN
      views/fonts/FiraSans-Medium.ttf
  70. BIN
      views/fonts/FiraSans-Regular.eot
  71. BIN
      views/fonts/FiraSans-Regular.otf
  72. BIN
      views/fonts/FiraSans-Regular.ttf
  73. BIN
      views/img/aside.jpg
  74. BIN
      views/img/favicon.png
  75. 104 0
      views/img/header.svg
  76. 104 0
      views/img/header_rss.svg
  77. BIN
      views/img/lstronic.png
  78. BIN
      views/img/thumb1.jpg
  79. BIN
      views/img/thumb2.jpg
  80. BIN
      views/img/thumb3.jpg
  81. 40 0
      views/js/d.avatar.js
  82. 22 0
      views/js/d.captcha.js
  83. 81 0
      views/js/d.header.js
  84. 64 0
      views/js/d.map.js
  85. 2337 0
      views/third/font-awesome-4.7.0/css/font-awesome.css
  86. 4 0
      views/third/font-awesome-4.7.0/css/font-awesome.min.css
  87. BIN
      views/third/font-awesome-4.7.0/fonts/FontAwesome.otf
  88. BIN
      views/third/font-awesome-4.7.0/fonts/fontawesome-webfont.eot
  89. 2671 0
      views/third/font-awesome-4.7.0/fonts/fontawesome-webfont.svg
  90. BIN
      views/third/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf
  91. BIN
      views/third/font-awesome-4.7.0/fonts/fontawesome-webfont.woff
  92. BIN
      views/third/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2
  93. 4 0
      views/third/jquery-3.1.1.min.js
  94. 56 0
      views/third/leaflet-easybutton/easy-button.css
  95. 379 0
      views/third/leaflet-easybutton/easy-button.js
  96. 152 0
      views/third/leaflet-fullscreen/Leaflet.fullscreen.js
  97. 1 0
      views/third/leaflet-fullscreen/Leaflet.fullscreen.min.js
  98. BIN
      views/third/leaflet-fullscreen/fullscreen.png
  99. BIN
      views/third/leaflet-fullscreen/fullscreen@2x.png
  100. 0 0
      views/third/leaflet-fullscreen/leaflet.fullscreen.css

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+/includes/config.php
+/medias/*
+*.sublime-project
+*.sublime-workspace
+*.log

+ 9 - 0
.htaccess

@@ -0,0 +1,9 @@
+AddDefaultCharset UTF-8
+
+RewriteEngine On
+
+# Everything uses the routing system
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_FILENAME} !-l
+RewriteRule . %{ENV:BASE}index.php [L]

+ 58 - 0
controllers/d.admin.php

@@ -0,0 +1,58 @@
+<?
+
+if(isset($controller->splitted_url[1]) && $user->role >= 800) {
+	switch ($controller->splitted_url[1]) {
+		case '': case 'admin':
+			$head['title'] = "Administration";
+			include ($config['views_folder']."d.admin.html");
+			break;
+		case 'git-pull':
+			if ($user->role >= 1000) {
+				$head['title'] = "Mise à jour";
+
+				$output = array();
+				chdir($config['abs_root_folder']);
+				exec("git pull origin master", $output);
+
+				include ($config['views_folder']."d.admin.git-pull.html");
+			}
+			else {
+				$notfound = 1;
+			}
+			break;
+		case 'logs':
+			if ($user->role >= 800) {
+				$head['title'] = "Logs";
+
+				$files_list = scandir($config['logs_folder']);
+
+				if (isset($controller->splitted_url[2]) && is_numeric($controller->splitted_url[2]) && intval($controller->splitted_url[2]) < count($files_list)-2) {
+					$filenb = $controller->splitted_url[2];
+				}
+				else {
+					$filenb = 0;
+				}
+
+				chdir($config['logs_folder']);
+				exec("tail -n 200 ".$files_list[$filenb+2]." | tac", $output);
+
+				include ($config['views_folder']."d.admin.logs.html");
+			}
+			else {
+				$notfound = 1;
+			}
+			break;
+		default:
+			$notfound = 1;
+			break;
+	}
+}
+else if($user->role >= 800) {
+	$head['title'] = "Administration";
+	include ($config['views_folder']."d.admin.html");
+}
+else {
+	$notfound = 1;
+}
+
+?>

+ 204 - 0
controllers/d.blog.php

@@ -0,0 +1,204 @@
+<?
+
+require_once($config['models_folder']."d.blog.php");
+require_once($config['models_folder']."d.users.php");
+
+$head['css'] = "d.index.css;d.blog.css";
+
+$blogArticle = new BlogArticle();
+
+// In case we are in the list of articles, we set url to switch with according parameters
+if (!isset($controller->splitted_url[1]) OR $controller->splitted_url[1]=="" OR is_numeric($controller->splitted_url[1])) {
+	$head['title'] = "Blog";
+
+	// Get the correct page number
+	if (!isset($controller->splitted_url[1]) OR $controller->splitted_url[1]=="") {
+		$page = 0;
+	} else {
+		$page = $controller->splitted_url[1] - 1;
+	}
+
+	$controller->splitted_url[1] = "list";
+	$list = "html";
+	$articles_per_pages = 5;
+}
+
+switch ($controller->splitted_url[1]) {
+	case "rss":
+		$page = 0;
+		$list = "rss";
+		$articles_per_pages = 20;
+	case "list":
+		$blogArticles = new BlogArticles();
+
+		$blogArticles->number(($user->role >= 600));
+
+		// In case the wanted page is too big
+		if($articles_per_pages * $page >= $blogArticles->number)
+			$page = 0;
+
+		$blogArticles->listArticles($page*$articles_per_pages,$articles_per_pages,($user->role >= 600));
+
+		$i = 0;
+		$blogArticles_list = array();
+		foreach ($blogArticles->ids as $row) {
+			$blogArticles_list[$i] = new BlogArticle();
+			$blogArticles_list[$i]->id = $row;
+			$blogArticles_list[$i]->populate();
+			$blogArticles_list[$i]->md2txt();
+			$tempUser = new User();
+			$tempUser->id = $blogArticles_list[$i]->author;
+			$tempUser->populate();
+			$blogArticles_list[$i]->author_name = $tempUser->name;
+			unset($tempUser);
+			$i++;
+		}
+
+		$first = $page*$articles_per_pages+1;
+		$last = (($page+1)*$articles_per_pages > $blogArticles->number ? $blogArticles->number : ($page+1)*$articles_per_pages);
+
+		if ($list == "rss") {
+			include ($config['views_folder']."d.blog.list.rss");
+		} else {
+			include ($config['views_folder']."d.blog.list.html");
+		}
+		break;
+	case "new":
+		if($user->role >= 800) {
+			if(isset($_POST['submit'])) {
+				$blogArticle->content = $_POST['content'];
+				$blogArticle->locale = $_POST['locale'];
+				$blogArticle->title = $_POST['title'];
+				$blogArticle->comments = isset($_POST['comments'])?'t':'f';
+				$blogArticle->author = $user->id;
+				if(!$blogArticle->checkUrl($_POST['url'],1)) {
+					$blogArticle->insert();
+					header('Location: '.$config['rel_root_folder']."blog/".$blogArticle->url);
+				}
+				else {
+					$head['title'] = $blogArticle->title;
+					$error = "url";
+					$new = 1;
+					include ($config['views_folder']."d.blog.edit.html");
+				}
+			}
+			else {
+				$head['title'] = "Nouvel article";
+				$new = 1;
+				include ($config['views_folder']."d.blog.edit.html");
+			}
+			break;
+		}
+	default:
+		// If the page exists
+		if ($blogArticle->checkUrl($controller->splitted_url[1],$user->role >= 600)) {
+			if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "delete" && $user->role >= 800) {
+				$blogArticle->delete();
+				header('Location: '.$config['rel_root_folder']."blog/".$blogArticle->url);
+			}
+			else if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "edit" && $user->role >= 800) {
+				if(isset($_POST['submit'])) {
+					$blogArticle->content = $_POST['content'];
+					$blogArticle->locale = $_POST['locale'];
+					$blogArticle->title = $_POST['title'];
+					$blogArticle->comments = isset($_POST['comments'])?'t':'f';
+					$blogArticle->author = $user->id;
+					$blogArticle->update();
+					header('Location: '.$config['rel_root_folder']."blog/".$blogArticle->url);
+				}
+				else {
+					$blogArticle->populate();
+					$head['title'] = $blogArticle->title;
+					include ($config['views_folder']."d.blog.edit.html");
+				}
+			}
+			else {
+				// Manage history of an article
+				if($user->role >= 600) {
+					$blogArticles_history = new BlogArticles();
+					$blogArticles_history->getHistory($controller->splitted_url[1]);
+
+					$i = 0;
+					foreach ($blogArticles_history->ids as $row) {
+						$blogArticles_history_list[$i] = new BlogArticle();
+						$blogArticles_history_list[$i]->id = $row;
+						$blogArticles_history_list[$i]->populate();
+						$i++;
+					}
+				}
+				if (isset($controller->splitted_url[2]) && is_numeric($controller->splitted_url[2]))
+					$blogArticle->checkUrl($controller->splitted_url[1],$user->role>=600,$controller->splitted_url[2]);
+
+				// Manage comment creation
+				if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="new_comment") {
+					if (isset($_POST['submit']) && $user->role > 0) {
+						$blogComment = new BlogComment();
+						$blogComment->locale = $user->locale;
+						$blogComment->author = $user->id;
+						$blogComment->article = $blogArticle->id;
+						$blogComment->content = $_POST['comment'];
+						$blogComment->insert();
+					}
+				}
+
+				// Manage comment deletion
+				if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="delete_comment") {
+					if (isset($controller->splitted_url[3]) && is_numeric($controller->splitted_url[3])) {
+						$blogComment = new BlogComment();
+						$blogComment->id = $controller->splitted_url[3];
+						$blogComment->populate();
+						if ($user->role >= 800 || $user->id == $blogComment->author)
+							$blogComment->delete();
+					}
+				}
+
+				// Manage comment undeletion
+				if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="undelete_comment") {
+					if (isset($controller->splitted_url[3]) && is_numeric($controller->splitted_url[3])) {
+						$blogComment = new BlogComment();
+						$blogComment->id = $controller->splitted_url[3];
+						$blogComment->populate();
+						if ($user->role >= 800 || $user->id == $blogComment->author)
+							$blogComment->undelete();
+					}
+				}
+
+				$blogArticle->populate();
+				$blogArticle->md2html();
+
+				// Manage comments
+				if ($blogArticle->comments == "t") {
+					$blogArticles_comments = new BlogComments();
+					$blogArticles_comments->listComments($blogArticle->id, ($user->role>400));
+
+					$i = 0;
+					foreach ($blogArticles_comments->ids as $row) {
+						$blogArticles_comments_list[$i] = new BlogComment();
+						$blogArticles_comments_list[$i]->id = $row;
+						$blogArticles_comments_list[$i]->populate();
+						$blogArticles_comments_list[$i]->md2html();
+						$blogArticles_comments_list[$i]->author_obj = new User();
+						$blogArticles_comments_list[$i]->author_obj->id = $blogArticles_comments_list[$i]->author;
+						$blogArticles_comments_list[$i]->author_obj->populate();
+						$i++;
+					}
+				}
+
+
+				$tempUser = new User();
+				$tempUser->id = $blogArticle->author;
+				$tempUser->populate();
+				$blogArticle->author_name = $tempUser->name;
+				unset($tempUser);
+
+				$head['title'] = $blogArticle->title;
+				include ($config['views_folder']."d.blog.view.html");
+			}
+		}
+		else {
+			$notfound = 1;	
+		}
+		break;
+}
+
+?>

+ 77 - 0
controllers/d.contact.php

@@ -0,0 +1,77 @@
+<?
+
+function post($index) {
+	return isset($_POST[$index]) ? $_POST[$index] : '';
+}
+
+$error = "no";
+
+if(isset($_POST['submit'])) {
+	$message = "Message reçu depuis Kabano par ".post('name').".<br>\r\n";
+	$message .= "<hr>\r\n";
+	$message .= "<pre style='padding: 10px; background: #ccc;'>".strip_tags(post('message'))."</pre><br>\r\n";
+
+	$headers = 'From: '. post('mail') . "\r\n" .
+	'Reply-To: '. post('mail') . "\r\n" .
+	'X-Mailer: PHP/' . phpversion() . "\r\n" .
+	'MIME-Version: 1.0' . "\r\n" .
+	'Content-type: text/html; charset=UTF-8' . "\r\n"; 
+
+	if(post('ns') == '' && $_POST['captcha'] == -2) {
+		$send = true;
+		if(post('name') == '') {
+			$error = "name";
+			$send = false;
+		}
+		if(post('subject') == '') {
+			$error = "subject";
+			$send = false;
+		}
+		if(post('mail') == '') {
+			$error = "mail";
+			$send = false;
+		}
+		if(post('message') == '') {
+			$error = "message";
+			$send = false;
+		}
+		if($send) {
+			if(mail($config['admin_mail'], "Kabano :: ".post('subject'), $message, $headers)) {
+				$error = "none";
+			} else {
+				$error = "unknown";
+			}
+		}
+	}
+	else {
+		$error = "spam";
+	}
+}
+
+if(post('name') != '')
+	$contact['name'] = post('name');
+else if($user->role > 0)
+	$contact['name'] = $user->name;
+else
+	$contact['name'] = '';
+
+if(post('mail') != '')
+	$contact['mail'] = post('mail');
+else if($user->role > 0)
+	$contact['mail'] = $user->mail;
+else
+	$contact['mail'] = '';
+
+$contact['subject'] = post('subject');
+$contact['message'] = post('message');
+$contact['ns'] = post('ns');
+
+
+$head['css'] = "d.index.css;d.user.css";
+$head['js'] = "d.captcha.js";
+$head['title'] = "Contact";
+
+include ($config['views_folder']."d.contact.html");
+
+
+?>

+ 20 - 0
controllers/d.map.php

@@ -0,0 +1,20 @@
+<?
+
+$head['css'] = "d.index.css";
+
+if(isset($controller->splitted_url[1]) && $controller->splitted_url[1] != '') {
+	switch ($controller->splitted_url[1]) {
+		default:
+			$notfound = 1;
+			break;
+	}
+}
+else {
+	$head['title'] = "Carte";
+	$head['third'] = "leaflet/leaflet.js;leaflet-fullscreen/Leaflet.fullscreen.min.js;leaflet-easybutton/easy-button.js";
+	$head['css'] .= ";d.map.css;../third/leaflet/leaflet.css;../third/leaflet-fullscreen/leaflet.fullscreen.css;../third/leaflet-easybutton/easy-button.css";
+	$head['js'] = "d.map.js";
+	include ($config['views_folder']."d.map.html");
+}
+
+?>

+ 237 - 0
controllers/d.users.php

@@ -0,0 +1,237 @@
+<?
+
+require_once($config['models_folder']."d.users.php");
+
+$head['css'] = "d.index.css;d.user.css";
+
+if(isset($controller->splitted_url[1])) {
+	switch ($controller->splitted_url[1]) {
+		case 'login':
+			$head['title'] = "Connexion";
+			if ($user->role == 0) {
+				if (isset($_POST['submit'])) {
+					// PROCESS DATA FROM FORM
+					$user = new User();
+					$user->login($_POST['login'], $_POST['password']);
+
+					if($user->id != 0) {
+						// SUCESSFULL LOGIN
+						$_SESSION['userid'] = $user->id;
+						header('Location: '.$_SERVER['HTTP_REFERER']);
+					}
+					else {
+						header('Location: '.$config['rel_root_folder'].'user/login?error=1');
+					}
+				}
+				include ($config['views_folder']."d.user.login.html");
+			} else {
+				header('Location: '.$config['rel_root_folder']);
+			}
+			break;
+		case 'logout':
+			session_destroy();
+			header('Location: '.$_SERVER['HTTP_REFERER']);
+			break;
+		case 'signin':
+			$head['js'] = "d.captcha.js";
+			$head['title'] = "Création de compte";
+			if ($user->role == 0) {
+				if (isset($_POST['submit'])) {
+					// PROCESS DATA FROM FORM
+					$user = new User();
+					$user->password = sha1($_POST['password']);
+					$user->name = $_POST['login'];
+					$user->mail = strtolower($_POST['mail']);
+					$user->role = 400;
+					$user->avatar = 'f';
+					$user->locale = "fr";
+
+					if($_POST['captcha'] == -2) {
+						if($user->availableName()) {
+							if($user->availableMail()) {
+								if($user->password != "" AND $user->name != "" AND $user->mail != "") {
+									$user->create();
+									header('Location: '.$config['rel_root_folder'].'user/login?status=created');
+								}
+								else {
+									header('Location: '.$config['rel_root_folder'].'user/signin?error=empty');
+								}
+							}
+							else {
+								header('Location: '.$config['rel_root_folder'].'user/signin?error=mail');
+							}
+						}
+						else {
+							header('Location: '.$config['rel_root_folder'].'user/signin?error=name');
+						}
+					}
+					else {
+						header('Location: '.$config['rel_root_folder'].'user/signin?error=captcha');
+					}
+				}
+				include ($config['views_folder']."d.user.signin.html");
+			} else {
+				header('Location: '.$config['rel_root_folder']);
+			}
+			break;
+		case 'password_lost':
+			$head['title'] = "Récupération de mot de passe";
+			if ($user->role == 0) {
+				if (isset($_POST['submit'])) {
+					// PROCESS DATA FROM FORM
+					$user = new User();
+					$user->mail = strtolower($_POST['mail']);
+
+					if($user->availableMail()) {
+						header('Location: '.$config['rel_root_folder'].'user/password_lost?error=1');
+					}
+					else {
+						$user->sendPassword();
+						header('Location: '.$config['rel_root_folder'].'user/login?status=password_sent');
+					}
+				}
+				include ($config['views_folder']."d.user.password_lost.html");
+			} else {
+				header('Location: '.$config['rel_root_folder']);
+			}
+			break;
+		case 'p':
+			if ($user->role >= 200) {
+				$userProfile = new User();
+				if (!isset($controller->splitted_url[2]) OR $controller->splitted_url[2]=="") {
+					// WE DISPLAY THE CONNECTED USER PROFILE
+					$userProfile = $user;
+				} else {
+					// WE DISPLAY THE SELECTED USER PROFILE FROM ID
+					$userProfile->checkID(intval($controller->splitted_url[2]));
+				}
+				$head['title'] = "Profil inexistant";
+				if($userProfile->id != 0) {
+					$userProfile->populate();
+					$head['title'] = "Profil de ".$userProfile->name;
+				}
+
+				// If we are editing the profile
+				if(isset($controller->splitted_url[3]) && $controller->splitted_url[3]=="edit" && ($user->role >= 800 || $user->id == $userProfile->id)) {
+					$head['js'] = "d.avatar.js";
+					if (isset($_POST['submit'])) {
+						$receivedUser = new User();
+						$receivedUser->name = $_POST['name'];
+						if($receivedUser->name != $userProfile->name && $receivedUser->availableName())
+							$userProfile->name = $receivedUser->name;
+						else if($receivedUser->name != $userProfile->name)
+							$nameError=1;
+						$receivedUser->mail = strtolower($_POST['mail']);
+						if($receivedUser->mail != $userProfile->mail && $receivedUser->availableMail())
+							$userProfile->mail = $receivedUser->mail;
+						else if ($receivedUser->mail != $userProfile->mail)
+							$mailError=1;
+						if($_POST['password']!='')
+							$userProfile->password=sha1($_POST['password']);
+						$userProfile->locale=$_POST['locale'];
+						if($user->role>=1000)
+							$userProfile->role = $_POST['role'];
+						$userProfile->website=$_POST['website'];
+
+						// Is the file correctly sent to the server ?
+						$pathToFile = $config['medias_folder']."avatars/".$userProfile->id;
+						if(isset($_FILES['avatarfile']['tmp_name']) && $_FILES['avatarfile']['tmp_name']!='' && $_FILES['avatarfile']['size'] < 16000000 && isset($_POST['avatar'])) {
+
+							require_once($config['includes_folder']."images.php");
+
+							if(file_exists($pathToFile)) unlink($pathToFile);
+							move_uploaded_file($_FILES['avatarfile']['tmp_name'], $pathToFile);
+
+							if(file_exists($pathToFile."_p.jpg")) unlink($pathToFile."_p.jpg");
+							generate_image_thumbnail($pathToFile, $pathToFile."_p.jpg", 220, 240);
+							if(file_exists($pathToFile."_s.jpg")) unlink($pathToFile."_s.jpg");
+							generate_image_thumbnail($pathToFile, $pathToFile."_s.jpg", 28, 28);
+
+							$userProfile->avatar = 't';
+						}
+						elseif (!isset($_POST['avatar'])) {
+							if(file_exists($pathToFile)) unlink($pathToFile);
+							if(file_exists($pathToFile."_p.jpg")) unlink($pathToFile."_p.jpg");
+							if(file_exists($pathToFile."_s.jpg")) unlink($pathToFile."_s.jpg");
+							$userProfile->avatar = 'f';
+						}
+
+						$userProfile->update();
+
+						$updated = 1;
+					}
+					include ($config['views_folder']."d.user.profile.edit.html");
+
+				}
+				// If we are displaying the profile
+				else {
+					if (isset($_POST['submit']) && $user->role >= 400) {
+						// PROCESS DATA FROM CONTACT FORM
+						$message = $_POST['message'];
+						
+						$userProfile->sendMail($message, $user);
+						$mailsent = 1;
+					}
+					include ($config['views_folder']."d.user.profile.html");
+				}
+			}
+			else {
+				header('Location: '.$config['rel_root_folder']);
+			}
+			break;
+		case 'member_list':
+			if ($user->role >= 200) {
+				$rows_per_pages = 50;
+				// Get the correct page number
+				if (!isset($controller->splitted_url[2]) OR $controller->splitted_url[2]=="" OR $controller->splitted_url[2]=="0" OR !is_numeric($controller->splitted_url[2])) {
+					$page = 0;
+				} else {
+					$page = $controller->splitted_url[2] - 1;
+				}
+				$head['title'] = "Liste des membres";
+
+				$users = new Users();
+				$users->number();
+
+				// In case the wanted page is too big
+				if($rows_per_pages * $page >= $users->number)
+					$page = 0;
+
+				if(isset($_GET['order']))
+					$order = $_GET['order'];
+				else
+					$order = 'ASC';
+				if(isset($_GET['orderby']))
+					$orderby = $_GET['orderby'];
+				else
+					$orderby = 'id';
+
+				$users->list_users($page*$rows_per_pages,$rows_per_pages,$orderby,$order);
+
+				$i = 0;
+				foreach ($users->ids as $row) {
+					$user_list[$i] = new User();
+					$user_list[$i]->id = $row;
+					$user_list[$i]->populate();
+					$i++;
+				}
+
+				$first = $page*$rows_per_pages+1;
+				$last = (($page+1)*$rows_per_pages > $users->number ? $users->number : ($page+1)*$rows_per_pages);
+				
+				include ($config['views_folder']."d.user.member_list.html");
+			}
+			else {
+				header('Location: '.$config['rel_root_folder']);
+			}
+			break;
+		default:
+			$notfound = 1;
+			break;
+	}
+}
+else {
+	$notfound = 1;
+}
+
+?>

+ 77 - 0
controllers/d.wiki.php

@@ -0,0 +1,77 @@
+<?
+
+require_once($config['models_folder']."d.wiki.php");
+
+$head['css'] = "d.index.css;d.wiki.css";
+
+$wikiPage = new WikiPage();
+// Page doesn't exists
+if(isset($controller->splitted_url[1]) && !$wikiPage->checkUrl($controller->splitted_url[1],$user->role >= 600) && $controller->splitted_url[1]!="") {
+	if($user->role >= 800) {
+		// Create new page
+		if(isset($_POST['submit'])) {
+			$wikiPage->content = $_POST['content'];
+			$wikiPage->locale = $_POST['locale'];
+			$wikiPage->title = $_POST['title'];
+			$wikiPage->insert();
+
+			header('Location: '.$config['rel_root_folder']."wiki/".$wikiPage->url);
+		}
+		else {
+			$head['title'] = "Nouvelle page";
+			include ($config['views_folder']."d.wiki.edit.html");
+		}
+	}
+	else {
+		$notfound = 1;
+	}
+}
+// Page exists
+else if(isset($controller->splitted_url[1]) && $wikiPage->checkUrl($controller->splitted_url[1],$user->role >= 600)) {
+	if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="edit" && $user->role >= 800) {
+		// Edit page
+		if(isset($_POST['submit'])) {
+			$wikiPage->content = $_POST['content'];
+			$wikiPage->locale = $_POST['locale'];
+			$wikiPage->title = $_POST['title'];
+			$wikiPage->update();
+
+			header('Location: '.$config['rel_root_folder']."wiki/".$wikiPage->url);
+		}
+		else {
+			$wikiPage->populate();
+			$head['title'] = $wikiPage->title;
+			include ($config['views_folder']."d.wiki.edit.html");
+		}
+	} else if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="delete" && $user->role >= 800) {
+		// Delete page
+		$wikiPage->delete();
+		header('Location: '.$config['rel_root_folder']."wiki/".$wikiPage->url);
+	} else {
+		// Display page
+		if($user->role >= 600) {
+			$wikiHistory = new WikiPages();
+			$wikiHistory->getHistory($controller->splitted_url[1]);
+
+			$i = 0;
+			foreach ($wikiHistory->ids as $row) {
+				$wikiHistory_list[$i] = new WikiPage();
+				$wikiHistory_list[$i]->id = $row;
+				$wikiHistory_list[$i]->populate();
+				$i++;
+			}
+		}
+		if (isset($controller->splitted_url[2]) && is_numeric($controller->splitted_url[2]))
+			$wikiPage->checkUrl($controller->splitted_url[1],$user->role>=600, $controller->splitted_url[2]);
+
+		$wikiPage->populate();
+		$wikiPage->md2html();
+		$head['title'] = $wikiPage->title;
+		include ($config['views_folder']."d.wiki.view.html");
+	}
+}
+else {
+	$notfound = 1;
+}
+
+?>

+ 65 - 0
includes/config.example.php

@@ -0,0 +1,65 @@
+<?
+
+ini_set('display_errors', 1);
+ini_set('display_startup_errors', 1);
+error_reporting(E_ALL);
+
+
+/*****
+** Management of folder names
+*****/
+
+// It is the include folder name
+$config['include_folder']=basename(__DIR__);
+// This is the absolute folder to the root of the website
+$config['abs_root_folder']=str_replace($config['include_folder'],"",__DIR__);
+// This is the relative folder to the root of the website from the DocumentRoot (can also be called subfolder)
+$config['rel_root_folder']=str_replace($_SERVER['DOCUMENT_ROOT'],"",$config['abs_root_folder']);
+if($config['rel_root_folder']=="") $config['rel_root_folder']="/";
+
+// Here all the absolute paths to specific folders
+$config['views_folder'] = $config['abs_root_folder']."views/";
+$config['controllers_folder'] = $config['abs_root_folder']."controllers/";
+$config['models_folder'] = $config['abs_root_folder']."models/";
+$config['medias_folder'] = $config['abs_root_folder']."medias/";
+$config['includes_folder'] = $config['abs_root_folder']."includes/";
+$config['third_folder'] = $config['abs_root_folder']."third/";
+$config['logs_folder'] = $config['abs_root_folder']."logs/";
+
+// Here all the relative url to specific folders
+$config['views_url'] = $config['rel_root_folder']."views/";
+
+
+/*****
+** SQL Database configuration
+*****/
+
+$config['SQL_host'] = "localhost";
+$config['SQL_user'] = "kabano";
+$config['SQL_pass'] = "PASSWORD";
+$config['SQL_db'] = "postgres";
+
+/*****
+** Mail configuration
+*****/
+
+$config['admin_mail'] = "leo@lstronic.com";
+$config['bot_mail'] = "robot@kabano.com";
+
+/*****
+** Locales configuration
+*****/
+
+$config['locales'] = array(
+	"fr" => array("fr","fr_FR.UTF8","french","fr_FR","fr_FR.UTF-8", "Français")
+	);
+$config['roles'] = array(
+	1000 => array(1000,"Administrateur", "red"),
+	800 => array(800,"Modérateur", "orangered"),
+	600 => array(600,"Membre premium", "orange"),
+	400 => array(400,"Utilisateur", "green"),
+	200 => array(200,"Membre archivé", "#aaa"),
+	0 => array(0,"Visiteur", "black")
+	);
+
+?>

+ 42 - 0
includes/images.php

@@ -0,0 +1,42 @@
+<?
+
+function generate_image_thumbnail($source_image_path, $thumbnail_image_path, $width, $height)
+{
+	list($source_image_width, $source_image_height, $source_image_type) = getimagesize($source_image_path);
+	switch ($source_image_type) {
+		case IMAGETYPE_GIF:
+			$source_gd_image = imagecreatefromgif($source_image_path);
+			break;
+		case IMAGETYPE_JPEG:
+			$source_gd_image = imagecreatefromjpeg($source_image_path);
+			break;
+		case IMAGETYPE_PNG:
+			$source_gd_image = imagecreatefrompng($source_image_path);
+			break;
+	}
+	if ($source_gd_image === false) {
+		return false;
+	}
+
+	$src_x = 0;
+	$src_y = 0;
+	$thumbnail_image_height = $height;
+	$thumbnail_image_width = $width;
+	// If the limitation is on the height (cuts on the width)
+	if($height*$source_image_width/$source_image_height > $width) {
+		$src_x = (int)(($source_image_width - $source_image_height * $width / $height) / 2);
+		$source_image_width = $source_image_height * $width / $height;
+	} else {
+		$src_y = (int)(($source_image_height - $source_image_width * $height / $width) / 2);
+		$source_image_height = $source_image_width * $height / $width;
+	}
+
+	$thumbnail_gd_image = imagecreatetruecolor($thumbnail_image_width, $thumbnail_image_height);
+	imagecopyresampled($thumbnail_gd_image, $source_gd_image, 0, 0, $src_x, $src_y, $thumbnail_image_width, $thumbnail_image_height, $source_image_width, $source_image_height);
+	imagejpeg($thumbnail_gd_image, $thumbnail_image_path, 90);
+	imagedestroy($source_gd_image);
+	imagedestroy($thumbnail_gd_image);
+	return true;
+}
+
+?>

+ 71 - 0
includes/routes.php

@@ -0,0 +1,71 @@
+<?
+
+/*****
+** This file contains the routing from any request to the correct view and controller
+*****/
+
+$controller = new stdClass;
+$view = new stdClass;
+
+$controller->full_url = $_SERVER['REQUEST_URI'];
+$controller->url_no_param = explode('?',$controller->full_url);
+
+// URL without ?parameters and /subfolder/
+$controller->base_url=str_replace('RACINE'.$config['rel_root_folder'],'','RACINE'.$controller->url_no_param[0]);
+$controller->splitted_url = explode ('/',$controller->base_url);
+
+// By default we use the desktop 
+$view->prefix = "d.";
+$controller->prefix = "d.";
+$notfound = 0;
+$session = 1;
+
+// Routing to the correct page from the correct link
+switch ($controller->splitted_url[0])
+{
+    case "index": case "" :
+        $controller->name="";
+        $view->name="index";
+        break;
+    case "user" :
+        $controller->name="users";
+        $view->name="";
+        break;
+    case "contact" :
+    case "wiki" :
+    case "blog" :
+    case "map" :
+    case "admin" :
+        $controller->name=$controller->splitted_url[0];
+        $view->name="";
+        break;
+    default : 
+        $controller->name="";
+        $view->name="";
+		$notfound = 1;
+        break;
+}
+
+if($session==1) {
+	require_once('session.php');
+}
+if($controller->name != "") {
+	include ($config['controllers_folder'].$controller->prefix.$controller->name.".php");
+}
+if($view->name != "") {
+	include ($config['views_folder'].$view->prefix.$view->name.".html");
+}
+
+if($notfound) {
+    require_once('session.php');
+    require_once($config['models_folder']."d.wiki.php");
+    $wikiPage = new WikiPage();
+    $wikiPage->checkUrl('404');
+    $wikiPage->populate();
+    $wikiPage->md2html();
+    $head['css'] = "d.index.css;d.wiki.css";
+    $head['title'] = $wikiPage->title;
+    include ($config['views_folder']."d.wiki.view.html");
+}
+
+?>

+ 23 - 0
includes/session.php

@@ -0,0 +1,23 @@
+<?
+
+require_once($config['models_folder']."d.users.php");
+
+ini_set("session.cookie_lifetime",60*60*24*30);
+session_start();
+
+$user = new User();
+$user->role == 0; // All users are visitors
+
+if(isset($_SESSION['userid'])) {
+	$user->checkID($_SESSION['userid']);
+	if ($user->id != 0) {
+		$user->updateLoginDate();
+		$user->populate();
+		setlocale(LC_ALL, $config['locales'][$user->locale][4]);
+	}
+	else {
+		session_destroy();
+	}
+}
+
+?>

+ 6 - 0
index.php

@@ -0,0 +1,6 @@
+<?
+
+require_once('includes/config.php');
+require_once('includes/routes.php');
+
+?>

+ 6 - 0
info.php

@@ -0,0 +1,6 @@
+<?
+
+phpinfo();
+exit();
+
+?>

+ 487 - 0
models/d.blog.php

@@ -0,0 +1,487 @@
+<?
+
+/**********************************************************
+***********************************************************
+**  
+**  This class is to manage a blog article object
+**  
+***********************************************************
+**********************************************************/
+
+require_once($config['third_folder']."Md/MarkdownExtra.inc.php");
+
+class BlogArticle
+{
+	public $id = 0;
+	public $title = NULL;
+	public $url = NULL;
+	public $locale = NULL;
+	public $lastedit = NULL;
+	public $archive = NULL;
+	public $content = NULL;
+	public $author = NULL;
+	public $comments = NULL;
+
+	/*****
+	** Checks if a page at this URL exists and return the ID
+	*****/
+	public function checkUrl($url, $withArchive=0, $elementNb=0) {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM blog_articles WHERE url=$1";
+		if($withArchive==0) {
+			$query .= " AND archive=FALSE";
+		}
+		$query .= " ORDER BY lastedit DESC LIMIT 1 OFFSET $2";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($url, $elementNb))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		if(pg_num_rows($result) == 1) {
+			$article = pg_fetch_assoc($result);
+			$this->id = $article['id'];
+			$this->url = $url;
+			return 1;
+		}
+		else {
+			$this->url = $url;
+			return 0;
+		}
+	}
+
+	/*****
+	** Populate the object using its ID
+	*****/
+	public function populate() {
+		global $config;
+		
+		if($this->id != 0) {
+			$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+				or die ("Could not connect to server\n");
+
+			$query = "SELECT * FROM blog_articles WHERE id=$1";
+
+			pg_prepare($con, "prepare1", $query) 
+				or die ("Cannot prepare statement\n");
+			$result = pg_execute($con, "prepare1", array($this->id))
+				or die ("Cannot execute statement\n");
+
+			pg_close($con);
+
+			$blog_article = pg_fetch_assoc($result);
+
+			$this->title = $blog_article['title'];
+			$this->url = $blog_article['url'];
+			$this->locale = $blog_article['locale'];
+			$this->lastedit = $blog_article['lastedit'];
+			$this->archive = $blog_article['archive'];
+			$this->content = $blog_article['content'];
+			$this->author = $blog_article['author'];
+			$this->comments = $blog_article['comments'];
+		}
+		else {
+			die("Cannot populate a blog article without ID");
+		}
+	}
+
+	/*****
+	** Edit a page by archiving the current one and inserting a new one ID
+	*****/
+	public function update() {
+		global $config;
+		global $user;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		// Archive previous article
+		$query = "UPDATE blog_articles SET archive = TRUE WHERE url = $1";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($this->url))
+			or die ("Cannot execute statement\n");
+
+		// Publish the new one
+		$query = "INSERT INTO blog_articles (url, title, content, lastedit, archive, locale, author, comments) VALUES
+			($1, $2, $3, $4, FALSE, $5, $6, $7) RETURNING id";
+
+		pg_prepare($con, "prepare2", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare2", array($this->url, $this->title, $this->content, date('r'), $this->locale, $this->author, $this->comments))
+			or die ("Cannot execute statement\n");
+
+		$this->id = pg_fetch_assoc($result)['id'];
+
+		// Move all comments to the new one
+
+		$query = "UPDATE blog_comments bc SET article = $1 FROM blog_articles ba WHERE bc.article = ba.id AND ba.url = $2";
+
+		pg_prepare($con, "prepare3", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare3", array($this->id, $this->url))
+			or die ("Cannot execute statement\n");
+
+
+		pg_close($con);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tUPDATE \tEdit blog article '".$this->url."'\r\n",
+			3,
+			$config['logs_folder'].'blog.articles.log');
+	}
+
+	/*****
+	** Delete an article by archiving it
+	*****/
+	public function delete() {
+		global $config;
+		global $user;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "UPDATE blog_articles SET archive = TRUE WHERE url = $1";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($this->url))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tDELETE \tArchive blog article '".$this->url."'\r\n",
+			3,
+			$config['logs_folder'].'blog.articles.log');
+	}
+
+	/*****
+	** Create an article
+	*****/
+	public function insert() {
+		global $config;
+		global $user;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "INSERT INTO blog_articles (url, title, content, lastedit, archive, locale, author, comments) VALUES
+			($1, $2, $3, $4, FALSE, $5, $6, $7)";
+
+		pg_prepare($con, "prepare2", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare2", array($this->url, $this->title, $this->content, date('r'), $this->locale, $this->author, $this->comments))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tINSERT \tCreate new blog article '".$this->url."'\r\n",
+			3,
+			$config['logs_folder'].'blog.articles.log');
+	}
+
+	/*****
+	** Converts the Markdown content to HTML
+	*****/
+	public function md2html() {
+		$this->content_html = \Michelf\MarkdownExtra::defaultTransform($this->content);
+	}
+
+	/*****
+	** Converts the Markdown content to text
+	*****/
+	public function md2txt() {
+		$this->md2html();
+		$this->content_txt = strip_tags($this->content_html);
+	}
+}
+
+
+/**********************************************************
+***********************************************************
+**  
+**  This class is to manage a list of blog articles
+**  
+***********************************************************
+**********************************************************/
+
+class BlogArticles
+{
+	public $ids = array();
+	public $number = NULL;
+
+	/*****
+	** Return the list of different articles
+	*****/
+	public function listArticles($first, $count, $archive=0) {
+		global $config;
+
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		if ($archive == 1) {
+			// You just want one per url and the criteria is ORDER BY archives = true, time DES=C
+			$query = "SELECT id FROM (SELECT a.id, a.lastedit , ROW_NUMBER() OVER (PARTITION BY a.url ORDER BY CASE WHEN a.archive IS TRUE THEN 1 ELSE 0 END, a.lastedit DESC) AS r FROM blog_articles AS a) AS b WHERE r = 1 ORDER BY lastedit DESC";
+		}
+		else {
+			$query = "SELECT id FROM blog_articles WHERE archive IS NOT TRUE ORDER BY lastedit DESC";
+		}
+		$query .= " LIMIT $1 OFFSET $2";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($count, $first))
+				or die ("Cannot execute statement\n");
+		
+		pg_close($con);
+
+		for($i = 0; $i < pg_num_rows($result); $i++) {
+			$row = pg_fetch_assoc($result, $i);
+			$this->ids[$i] = $row['id'];
+		}
+	}
+	/*****
+	** Return the number of articles
+	*****/
+	public function number($archive=0) {
+		global $config;
+
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		if ($archive == 1) {
+			// You just want one per url and the criteria is ORDER BY archives = true, time DES=C
+			$query = "SELECT id FROM (SELECT a.id, a.lastedit , ROW_NUMBER() OVER (PARTITION BY a.url ORDER BY CASE WHEN a.archive IS TRUE THEN 1 ELSE 0 END, a.lastedit DESC) AS r FROM blog_articles AS a) AS b WHERE r = 1 ORDER BY lastedit DESC";
+		}
+		else {
+			$query = "SELECT id FROM blog_articles WHERE archive IS NOT TRUE ORDER BY lastedit DESC";
+		}
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array())
+				or die ("Cannot execute statement\n");
+		
+		pg_close($con);
+
+		$this->number = pg_num_rows($result);
+	}
+	/*****
+	** Return the list of archived version of a blog article
+	*****/
+	public function getHistory($url) {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM blog_articles WHERE url=$1 ORDER BY lastedit DESC";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($url))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		$this->number = pg_num_rows($result);
+
+		for($i = 0; $i < $this->number; $i++) {
+			$row = pg_fetch_assoc($result, $i);
+			$this->ids[$i] = $row['id'];
+		}
+	}
+}
+
+
+/**********************************************************
+***********************************************************
+**  
+**  This class is to manage a blog comment object
+**  
+***********************************************************
+**********************************************************/
+
+class BlogComment
+{
+	public $id = 0;
+	public $locale = NULL;
+	public $lastedit = NULL;
+	public $archive = NULL;
+	public $content = NULL;
+	public $author = NULL;
+	public $article = NULL;
+
+	/*****
+	** Populate the object using its ID
+	*****/
+	public function populate() {
+		global $config;
+		
+		if($this->id != 0) {
+			$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+				or die ("Could not connect to server\n");
+
+			$query = "SELECT * FROM blog_comments WHERE id=$1";
+
+			pg_prepare($con, "prepare1", $query) 
+				or die ("Cannot prepare statement\n");
+			$result = pg_execute($con, "prepare1", array($this->id))
+				or die ("Cannot execute statement\n");
+
+			pg_close($con);
+
+			$blog_comment = pg_fetch_assoc($result);
+
+			$this->locale = $blog_comment['locale'];
+			$this->lastedit = $blog_comment['lastedit'];
+			$this->archive = $blog_comment['archive'];
+			$this->content = $blog_comment['content'];
+			$this->author = $blog_comment['author'];
+			$this->article = $blog_comment['article'];
+		}
+		else {
+			die("Cannot populate a blog article without ID");
+		}
+	}
+
+	/*****
+	** Create a new comment
+	*****/
+	public function insert() {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "INSERT INTO blog_comments (content, lastedit, archive, locale, author, article) VALUES
+			($1, $2, FALSE, $3, $4, $5)";
+
+		pg_prepare($con, "prepare2", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare2", array($this->content, date('r'), $this->locale, $this->author, $this->article))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+	}
+
+	/*****
+	** Archive a comment
+	*****/
+	public function delete() {
+		global $config;
+		global $user;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "UPDATE blog_comments SET archive = TRUE WHERE id = $1";
+
+		pg_prepare($con, "prepare2", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare2", array($this->id))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tDELETE  \tArchive comment ".$this->id."\r\n",
+			3,
+			$config['logs_folder'].'blog.comments.log');
+	}
+
+	/*****
+	** DeArchive a comment
+	*****/
+	public function undelete() {
+		global $config;
+		global $user;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "UPDATE blog_comments SET archive = FALSE WHERE id = $1";
+
+		pg_prepare($con, "prepare2", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare2", array($this->id))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tPUBLISH \tUn archive comment ".$this->id."\r\n",
+			3,
+			$config['logs_folder'].'blog.comments.log');
+	}
+
+	/*****
+	** Converts the Markdown content to HTML
+	*****/
+	public function md2html() {
+		$this->content_html = \Michelf\MarkdownExtra::defaultTransform($this->content);
+	}
+
+	/*****
+	** Converts the Markdown content to text
+	*****/
+	public function md2txt() {
+		$this->md2html();
+		$this->content_txt = strip_tags($this->content_html);
+	}
+}
+
+
+/**********************************************************
+***********************************************************
+**  
+**  This class is to manage a list of blog comments
+**  
+***********************************************************
+**********************************************************/
+
+class BlogComments
+{
+	public $ids = array();
+	public $number = NULL;
+
+	/*****
+	** Return the list of different articles
+	*****/
+	public function listComments($id, $archive=0) {
+		global $config;
+
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM blog_comments WHERE article = $1 ";
+		if ($archive == 0)
+			$query .= "AND archive IS FALSE ";
+		$query .= "ORDER BY lastedit DESC";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($id))
+				or die ("Cannot execute statement\n");
+		
+		pg_close($con);
+
+		$this->number = pg_num_rows($result);
+
+		for($i = 0; $i < pg_num_rows($result); $i++) {
+			$row = pg_fetch_assoc($result, $i);
+			$this->ids[$i] = $row['id'];
+		}
+	}
+}
+
+?>

+ 410 - 0
models/d.users.php

@@ -0,0 +1,410 @@
+<?
+
+/**********************************************************
+***********************************************************
+**  
+**  This class is to manage User object
+**  
+***********************************************************
+**********************************************************/
+
+class User
+{
+	public $id = 0;
+	public $name = NULL;
+	public $avatar = NULL;
+	public $locale = NULL;
+	public $role = NULL;
+	public $lastlogin = NULL;
+	public $mail = NULL;
+	public $website = NULL;
+	public $password = NULL;
+	public $registered = NULL;
+
+	/*****
+	** Connect to correct account using ID and stores its ID
+	*****/
+	public function checkID($id) {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM users WHERE id=$1";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($id))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		if(pg_num_rows($result) == 1) {
+			$this->id = $id;
+			return 1;
+		}
+		else {
+			return 0;
+		}
+	}
+	/*****
+	** Connect to correct account using user/pass and stores its ID
+	*****/
+	public function login($login, $pass) {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM users WHERE name=$1 AND password=$2";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($login, sha1($pass)))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con); 
+
+		if(pg_num_rows($result) == 1) {
+			$user = pg_fetch_assoc($result);
+			$this->id = $user['id'];
+		}
+	}
+	/*****
+	** Populate the object using its ID
+	*****/
+	public function populate() {
+		global $config;
+		
+		if($this->id != 0) {
+			$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+				or die ("Could not connect to server\n");
+
+			$query = "SELECT * FROM users WHERE id=$1";
+
+			pg_prepare($con, "prepare1", $query) 
+				or die ("Cannot prepare statement\n");
+			$result = pg_execute($con, "prepare1", array($this->id))
+				or die ("Cannot execute statement\n");
+
+			pg_close($con);
+
+			$user = pg_fetch_assoc($result);
+
+			$this->name = $user['name'];
+			$this->avatar = $user['avatar'];
+			$this->locale = $user['locale'];
+			$this->role = $user['role'];
+			$this->lastlogin = $user['lastlogin'];
+			$this->mail = $user['mail'];
+			$this->website = $user['website'];
+			$this->registered = $user['registered'];
+		}
+		else {
+			die("Cannot populate an User without ID");
+		}
+	}
+	/*****
+	** Checks if the user's name is available or not
+	*****/
+	public function availableName() {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM users WHERE lower(name)=$1";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array(strtolower($this->name)))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		if(pg_num_rows($result) < 1) {
+			return 1;
+		}
+		else {
+			if(pg_num_rows($result)==1) {
+				$user = pg_fetch_assoc($result);
+				$this->id = $user['id'];
+			}
+			return 0;
+		}
+	}
+	/*****
+	** Checks if the user's mail address exists in the database
+	*****/
+	public function availableMail() {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM users WHERE lower(mail)=$1";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array(strtolower($this->mail)))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		if(pg_num_rows($result) < 1) {
+			return 1;
+		}
+		else {
+			if(pg_num_rows($result)==1) {
+				$user = pg_fetch_assoc($result);
+				$this->id = $user['id'];
+			}
+			return 0;
+		}
+	}
+	/*****
+	** Creates a new user.
+	*****/
+	public function create() {
+		global $config;
+
+		$regex = '/^(https?:\/\/)/';
+		if (!preg_match($regex, $this->website) && $this->website!="")
+			$this->website = "http://".$this->website;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "INSERT INTO users (name, password, avatar, locale, role, lastlogin, mail, website, registered) VALUES
+			($1, $2, $3, $4, $5, $6, $7, $8, $9)";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		pg_execute($con, "prepare1", array($this->name, $this->password, $this->avatar, $this->locale, $this->role, $this->lastlogin, $this->mail, $this->website, date('r')))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+	
+		$this->updateLoginDate();
+	}
+	/*****
+	** Update the user profile
+	*****/
+	public function update() {
+		global $config;
+		global $user;
+
+		$regex = '/^(https?:\/\/)/';
+		if (!preg_match($regex, $this->website) && $this->website!="")
+			$this->website = "http://".$this->website;
+
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		if($this->password=='') {
+			$query = "UPDATE users SET name = $1, avatar = $2, locale = $3, role = $4, mail = $5, website = $6 WHERE id = $7";
+			pg_prepare($con, "prepare1", $query) 
+				or die ("Cannot prepare statement\n");
+			pg_execute($con, "prepare1", array($this->name, $this->avatar, $this->locale, $this->role, $this->mail, $this->website, $this->id))
+				or die ("Cannot execute statement\n");
+		}
+		else {
+			$query = "UPDATE users SET name = $1, avatar = $2, locale = $3, role = $4, mail = $5, website = $6, password = $7 WHERE id = $8";
+			pg_prepare($con, "prepare1", $query) 
+				or die ("Cannot prepare statement\n");
+			pg_execute($con, "prepare1", array($this->name, $this->avatar, $this->locale, $this->role, $this->mail, $this->website, $this->password, $this->id))
+				or die ("Cannot execute statement\n");
+		}
+
+		pg_close($con);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tUPDATE \tEdit user ".$this->name." (".$this->id.")\r\n",
+			3,
+			$config['logs_folder'].'users.log');
+	}
+	/*****
+	** Generates a random passwords, update the base and send the new password by mail.
+	*****/
+	public function sendPassword() {
+		global $config;
+
+		$newPass = randomPassword();
+		$this->password = sha1($newPass);
+
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "UPDATE users SET password = $1 WHERE mail = $2";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		pg_execute($con, "prepare1", array($this->password, $this->mail))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		$this->availableMail();
+		$this->populate();
+
+		$url = "http://".$_SERVER['SERVER_NAME'].$config['rel_root_folder'];
+
+		$message = "Bonjour ".$this->name.",<br>\r\n";
+		$message .= "<br>\r\n";
+		$message .= "Voici votre nouveau mot de passe <a href='".$url."'>Kabano</a> : <b>".$newPass."</b><br>\r\n";
+		$message .= "<br>\r\n";
+		$message .= "Cordialement,<br>\r\n";
+		$message .= "<br>\r\n";
+		$message .= "L'équipe Kabano.<br>\r\n";
+		$message .= "<small style='color:#777;'><i>Fait avec ♥ depuis Toulouse.</i></small><br>\r\n";
+
+		$headers = 'From: '. $config['bot_mail'] . "\r\n" .
+		'Reply-To: '. $config['bot_mail'] . "\r\n" .
+		'X-Mailer: PHP/' . phpversion() . "\r\n" .
+		'MIME-Version: 1.0' . "\r\n" .
+		'Content-type: text/html; charset=UTF-8' . "\r\n"; 
+
+		mail($this->mail, 'Kabano - Nouveau mot de passe', $message, $headers);
+	}
+	/*****
+	** Update the last login date
+	*****/
+	public function updateLoginDate() {
+		global $config;
+
+		$this->lastlogin = date('r');
+
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "UPDATE users SET lastlogin = $1 WHERE id = $2";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		pg_execute($con, "prepare1", array($this->lastlogin, $this->id))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+	}
+	/*****
+	** Outputs the role of the user
+	*****/
+	public function role() {
+		global $config;
+		return '<span class="userrole" style="color: '.$config['roles'][$this->role][2].';">'.$config['roles'][$this->role][1].'</span>';
+	}
+	/*****
+	** Sends an email to the user from an other user
+	*****/
+	public function sendMail($content, $from) {
+		global $config;
+		global $user;
+
+		$this->populate();
+		$url = "http://".$_SERVER['SERVER_NAME'].$config['rel_root_folder'];
+
+		$message = "Bonjour ".$this->name.",<br>\r\n";
+		$message .= "<br>\r\n";
+		$message .= "Vous venez de recevoir un message de <b>".$from->name."</b> envoyé depuis <a href='".$url."'>Kabano</a>.<br>\r\n";
+		$message .= "<br>\r\n";
+		$message .= "<pre style='padding: 10px; background: #ccc;'>".strip_tags($content)."</pre><br>\r\n";
+		$message .= "<br>\r\n";
+		$message .= "Vous pouvez simplement répondre à cet email.<br>\r\n";
+		$message .= "<br>\r\n";
+		$message .= "L'équipe Kabano.<br>\r\n";
+		$message .= "<small style='color:#777;'><i>Fait avec ♥ depuis Toulouse.</i></small><br>\r\n";
+
+		$headers = 'From: '. $from->mail . "\r\n" .
+		'Reply-To: '. $from->mail . "\r\n" .
+		'X-Mailer: PHP/' . phpversion() . "\r\n" .
+		'MIME-Version: 1.0' . "\r\n" .
+		'Content-type: text/html; charset=UTF-8' . "\r\n"; 
+
+		mail($this->mail, 'Kabano - Nouveau message privé', $message, $headers);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tMAIL \tMail sent to ".$this->name." (".$this->id.")\r\n",
+			3,
+			$config['logs_folder'].'users.log');
+	}
+}
+
+function randomPassword() {
+    $alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
+    $pass = array(); //remember to declare $pass as an array
+    $alphaLength = strlen($alphabet) - 1; //put the length -1 in cache
+    for ($i = 0; $i < 8; $i++) {
+        $n = rand(0, $alphaLength);
+        $pass[] = $alphabet[$n];
+    }
+    return implode($pass); //turn the array into a string
+}
+
+/**********************************************************
+***********************************************************
+**  
+**  This class is to manage Users list object
+**  
+***********************************************************
+**********************************************************/
+
+class Users
+{
+	public $ids = array();
+	public $number = NULL;
+
+	/*****
+	** Get the users number and return the value
+	*****/
+	public function number() {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM users";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array())
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		$this->number = pg_num_rows($result);
+	}
+
+	/*****
+	** Get a list of users if according to the arguments
+	*****/
+	public function list_users($first, $count, $orderby = "id", $order = "ASC") {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$orders=array("id","name","lastlogin","registered","website","role");
+		$key=array_search($orderby,$orders);
+		$orderbysafe=$orders[$key];
+
+		if ($order == 'ASC')
+			$query = "SELECT id FROM users ORDER BY $orderbysafe ASC LIMIT $1 OFFSET $2";
+		else
+			$query = "SELECT id FROM users ORDER BY $orderbysafe DESC LIMIT $1 OFFSET $2";
+		
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($count, $first))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		for($i = 0; $i < pg_num_rows($result); $i++) {
+			$row = pg_fetch_assoc($result, $i);
+			$this->ids[$i] = $row['id'];
+		}
+	}
+}
+
+?>

+ 215 - 0
models/d.wiki.php

@@ -0,0 +1,215 @@
+<?
+
+/**********************************************************
+***********************************************************
+**  
+**  This class is to manage a wiki page object
+**  
+***********************************************************
+**********************************************************/
+
+require_once($config['third_folder']."Md/MarkdownExtra.inc.php");
+
+class WikiPage
+{
+	public $id = 0;
+	public $title = NULL;
+	public $url = NULL;
+	public $locale = NULL;
+	public $lastedit = NULL;
+	public $archive = NULL;
+	public $content = NULL;
+
+	/*****
+	** Checks if a page at this URL exists and return the ID
+	*****/
+	public function checkUrl($url, $withArchive=0, $elementNb=0) {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM wiki WHERE url=$1";
+		if($withArchive==0) {
+			$query .= " AND archive=FALSE";
+		}
+		$query .= " ORDER BY lastedit DESC LIMIT 1 OFFSET $2";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($url, $elementNb))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		if(pg_num_rows($result) == 1) {
+			$wiki = pg_fetch_assoc($result);
+			$this->id = $wiki['id'];
+			$this->url = $url;
+			return 1;
+		}
+		else {
+			$this->url = $url;
+			return 0;
+		}
+	}
+
+	/*****
+	** Populate the object using its ID
+	*****/
+	public function populate() {
+		global $config;
+		
+		if($this->id != 0) {
+			$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+				or die ("Could not connect to server\n");
+
+			$query = "SELECT * FROM wiki WHERE id=$1";
+
+			pg_prepare($con, "prepare1", $query) 
+				or die ("Cannot prepare statement\n");
+			$result = pg_execute($con, "prepare1", array($this->id))
+				or die ("Cannot execute statement\n");
+
+			pg_close($con);
+
+			$wiki = pg_fetch_assoc($result);
+
+			$this->title = $wiki['title'];
+			$this->url = $wiki['url'];
+			$this->locale = $wiki['locale'];
+			$this->lastedit = $wiki['lastedit'];
+			$this->archive = $wiki['archive'];
+			$this->content = $wiki['content'];
+		}
+		else {
+			die("Cannot populate a wiki page without ID");
+		}
+	}
+
+	/*****
+	** Edit a page by archiving the current one and inserting a new one ID
+	*****/
+	public function update() {
+		global $config;
+		global $user;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "UPDATE wiki SET archive = TRUE WHERE url = $1";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($this->url))
+			or die ("Cannot execute statement\n");
+
+
+		$query = "INSERT INTO wiki (url, title, content, lastedit, archive, locale) VALUES
+			($1, $2, $3, $4, FALSE, $5)";
+
+		pg_prepare($con, "prepare2", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare2", array($this->url, $this->title, $this->content, date('r'), $this->locale))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tUPDATE \tEdit wiki page '".$this->url."'\r\n",
+			3,
+			$config['logs_folder'].'wiki.log');
+	}
+
+	/*****
+	** Delete a page by archiving it
+	*****/
+	public function delete() {
+		global $config;
+		global $user;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "UPDATE wiki SET archive = TRUE WHERE url = $1";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($this->url))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tDELETE \tArchive wiki page '".$this->url."'\r\n",
+			3,
+			$config['logs_folder'].'wiki.log');
+	}
+
+	/*****
+	** Create a page by archiving the current one and inserting a new one ID
+	*****/
+	public function insert() {
+		global $config;
+		global $user;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "INSERT INTO wiki (url, title, content, lastedit, archive, locale) VALUES
+			($1, $2, $3, $4, FALSE, $5)";
+
+		pg_prepare($con, "prepare2", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare2", array($this->url, $this->title, $this->content, date('r'), $this->locale))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		error_log(
+			date('r')." \t".$user->name." (".$user->id.") \tINSERT \tCreate new wiki page '".$this->url."'\r\n",
+			3,
+			$config['logs_folder'].'wiki.log');
+	}
+
+	/*****
+	** Converts the Markdown content to HTML
+	*****/
+	public function md2html() {
+		$this->content_html = \Michelf\MarkdownExtra::defaultTransform($this->content);
+	}
+}
+
+class WikiPages
+{
+	public $ids = array();
+	public $number = NULL;
+
+	/*****
+	** Checks if a page at this URL exists and return the ID
+	*****/
+	public function getHistory($url) {
+		global $config;
+		
+		$con = pg_connect("host=".$config['SQL_host']." dbname=".$config['SQL_db']." user=".$config['SQL_user']." password=".$config['SQL_pass'])
+			or die ("Could not connect to server\n");
+
+		$query = "SELECT id FROM wiki WHERE url=$1 ORDER BY lastedit DESC";
+
+		pg_prepare($con, "prepare1", $query) 
+			or die ("Cannot prepare statement\n");
+		$result = pg_execute($con, "prepare1", array($url))
+			or die ("Cannot execute statement\n");
+
+		pg_close($con);
+
+		$this->number = pg_num_rows($result);
+
+		for($i = 0; $i < $this->number; $i++) {
+			$row = pg_fetch_assoc($result, $i);
+			$this->ids[$i] = $row['id'];
+		}
+	}
+}
+
+?>

File diff suppressed because it is too large
+ 48 - 0
src/Mercator-Mountain1.svg


+ 119 - 0
src/exportpgsql.backup

@@ -0,0 +1,119 @@
+COMMENT ON DATABASE kabano IS 'Kabano database';
+
+
+
+-- SEQUENCES
+
+CREATE SEQUENCE blog_articles_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+ALTER TABLE blog_articles_id_seq OWNER TO kabano;
+
+
+CREATE SEQUENCE blog_comments_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+ALTER TABLE blog_comments_id_seq OWNER TO kabano;
+
+
+CREATE SEQUENCE users_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+ALTER TABLE users_id_seq OWNER TO kabano;
+
+
+CREATE SEQUENCE wiki_id_seq
+    START WITH 5
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+ALTER TABLE wiki_id_seq OWNER TO kabano;
+
+
+
+-- TABLES
+
+CREATE TABLE blog_articles (
+    id integer DEFAULT nextval('blog_articles_id_seq'::regclass) NOT NULL,
+    url text,
+    title text,
+    content text,
+    lastedit timestamp without time zone,
+    archive boolean DEFAULT false NOT NULL,
+    locale text,
+    comments boolean DEFAULT true NOT NULL,
+    author integer
+);
+ALTER TABLE blog_articles OWNER TO kabano;
+COMMENT ON TABLE blog_articles IS 'This table contains all archived and visible blog articles';
+ALTER TABLE ONLY blog_articles
+    ADD CONSTRAINT blog_articles_pkey PRIMARY KEY (id);
+
+
+CREATE TABLE blog_comments (
+    id integer DEFAULT nextval('blog_comments_id_seq'::regclass) NOT NULL,
+    article integer,
+    lastedit timestamp without time zone,
+    author integer,
+    locale text,
+    content text,
+    archive boolean DEFAULT false NOT NULL
+);
+ALTER TABLE blog_comments OWNER TO kabano;
+COMMENT ON TABLE blog_comments IS 'This table contains all blog comments';
+ALTER TABLE ONLY blog_comments
+    ADD CONSTRAINT blog_comments_pkey PRIMARY KEY (id);
+
+
+CREATE TABLE users (
+    id integer DEFAULT nextval('users_id_seq'::regclass) NOT NULL,
+    name text,
+    password text,
+    locale text,
+    lastlogin timestamp without time zone,
+    mail text,
+    website text,
+    role integer DEFAULT 400 NOT NULL,
+    avatar boolean DEFAULT false NOT NULL
+);
+ALTER TABLE users OWNER TO kabano;
+COMMENT ON TABLE users IS 'This user database contains all users informations';
+ALTER TABLE ONLY users
+    ADD CONSTRAINT users_pkey PRIMARY KEY (id);
+
+
+CREATE TABLE wiki (
+    id integer DEFAULT nextval('wiki_id_seq'::regclass) NOT NULL,
+    url text,
+    title text,
+    content text,
+    lastedit timestamp without time zone,
+    archive boolean DEFAULT false NOT NULL,
+    locale text
+);
+ALTER TABLE wiki OWNER TO kabano;
+COMMENT ON TABLE wiki IS 'This wiki database contains all archived and displayed wiki pages';
+ALTER TABLE ONLY wiki
+    ADD CONSTRAINT wiki_pkey PRIMARY KEY (id);
+
+
+
+-- Foreign keys
+
+ALTER TABLE ONLY blog_articles
+    ADD CONSTRAINT blog_articles_author_fkey FOREIGN KEY (author) REFERENCES users(id);
+ALTER TABLE ONLY blog_comments
+    ADD CONSTRAINT blog_comments_article_fkey FOREIGN KEY (article) REFERENCES blog_articles(id);
+ALTER TABLE ONLY blog_comments
+    ADD CONSTRAINT blog_comments_author_fkey FOREIGN KEY (author) REFERENCES users(id);
+

File diff suppressed because it is too large
+ 6 - 0
src/logo.min.svg


+ 217 - 0
src/logo.svg

@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="180"
+   height="50"
+   viewBox="0 0 180 50.000001"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="logo.svg"
+   inkscape:export-filename="/home/leo/public_html/Kabano/views/img/header3.png"
+   inkscape:export-xdpi="84.599998"
+   inkscape:export-ydpi="84.599998">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient4272"
+       osb:paint="solid">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop4274" />
+    </linearGradient>
+    <filter
+       style="color-interpolation-filters:sRGB"
+       inkscape:label="Invert"
+       id="filter4242">
+      <feColorMatrix
+         type="hueRotate"
+         values="180"
+         result="color1"
+         id="feColorMatrix4244" />
+      <feColorMatrix
+         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 -0.21 -0.72 -0.07 2 0 "
+         result="fbSourceGraphic"
+         id="feColorMatrix4246" />
+      <feColorMatrix
+         result="fbSourceGraphicAlpha"
+         in="fbSourceGraphic"
+         values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
+         id="feColorMatrix4248" />
+      <feColorMatrix
+         id="feColorMatrix4250"
+         type="hueRotate"
+         values="180"
+         result="color1"
+         in="fbSourceGraphic" />
+      <feColorMatrix
+         id="feColorMatrix4252"
+         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 -0.21 -0.72 -0.07 2 0 "
+         result="fbSourceGraphic" />
+      <feColorMatrix
+         result="fbSourceGraphicAlpha"
+         in="fbSourceGraphic"
+         values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
+         id="feColorMatrix4254" />
+      <feColorMatrix
+         id="feColorMatrix4256"
+         type="hueRotate"
+         values="180"
+         result="color1"
+         in="fbSourceGraphic" />
+      <feColorMatrix
+         id="feColorMatrix4258"
+         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 -0.21 -0.72 -0.07 2 0 "
+         result="color2" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4272"
+       id="linearGradient4276"
+       x1="463.53574"
+       y1="598.1972"
+       x2="579.35376"
+       y2="598.1972"
+       gradientUnits="userSpaceOnUse" />
+    <filter
+       style="color-interpolation-filters:sRGB"
+       inkscape:label="Invert"
+       id="filter4278">
+      <feColorMatrix
+         type="hueRotate"
+         values="180"
+         result="color1"
+         id="feColorMatrix4280" />
+      <feColorMatrix
+         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 -0.21 -0.72 -0.07 2 0 "
+         result="color2"
+         id="feColorMatrix4282" />
+    </filter>
+    <filter
+       style="color-interpolation-filters:sRGB"
+       inkscape:label="Invert"
+       id="filter4284">
+      <feColorMatrix
+         type="hueRotate"
+         values="180"
+         result="color1"
+         id="feColorMatrix4286" />
+      <feColorMatrix
+         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 -0.21 -0.72 -0.07 2 0 "
+         result="color2"
+         id="feColorMatrix4288" />
+    </filter>
+    <filter
+       style="color-interpolation-filters:sRGB"
+       inkscape:label="Invert"
+       id="filter4290">
+      <feColorMatrix
+         type="hueRotate"
+         values="180"
+         result="color1"
+         id="feColorMatrix4292" />
+      <feColorMatrix
+         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 -0.21 -0.72 -0.07 2 0 "
+         result="color2"
+         id="feColorMatrix4294" />
+    </filter>
+    <filter
+       style="color-interpolation-filters:sRGB"
+       inkscape:label="Invert"
+       id="filter4296">
+      <feColorMatrix
+         type="hueRotate"
+         values="180"
+         result="color1"
+         id="feColorMatrix4298" />
+      <feColorMatrix
+         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 -0.21 -0.72 -0.07 2 0 "
+         result="color2"
+         id="feColorMatrix4300" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#8d9894"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8"
+     inkscape:cx="67.921239"
+     inkscape:cy="36.484649"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1366"
+     inkscape:window-height="745"
+     inkscape:window-x="0"
+     inkscape:window-y="23"
+     inkscape:window-maximized="1"
+     units="px" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1002.3622)">
+    <g
+       id="g4356"
+       transform="matrix(1.1515318,0,0,1.1657795,-6.3342807,-170.57805)">
+      <path
+         sodipodi:nodetypes="cccccc"
+         inkscape:connector-curvature="0"
+         id="path4260"
+         d="m 6.4114189,1048.2461 21.0000001,-34.6129 7,11.2258 12.00019,-17.7743 24.114,41.161 z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.72617102;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4278)" />
+      <path
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0"
+         id="path4264"
+         d="m 34.411419,1024.859 -14,23.3871"
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.72617102;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4284)" />
+      <path
+         sodipodi:nodetypes="cccc"
+         inkscape:connector-curvature="0"
+         id="path4335"
+         d="m 46.411609,1007.0847 0,41.161 24.114,0 z"
+         style="opacity:0.65;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter4296)"
+       x="54.34478"
+       y="1041.3604"
+       id="text4268"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4270"
+         x="54.34478"
+         y="1041.3604"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:35px;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans';stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"><tspan
+   style="font-weight:bold"
+   id="tspan4333">K</tspan>abano</tspan></text>
+  </g>
+</svg>

File diff suppressed because it is too large
+ 102 - 0
src/logo2.svg


+ 104 - 0
src/logo3.svg

@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="154"
+   height="44"
+   viewBox="0 0 154 44"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="logo3.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8"
+     inkscape:cx="56.867628"
+     inkscape:cy="65.836934"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     inkscape:window-width="1366"
+     inkscape:window-height="745"
+     inkscape:window-x="1280"
+     inkscape:window-y="23"
+     inkscape:window-maximized="1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(2.2888184e-7,-1008.3622)">
+    <g
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text4136"
+       transform="matrix(1.1379801,0,0,1.1379801,3.1581138e-8,-151.20504)">
+      <path
+         d="m 1.3999998,1024.6822 -1.40000002888184,0 0,27.28 1.40000002888184,0 0,-27.28 z m 13.9200002,0 -1.68,0 -12.1200002,12.6 12.7200002,14.68 1.72,0 -12.6800002,-14.68 12.0400002,-12.6 z"
+         style="font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Ultra-Light';fill:#ffffff;fill-opacity:1"
+         id="path4186"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 53.246875,1037.4822 c 3,-0.52 4.92,-2.44 4.92,-6.04 0,-4.56 -3.44,-6.76 -9.32,-6.76 l -5.8,0 0,27.28 7.36,0 c 5.76,0 9.12,-2.64 9.12,-7.44 0,-4.76 -2.88,-6.84 -6.28,-7.04 z m -4.24,-11.52 c 4.96,0 7.72,1.68 7.72,5.52 0,3.4 -2.32,5.48 -5.8,5.48 l -6.48,0 0,-11 4.56,0 z m 1.44,24.72 -6,0 0,-12.52 6.68,0 c 4.04,0 6.96,2 6.96,6.36 0,4.16 -2.88,6.16 -7.64,6.16 z"
+         style="font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Ultra-Light';fill:#ffffff;fill-opacity:1"
+         id="path4190"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 105.32312,1024.6822 -1.36,0 0,17.8 c 0,3.64 0.08,6.36 0.16,7.92 l -14.159995,-25.72 -1.76,0 0,27.28 1.36,0 0,-17.2 c 0,-4.8 -0.08,-6.92 -0.2,-8.52 l 14.199995,25.72 1.76,0 0,-27.28 z"
+         style="font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Ultra-Light';fill:#ffffff;fill-opacity:1"
+         id="path4194"
+         inkscape:connector-curvature="0" />
+      <path
+         d="m 123.80562,1024.2422 c -6.4,0 -10.84,5.12 -10.84,14.16 0,9 4.4,13.96 10.84,13.96 6.68,0 10.84,-5.12 10.84,-14 0,-9.24 -4.36,-14.12 -10.84,-14.12 z m 0,1.32 c 5.64,0 9.4,4.2 9.4,12.8 0,8.4 -3.56,12.72 -9.4,12.72 -5.52,0 -9.4,-4.32 -9.4,-12.68 0,-8.56 3.84,-12.84 9.4,-12.84 z"
+         style="font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Ultra-Light';fill:#ffffff;fill-opacity:1"
+         id="path4196"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text4136-1"
+       transform="matrix(1.1379801,0,0,1.1379801,-327.34608,665.85293)">
+      <path
+         d="m 327.11423,334.16668 -10.09786,-27.23536 -10.34214,27.23536 z m -18.81955,-1.19739 5.88391,-15.62044 4.87414,-1.40926 6.40569,16.95891 z"
+         style="font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Ultra-Light';fill:#ffffff;fill-opacity:1"
+         id="path4159"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ccccccccc" />
+      <path
+         d="m 372.70688,333.82719 -0.008,-1.24916 -1.36672,-0.006 -8.73903,-23.76226 0.83839,-1.55306 -0.73436,-0.62958 -0.82296,1.22506 -0.69448,-1.17371 -0.71601,0.66008 0.84437,1.49829 -9.42882,23.88393 -1.23813,0.005 0.0188,1.14693 z m -17.50259,-1.13043 6.57828,-15.51649 6.1435,15.46802 z"
+         style="font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;font-size:40px;line-height:125%;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Ultra-Light';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         id="path4159-4"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cccccccccccccccccc" />
+    </g>
+  </g>
+</svg>

BIN
src/logo3_black.png


+ 1 - 0
src/wiki.backup

@@ -0,0 +1 @@
+INSERT INTO wiki (id, url, title, content, lastedit, archive, locale) VALUES (1, '404', 'Erreur 404', 'Le page recherchée n''existe pas', '2016-03-30 08:42:11', true, 'fr');

+ 10 - 0
third/Md/Markdown.inc.php

@@ -0,0 +1,10 @@
+<?php
+
+# Use this file if you cannot use class autoloading. It will include all the
+# files needed for the Markdown parser.
+#
+# Take a look at the PSR-0-compatible class autoloading implementation
+# in the Readme.php file if you want a simple autoloader setup.
+
+require_once dirname(__FILE__) . '/MarkdownInterface.php';
+require_once dirname(__FILE__) . '/Markdown.php';

File diff suppressed because it is too large
+ 1616 - 0
third/Md/Markdown.php


+ 11 - 0
third/Md/MarkdownExtra.inc.php

@@ -0,0 +1,11 @@
+<?php
+
+# Use this file if you cannot use class autoloading. It will include all the
+# files needed for the MarkdownExtra parser.
+#
+# Take a look at the PSR-0-compatible class autoloading implementation
+# in the Readme.php file if you want a simple autoloader setup.
+
+require_once dirname(__FILE__) . '/MarkdownInterface.php';
+require_once dirname(__FILE__) . '/Markdown.php';
+require_once dirname(__FILE__) . '/MarkdownExtra.php';

File diff suppressed because it is too large
+ 1625 - 0
third/Md/MarkdownExtra.php


+ 9 - 0
third/Md/MarkdownInterface.inc.php

@@ -0,0 +1,9 @@
+<?php
+
+# Use this file if you cannot use class autoloading. It will include all the
+# files needed for the MarkdownInterface interface.
+#
+# Take a look at the PSR-0-compatible class autoloading implementation
+# in the Readme.php file if you want a simple autoloader setup.
+
+require_once dirname(__FILE__) . '/MarkdownInterface.php';

+ 34 - 0
third/Md/MarkdownInterface.php

@@ -0,0 +1,34 @@
+<?php
+#
+# Markdown  -  A text-to-HTML conversion tool for web writers
+#
+# PHP Markdown
+# Copyright (c) 2004-2015 Michel Fortin
+# <https://michelf.ca/projects/php-markdown/>
+#
+# Original Markdown
+# Copyright (c) 2004-2006 John Gruber
+# <https://daringfireball.net/projects/markdown/>
+#
+namespace Michelf;
+
+
+#
+# Markdown Parser Interface
+#
+
+interface MarkdownInterface {
+
+  #
+  # Initialize the parser and return the result of its transform method.
+  # This will work fine for derived classes too.
+  #
+  public static function defaultTransform($text);
+
+  #
+  # Main function. Performs some preprocessing on the input text
+  # and pass it through the document gamut.
+  #
+  public function transform($text);
+
+}

+ 7 - 0
views/blocks/d.footer.html

@@ -0,0 +1,7 @@
+		<footer>
+			<div id="footernav">
+				<a href="<?=$config['rel_root_folder']?>wiki/legal">Mentions Légales</a> &mdash; 
+				<a href="<?=$config['rel_root_folder']?>contact">Contact</a>
+			</div>
+			<p><i>Applications mobiles bientôt disponibles.</i></p>
+		</footer>

+ 46 - 0
views/blocks/d.head.html

@@ -0,0 +1,46 @@
+	<head>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, height=device-height">
+		<link rel="shortcut icon" href="<?=$config['views_url']?>img/favicon.png">
+<?	if (isset($head['css'])) { 
+		foreach(explode(";",$head['css']) as $css) { ?>
+			<link rel="stylesheet" href="<?=$config['views_url']?>css/<?=$css?>"/>
+<?		}
+	}
+	else { ?>
+		<link rel="stylesheet" href="<?=$config['views_url']?>css/d.index.css"/>
+<?	} ?>
+		<link rel="stylesheet" href="<?=$config['views_url']?>third/font-awesome-4.7.0/css/font-awesome.min.css"/>
+
+		<script type="text/javascript" src="<?=$config['views_url']?>third/jquery-3.1.1.min.js"></script>
+<?	if (isset($head['third'])) { 
+		foreach(explode(";",$head['third']) as $third) { ?>
+			<script type="text/javascript" src="<?=$config['views_url']?>third/<?=$third?>"></script>
+<?		}
+	} ?>
+		<script type="text/javascript" src="<?=$config['views_url']?>js/d.header.js"></script>
+<?	if (isset($head['js'])) { 
+		foreach(explode(";",$head['js']) as $js) { ?>
+			<script type="text/javascript" src="<?=$config['views_url']?>js/<?=$js?>"></script>
+<?		}
+	} ?>
+
+<?	if (isset($head['title'])) { ?>
+		<title><?=$head['title']?> &mdash; Kabano</title>
+<?	}
+	else { ?>
+		<title>Cabanes et bivouac en montagne</title>
+<?	} ?>
+<?	if (isset($head['description'])) { ?>
+		<meta name="description" content="<?=$head['description']?>">
+?	}
+	else { ?>
+		<meta name="description" content="Annuaire collaboratif des hébergements pour les activitées de plein air : cabanes, refuges, campings...">
+<?	} ?>
+<?	if (isset($head['keywords'])) { ?>
+		<meta name="keywords" content="<?=$head['keywords']?>">
+<?	}
+	else { ?>
+		<meta name="keywords" content="kabano, huts, mountain, hiking, cabanes, refuges, bivouac, montagne, randonnée">
+<?	} ?>
+	</head>

+ 52 - 0
views/blocks/d.nav.html

@@ -0,0 +1,52 @@
+		<header>
+			<div id="Hcontent">
+				<a href="<?=$config['rel_root_folder']?>" id="logo">
+					<img alt="Kabano logo" src="<?=$config['views_url'].'img/'?>header.svg">
+				</a>
+				<nav>
+					<ul>
+						<li class="on-bar"><a class="on-bar" href="<?=$config['rel_root_folder']?>map">Carte</a></li>
+						<li class="on-bar"><a class="on-bar" href="<?=$config['rel_root_folder']?>search">Recherche</a></li>
+						<li class="on-bar"><a class="on-bar" href="<?=$config['rel_root_folder']?>news">Nouveautés</a></li>
+						<li class="on-bar"><a class="on-bar" href="<?=$config['rel_root_folder']?>community">Contribuer</a></li>
+						<li class="on-bar has-sub">
+							<? if (isset($user->avatar) AND $user->avatar=='t') { ?>
+							<a class="on-bar" href="#"><img alt="Avatar" class="icon avatar" src="<?=$config['rel_root_folder']?>medias/avatars/<?=$user->id?>_s.jpg"></a>
+							<? } elseif (isset($user->avatar) AND $user->avatar=='f') { ?>
+							<a class="on-bar" href="#"><i class="icon fa fa-user-secret"></i></a>
+							<? } else { ?>
+							<a class="on-bar" href="#"><i class="icon fa fa-user"></i></a>
+							<? } ?>
+							<ul>
+								<? if($user->role == 0) { ?>
+								<li id="connectform">
+									<form action="<?=$config['rel_root_folder']?>user/login" method="post">
+										<input type="text" name="login" placeholder="Nom d'utilisateur">
+										<input type="password" name="password" placeholder="Mot de passe">
+										<input type="submit" name="submit" value="Se connecter">
+									</form>
+								</li>
+								<li><a href="<?=$config['rel_root_folder']?>user/signin">S'inscrire</a></li>
+								<? } else { ?>
+								<li><a href="<?=$config['rel_root_folder']?>user/p">Mon profil</a></li>
+								<li><a href="<?=$config['rel_root_folder']?>user/member_list">Liste des membres</a></li>
+								<? if($user->role >= 800) { ?>
+								<li><a href="<?=$config['rel_root_folder']?>admin">Administration</a></li>
+								<? } ?>
+								<li><a href="<?=$config['rel_root_folder']?>user/logout">Se déconnecter</a></li>
+								<? } ?>
+							</ul>
+						</li>
+						<li class="on-bar has-sub"><a class="on-bar" href="#"><i class="icon fa fa-question"></i></a>
+							<ul>
+								<li><a href="<?=$config['rel_root_folder']?>wiki/help">Aide</a></li>
+								<li><a href="<?=$config['rel_root_folder']?>blog">Blog</a></li>
+								<li><a href="<?=$config['rel_root_folder']?>contact">Contact</a></li>
+								<li><a href="<?=$config['rel_root_folder']?>wiki/api">API Développeurs</a></li>
+								<li><a href="<?=$config['rel_root_folder']?>wiki/about">À propos</a></li>
+							</ul>
+						</li>
+					</ul>
+				</nav>
+			</div>
+		</header>

+ 240 - 0
views/css/d.blog.css

@@ -0,0 +1,240 @@
+/*********************************/
+/** Blog list page             **/ 
+/*********************************/
+
+#blog_list article {
+	margin: 40px auto 0 auto;
+	width: 90%;
+	background: #efefef;
+	border: 1px solid #ccc;
+	border-bottom: 2px solid #ccc;
+	border-bottom-right-radius: 5px;
+	border-bottom-left-radius: 5px;
+}
+#blog_list #articles_list article:first-child {
+	margin-top: 80px;
+}
+
+#blog_list .article_title {
+	background: #ccc;
+	padding: 5px 10px;
+}
+
+#blog_list .article_content {
+	padding: 20px 10px 5px 10px;
+	white-space: pre-line;
+}
+
+#blog_list .article_legend {
+	font-style: italic;
+	padding: 0 10px;
+}
+#blog_list .article_link {
+	font-weight: 500;
+}
+#blog_list .article_infos {
+	float: right;
+}
+
+#blog_list .article_archive {
+	opacity: 0.5;
+}
+
+#blog_list .pagebuttons {
+	text-align: center;
+	margin: 40px 0 30px 0;
+}
+#blog_list .pagebuttons a {
+	background: #efefef;
+    border: 1px solid #ccc;
+    padding: 5px;
+    font-size: 1.5em;
+}
+#blog_list .pagebuttons .previous {
+    margin-right: -1px;
+}
+
+/*********************************/
+/** Blog view article page      **/ 
+/*********************************/
+
+
+#blogTimestamp {
+	font-size: 0.8em;
+	font-weight: 500;
+	float: right;
+	font-style: italic;
+}
+
+#blogContent {
+	margin: 50px 0 20px 0;
+}
+
+#blogContent h2 { margin: .75em 0 }
+#blogContent h3 { margin: .83em 0 }
+#blogContent h4, #blogContent p, #blogContent blockquote, #blogContent ul, #blogContent fieldset, #blogContent form, #blogContent ol, #blogContent dl { margin: 1.12em 0 }
+#blogContent h5 { margin: 1.5em 0 }
+#blogContent h6 { margin: 1.67em 0 }
+#blogContent blockquote { margin-left: 40px; margin-right: 40px }
+#blogContent ol, #blogContent ul, #blogContent dd { margin-left: 40px }
+#blogContent ol ul, #blogContent ul ol, #blogContent ul ul, #blogContent ol ol { margin-top: 0; margin-bottom: 0 }
+
+#blogContent .footnotes {
+	font-size: 0.8em;
+}
+#blogContent .footnotes p {
+	margin: 0.4em 0;
+}
+#blogContent hr {
+	border: 1px inset;
+}
+
+#new_comment {
+	clear: both;
+	margin: 100px auto 0 auto;
+	width: 90%;
+	background: #efefef;
+	border: 1px solid #ccc;
+	border-bottom: 3px solid #ccc;
+}
+#new_comment_label {
+	background: #ccc;
+	padding: 15px 10px;
+	cursor: pointer;
+	font-weight: 500;
+	font-size: 1.1em;
+}
+#new_comment_label p {
+	margin: 0;
+}
+#new_comment_label input {
+	display: none;
+	margin: -2px;
+	float: right;