Restructure MVC into public/src layout

Co-authored-by: LeOSW42 <673670+LeOSW42@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-24 14:00:58 +00:00
parent 1be6b7ff0c
commit f617ecb350
135 changed files with 75 additions and 63 deletions

55
src/Core/config.example.php Executable file
View File

@@ -0,0 +1,55 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
locale_set_default("fr_FR");
date_default_timezone_set("UTC"); // Default tz for date manipulation is UTC. Display tz is in session.php
/*****
** Management of folder names
*****/
$config['core_folder'] = rtrim(__DIR__, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
$config['src_folder'] = rtrim(dirname($config['core_folder']), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
$config['abs_root_folder'] = rtrim(dirname($config['src_folder']), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
$config['public_folder'] = $config['abs_root_folder']."public/";
// 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(rtrim($_SERVER['DOCUMENT_ROOT'], DIRECTORY_SEPARATOR), "", rtrim($config['public_folder'], DIRECTORY_SEPARATOR));
$config['web_root_folder']="https://kabano.test/";
if($config['rel_root_folder']=="") {
$config['rel_root_folder']="/";
} else {
$config['rel_root_folder'] = "/".trim($config['rel_root_folder'],"/")."/";
}
// Here all the absolute paths to specific folders
$config['views_folder'] = $config['public_folder']."views/";
$config['controllers_folder'] = $config['src_folder']."controllers/";
$config['models_folder'] = $config['src_folder']."models/";
$config['medias_folder'] = $config['public_folder']."medias/";
$config['includes_folder'] = $config['core_folder'];
$config['third_folder'] = $config['src_folder']."Thirds/";
$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";

21
src/Core/database.php Normal file
View File

@@ -0,0 +1,21 @@
<?php
namespace Kabano;
function sql_connect() {
global $config;
$connection = "host=".$config['SQL_host']
." dbname=".$config['SQL_db']
." user=".$config['SQL_user']
." password=".$config['SQL_pass'];
$con = pg_connect($connection);
if (!$con) {
$error = error_get_last();
$message = $error && isset($error['message']) ? $error['message'] : "unknown error";
die("Could not connect to server: ".$message."\n");
}
return $con;
}

40
src/Core/images.php Executable file
View File

@@ -0,0 +1,40 @@
<?php
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 = (int)($source_image_height * $width / $height);
} else {
$src_y = (int)(($source_image_height - $source_image_width * $height / $width) / 2);
$source_image_height = (int)($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;
}

View File

@@ -0,0 +1,91 @@
<?php
$poi_types = array(
"basic_hut" => array("Abri sommaire", "Abri", "#ef2929", "basic_hut",
"Un abri sommaire est un bâtiment qui ne permet pas l'hébergement, comme un kiosque.",
array(
't_owner' => "👤 Informations sur le⋅la propriétaire et moyens de contacts",
't_access' => "🧭 Description de l'accès, des transports en commun, et d'éventuels passages délicats",
't_description' => "📝 Description sur l'abri et remarques",
'b_usable' => "🚫 Abri condamné, détruit ou fermé ?",
'b_water' => "💧 Eau à proximité ?",
'b_wood' => "🌲 Bois à proximité ?"
)
),
"wilderness_hut" => array("Cabane non gardée", "Cabane", "#ef2929", "wilderness_hut",
"Une cabane non gardée est un bâtiment qui permet l'hébergement, même sommaire, sans gardien.",
array(
't_owner' => "👤 Informations sur le⋅la propriétaire et moyens de contacts",
't_access' => "🧭 Description de l'accès, des transports en commun, et d'éventuels passages délicats",
't_description' => "📝 Description sur la cabane et remarques",
'b_key' => "🔑 Nécessite une clé ?",
'b_usable' => "🚫 Cabane condamnée, détruite ou fermée ?",
'n_bed' => "🛏️ Nombre de places prévues pour dormir :",
'n_mattress' => "🛌 Nombre de matelas disponibles :",
'b_cover' => "🧣 Couvertures ?",
'b_water' => "💧 Eau à proximité ?",
'b_wood' => "🌲 Bois à proximité ?",
'b_fireplace' => "🔥 Cheminée ou poêle à bois ?",
'b_toilet' => "🚽 Latrines ou toilettes ?"
)
),
"alpine_hut" => array("Refuge gardé", "Refuge", "#ef2929", "alpine_hut",
"Un refuge gardé est un bâtiment qui permet l'hébergement toute l'année, gardé tout ou partie de l'année.",
array(
't_owner' => "👤 Informations sur le⋅la propriétaire, le⋅la gardien⋅ne et moyens de contacts",
't_access' => "🧭 Description de l'accès, des transports en commun, et d'éventuels passages délicats",
't_description' => "📝 Description sur le refuge et remarques",
'b_usable' => "🚫 Refuge condamné, détruit ou fermé ?",
'n_bed' => "☀️ Places en période gardée :",
'n_bed_winter' => "❄️ Places en période non gardée :",
'n_mattress' => "🛌 Matelas en période non gardée :",
'b_cover' => "🧣 Couvertures disponibles ?",
'b_water' => "💧 Possibilité de se ravitailler en eau ?",
'b_wood' => "🌲 Bois à proximité ?",
'b_fireplace' => "🔥 Cheminée ou poêle à bois ?",
'b_toilet' => "🚽 Latrines ou toilettes ?",
'l_water' => "URL du site web :"
)
),
"halt" => array("Gîte d'étape", "Gîte", "#4e9a06", "halt",
"Un gîte d'étape est un bâtiment qui permet l'hébergement uniquement sur ses périodes d'ouvertures.",
array(
't_owner' => "👤 Informations sur le⋅la propriétaire, le⋅la gardien⋅ne et moyens de contacts",
't_access' => "🧭 Description de l'accès, des transports en commun, et d'éventuels passages délicats",
't_description' => "📝 Description sur le gîte et remarques",
'b_usable' => "🚫 Gîte condamné, détruit ou fermé ?",
'n_bed' => "🛏️ Nombre de places prévues pour dormir :",
'b_water' => "💧 Possibilité de se ravitailler en eau ?",
'l_water' => "URL du site web :"
)
),
"bivouac" => array("Zone de bivouac", "Bivouac", "#ef2929", "bivouac",
"Une zone de bivouac est un espace aménagé permettant de planter la tente.",
array(
't_access' => "🧭 Description de l'accès, des transports en commun, et d'éventuels passages délicats",
't_description' => "📝 Description sur la zone de bivouac et remarques",
'n_bed' => "⛺ Nombre d'emplacements :",
'b_water' => "💧 Eau à proximité ?",
'b_wood' => "🌲 Bois à proximité ?",
'b_fireplace' => "🔥 Emplacement pour faire un feu ?"
)
),
"campsite" => array("Camping", "Camping", "#4e9a06", "campsite",
"Un camping est un espace aménagé permettant de planter la tente plusieurs jours, avec gardien.",
array(
't_owner' => "👤 Informations sur le⋅la propriétaire, le⋅la gardien⋅ne et moyens de contacts",
't_access' => "🧭 Description de l'accès, des transports en commun, et d'éventuels passages délicats",
't_description' => "📝 Description du camping et remarques",
'n_bed' => "⛺ Nombre d'emplacements :",
'b_water' => "💧 Possibilité de se ravitailler en eau ?",
'l_water' => "URL du site web :"
)
)
);

72
src/Core/routes.php Executable file
View File

@@ -0,0 +1,72 @@
<?php
/*****
** 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;
if($controller->splitted_url[0]=="") $controller->splitted_url[0]="index";
// Routing to the correct page from the correct link
switch ($controller->splitted_url[0])
{
case "index":
case "community" :
$controller->name="";
$view->name=$controller->splitted_url[0];
break;
case "user" :
$controller->name="users";
$view->name="";
break;
case "contact" :
case "wiki" :
case "blog" :
case "map" :
case "poi" :
case "admin" :
$controller->name=$controller->splitted_url[0];
$view->name="";
break;
default :
$controller->name="";
$view->name="";
$notfound = 1;
break;
}
if($session==1) {
require_once($config['includes_folder']."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($config['includes_folder']."session.php");
require_once($config['models_folder']."d.wiki.php");
$wikiPage = new Kabano\WikiPage();
$wikiPage->checkPermalink('404');
$wikiPage->md2html();
$head['css'] = "d.index.css;d.wiki.css";
$head['title'] = $wikiPage->name;
include ($config['views_folder']."d.wiki.view.html");
}

35
src/Core/session.php Executable file
View File

@@ -0,0 +1,35 @@
<?php
require_once($config['models_folder']."d.users.php");
ini_set("session.cookie_lifetime",60*60*24*30);
session_start();
$user = new Kabano\User();
if(isset($_SESSION['userid'])) {
if ($user->checkID($_SESSION['userid'])) {
$user->updateLoginDate();
$config['locale'] = $user->locale;
$config['timezone'] = $user->timezone;
}
else {
session_destroy();
$config['locale'] = "fr_FR";
$config['timezone'] = "Europe/Paris";
$user->rank = "visitor"; // All users are visitors
}
}
else {
$config['locale'] = "fr_FR";
$config['timezone'] = "Europe/Paris";
$user->rank = "visitor"; // All users are visitors
}
if (PHP_VERSION_ID < 80000) {
$user->date_format = new IntlDateFormatter($config['locale'], IntlDateFormatter::LONG, IntlDateFormatter::NONE, $config['timezone']);
} else {
$user->date_format = new IntlDateFormatter($config['locale'], IntlDateFormatter::RELATIVE_LONG, IntlDateFormatter::NONE, $config['timezone']);
}
$user->datetime_format = new IntlDateFormatter($config['locale'], IntlDateFormatter::LONG, IntlDateFormatter::SHORT, $config['timezone']);
$user->datetimeshort_format = new IntlDateFormatter($config['locale'], IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, $config['timezone']);

10
src/Thirds/Md/Markdown.inc.php Executable file
View File

@@ -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';

1909
src/Thirds/Md/Markdown.php Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -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';

1893
src/Thirds/Md/MarkdownExtra.php Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -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';

View File

@@ -0,0 +1,38 @@
<?php
/**
* Markdown - A text-to-HTML conversion tool for web writers
*
* @package php-markdown
* @author Michel Fortin <michel.fortin@michelf.com>
* @copyright 2004-2019 Michel Fortin <https://michelf.com/projects/php-markdown/>
* @copyright (Original Markdown) 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.
*
* @api
*
* @param string $text
* @return string
*/
public static function defaultTransform($text);
/**
* Main function. Performs some preprocessing on the input text
* and pass it through the document gamut.
*
* @api
*
* @param string $text
* @return string
*/
public function transform($text);
}

324
src/controllers/d.admin.php Executable file
View File

@@ -0,0 +1,324 @@
<?php
if(isset($controller->splitted_url[1]) && $user->rankIsHigher("moderator")) {
switch ($controller->splitted_url[1]) {
case '': case 'admin':
$head['title'] = "Administration";
include ($config['views_folder']."d.admin.html");
break;
case 'git-pull':
if ($user->rankIsHigher("administrator")) {
$head['title'] = "Mise à jour";
$output = array();
chdir($config['abs_root_folder']);
exec("git pull 2>&1", $output);
include ($config['views_folder']."d.admin.git-pull.html");
}
else {
$notfound = 1;
}
break;
case 'logs':
if ($user->rankIsHigher("moderator")) {
$head['title'] = "Logs";
$output = array();
$files_list = scandir($config['logs_folder']);
$logs_folder = realpath($config['logs_folder']);
$logs_folder_root = $logs_folder !== false ? rtrim($logs_folder, DIRECTORY_SEPARATOR) : null;
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;
}
$log_file = $files_list[$filenb+2] ?? null;
if ($logs_folder_root && $log_file) {
$log_file = basename($log_file);
$log_path = $logs_folder_root . DIRECTORY_SEPARATOR . $log_file;
$real_log_path = realpath($log_path);
if ($real_log_path && str_starts_with($real_log_path, $logs_folder_root . DIRECTORY_SEPARATOR)) {
exec("tail -n 200 ".escapeshellarg($real_log_path)." | tac", $output);
}
}
include ($config['views_folder']."d.admin.logs.html");
}
else {
$notfound = 1;
}
break;
case 'wiki-files':
if ($user->rankIsHigher("moderator")) {
$head['css'] = "d.index.css;d.admin.css";
$head['title'] = "Fichiers attachés au wiki";
$rows_per_pages = 50;
$files_folder = $config['medias_folder']."wiki/";
$files_folder_real = realpath($files_folder);
$files_folder_root = $files_folder_real !== false ? rtrim($files_folder_real, DIRECTORY_SEPARATOR) : rtrim($files_folder, DIRECTORY_SEPARATOR);
// Delete a file
if ($user->rankIsHigher("administrator")) {
if(isset($controller->splitted_url[2]) && $controller->splitted_url[2]=='delete' && isset($controller->splitted_url[3])) {
$safe_name = basename($controller->splitted_url[3]);
$filename = $files_folder_root . DIRECTORY_SEPARATOR . $safe_name;
$real_filename = realpath($filename);
if ($real_filename && str_starts_with($real_filename, $files_folder_root . DIRECTORY_SEPARATOR)) {
if (file_exists($real_filename)) {
unlink($real_filename);
}
error_log(date('r')." \t".$user->name." (".$user->id.") \tDELETE \tDelete wiki file '".$safe_name."'\r\n",3,$config['logs_folder'].'wiki-files.log');
}
}
}
// Add a file
if(isset($controller->splitted_url[2]) && $controller->splitted_url[2]=='upload' && isset($_FILES['file'])) {
$safe_name = basename($_FILES['file']['name']);
$filename = $files_folder_root . DIRECTORY_SEPARATOR . $safe_name;
if($safe_name !== '' && move_uploaded_file($_FILES['file']['tmp_name'], $filename)) {
error_log(date('r')." \t".$user->name." (".$user->id.") \tUPLOAD Upload wiki file '".$safe_name."'\r\n",3,$config['logs_folder'].'wiki-files.log');
}
}
// Get the file list
$files_list = scandir($files_folder_root);
// Populate table
foreach ($files_list as $file) {
$file_path = $files_folder_root . DIRECTORY_SEPARATOR . $file;
if (is_file($file_path)) {
$file_info = [
'name' => $file,
'type' => mime_content_type($file_path),
'creation_date' => date("Y-m-d H:i:s", filectime($file_path)),
'size' => filesize($file_path),
];
$files[] = $file_info;
}
}
$filenb = count($files);
// Manage sorting
if(isset($_GET['orderby']))
$orderby = $_GET['orderby'];
else
$orderby = 'name';
if(isset($_GET['order']) && $_GET['order']=='ASC') {
$order = 'ASC';
usort($files, function ($a, $b) use ($orderby) { return $a[$orderby] <=> $b[$orderby]; });
}
else {
$order = 'DESC';
usort($files, function ($a, $b) use ($orderby) { return $b[$orderby] <=> $a[$orderby]; });
}
// 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;
}
// In case the wanted page is too big
if($rows_per_pages * $page >= $filenb)
$page = 0;
$first = $page*$rows_per_pages+1;
$last = (($page+1)*$rows_per_pages > $filenb ? $filenb : ($page+1)*$rows_per_pages);
include ($config['views_folder']."d.admin.wiki-files.html");
}
else {
$notfound = 1;
}
break;
case 'stats':
if ($user->rankIsHigher("moderator")) {
$head['title'] = "Statistiques";
$report = $config['abs_root_folder'].'tmp/report.html';
$files = glob('/var/log/nginx/kabano.org-access.log*.gz');
$parts = [];
if (!empty($files)) {
$parts[] = 'zcat ' . implode(' ', array_map('escapeshellarg', $files));
}
if (file_exists('/var/log/nginx/kabano.org-access.log.1')) {
$parts[] = 'cat /var/log/nginx/kabano.org-access.log.1';
}
$parts[] = 'cat /var/log/nginx/kabano.org-access.log';
$command = '/bin/bash -c \'(' . implode(' && ', $parts) . ')'
. ' | goaccess --log-format=COMBINED --no-progress -o '
. escapeshellarg($report)
. ' -\' 2>&1';
$output = shell_exec($command);
include ($config['views_folder']."d.admin.stats.html");
}
else {
$notfound = 1;
}
break;
case 'sql-backup':
if ($user->rankIsHigher("administrator")) {
$head['title'] = "Export SQL";
if(isset($controller->splitted_url[2]) && $controller->splitted_url[2]=='delete' && isset($controller->splitted_url[3])) {
$tmp_folder = realpath($config['abs_root_folder'].'tmp');
if ($tmp_folder !== false) {
$safe_name = basename($controller->splitted_url[3]);
$tmp_folder_root = rtrim($tmp_folder, DIRECTORY_SEPARATOR);
$delete_path = $tmp_folder_root . DIRECTORY_SEPARATOR . $safe_name;
$real_delete_path = realpath($delete_path);
if ($real_delete_path && str_starts_with($real_delete_path, $tmp_folder_root . DIRECTORY_SEPARATOR)) {
if (file_exists($real_delete_path)) {
unlink($real_delete_path);
}
}
}
$output = Array();
$backup_file = Array();
}
else {
// Nom du fichier de sauvegarde
$timestamp = date('Ymd_His');
$backup_filename[0] = $timestamp.'_backup.sql';
$backup_file[0] = $config['abs_root_folder'].'tmp/'.$backup_filename[0];
// Construction de la commande pg_dump
$cmd = 'PGPASSWORD="'.$config['SQL_pass'].'" pg_dump -h '.$config['SQL_host'].' -U '.$config['SQL_user'].' -F c -b -v -f "'.$backup_file[0].'" '.$config['SQL_db'].' 2>&1';
$output = [];
$return_var = 0;
exec($cmd, $output, $return_var);
}
$backup_files = glob($config['abs_root_folder'].'tmp/*.sql');
include ($config['views_folder']."d.admin.backup.html");
}
else {
$notfound = 1;
}
break;
case 'files-backup':
if ($user->rankIsHigher("administrator")) {
$head['title'] = "Export des fichiers";
$output = Array();
$backup_file = Array();
if(isset($controller->splitted_url[2]) && $controller->splitted_url[2]=='delete' && isset($controller->splitted_url[3])) {
$tmp_folder = realpath($config['abs_root_folder'].'tmp');
if ($tmp_folder !== false) {
$safe_name = basename($controller->splitted_url[3]);
$tmp_folder_root = rtrim($tmp_folder, DIRECTORY_SEPARATOR);
$delete_path = $tmp_folder_root . DIRECTORY_SEPARATOR . $safe_name;
$real_delete_path = realpath($delete_path);
if ($real_delete_path && str_starts_with($real_delete_path, $tmp_folder_root . DIRECTORY_SEPARATOR)) {
if (file_exists($real_delete_path)) {
unlink($real_delete_path);
}
}
}
}
else {
// Nom du fichier de sauvegarde
$timestamp = date('Ymd_His');
$backup_source[0] = $config['abs_root_folder'].'medias/avatars';
$backup_source[1] = $config['abs_root_folder'].'medias/wiki';
$backup_filename[0] = $timestamp.'_avatar_files.zip';
$backup_filename[1] = $timestamp.'_wiki_files.zip';
for($i=0;$i<2;$i++) {
$backup_file[$i] = $config['abs_root_folder'].'tmp/'.$backup_filename[$i];
$backup[$i] = new ZipArchive();
if ($backup[$i]->open($backup_file[$i], ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($backup_source[$i]),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file) {
if (!$file->isDir()) {
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen(realpath($backup_source[$i])) + 1);
$backup[$i]->addFile($filePath, $relativePath);
}
}
$backup[$i]->close();
} else {
$output[0] = "Erreur lors de la création de l'archive $backup_filename[$i] avec les avatars de $backup_source[$i]";
}
}
}
$backup_files = glob($config['abs_root_folder'].'tmp/*.zip');
include ($config['views_folder']."d.admin.backup.html");
}
else {
$notfound = 1;
}
break;
default:
$notfound = 1;
break;
}
}
else if($user->rankIsHigher("moderator")) {
$head['title'] = "Administration";
include ($config['views_folder']."d.admin.html");
}
else {
$notfound = 1;
}
// Fonctions de mise en forme
function getFontAwesomeIcon($mimeType) {
$icons = [
'application/pdf' => 'fa-file-pdf',
'image/jpeg' => 'fa-file-image',
'image/png' => 'fa-file-image',
'application/zip' => 'fa-file-archive',
'text/plain' => 'fa-file-alt',
'application/vnd.ms-excel' => 'fa-file-excel',
'application/msword' => 'fa-file-word',
'video/mp4' => 'fa-file-video',
'audio/mpeg' => 'fa-file-audio',
];
return $icons[$mimeType] ?? 'fa-file'; // Default
}
function formatBytes($bytes, $locale = 'en', $precision = 2) {
$unitMap = [
'en' => ['B', 'KB', 'MB', 'GB', 'TB', 'PB'],
'fr' => ['o', 'Ko', 'Mo', 'Go', 'To', 'Po']
];
$locale = explode('_', $locale)[0];
$units = $unitMap[$locale] ?? $unitMap['en'];
if ($bytes == 0) {
return '0 ' . $units[0];
}
$power = floor(log($bytes, 1024));
$formatted = round($bytes / pow(1024, $power), $precision);
return $formatted . ' ' . $units[$power];
}

