347 lines
No EOL
8.8 KiB
PHP
347 lines
No EOL
8.8 KiB
PHP
<?php
|
|
|
|
require_once 'analytics_interface.php';
|
|
require_once 'db_interface.php';
|
|
|
|
function taglist_escape_tag($tag) {
|
|
return preg_replace_callback('/[\WZ]/', function($match) {
|
|
return "Z" . ord($match[0]);
|
|
}, strtolower($tag));
|
|
}
|
|
|
|
function taglist_to_sql_string($post_tags) {
|
|
$post_tags = array_unique($post_tags);
|
|
$post_tags = array_map(function($val) {
|
|
return taglist_escape_tag($val);
|
|
}, $post_tags);
|
|
|
|
asort($post_tags);
|
|
$post_tags = join(' ', $post_tags);
|
|
|
|
return $post_tags;
|
|
}
|
|
|
|
class MySQLHandler
|
|
implements PostdataInterface {
|
|
|
|
CONST SQL_READ_COLUMNS = [
|
|
'id', 'path', 'created_at', 'updated_at',
|
|
'title', 'view_count', 'brief'];
|
|
|
|
CONST SQL_WRITE_COLUMNS = ['path', 'title', 'brief'];
|
|
|
|
private $sql_connection;
|
|
|
|
public $hostname;
|
|
|
|
public $debugging;
|
|
|
|
function __construct($sql_connection, $hostname) {
|
|
$this->sql_connection = $sql_connection;
|
|
$this->hostname = $hostname;
|
|
|
|
$this->debugging = false;
|
|
}
|
|
|
|
private function _dbg($message) {
|
|
if($this->debugging) {
|
|
echo $message;
|
|
}
|
|
}
|
|
|
|
private function _exec($qery, $argtypes = '', ...$args) {
|
|
$stmt = $this->sql_connection->prepare($qery);
|
|
|
|
if($argtypes != ""){
|
|
$stmt->bind_param($argtypes, ...$args);
|
|
}
|
|
$stmt->execute();
|
|
|
|
return $stmt->get_result();
|
|
}
|
|
|
|
private function clear_post_settings_cache($post_path) {
|
|
$post_path = sanitize_post_path($post_path);
|
|
|
|
$this->_exec("
|
|
UPDATE posts
|
|
SET post_settings_cache=NULL
|
|
WHERE host = ? AND post_path LIKE ?;
|
|
", "ss", $this->hostname, $post_path . "%");
|
|
}
|
|
|
|
public function stub_postdata($path) {
|
|
$post_path = sanitize_post_path($path);
|
|
$path_depth = substr_count($post_path, "/");
|
|
|
|
|
|
$qry = "
|
|
INSERT INTO posts
|
|
(host, post_path, post_path_depth)
|
|
VALUES
|
|
( ?, ?, ?) AS new
|
|
ON DUPLICATE KEY UPDATE post_path=new.post_path;";
|
|
|
|
$this->_exec($qry, "ssi",
|
|
$this->hostname,
|
|
$post_path,
|
|
$path_depth);
|
|
}
|
|
|
|
public function stub_postdata_tree($path) {
|
|
$post_path = sanitize_post_path($path);
|
|
|
|
while(true) {
|
|
if($post_path == '/') {
|
|
$post_path = '';
|
|
}
|
|
|
|
try {
|
|
$this->stub_postdata($post_path);
|
|
}
|
|
catch(Exception $e) {
|
|
}
|
|
|
|
$post_path = dirname($post_path);
|
|
if(strlen($post_path) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public function set_postdata($data) {
|
|
$data['path'] = sanitize_post_path($data['path']);
|
|
$post_path = $data['path'];
|
|
unset($data['path']);
|
|
|
|
$this->stub_postdata_tree($post_path);
|
|
|
|
$data['title'] ??= basename($post_path);
|
|
|
|
$post_tags = $data['tags'] ?? [];
|
|
array_push($post_tags,
|
|
'path:' . $post_path
|
|
);
|
|
|
|
$sql_args = [
|
|
$this->hostname,
|
|
$post_path,
|
|
substr_count($post_path, "/"),
|
|
$data['title'],
|
|
taglist_to_sql_string($post_tags),
|
|
$data['brief'] ?? null
|
|
];
|
|
|
|
unset($data['title']);
|
|
unset($data['brief']);
|
|
|
|
$post_markdown = $data['markdown'] ?? null;
|
|
unset($data['markdown']);
|
|
unset($data['html']);
|
|
|
|
array_push($sql_args, json_encode($data));
|
|
|
|
$qry =
|
|
"INSERT INTO posts
|
|
(host,
|
|
post_path, post_path_depth,
|
|
post_title, post_tags, post_brief,
|
|
post_metadata, post_settings_cache)
|
|
VALUES
|
|
( ?, ?, ?, ?, ?, ?, ?, null) AS new
|
|
ON DUPLICATE KEY
|
|
UPDATE post_title=new.post_title,
|
|
post_tags=new.post_tags,
|
|
post_brief=new.post_brief,
|
|
post_metadata=new.post_metadata,
|
|
post_updated_at=CURRENT_TIMESTAMP;
|
|
";
|
|
|
|
$this->_exec($qry, "ssissss", ...$sql_args);
|
|
|
|
if(isset($post_markdown)) {
|
|
$this->set_post_markdown($this->sql_connection->insert_id, $post_markdown);
|
|
}
|
|
|
|
$this->clear_post_settings_cache($post_path);
|
|
}
|
|
|
|
public function set_post_markdown($id, $markdown) {
|
|
$qry =
|
|
"INSERT INTO post_markdown ( post_id, post_markdown )
|
|
VALUES (?, ?) AS new
|
|
ON DUPLICATE KEY UPDATE post_markdown=new.post_markdown;
|
|
";
|
|
|
|
$this->_exec($qry, "is", $id, $markdown);
|
|
}
|
|
|
|
private function get_post_settings($post_path) {
|
|
$post_path = sanitize_post_path($post_path);
|
|
|
|
$this->_dbg("-> gps: getting path " . $post_path . "\n");
|
|
|
|
$post_settings = $this->_exec("
|
|
SELECT post_settings_cache
|
|
FROM posts
|
|
WHERE post_path = ? AND host = ?
|
|
", "ss", $post_path, $this->hostname)->fetch_assoc();
|
|
|
|
if(!isset($post_settings)) {
|
|
$this->_dbg("-> gps: Returning because of no result\n");
|
|
return [];
|
|
}
|
|
if(isset($post_settings['post_settings_cache'])) {
|
|
$result = json_decode($post_settings['post_settings_cache'], true);
|
|
if($this->debugging) {
|
|
echo "-> gps: Returning because of cached result:\n";
|
|
echo "--> " . json_encode($result) . "\n";
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
$parent_settings = [];
|
|
if($post_path != "") {
|
|
$parent_settings = $this->get_post_settings(dirname($post_path));
|
|
}
|
|
|
|
$post_settings = [];
|
|
$post_metadata = $this->_exec("
|
|
SELECT post_path, post_metadata
|
|
FROM posts
|
|
WHERE post_path = ? AND host = ?
|
|
", "ss", $post_path, $this->hostname)->fetch_assoc();
|
|
|
|
if(isset($post_metadata['post_metadata'])) {
|
|
$post_metadata = json_decode($post_metadata['post_metadata'], true);
|
|
|
|
if(isset($post_metadata['settings'])) {
|
|
$post_settings = $post_metadata['settings'];
|
|
}
|
|
}
|
|
|
|
$post_settings = array_merge($parent_settings, $post_settings);
|
|
|
|
$this->_dbg("-> gps: Merged post settings are " . json_encode($post_settings) . ", saving...\n");
|
|
|
|
$this->_exec("
|
|
UPDATE posts SET post_settings_cache=? WHERE post_path=? AND host=?
|
|
", "sss",
|
|
json_encode($post_settings), $post_path, $this->hostname);
|
|
|
|
return $post_settings;
|
|
}
|
|
|
|
private function process_postdata($data) {
|
|
if(!isset($data)) {
|
|
return null;
|
|
}
|
|
|
|
if(!isset($data['post_path'])) {
|
|
echo "ERROR, trying to get a post data package without path!";
|
|
die();
|
|
}
|
|
|
|
$outdata = [];
|
|
foreach($this::SQL_READ_COLUMNS as $key) {
|
|
if(isset($data['post_' . $key])) {
|
|
$outdata[$key] = $data['post_' . $key];
|
|
}
|
|
}
|
|
|
|
$post_metadata = json_decode($data['post_metadata'] ?? '{}', true);
|
|
|
|
$post_settings = [];
|
|
|
|
if(isset($data['post_settings_cache'])) {
|
|
$post_settings = json_decode($data['post_settings_cache'], true);
|
|
}
|
|
else {
|
|
$post_settings = $this->get_post_settings($data['post_path']);
|
|
}
|
|
|
|
$outdata = array_merge($post_settings, $post_metadata, $outdata);
|
|
|
|
return $outdata;
|
|
}
|
|
|
|
public function get_postdata($path) {
|
|
$path = sanitize_post_path($path);
|
|
|
|
$qry = "
|
|
SELECT *
|
|
FROM posts
|
|
WHERE post_path = ? AND host = ?;
|
|
";
|
|
|
|
$data = $this->_exec($qry, "ss", $path, $this->hostname)->fetch_assoc();
|
|
|
|
return $this->process_postdata($data);
|
|
}
|
|
|
|
public function get_post_children($path,
|
|
$limit = 50, $depth_start = 1, $depth_end = 1,
|
|
$order_by = 'path') {
|
|
|
|
$path = sanitize_post_path($path);
|
|
|
|
$path_depth = substr_count($path, "/");
|
|
|
|
$allowed_ordering = [
|
|
'path' => true,
|
|
'path DESC' => true,
|
|
'created_at' => true,
|
|
'created_at DESC' => true,
|
|
'modified_at' => true,
|
|
'modified_at DESC' => true
|
|
];
|
|
|
|
if(!isset($allowed_ordering[$order_by])) {
|
|
throw new Exception('Children ordering not allowed');
|
|
}
|
|
$order_by = 'post_' . $order_by;
|
|
|
|
if($this->debugging) {
|
|
echo "-> GPC: Getting children for path " . $path;
|
|
}
|
|
|
|
$qry = "
|
|
SELECT *
|
|
FROM posts
|
|
WHERE post_path_depth BETWEEN ? AND ?
|
|
AND post_path LIKE ?
|
|
ORDER BY " . $order_by .
|
|
" LIMIT ?";
|
|
|
|
$data = $this->_exec($qry, "iisi",
|
|
$path_depth + $depth_start, $path_depth + $depth_end,
|
|
$path.'/%', $limit
|
|
)->fetch_all(MYSQLI_ASSOC);
|
|
$outdata = [];
|
|
|
|
foreach($data AS $post_element) {
|
|
$outdata[$post_element['post_path']] =
|
|
$this->process_postdata($post_element);
|
|
}
|
|
|
|
return $outdata;
|
|
}
|
|
|
|
public function get_post_markdown($id) {
|
|
$qry =
|
|
"SELECT post_markdown
|
|
FROM post_markdown
|
|
WHERE post_id = ?
|
|
";
|
|
|
|
$data = $this->_exec($qry, "i", $id)->fetch_assoc();
|
|
|
|
if(!isset($data)) {
|
|
return "";
|
|
}
|
|
|
|
return $data['post_markdown'];
|
|
}
|
|
}
|
|
|
|
?>
|