191
src/controllers/d.blog.php Executable file
View File

@@ -0,0 +1,191 @@
<?php
require_once($config['models_folder']."d.blog.php");
require_once($config['models_folder']."d.comments.php");
require_once($config['models_folder']."d.users.php");
$head['css'] = "d.index.css;d.blog.css";
$blogArticle = new Kabano\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 Kabano\BlogArticles();
$blogArticles->number(($user->rankIsHigher("premium")));
// 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->rankIsHigher("premium")));
$i = 0;
$blogArticles_list = array();
foreach ($blogArticles->objs as $row) {
$row->md2txt();
$tempUser = new Kabano\User();
$tempUser->checkId($row->author);
$row->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->rankIsHigher("moderator")) {
if(isset($_POST['submit'])) {
$blogArticle->content = $_POST['content'];
$blogArticle->locale = $_POST['locale'];
$blogArticle->name = $_POST['name'];
$blogArticle->is_commentable = isset($_POST['is_commentable'])?'t':'f';
$blogArticle->author = $user->id;
if(!$blogArticle->checkPermalink($_POST['permalink'],1)) {
$blogArticle->permalink = $_POST['permalink'];
$blogArticle->insert();
header('Location: '.$config['rel_root_folder']."blog/".$blogArticle->permalink);
}
else {
$head['title'] = $blogArticle->name;
$error = "permalink";
}
}
else {
$head['title'] = "Nouvel article";
}
$locales = new Kabano\Locales();
$locales->getAll();
$new = 1;
include ($config['views_folder']."d.blog.edit.html");
break;
}
default:
// If the page exists
if ($blogArticle->checkPermalink($controller->splitted_url[1],$user->rankIsHigher("premium"))) {
if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "delete" && $user->rankIsHigher("moderator")) {
$blogArticle->delete();
header('Location: '.$config['rel_root_folder']."blog/".$blogArticle->permalink);
}
else if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "restore" && $user->rankIsHigher("moderator")) {
$blogArticle->restore();
header('Location: '.$config['rel_root_folder']."blog/".$blogArticle->permalink);
}
else if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "edit" && $user->rankIsHigher("moderator")) {
if(isset($_POST['submit'])) {
$blogArticle->content = $_POST['content'];
$blogArticle->locale = $_POST['locale'];
$blogArticle->name = $_POST['name'];
$blogArticle->is_commentable = $_POST['is_commentable'];
$blogArticle->author = $user->id;
$blogArticle->update();
header('Location: '.$config['rel_root_folder']."blog/".$blogArticle->permalink);
}
else {
$locales = new Kabano\Locales();
$locales->getAll();
$head['title'] = $blogArticle->name;
include ($config['views_folder']."d.blog.edit.html");
}
}
else {
// Manage history of an article
if($user->rankIsHigher("premium")) {
$blogHistory = new Kabano\BlogArticles();
$blogHistory->getHistory($controller->splitted_url[1]);
}
if (isset($controller->splitted_url[2]) && is_numeric($controller->splitted_url[2]))
$blogArticle->checkPermalink($controller->splitted_url[1],$user->rankIsHigher("premium"),$controller->splitted_url[2]);
// Manage comment creation
if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="new_comment") {
if (isset($_POST['submit']) && $user->rankIsHigher("registered")) {
$Comment = new Kabano\Comment();
$Comment->locale = $user->locale;
$Comment->author = $user->id;
$Comment->content = $blogArticle->content_id;
$Comment->comment = $_POST['comment'];
$Comment->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])) {
$Comment = new Kabano\Comment();
if($Comment->checkId($controller->splitted_url[3]))
if ($user->rankIsHigher("moderator") || $user->id == $Comment->author)
$Comment->delete();
}
}
// Manage comment restoration
if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="restore_comment") {
if (isset($controller->splitted_url[3]) && is_numeric($controller->splitted_url[3])) {
$Comment = new Kabano\Comment();
if($Comment->checkId($controller->splitted_url[3]))
if ($user->rankIsHigher("moderator") || $user->id == $Comment->author)
$Comment->restore();
}
}
$blogArticle->md2html();
// Manage comments
if ($blogArticle->is_commentable == "t") {
$blogArticles_comments = new Kabano\Comments();
$blogArticles_comments->listComments($blogArticle->content_id, ($user->rankIsHigher("premium")));
$i = 0;
foreach ($blogArticles_comments->objs as $comment) {
$comment->md2html();
$comment->author_obj = new Kabano\User();
$comment->author_obj->checkId($comment->author);
}
}
$tempUser = new Kabano\User();
$tempUser->checkId($blogArticle->author);
$blogArticle->author_name = $tempUser->name;
unset($tempUser);
$head['title'] = $blogArticle->name;
include ($config['views_folder']."d.blog.view.html");
}
}
else {
$notfound = 1;
}
break;
}

75
src/controllers/d.contact.php Executable file
View File

@@ -0,0 +1,75 @@
<?php
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";
$sender = str_replace(["\r", "\n"], '', post('email'));
$headers = 'From: '. $sender . "\r\n" .
'Reply-To: '. $sender . "\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('email') == '') {
$error = "email";
$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->rankIsHigher("registered"))
$contact['name'] = $user->name;
else
$contact['name'] = '';
if(post('email') != '')
$contact['email'] = post('email');
else if($user->rankIsHigher("registered"))
$contact['email'] = $user->email;
else
$contact['email'] = '';
$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
src/controllers/d.map.php Executable file
View File

@@ -0,0 +1,20 @@
<?php
$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";
require_once($config['includes_folder']."poi_types.struct.php");
include ($config['views_folder']."d.map.html");
}

281
src/controllers/d.poi.php Executable file
View File

@@ -0,0 +1,281 @@
<?php
require_once($config['models_folder']."d.poi.php");
require_once($config['models_folder']."d.comments.php");
require_once($config['models_folder']."d.users.php");
require_once($config['includes_folder']."poi_types.struct.php");
$head['css'] = "d.index.css;d.poi.css";
$poi = new Kabano\Poi();
switch ($controller->splitted_url[1]) {
case "new":
if ($user->rankIsHigher("registered")) {
if (isset($_POST['submit'])) {
$poi->name = $_POST['name'];
$poi->locale = $_POST['locale'];
$poi->poi_type = $_POST['poi_type'];
$poi->lat = $_POST['lat'];
$poi->lon = $_POST['lon'];
$poi->ele = empty($_POST['ele']) ? 0 : (int) $_POST['ele'];
$poi->author = $user->id;
$poi->source = "kab";
$poi->is_commentable = 't';
$definition = $poi_types[$poi->poi_type][5];
$params = [];
foreach ($definition as $key => $label) {
if (isset($_POST[$key])) {
$value = $_POST[$key];
if (str_starts_with($key, 'b_')) {
if ($value === "0" || $value === "1" || $value === "2") {
$params[$key] = intval($value);
} else {
$params[$key] = -1; // non renseigné
}
}
elseif (str_starts_with($key, 'n_')) {
$params[$key] = is_numeric($value) ? (0 + $value) : null;
}
elseif (str_starts_with($key, 't_') || str_starts_with($key, 'l_')) {
$params[$key] = trim($value);
}
else {
$params[$key] = $value;
}
} else {
// Champ absent → booléen = -1 (non renseigné)
if (str_starts_with($key, 'b_')) {
$params[$key] = -1;
} else {
$params[$key] = null;
}
}
}
$poi->parameters = json_encode($params, JSON_UNESCAPED_UNICODE);
if (!$poi->checkPermalink($_POST['permalink'], 1)) {
$poi->permalink = $_POST['permalink'];
$poi->insert();
header('Location: '.$config['rel_root_folder']."poi/".$poi->permalink);
} else {
$head['title'] = $poi->name;
$error = "permalink";
}
} else {
$head['title'] = "Nouvel hébergement";
}
$locales = new Kabano\Locales();
$locales->getAll();
$head['third'] = "leaflet/leaflet.js;leaflet-fullscreen/Leaflet.fullscreen.min.js;leaflet-easybutton/easy-button.js";
$head['css'] .= ";../third/leaflet/leaflet.css;../third/leaflet-fullscreen/leaflet.fullscreen.css;../third/leaflet-easybutton/easy-button.css";
$head['js'] = "d.poi_map.js";
$poi->lat = "";
$poi->lon = "";
$poi->ele = "";
$new = 1;
include ($config['views_folder']."d.poi.edit.html");
break;
} else {
$notfound = 1;
}
break;
case "elevation_proxy":
if (isset($_GET['location'])) {
if (!preg_match('/^[0-9,\.\|\-]+$/', $_GET['location'])) {
$notfound = 1;
break;
}
$location = urlencode($_GET['location']);
header("Content-Type: application/json;charset=utf-8");
echo(file_get_contents("https://api.opentopodata.org/v1/mapzen?locations=".$location));
break;
} else {
$notfound = 1;
}
break;
case "api_list":
header("Content-Type: application/json; charset=utf-8");
$pois = new Kabano\Pois();
$pois->listPois($user->rankIsHigher("premium") ? 1 : 0);
$out = [];
foreach ($pois->objs as $poi) {
$out[] = [
"id" => $poi->content_id,
"name" => $poi->name,
"lat" => floatval($poi->lat),
"lon" => floatval($poi->lon),
"type" => $poi->poi_type,
"permalink" => $poi->permalink
];
}
echo json_encode($out, JSON_UNESCAPED_UNICODE);
break;
default:
// Affichage / édition / suppression dun POI
if ($poi->checkPermalink($controller->splitted_url[1], $user->rankIsHigher("premium"))) {
// Suppression
if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "delete" && $user->rankIsHigher("moderator")) {
$poi->delete();
header('Location: '.$config['rel_root_folder']."poi/".$poi->permalink);
}
// Restauration
else if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "restore" && $user->rankIsHigher("moderator")) {
$poi->restore();
header('Location: '.$config['rel_root_folder']."poi/".$poi->permalink);
}
// Édition
else if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "edit" && $user->rankIsHigher("moderator")) {
if (isset($_POST['submit'])) {
$poi->name = $_POST['name'];
$poi->locale = $_POST['locale'];
$poi->poi_type = $_POST['poi_type'];
$poi->lat = $_POST['lat'];
$poi->lon = $_POST['lon'];
$poi->ele = empty($_POST['ele']) ? 0 : (int) $_POST['ele'];
$poi->is_commentable = 't';
$definition = $poi_types[$poi->poi_type][5];
$params = [];
foreach ($definition as $key => $label) {
if (isset($_POST[$key])) {
$value = $_POST[$key];
if (str_starts_with($key, 'b_')) {
if ($value === "0" || $value === "1" || $value === "2") {
$params[$key] = intval($value);
} else {
$params[$key] = -1;
}
}
elseif (str_starts_with($key, 'n_')) {
$params[$key] = is_numeric($value) ? (0 + $value) : null;
}
elseif (str_starts_with($key, 't_') || str_starts_with($key, 'l_')) {
$params[$key] = trim($value);
}
else {
$params[$key] = $value;
}
} else {
// Champ absent → booléen = -1 (non renseigné)
if (str_starts_with($key, 'b_')) {
$params[$key] = -1;
} else {
$params[$key] = null;
}
}
}
$poi->parameters = json_encode($params, JSON_UNESCAPED_UNICODE);
$poi->update();
header('Location: '.$config['rel_root_folder']."poi/".$poi->permalink);
} else {
$locales = new Kabano\Locales();
$locales->getAll();
$head['third'] = "leaflet/leaflet.js;leaflet-fullscreen/Leaflet.fullscreen.min.js;leaflet-easybutton/easy-button.js";
$head['css'] .= ";../third/leaflet/leaflet.css;../third/leaflet-fullscreen/leaflet.fullscreen.css;../third/leaflet-easybutton/easy-button.css";
$head['js'] = "d.poi_map.js";
$head['title'] = $poi->name;
include ($config['views_folder']."d.poi.edit.html");
}
}
// Affichage
else {
// Historique
if ($user->rankIsHigher("premium")) {
$PoiHistory = new Kabano\Pois();
$PoiHistory->getHistory($controller->splitted_url[1]);
}
if (isset($controller->splitted_url[2]) && is_numeric($controller->splitted_url[2])) {
$poi->checkPermalink($controller->splitted_url[1], $user->rankIsHigher("premium"), $controller->splitted_url[2]);
}
// Création dun commentaire
if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "new_comment") {
if (isset($_POST['submit']) && $user->rankIsHigher("registered")) {
$Comment = new Kabano\Comment();
$Comment->locale = $user->locale;
$Comment->author = $user->id;
$Comment->content = $poi->content_id;
$Comment->comment = $_POST['comment'];
$Comment->insert();
}
}
// Suppression dun commentaire
if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "delete_comment") {
if (isset($controller->splitted_url[3]) && is_numeric($controller->splitted_url[3])) {
$Comment = new Kabano\Comment();
if ($Comment->checkId($controller->splitted_url[3]))
if ($user->rankIsHigher("moderator") || $user->id == $Comment->author)
$Comment->delete();
}
}
// Restauration dun commentaire
if (isset($controller->splitted_url[2]) && $controller->splitted_url[2] == "restore_comment") {
if (isset($controller->splitted_url[3]) && is_numeric($controller->splitted_url[3])) {
$Comment = new Kabano\Comment();
if ($Comment->checkId($controller->splitted_url[3]))
if ($user->rankIsHigher("moderator") || $user->id == $Comment->author)
$Comment->restore();
}
}
// Commentaires
if ($poi->is_commentable == "t") {
$poi_comments = new Kabano\Comments();
$poi_comments->listComments($poi->content_id, ($user->rankIsHigher("premium")));
foreach ($poi_comments->objs as $comment) {
$comment->md2html();
$comment->author_obj = new Kabano\User();
$comment->author_obj->checkId($comment->author);
}
}
// Auteur
$tempUser = new Kabano\User();
$tempUser->checkId($poi->author);
$poi->author_name = $tempUser->name;
unset($tempUser);
$head['third'] = "leaflet/leaflet.js;leaflet-fullscreen/Leaflet.fullscreen.min.js;leaflet-easybutton/easy-button.js";
$head['css'] .= ";../third/leaflet/leaflet.css;../third/leaflet-fullscreen/leaflet.fullscreen.css;../third/leaflet-easybutton/easy-button.css";
$head['js'] = "d.poi_map.js";
$head['title'] = $poi->name;
include ($config['views_folder']."d.poi.view.html");
}
} else {
$notfound = 1;
}
break;
}

245
src/controllers/d.users.php Executable file
View File

@@ -0,0 +1,245 @@
<?php
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->rank == "visitor") {
if (isset($_POST['submit'])) {
// PROCESS DATA FROM FORM
$user = new Kabano\User();
if($user->login($_POST['login'], $_POST['password'])) {
// SUCESSFULL LOGIN
$_SESSION['userid'] = $user->id;
$redirect = $config['rel_root_folder'];
if (!empty($_SERVER['HTTP_REFERER'])) {
$referer = $_SERVER['HTTP_REFERER'];
$parts = parse_url($referer);
$host = $_SERVER['HTTP_HOST'] ?? '';
if ($parts !== false && ((empty($parts['host']) && empty($parts['scheme'])) || (!empty($host) && isset($parts['host']) && $parts['host'] === $host))) {
$redirect = $referer;
}
}
header('Location: '.$redirect);
exit;
}
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();
$redirect = $config['rel_root_folder'];
if (!empty($_SERVER['HTTP_REFERER'])) {
$referer = $_SERVER['HTTP_REFERER'];
$parts = parse_url($referer);
$host = $_SERVER['HTTP_HOST'] ?? '';
if ($parts !== false && ((empty($parts['host']) && empty($parts['scheme'])) || (!empty($host) && isset($parts['host']) && $parts['host'] === $host))) {
$redirect = $referer;
}
}
header('Location: '.$redirect);
exit;
break;
case 'signin':
$head['js'] = "d.captcha.js";
$head['title'] = "Création de compte";
if ($user->rank == "visitor") {
if (isset($_POST['submit'])) {
// PROCESS DATA FROM FORM
$user = new Kabano\User();
$user->name = $_POST['login'];
$user->email = strtolower($_POST['email']);
$user->rank = "registered";
if($_POST['captcha'] == -2) {
if($user->availableName()) {
if($user->availableMail()) {
if($_POST['password'] AND $user->name != "" AND $user->email != "") {
$user->create(sha1($_POST['password']));
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=email');
}
}
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->rank == "visitor") {
if (isset($_POST['submit'])) {
// PROCESS DATA FROM FORM
$user = new Kabano\User();
$user->email = strtolower($_POST['email']);
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->rankIsHigher("registered")) {
$userProfile = new Kabano\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) {
$head['title'] = "Profil de ".$userProfile->name;
}
// If we are editing the profile
if(isset($controller->splitted_url[3]) && $controller->splitted_url[3]=="edit" && ($user->rankIsHigher("moderator") || $user->id == $userProfile->id)) {
$locales = new Kabano\Locales();
$locales->getAll();
$head['js'] = "d.avatar.js";
if (isset($_POST['submit'])) {
$receivedUser = new Kabano\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->email = strtolower($_POST['email']);
if($receivedUser->email != $userProfile->email && $receivedUser->availableMail())
$userProfile->email = $receivedUser->email;
else if ($receivedUser->email != $userProfile->email)
$emailError=1;
if($_POST['password']!='')
$userProfile->password=sha1($_POST['password']);
$userProfile->locale=$_POST['locale'];
$userProfile->timezone=$_POST['timezone'];
if($user->rankIsHigher("administrator"))
$userProfile->rank = $_POST['rank'];
$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->is_avatar_present = '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->is_avatar_present = '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->rankIsHigher("registered")) {
// 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->rankIsHigher("registered")) {
$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 Kabano\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);
$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;
}

79
src/controllers/d.wiki.php Executable file
View File

@@ -0,0 +1,79 @@
<?php
require_once($config['models_folder']."d.wiki.php");
$head['css'] = "d.index.css;d.wiki.css";
$wikiPage = new Kabano\WikiPage();
// Page doesn't exists
if(isset($controller->splitted_url[1]) && !$wikiPage->checkPermalink($controller->splitted_url[1],$user->rankIsHigher('premium')) && $controller->splitted_url[1]!="") {
if($user->rankIsHigher('moderator')) {
$wikiPage->permalink = $controller->splitted_url[1];
// Create new page
if(isset($_POST['submit'])) {
$wikiPage->content = $_POST['content'];
$wikiPage->locale = $_POST['locale'];
$wikiPage->name = $_POST['name'];
$wikiPage->insert();
header('Location: '.$config['rel_root_folder']."wiki/".$wikiPage->permalink);
}
else {
$locales = new Kabano\Locales();
$locales->getAll();
$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->checkPermalink($controller->splitted_url[1],$user->rankIsHigher('premium'))) {
$wikiPage->permalink = $controller->splitted_url[1];
if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="edit" && $user->rankIsHigher('moderator')) {
// Edit page
if(isset($_POST['submit'])) {
$wikiPage->content = $_POST['content'];
$wikiPage->locale = $_POST['locale'];
$wikiPage->name = $_POST['name'];
$wikiPage->update();
header('Location: '.$config['rel_root_folder']."wiki/".$wikiPage->permalink);
}
else {
$locales = new Kabano\Locales();
$locales->getAll();
$head['title'] = $wikiPage->name;
include ($config['views_folder']."d.wiki.edit.html");
}
} else if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="delete" && $user->rankIsHigher('moderator')) {
// Delete page
$wikiPage->delete();
header('Location: '.$config['rel_root_folder']."wiki/".$wikiPage->permalink);
} else if (isset($controller->splitted_url[2]) && $controller->splitted_url[2]=="restore" && $user->rankIsHigher('moderator')) {
// Restore page
$wikiPage->restore();
header('Location: '.$config['rel_root_folder']."wiki/".$wikiPage->permalink);
} else {
// Display page
if($user->rankIsHigher('premium')) {
$wikiHistory = new Kabano\WikiPages();
$wikiHistory->getHistory($controller->splitted_url[1]);
}
if (isset($controller->splitted_url[2]) && is_numeric($controller->splitted_url[2]))
$wikiPage->checkPermalink($controller->splitted_url[1], $user->rankIsHigher('premium'), $controller->splitted_url[2]);
$wikiPage->md2html();
$head['title'] = $wikiPage->name;
$head['css'] .= ";../third/simplelightbox/simple-lightbox.min.css";
$head['third'] = "simplelightbox/simple-lightbox.min.js";
include ($config['views_folder']."d.wiki.view.html");
}
}
else {
$notfound = 1;
}

415
src/models/d.blog.php Executable file
View File

@@ -0,0 +1,415 @@
<?php
namespace Kabano;
/**********************************************************
***********************************************************
**
** This class is to manage a blog article object
**
***********************************************************
**********************************************************/
require_once($config['third_folder']."Md/MarkdownExtra.inc.php");
require_once($config['includes_folder']."database.php");
class BlogArticle
{
public $content_id = NULL;
public $locale_id = NULL;
public $version_id = NULL;
public $permalink = NULL;
public $version = 0;
public $locale = NULL;
public $creation_date = NULL;
public $update_date = NULL;
public $author = NULL;
public $is_public = NULL;
public $is_archive = NULL;
public $is_commentable = NULL;
public $type = "blog";
public $name = NULL;
public $content = NULL;
public $content_html = NULL;
public $content_txt = NULL;
public $author_name = NULL;
private function decodeJsonText($value) {
if ($value === null || $value === '') {
return '';
}
$decoded = json_decode($value, true);
if (!is_array($decoded)) {
return '';
}
return isset($decoded['text']) ? $decoded['text'] : '';
}
/*****
** Checks if a page at this URL exists and return the ID
*****/
public function checkPermalink($permalink, $withArchive=0, $elementNb=0) {
global $config;
$con = sql_connect();
$query = "SELECT content_versions.id AS version_id, * FROM contents INNER JOIN content_locales ON contents.id = content_locales.content_id INNER JOIN content_versions ON content_locales.id = content_versions.locale_id WHERE permalink=$1 AND type='blog'";
if($withArchive==0) {
$query .= " AND is_archive=FALSE AND is_public=TRUE";
}
$query .= " ORDER BY update_date DESC LIMIT 1 OFFSET $2";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($permalink, $elementNb))
or die ("Cannot execute statement\n");
pg_close($con);
if(pg_num_rows($result) == 1) {
$row = pg_fetch_assoc($result);
$this->populate($row);
return 1;
}
else {
return 0;
}
}
/*****
** Populate the object using its ID
*****/
public function populate($row) {
if (!is_array($row)) {
return;
}
$decodedContent = null;
if (array_key_exists('content', $row)) {
$decodedContent = $this->decodeJsonText($row['content']);
}
if (array_key_exists('content_id', $row)) {
$this->content_id = $row['content_id'];
}
if (array_key_exists('locale_id', $row)) {
$this->locale_id = $row['locale_id'];
}
if (array_key_exists('version_id', $row)) {
$this->version_id = $row['version_id'];
}
if (array_key_exists('permalink', $row)) {
$this->permalink = $row['permalink'];
}
if (array_key_exists('version', $row)) {
$this->version = $row['version'];
}
if (array_key_exists('locale', $row)) {
$this->locale = $row['locale'];
}
if (array_key_exists('creation_date', $row)) {
$this->creation_date = $row['creation_date'];
}
if (array_key_exists('update_date', $row)) {
$this->update_date = $row['update_date'];
}
if (array_key_exists('author', $row)) {
$this->author = $row['author'];
}
if (array_key_exists('is_public', $row)) {
$this->is_public = $row['is_public'];
}
if (array_key_exists('is_archive', $row)) {
$this->is_archive = $row['is_archive'];
}
if (array_key_exists('is_commentable', $row)) {
$this->is_commentable = $row['is_commentable'];
}
if (array_key_exists('type', $row)) {
$this->type = $row['type'];
}
if (array_key_exists('name', $row)) {
$this->name = $row['name'];
}
if ($decodedContent !== null) {
$this->content = $decodedContent;
}
}
/*****
** Edit a page by archiving the current one and inserting a new one ID
*****/
public function update() {
global $config;
global $user;
if($this->content_id == 0 || $this->locale_id == 0 || $this->version_id == 0)
die("Cannot update entry without giving ID");
$this->version++;
$con = sql_connect();
pg_query($con, "BEGIN");
$query = "UPDATE content_versions SET is_archive = TRUE WHERE locale_id = $1";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($this->locale_id))
or die ("Cannot execute statement\n");
$query = "INSERT INTO content_versions (version, update_date, is_archive, name, content, locale_id) VALUES
($1, $2, FALSE, $3, $4, $5) RETURNING id";
pg_prepare($con, "prepare2", $query)
or die ("Cannot prepare statement\n");
$jsonContent = json_encode(['text' => $this->content]);
$result = pg_execute($con, "prepare2", array($this->version, date('r'), $this->name, $jsonContent, $this->locale_id))
or die ("Cannot execute statement\n");
$this->version_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_contributors (content, contributor) VALUES
($1, $2) ON CONFLICT (content, contributor) DO NOTHING";
pg_prepare($con, "prepare3", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare3", array($this->locale_id, $user->id))
or die ("Cannot execute statement\n");
$query = "UPDATE contents SET is_commentable = $1 WHERE id = $2";
pg_prepare($con, "prepare4", $query)
or die ("Cannot prepare statement\n");
pg_execute($con, "prepare4", array($this->is_commentable ? 't' : 'f', $this->content_id))
or die ("Cannot prepare statement\n");
pg_query($con, "COMMIT");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tUPDATE \tEdit blog article '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'blog.articles.log');
}
/*****
** Delete an article by archiving it
*****/
public function delete() {
global $config;
global $user;
$con = sql_connect();
$query = "UPDATE contents SET is_public=FALSE WHERE permalink=$1 AND type='blog'";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($this->permalink))
or die ("Cannot execute statement\n");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tDELETE \tArchive blog article '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'blog.articles.log');
}
/*****
** Restore a page from unpublishing it
*****/
public function restore() {
global $config;
global $user;
$con = sql_connect();
$query = "UPDATE contents SET is_public=TRUE WHERE permalink=$1 AND type='blog'";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($this->permalink))
or die ("Cannot execute statement\n");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tRESTORE \tPublish blog article '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'blog.articles.log');
}
/*****
** Create an article
*****/
public function insert() {
global $config;
global $user;
$con = sql_connect();
pg_query($con, "BEGIN");
$query = "INSERT INTO contents (permalink, creation_date, is_public, is_commentable, type) VALUES
($1, $2, TRUE, $3, 'blog') RETURNING id";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($this->permalink, date('r'), $this->is_commentable))
or die ("Cannot execute statement\n");
$this->content_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_locales (content_id, locale, author) VALUES
($1, $2, $3) RETURNING id";
pg_prepare($con, "prepare2", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare2", array($this->content_id, $this->locale, $user->id))
or die ("Cannot execute statement\n");
$this->locale_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_versions (version, update_date, is_archive, name, content, locale_id) VALUES
('0', $1, FALSE, $2, $3, $4) RETURNING id";
pg_prepare($con, "prepare3", $query)
or die ("Cannot prepare statement\n");
$jsonContent = json_encode(['text' => $this->content]);
$result = pg_execute($con, "prepare3", array(date('r'), $this->name, $jsonContent, $this->locale_id))
or die ("Cannot execute statement\n");
$this->version_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_contributors (content, contributor) VALUES
($1, $2)";
pg_prepare($con, "prepare4", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare4", array($this->locale_id, $user->id))
or die ("Cannot execute statement\n");
pg_query($con, "COMMIT");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tINSERT \tCreate new blog article '".$this->permalink."'\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 $objs = array();
public $number = NULL;
/*****
** Return the list of different articles
*****/
public function listArticles($first, $count, $archive=0) {
global $config;
$con = sql_connect();
$query = "SELECT content_versions.id AS version_id, * FROM contents INNER JOIN content_locales ON contents.id = content_locales.content_id INNER JOIN content_versions ON content_locales.id = content_versions.locale_id WHERE is_archive=FALSE ";
if ($archive != 1)
$query .= "AND is_public=TRUE ";
$query .= "AND type='blog' ORDER BY update_date 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->objs[$i] = new BlogArticle;
$this->objs[$i]->populate($row);
}
}
/*****
** Return the number of articles
*****/
public function number($archive=0) {
global $config;
$con = sql_connect();
$query = "SELECT content_versions.id AS version_id, * FROM contents INNER JOIN content_locales ON contents.id = content_locales.content_id INNER JOIN content_versions ON content_locales.id = content_versions.locale_id WHERE is_archive=FALSE ";
if ($archive == 1)
$query .= "AND is_public=TRUE ";
$query .= "AND type='blog' ORDER BY update_date 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 = sql_connect();
$query = "SELECT content_versions.id AS version_id, * FROM contents INNER JOIN content_locales ON contents.id = content_locales.content_id INNER JOIN content_versions ON content_locales.id = content_versions.locale_id WHERE permalink=$1 AND type='blog' ORDER BY update_date 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->objs[$i] = new BlogArticle;
$this->objs[$i]->populate($row);
}
}
}

226
src/models/d.comments.php Normal file
View File

@@ -0,0 +1,226 @@
<?php
namespace Kabano;
/**********************************************************
***********************************************************
**
** This class is to manage a comment object
**
***********************************************************
**********************************************************/
require_once($config['third_folder']."Md/MarkdownExtra.inc.php");
require_once($config['includes_folder']."database.php");
class Comment
{
public $id = NULL;
public $version = 0;
public $creation_date = NULL;
public $update_date = NULL;
public $author = NULL;
public $is_public = NULL;
public $is_archive = NULL;
public $content = NULL;
public $comment = NULL;
public $locale = NULL;
public $comment_html = NULL;
public $author_obj = NULL;
/*****
** Connect to correct account using ID and stores its ID
*****/
public function checkID($id) {
global $config;
$con = sql_connect();
$query = "SELECT * FROM content_comments 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) {
$row = pg_fetch_assoc($result);
$this->populate($row);
return 1;
}
else {
return 0;
}
}
/*****
** Populate the object using its ID
*****/
public function populate($row) {
if (!is_array($row)) {
return;
}
if (array_key_exists('id', $row)) {
$this->id = $row['id'];
}
if (array_key_exists('version', $row)) {
$this->version = $row['version'];
}
if (array_key_exists('creation_date', $row)) {
$this->creation_date = $row['creation_date'];
}
if (array_key_exists('update_date', $row)) {
$this->update_date = $row['update_date'];
}
if (array_key_exists('author', $row)) {
$this->author = $row['author'];
}
if (array_key_exists('is_public', $row)) {
$this->is_public = $row['is_public'];
}
if (array_key_exists('is_archive', $row)) {
$this->is_archive = $row['is_archive'];
}
if (array_key_exists('content', $row)) {
$this->content = $row['content'];
}
if (array_key_exists('comment', $row)) {
$this->comment = $row['comment'];
}
if (array_key_exists('locale', $row)) {
$this->locale = $row['locale'];
}
}
/*****
** Create a new comment
*****/
public function insert() {
global $config;
$con = sql_connect();
$query = "INSERT INTO content_comments (version, creation_date, update_date, author, is_public, is_archive, content, comment, locale) VALUES
('0', $1, $2, $3, TRUE, FALSE, $4, $5, $6) RETURNING id";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array(date('r'), date('r'), $this->author, $this->content, $this->comment, $this->locale))
or die ("Cannot execute statement\n");
$this->id = pg_fetch_assoc($result)['id'];
pg_close($con);
}
/*****
** Archive a comment
*****/
public function delete() {
global $config;
global $user;
$con = sql_connect();
$query = "UPDATE content_comments SET is_public = FALSE 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);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tDELETE \tArchive comment ".$this->id."\r\n",
3,
$config['logs_folder'].'blog.comments.log');
}
/*****
** Restore a comment
*****/
public function restore() {
global $config;
global $user;
$con = sql_connect();
$query = "UPDATE content_comments SET is_public = TRUE 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);
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 comment to HTML
*****/
public function md2html() {
$this->comment_html = \Michelf\MarkdownExtra::defaultTransform($this->comment);
}
/*****
** Converts the Markdown comment to text
*****/
public function md2txt() {
$this->md2html();
$this->comment_txt = strip_tags($this->comment_html);
}
}
/**********************************************************
***********************************************************
**
** This class is to manage a list of blog comments
**
***********************************************************
**********************************************************/
class Comments
{
public $objs = array();
public $number = NULL;
/*****
** Return the list of different articles
*****/
public function listComments($id, $archive=0) {
global $config;
$con = sql_connect();
$query = "SELECT * FROM content_comments WHERE content = $1 ";
if ($archive == 0)
$query .= "AND is_archive IS FALSE AND is_public IS TRUE ";
$query .= "ORDER BY update_date 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->objs[$i] = new Comment;
$this->objs[$i]->populate($row);
}
}
}

106
src/models/d.locales.php Executable file
View File

@@ -0,0 +1,106 @@
<?php
namespace Kabano;
/**********************************************************
***********************************************************
**
** This class is to manage Locale object
**
***********************************************************
**********************************************************/
require_once($config['includes_folder']."database.php");
class Locale
{
public $name = 0;
public $display_name = NULL;
public $flag_name = NULL;
/*****
** populate object using name
*****/
public function checkName($name) {
global $config;
$con = sql_connect();
$query = "SELECT * FROM locales WHERE name=$1";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($name))
or die ("Cannot execute statement\n");
pg_close($con);
if(pg_num_rows($result) == 1) {
$row = pg_fetch_assoc($result);
$this->populate($row);
return 1;
}
else {
return 0;
}
}
/*****
** Populate the object using raw data from SQL
*****/
public function populate($row) {
if (!is_array($row)) {
return;
}
if (array_key_exists('name', $row)) {
$this->name = $row['name'];
}
if (array_key_exists('display_name', $row)) {
$this->display_name = $row['display_name'];
}
if (array_key_exists('flag_name', $row)) {
$this->flag_name = $row['flag_name'];
}
}
}
/**********************************************************
***********************************************************
**
** This class is to manage Locales list object
**
***********************************************************
**********************************************************/
class Locales
{
public $number = 0;
public $objs = array();
/*****
** Get all locales
*****/
public function getAll() {
global $config;
$con = sql_connect();
$query = "SELECT * FROM locales";
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);
for($i = 0; $i < $this->number; $i++) {
$locale = pg_fetch_assoc($result, $i);
$this->objs[$i] = new Locale;
$this->objs[$i]->populate($locale);
}
}
}

515
src/models/d.poi.php Executable file
View File

@@ -0,0 +1,515 @@
<?php
namespace Kabano;
/**********************************************************
***********************************************************
**
** This class is to manage a poi object
**
***********************************************************
**********************************************************/
require_once($config['third_folder']."Md/MarkdownExtra.inc.php");
require_once($config['includes_folder']."poi_types.struct.php");
require_once($config['includes_folder']."database.php");
class Poi
{
public $content_id = NULL;
public $locale_id = NULL;
public $source_id = NULL;
public $version_id = NULL;
public $permalink = NULL;
public $version = 0;
public $locale = 0;
public $creation_date = NULL;
public $update_date = NULL;
public $author = NULL;
public $is_public = NULL;
public $is_archive = NULL; // Means destroyed for a POI
public $is_commentable = NULL;
public $type = "poi";
public $poi_type = NULL;
public $name = NULL;
public $source = NULL;
public $remote_source_id = NULL;
public $parameters = NULL;
public $lat;
public $lon;
public $ele;
public $author_name;
private function decodeJsonArray($value) {
if ($value === null || $value === '') {
return [];
}
$decoded = json_decode($value, true);
if (!is_array($decoded)) {
return [];
}
return $decoded;
}
/*****
** Checks if a page at this URL exists and return the ID
*****/
public function checkPermalink($permalink, $withArchive=0, $elementNb=0) {
global $config;
$con = sql_connect();
$query = "SELECT
content_versions.id AS version_id,
content_versions.version,
content_versions.update_date,
content_versions.is_archive,
content_versions.name,
content_versions.content AS parameters,
contents.id AS content_id,
contents.permalink,
contents.creation_date,
contents.is_public,
contents.is_commentable,
contents.type,
contents.poi_type,
content_locales.id AS locale_id,
content_locales.locale,
content_locales.author,
specs.source_id,
specs.remote_source_id,
ST_X(specs.geom) AS lon,
ST_Y(specs.geom) AS lat,
ST_Z(specs.geom) AS ele,
sources.display_name AS source
FROM contents
INNER JOIN content_locales
ON contents.id = content_locales.content_id
INNER JOIN content_versions
ON content_locales.id = content_versions.locale_id
LEFT JOIN content_version_poi_specifications specs
ON specs.content_version_id = content_versions.id
LEFT JOIN sources
ON sources.id = specs.source_id
WHERE contents.permalink = $1
AND contents.type = 'poi'";
if ($withArchive == 0) {
$query .= " AND content_versions.is_archive = FALSE AND contents.is_public = TRUE";
}
$query .= " ORDER BY content_versions.update_date DESC LIMIT 1 OFFSET $2";
pg_prepare($con, "poi_check_permalink", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "poi_check_permalink", [$permalink, $elementNb])
or die ("Cannot execute statement\n");
pg_close($con);
if (pg_num_rows($result) == 1) {
$row = pg_fetch_assoc($result);
$this->populate($row);
return 1;
}
return 0;
}
/*****
** Populate the object using its ID
*****/
public function populate($row) {
if (!is_array($row)) {
return;
}
$decodedParameters = null;
if (array_key_exists('parameters', $row)) {
$decodedParameters = $this->decodeJsonArray($row['parameters']);
}
if (array_key_exists('content_id', $row)) {
$this->content_id = $row['content_id'];
}
if (array_key_exists('locale_id', $row)) {
$this->locale_id = $row['locale_id'];
}
if (array_key_exists('source_id', $row)) {
$this->source_id = $row['source_id'];
}
if (array_key_exists('version_id', $row)) {
$this->version_id = $row['version_id'];
}
if (array_key_exists('permalink', $row)) {
$this->permalink = $row['permalink'];
}
if (array_key_exists('version', $row)) {
$this->version = $row['version'];
}
if (array_key_exists('locale', $row)) {
$this->locale = $row['locale'];
}
if (array_key_exists('creation_date', $row)) {
$this->creation_date = $row['creation_date'];
}
if (array_key_exists('update_date', $row)) {
$this->update_date = $row['update_date'];
}
if (array_key_exists('author', $row)) {
$this->author = $row['author'];
}
if (array_key_exists('is_public', $row)) {
$this->is_public = $row['is_public'];
}
if (array_key_exists('is_archive', $row)) {
$this->is_archive = $row['is_archive'];
}
if (array_key_exists('is_commentable', $row)) {
$this->is_commentable = $row['is_commentable'];
}
if (array_key_exists('type', $row)) {
$this->type = $row['type'];
}
if (array_key_exists('poi_type', $row)) {
$this->poi_type = $row['poi_type'];
}
if (array_key_exists('name', $row)) {
$this->name = $row['name'];
}
if ($decodedParameters !== null) {
$this->parameters = $decodedParameters;
}
if (array_key_exists('lon', $row)) {
$this->lon = $row['lon'];
}
if (array_key_exists('lat', $row)) {
$this->lat = $row['lat'];
}
if (array_key_exists('ele', $row)) {
$this->ele = $row['ele'];
}
if (array_key_exists('source', $row)) {
$this->source = $row['source'];
}
if (array_key_exists('remote_source_id', $row)) {
$this->remote_source_id = $row['remote_source_id'];
}
}
/*****
** Create a new poi
*****/
public function insert() {
global $config;
global $user;
$con = sql_connect();
pg_query($con, "BEGIN");
$query = "INSERT INTO contents (is_commentable, is_public, permalink, creation_date, type, poi_type) VALUES
($1, TRUE, $2, $3, $4, $5) RETURNING id";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($this->is_commentable, $this->permalink, date('r'), $this->type, $this->poi_type))
or die ("Cannot execute statement\n");
$this->content_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_locales (content_id, locale, author) VALUES
($1, $2, $3) RETURNING id";
pg_prepare($con, "prepare2", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare2", array($this->content_id, $this->locale, $user->id))
or die ("Cannot execute statement\n");
$this->locale_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_versions (version, update_date, is_archive, name, content, locale_id) VALUES
('0', $1, FALSE, $2, $3, $4) RETURNING id";
pg_prepare($con, "prepare3", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare3", array(date('r'), $this->name, $this->parameters, $this->locale_id))
or die ("Cannot execute statement\n");
$this->version_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_version_poi_specifications (content_version_id, geom, source_id, remote_source_id) VALUES
($1, ST_SetSRID(ST_MakePoint($2, $3, $4), 4326), $5, $6)";
pg_prepare($con, "prepare4", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare4", array($this->version_id, $this->lon, $this->lat, $this->ele ,$this->source_id, $this->remote_source_id))
or die ("Cannot execute statement\n");
$query = "INSERT INTO content_contributors (content, contributor) VALUES
($1, $2)";
pg_prepare($con, "prepare5", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare5", array($this->locale_id, $user->id))
or die ("Cannot execute statement\n");
pg_query($con, "COMMIT");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tINSERT \tCreate new poi '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'poi.log');
}
/*****
** Edit a POI by archiving the current version and inserting a new one
*****/
public function update() {
global $config;
global $user;
if ($this->content_id == 0 || $this->locale_id == 0 || $this->version_id == 0)
die("Cannot update entry without giving ID");
$this->version++;
$con = sql_connect();
pg_query($con, "BEGIN");
// 1) Archive old versions
$query = "UPDATE content_versions SET is_archive = TRUE WHERE locale_id = $1";
pg_prepare($con, "poi_update_archive", $query);
pg_execute($con, "poi_update_archive", array($this->locale_id));
// 2) Insert new version
$query = "INSERT INTO content_versions (version, update_date, is_archive, name, content, locale_id) VALUES
($1, $2, FALSE, $3, $4, $5) RETURNING id";
pg_prepare($con, "poi_update_newversion", $query);
$result = pg_execute($con, "poi_update_newversion", array($this->version, date('r'), $this->name, $this->parameters, $this->locale_id));
$this->version_id = pg_fetch_assoc($result)['id'];
// 3) Insert new geometry + source info for this new version
$query = "INSERT INTO content_version_poi_specifications (content_version_id, geom, source_id, remote_source_id) VALUES
($1, ST_SetSRID(ST_MakePoint($2, $3, $4), 4326), $5, $6)";
pg_prepare($con, "poi_insert_specs_update", $query);
pg_execute($con, "poi_insert_specs_update", array( $this->version_id, $this->lon, $this->lat, $this->ele, $this->source, $this->remote_source_id ));
// 4) Update is_commentable
$query = "UPDATE contents SET is_commentable = $1, poi_type = $2 WHERE id = $3";
pg_prepare($con, "poi_update_commentable", $query);
pg_execute($con, "poi_update_commentable", array( $this->is_commentable ? 't' : 'f', $this->poi_type, $this->content_id));
// 5) Add contributor
$query = "INSERT INTO content_contributors (content, contributor)
VALUES ($1, $2) ON CONFLICT (content, contributor) DO NOTHING";
pg_prepare($con, "poi_update_contrib", $query);
pg_execute($con, "poi_update_contrib", array($this->content_id, $user->id));
pg_query($con, "COMMIT");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tUPDATE \tEdit POI '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'poi.log'
);
}
/*****
** Archive a POI
*****/
public function delete() {
global $config;
global $user;
$con = sql_connect();
$query = "UPDATE contents SET is_public = FALSE WHERE id = $1";
pg_prepare($con, "poi_delete", $query);
pg_execute($con, "poi_delete", array($this->content_id));
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tDELETE \tArchive POI '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'poi.log'
);
}
/*****
** Restore a POI
*****/
public function restore() {
global $config;
global $user;
$con = sql_connect();
$query = "UPDATE contents SET is_public = TRUE WHERE id = $1";
pg_prepare($con, "poi_restore", $query);
pg_execute($con, "poi_restore", array($this->content_id));
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tRESTORE \tPublish POI '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'poi.log'
);
}
}
class Pois
{
public $objs = [];
public $number = 0;
public function listPois($archive=0) {
global $config;
$con = sql_connect();
$query = "SELECT
content_versions.id AS version_id,
content_versions.version,
content_versions.update_date,
content_versions.is_archive,
content_versions.name,
content_versions.content AS parameters,
contents.id AS content_id,
contents.permalink,
contents.creation_date,
contents.is_public,
contents.is_commentable,
contents.type,
contents.poi_type,
content_locales.id AS locale_id,
content_locales.locale,
content_locales.author,
specs.source_id,
specs.remote_source_id,
ST_X(specs.geom) AS lon,
ST_Y(specs.geom) AS lat,
ST_Z(specs.geom) AS ele,
sources.display_name AS source
FROM contents
INNER JOIN content_locales
ON contents.id = content_locales.content_id
INNER JOIN content_versions
ON content_locales.id = content_versions.locale_id
LEFT JOIN content_version_poi_specifications specs
ON specs.content_version_id = content_versions.id
LEFT JOIN sources
ON sources.id = specs.source_id
WHERE contents.type = 'poi'
AND content_versions.is_archive = FALSE";
if ($archive != 1)
$query .= " AND contents.is_public=TRUE ";
$query .= " ORDER BY content_versions.update_date DESC";
pg_prepare($con, "pois_list", $query);
$result = pg_execute($con, "pois_list", []);
pg_close($con);
for ($i = 0; $i < pg_num_rows($result); $i++) {
$row = pg_fetch_assoc($result, $i);
$this->objs[$i] = new Poi;
$this->objs[$i]->populate($row);
}
}
public function getHistory($permalink) {
global $config;
$con = sql_connect();
$query = "SELECT
content_versions.id AS version_id,
content_versions.version,
content_versions.update_date,
content_versions.is_archive,
content_versions.name,
content_versions.content AS parameters,
contents.id AS content_id,
contents.permalink,
contents.creation_date,
contents.is_public,
contents.is_commentable,
contents.type,
contents.poi_type,
content_locales.id AS locale_id,
content_locales.locale,
content_locales.author,
specs.source_id,
specs.remote_source_id,
ST_X(specs.geom) AS lon,
ST_Y(specs.geom) AS lat,
ST_Z(specs.geom) AS ele,
sources.display_name AS source
FROM contents
INNER JOIN content_locales
ON contents.id = content_locales.content_id
INNER JOIN content_versions
ON content_locales.id = content_versions.locale_id
LEFT JOIN content_version_poi_specifications specs
ON specs.content_version_id = content_versions.id
LEFT JOIN sources
ON sources.id = specs.source_id
WHERE contents.permalink = $1
AND contents.type = 'poi'
ORDER BY content_versions.update_date DESC
";
pg_prepare($con, "poi_history", $query);
$result = pg_execute($con, "poi_history", array($permalink));
pg_close($con);
$this->number = pg_num_rows($result);
for ($i = 0; $i < $this->number; $i++) {
$row = pg_fetch_assoc($result, $i);
$this->objs[$i] = new Poi;
$this->objs[$i]->populate($row);
}
}
}

474
src/models/d.users.php Executable file
View File

@@ -0,0 +1,474 @@
<?php
namespace Kabano;
/**********************************************************
***********************************************************
**
** This class is to manage User object
**
***********************************************************
**********************************************************/
require_once($config['models_folder']."d.locales.php");
require_once($config['includes_folder']."database.php");
// This array is related to the defined SQL enum, do not touch.
$ranks = array(
"administrator" => array(1000,"Administrateur", "red", "administrator"),
"moderator" => array(800,"Modérateur", "orangered", "moderator"),
"premium" => array(600,"Membre premium", "orange", "premium"),
"registered" => array(400,"Utilisateur", "green", "registered"),
"blocked" => array(200,"Membre archivé", "#aaa", "blocked"),
"visitor" => array(0,"Visiteur", "black", "visitor")
);
class User
{
public $id = 0;
public $name = NULL;
public $version = NULL;
public $email = NULL;
public $password = NULL;
public $website = NULL;
public $is_avatar_present = NULL;
public $is_archive = NULL;
public $rank = NULL;
public $locale = NULL;
public $timezone = NULL;
public $visit_date = NULL;
public $register_date = NULL;
public $date_format;
public $datetime_format;
public $datetimeshort_format;
public $locale_obj;
public $locale_loaded = false;
/*****
** Connect to correct account using ID and stores its ID
*****/
public function checkID($id) {
global $config;
$con = sql_connect();
$query = "SELECT * 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) {
$row = pg_fetch_assoc($result);
$this->populate($row);
return 1;
}
else {
return 0;
}
}
/*****
** Connect to correct account using user/pass and stores its ID
*****/
public function login($login, $pass) {
global $config;
$con = sql_connect();
$query = "SELECT * 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) {
$row = pg_fetch_assoc($result);
$this->populate($row);
return 1;
}
}
/*****
** Populate the object using raw data from SQL
*****/
public function populate($row) {
if (!is_array($row)) {
return;
}
if (array_key_exists('id', $row)) {
$this->id = $row['id'];
}
if (array_key_exists('name', $row)) {
$this->name = $row['name'];
}
if (array_key_exists('version', $row)) {
$this->version = $row['version'];
}
if (array_key_exists('email', $row)) {
$this->email = $row['email'];
}
if (array_key_exists('password', $row)) {
$this->password = $row['password'];
}
if (array_key_exists('website', $row)) {
$this->website = $row['website'];
}
if (array_key_exists('is_avatar_present', $row)) {
$this->is_avatar_present = $row['is_avatar_present'];
}
if (array_key_exists('is_archive', $row)) {
$this->is_archive = $row['is_archive'];
}
if (array_key_exists('rank', $row)) {
$this->rank = $row['rank'];
}
if (array_key_exists('locale', $row)) {
$this->locale = $row['locale'];
}
if (array_key_exists('timezone', $row)) {
$this->timezone = $row['timezone'];
}
if (array_key_exists('visit_date', $row)) {
$this->visit_date = $row['visit_date'];
}
if (array_key_exists('register_date', $row)) {
$this->register_date = $row['register_date'];
}
}
/*****
** Simple return only functions
*****/
public function get_rank() {
global $ranks;
return '<span class="userrole" style="color: '.$ranks[$this->rank][2].';">'.$ranks[$this->rank][1].'</span>';
}
public function get_locale() {
if( $this->locale_loaded) {
return $this->locale_obj->display_name;
}
else {
$this->locale_obj = new Locale;
$this->locale_loaded = true;
if( $this->locale_obj->checkName($this->locale) )
return $this->locale_obj->display_name;
else
return false;
}
}
/*****
** Returns true if user permissions are higher than $rank
*****/
public function rankIsHigher($rank) {
global $ranks;
return $ranks[$this->rank][0] >= $ranks[$rank][0];
}
/*****
** Checks if the user's name is available or not
*****/
public function availableName() {
global $config;
$con = sql_connect();
$query = "SELECT * 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) {
$row = pg_fetch_assoc($result);
$this->populate($row);
}
return 0;
}
}
/*****
** Checks if the user's mail address exists in the database
*****/
public function availableMail() {
global $config;
$con = sql_connect();
$query = "SELECT * FROM users WHERE lower(email)=$1";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array(strtolower($this->email)))
or die ("Cannot execute statement\n");
pg_close($con);
if(pg_num_rows($result) < 1) {
return 1;
}
else {
if(pg_num_rows($result)==1) {
$row = pg_fetch_assoc($result);
$this->populate($row);
}
return 0;
}
}
/*****
** Creates a new user giving a sha1 password
*****/
public function create($password) {
global $config;
$regex = '/^(https?:\/\/)/';
if (!preg_match($regex, $this->website) && $this->website!="")
$this->website = "http://".$this->website;
$this->visit_date = date('r');
$this->register_date = date('r');
$this->locale = "fr_FR";
$this->timezone = "Europe/Paris";
$con = sql_connect();
$query = "INSERT INTO users (name, version, email, password, website, is_avatar_present, is_archive, rank, locale, timezone, visit_date, register_date) VALUES
($1, '0', $2, $3, $4, FALSE, FALSE, 'registered', $5, $6, $7, $8)";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
pg_execute($con, "prepare1", array($this->name, $this->email, $password, $this->website, $this->locale, $this->timezone, $this->visit_date, $this->register_date))
or die ("Cannot execute statement\n");
pg_close($con);
}
/*****
** 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;
$this->version++;
$con = sql_connect();
if($this->password=='') {
$query = "UPDATE users SET version = $1, name = $2, is_avatar_present = $3, locale = $4, rank = $5, email = $6, website = $7, timezone = $8 WHERE id = $9";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
pg_execute($con, "prepare1", array($this->version, $this->name, $this->is_avatar_present, $this->locale, $this->rank, $this->email, $this->website, $this->timezone, $this->id))
or die ("Cannot execute statement\n");
}
else {
$query = "UPDATE users SET name = $1, is_avatar_present = $2, locale = $3, rank = $4, email = $5, website = $6, password = $7, timezone = $8, version = $9 WHERE id = $10";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
pg_execute($con, "prepare1", array($this->name, $this->is_avatar_present, $this->locale, $this->rank, $this->email, $this->website, $this->password, $this->timezone, $this->version, $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 = sql_connect();
$query = "UPDATE users SET password = $1 WHERE email = $2";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
pg_execute($con, "prepare1", array($this->password, $this->email))
or die ("Cannot execute statement\n");
pg_close($con);
$this->availableMail(); // Retreive user data from email
$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 ♥ en Ariège.</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->email, 'Kabano - Nouveau mot de passe', $message, $headers);
}
/*****
** Update the last login date
*****/
public function updateLoginDate() {
global $config;
$this->visit_date = date('r');
$con = sql_connect();
$query = "UPDATE users SET visit_date = $1 WHERE id = $2";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
pg_execute($con, "prepare1", array($this->visit_date, $this->id))
or die ("Cannot execute statement\n");
pg_close($con);
}
/*****
** Sends an email to the user from an other user
*****/
public function sendMail($content, $from) {
global $config;
global $user;
$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->email . "\r\n" .
'Reply-To: '. $from->email . "\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->email, '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 = random_int(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 $objs = array();
public $number = NULL;
/*****
** Get the users number and return the value
*****/
public function number() {
global $config;
$con = sql_connect();
$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 = sql_connect();
$orders=array("id","name","lastlogin","registered","website","role");
$key=array_search($orderby,$orders);
$orderbysafe=$orders[$key];
if ($order == 'ASC')
$query = "SELECT * FROM users ORDER BY $orderbysafe ASC LIMIT $1 OFFSET $2";
else
$query = "SELECT * 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->objs[$i] = new User;
$this->objs[$i]->populate($row);
}
}
}

348
src/models/d.wiki.php Executable file
View File

@@ -0,0 +1,348 @@
<?php
namespace Kabano;
/**********************************************************
***********************************************************
**
** This class is to manage a wiki page object
**
***********************************************************
**********************************************************/
require_once($config['third_folder']."Md/MarkdownExtra.inc.php");
require_once($config['includes_folder']."database.php");
class WikiPage
{
public $content_id = NULL;
public $locale_id = NULL;
public $version_id = NULL;
public $permalink = NULL;
public $version = 0;
public $locale = NULL;
public $creation_date = NULL;
public $update_date = NULL;
public $author = NULL;
public $is_public = NULL;
public $is_archive = NULL;
public $is_commentable = NULL;
public $type = "wiki";
public $name = NULL;
public $content = NULL;
public $content_html;
private function decodeJsonText($value) {
if ($value === null || $value === '') {
return '';
}
$decoded = json_decode($value, true);
if (!is_array($decoded)) {
return '';
}
return isset($decoded['text']) ? $decoded['text'] : '';
}
/*****
** Checks if a page at this permalink exists and return the populated element
*****/
public function checkPermalink($permalink, $withArchive=0, $elementNb=0) {
global $config;
$con = sql_connect();
$query = "SELECT content_versions.id AS version_id, * FROM contents INNER JOIN content_locales ON contents.id = content_locales.content_id INNER JOIN content_versions ON content_locales.id = content_versions.locale_id WHERE permalink=$1 AND type='wiki'";
if($withArchive==0) {
$query .= " AND is_archive=FALSE AND is_public=TRUE";
}
$query .= " ORDER BY update_date DESC LIMIT 1 OFFSET $2";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($permalink, $elementNb))
or die ("Cannot execute statement\n");
pg_close($con);
if(pg_num_rows($result) == 1) {
$row = pg_fetch_assoc($result);
$this->populate($row);
return 1;
}
else {
return 0;
}
}
/*****
** Populate the object using raw data from SQL
*****/
public function populate($row) {
if (!is_array($row)) {
return;
}
$decodedContent = null;
if (array_key_exists('content', $row)) {
$decodedContent = $this->decodeJsonText($row['content']);
}
if (array_key_exists('content_id', $row)) {
$this->content_id = $row['content_id'];
}
if (array_key_exists('locale_id', $row)) {
$this->locale_id = $row['locale_id'];
}
if (array_key_exists('version_id', $row)) {
$this->version_id = $row['version_id'];
}
if (array_key_exists('permalink', $row)) {
$this->permalink = $row['permalink'];
}
if (array_key_exists('version', $row)) {
$this->version = $row['version'];
}
if (array_key_exists('locale', $row)) {
$this->locale = $row['locale'];
}
if (array_key_exists('creation_date', $row)) {
$this->creation_date = $row['creation_date'];
}
if (array_key_exists('update_date', $row)) {
$this->update_date = $row['update_date'];
}
if (array_key_exists('author', $row)) {
$this->author = $row['author'];
}
if (array_key_exists('is_public', $row)) {
$this->is_public = $row['is_public'];
}
if (array_key_exists('is_archive', $row)) {
$this->is_archive = $row['is_archive'];
}
if (array_key_exists('is_commentable', $row)) {
$this->is_commentable = $row['is_commentable'];
}
if (array_key_exists('type', $row)) {
$this->type = $row['type'];
}
if (array_key_exists('name', $row)) {
$this->name = $row['name'];
}
if ($decodedContent !== null) {
$this->content = $decodedContent;
}
}
/*****
** Edit a page by archiving the current one and inserting a new one ID
*****/
public function update() {
global $config;
global $user;
if($this->content_id == 0 || $this->locale_id == 0 || $this->version_id == 0)
die("Cannot update entry without giving ID");
$this->version++;
$con = sql_connect();
pg_query($con, "BEGIN");
$query = "UPDATE content_versions SET is_archive = TRUE WHERE locale_id = $1";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($this->locale_id))
or die ("Cannot execute statement\n");
$query = "INSERT INTO content_versions (version, update_date, is_archive, name, content, locale_id) VALUES
($1, $2, FALSE, $3, $4, $5) RETURNING id";
pg_prepare($con, "prepare2", $query)
or die ("Cannot prepare statement\n");
$jsonContent = json_encode(['text' => $this->content]);
$result = pg_execute($con, "prepare2", array($this->version, date('r'), $this->name, $jsonContent, $this->locale_id))
or die ("Cannot execute statement\n");
$this->version_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_contributors (content, contributor) VALUES
($1, $2) ON CONFLICT (content, contributor) DO NOTHING";
pg_prepare($con, "prepare3", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare3", array($this->locale_id, $user->id))
or die ("Cannot execute statement\n");
pg_query($con, "COMMIT");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tUPDATE \tEdit wiki page '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'wiki.log');
}
/*****
** Delete a page by archiving it
*****/
public function delete() {
global $config;
global $user;
$con = sql_connect();
$query = "UPDATE contents SET is_public=FALSE WHERE permalink=$1 AND type='wiki'";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($this->permalink))
or die ("Cannot execute statement\n");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tDELETE \tUnpublish wiki page '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'wiki.log');
}
/*****
** Restore a page from unpublishing it
*****/
public function restore() {
global $config;
global $user;
$con = sql_connect();
$query = "UPDATE contents SET is_public=TRUE WHERE permalink=$1 AND type='wiki'";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($this->permalink))
or die ("Cannot execute statement\n");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tRESTORE \tPublish wiki page '".$this->permalink."'\r\n",
3,
$config['logs_folder'].'wiki.log');
}
/*****
** Create a new page, fails if permalink already exists
*****/
public function insert() {
global $config;
global $user;
$con = sql_connect();
pg_query($con, "BEGIN");
$query = "INSERT INTO contents (permalink, creation_date, is_public, is_commentable, type) VALUES
($1, $2, TRUE, FALSE, 'wiki') RETURNING id";
pg_prepare($con, "prepare1", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare1", array($this->permalink, date('r')))
or die ("Cannot execute statement\n");
$this->content_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_locales (content_id, locale, author) VALUES
($1, $2, $3) RETURNING id";
pg_prepare($con, "prepare2", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare2", array($this->content_id, $this->locale, $user->id))
or die ("Cannot execute statement\n");
$this->locale_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_versions (version, update_date, is_archive, name, content, locale_id) VALUES
('0', $1, FALSE, $2, $3, $4) RETURNING id";
pg_prepare($con, "prepare3", $query)
or die ("Cannot prepare statement\n");
$jsonContent = json_encode(['text' => $this->content]);
$result = pg_execute($con, "prepare3", array(date('r'), $this->name, $jsonContent, $this->locale_id))
or die ("Cannot execute statement\n");
$this->version_id = pg_fetch_assoc($result)['id'];
$query = "INSERT INTO content_contributors (content, contributor) VALUES
($1, $2)";
pg_prepare($con, "prepare4", $query)
or die ("Cannot prepare statement\n");
$result = pg_execute($con, "prepare4", array($this->locale_id, $user->id))
or die ("Cannot execute statement\n");
pg_query($con, "COMMIT");
pg_close($con);
error_log(
date('r')." \t".$user->name." (".$user->id.") \tINSERT \tCreate new wiki page '".$this->permalink."'\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);
}
}
/**********************************************************
***********************************************************
**
** This class is to manage a wiki page object
**
***********************************************************
**********************************************************/
class WikiPages
{
public $objs = array();
public $number = NULL;
/*****
** Checks if a page at this URL exists and return the ID
*****/
public function getHistory($url) {
global $config;
$con = sql_connect();
$query = "SELECT content_versions.id AS version_id, * FROM contents INNER JOIN content_locales ON contents.id = content_locales.content_id INNER JOIN content_versions ON content_locales.id = content_versions.locale_id WHERE permalink=$1 AND type='wiki' ORDER BY update_date 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->objs[$i] = new WikiPage;
$this->objs[$i]->populate($row);
}
}
}