feat(yaps): ✨ first lifesigns of the yapping system
Some checks reported warnings
/ phplint (push) Has been cancelled
Some checks reported warnings
/ phplint (push) Has been cancelled
This commit is contained in:
parent
143c932c88
commit
f8af3c5c59
7 changed files with 321 additions and 17 deletions
|
@ -55,32 +55,33 @@ CREATE TABLE dev_post_markdown (
|
||||||
FULLTEXT(post_markdown)
|
FULLTEXT(post_markdown)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE dev_feeds (
|
CREATE TABLE dev_yaps (
|
||||||
post_id INTEGER NOT NULL,
|
yap_id INTEGER AUTO_INCREMENT,
|
||||||
feed_key VARCHAR(32),
|
PRIMARY KEY(yap_id),
|
||||||
feed_id VARCHAR(40) NOT NULL,
|
|
||||||
|
|
||||||
feed_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
post_path VARCHAR(255) NOT NULL,
|
||||||
|
yap_category VARCHAR(32) NOT NULL,
|
||||||
|
yap_tag VARCHAR(40) NOT NULL,
|
||||||
|
|
||||||
feed_metadata JSON DEFAULT NULL,
|
-- Uniqueness detection based on associated path, category and tag
|
||||||
|
yap_hash CHAR(32) AS (MD5(CONCAT(post_path, yap_category, yap_tag))),
|
||||||
|
CONSTRAINT YAPS_UNIQUE UNIQUE(yap_hash),
|
||||||
|
|
||||||
feed_text TEXT,
|
yap_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
yap_metadata JSON DEFAULT NULL,
|
||||||
|
|
||||||
-- Primary key as true ID to allow for deterministic saving/recreating
|
yap_text TEXT,
|
||||||
CONSTRAINT PK_FEED PRIMARY KEY(post_id, feed_key, feed_id),
|
|
||||||
FOREIGN KEY(post_id) REFERENCES dev_posts(post_id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
|
|
||||||
-- Make it possible to look up changes from e.g. a commit hash inexpensively
|
-- Make it possible to look up changes from e.g. a commit hash inexpensively
|
||||||
INDEX(feed_id),
|
INDEX(yap_tag),
|
||||||
-- Make it possible to look up specific post feeds efficiently (e.g. changelog)
|
-- Make it possible to look up specific post feeds efficiently (e.g. changelog)
|
||||||
INDEX(post_id, feed_key, feed_created_at),
|
INDEX(post_path, yap_category, yap_created_at),
|
||||||
-- Make it possible to globally look up specific feeds efficiently
|
-- Make it possible to globally look up specific feeds efficiently
|
||||||
INDEX(feed_key, feed_created_at),
|
INDEX(yap_category, yap_created_at),
|
||||||
-- And just in general, make searching feeds in a timeframe efficient
|
-- And just in general, make searching feeds in a timeframe efficient
|
||||||
INDEX(feed_created_at),
|
INDEX(yap_created_at),
|
||||||
|
|
||||||
FULLTEXT(feed_text)
|
FULLTEXT(yap_text)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE analytics_summations (
|
CREATE TABLE analytics_summations (
|
||||||
|
|
158
www/src/db_handler/mysql_yaps_handler.php
Normal file
158
www/src/db_handler/mysql_yaps_handler.php
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once 'db_interface.php';
|
||||||
|
require_once 'yaps_interface.php';
|
||||||
|
|
||||||
|
class MySQL_YapsHandler
|
||||||
|
implements YapsInterface {
|
||||||
|
|
||||||
|
private $debugging;
|
||||||
|
|
||||||
|
private $sql_connection;
|
||||||
|
private $hostname;
|
||||||
|
private $db_prefix;
|
||||||
|
|
||||||
|
function __construct($sql_connection, $hostname, $db_prefix) {
|
||||||
|
$this->sql_connection = $sql_connection;
|
||||||
|
$this->hostname = $hostname;
|
||||||
|
$this->db_prefix = $db_prefix;
|
||||||
|
|
||||||
|
$this->debugging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add_yap($yap_data_ext) {
|
||||||
|
# A little copy to prevent accidentally mutating
|
||||||
|
# external data
|
||||||
|
$yap_data = $yap_data_ext;
|
||||||
|
|
||||||
|
if(!isset($yap_data['tag'])) {
|
||||||
|
throw new Exception('Yap is missing a tag!');
|
||||||
|
}
|
||||||
|
if(!isset($yap_data['post'])) {
|
||||||
|
throw new Exception('Yap is missing a post!');
|
||||||
|
}
|
||||||
|
if(!isset($yap_data['category'])) {
|
||||||
|
throw new Exception('Yap is missing a category!');
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump($yap_data);
|
||||||
|
|
||||||
|
$yap_tag = $yap_data['tag'];
|
||||||
|
unset($yap_data['tag']);
|
||||||
|
|
||||||
|
$yap_post = $yap_data['post'];
|
||||||
|
unset($yap_data['post']);
|
||||||
|
$yap_post = sanitize_post_path($yap_post);
|
||||||
|
|
||||||
|
$yap_cateogry = $yap_data['category'];
|
||||||
|
unset($yap_data['category']);
|
||||||
|
|
||||||
|
$yap_text = $yap_data['text'] ?? null;
|
||||||
|
unset($yap_data['text']);
|
||||||
|
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
|
||||||
|
$yap_created_at = $yap_data['created_at'] ?? null;
|
||||||
|
unset($yap_data['created_at']);
|
||||||
|
|
||||||
|
$yap_data = json_encode($yap_data);
|
||||||
|
|
||||||
|
$qry = "
|
||||||
|
INSERT INTO {$this->db_prefix}_yaps
|
||||||
|
(`post_path`, `yap_category`, `yap_tag`,
|
||||||
|
`yap_created_at`, `yap_metadata`, `yap_text` )
|
||||||
|
VALUES ( ?, ?, ?,
|
||||||
|
COALESCE(?, NOW()), ?, ? ) AS new
|
||||||
|
|
||||||
|
ON DUPLICATE KEY
|
||||||
|
UPDATE
|
||||||
|
{$this->db_prefix}_yaps.yap_metadata = new.yap_metadata,
|
||||||
|
{$this->db_prefix}_yaps.yap_text = new.yap_text;
|
||||||
|
";
|
||||||
|
|
||||||
|
$this->_exec($qry,
|
||||||
|
"ssssss",
|
||||||
|
$yap_post, $yap_cateogry, $yap_tag,
|
||||||
|
$yap_created_at, $yap_data, $yap_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_yaplist($args = []) {
|
||||||
|
|
||||||
|
$category = $args['category'] ?? null;
|
||||||
|
$post = $args['post'] ?? $args['path'] ?? null;
|
||||||
|
$tag = $args['tag'] ?? null;
|
||||||
|
$before = $args['before'] ?? null;
|
||||||
|
$limit = $args['limit'] ?? null;
|
||||||
|
|
||||||
|
|
||||||
|
$qry_where = [];
|
||||||
|
$qry_where_data = [];
|
||||||
|
$qry_where_types = '';
|
||||||
|
|
||||||
|
if(isset($category)) {
|
||||||
|
if($category[-1] = '%') {
|
||||||
|
$qry_where []= 'yap_category LIKE ?';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$qry_where []= 'yap_category = ?';
|
||||||
|
}
|
||||||
|
$qry_where_data []= $category;
|
||||||
|
$qry_where_types .= 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($post)) {
|
||||||
|
if($post[-1] = '%') {
|
||||||
|
$qry_where []= 'post_path LIKE ?';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$qry_where []= 'post_path = ?';
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry_where_data []= $post;
|
||||||
|
$qry_where_types .= 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($tag)) {
|
||||||
|
$qry_where []= 'yap_tag = ?';
|
||||||
|
$qry_where_data [] = $tag;
|
||||||
|
$qry_where_types .= 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($before)) {
|
||||||
|
$qry_where []= 'yap_created_at < ?';
|
||||||
|
$qry_where_data []= $before;
|
||||||
|
$qry_where_types .= 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
$limit = min(max(intval($limit ?? 30), 0), 30);
|
||||||
|
|
||||||
|
$qry_where []= 'TRUE';
|
||||||
|
|
||||||
|
$qry = "
|
||||||
|
SELECT *
|
||||||
|
FROM {$this->db_prefix}_yaps
|
||||||
|
WHERE
|
||||||
|
" . implode(' AND ', $qry_where) .
|
||||||
|
" ORDER BY yap_created_at DESC
|
||||||
|
LIMIT ?";
|
||||||
|
|
||||||
|
$yaplist = $this->_exec($qry,
|
||||||
|
$qry_where_types . "i",
|
||||||
|
...array_merge($qry_where_data, [$limit]))->fetch_all(MYSQLI_ASSOC);
|
||||||
|
|
||||||
|
return $yaplist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
68
www/src/db_handler/yaps_interface.php
Normal file
68
www/src/db_handler/yaps_interface.php
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
interface YapsInterface {
|
||||||
|
/* Yaps format:
|
||||||
|
*
|
||||||
|
* The yaps are even simpler versions of Postdata,
|
||||||
|
* as they will have almost no defined metadata.
|
||||||
|
*
|
||||||
|
* The purpose of yaps is to provide very lightweight
|
||||||
|
* additions to existing posts.
|
||||||
|
*
|
||||||
|
* The only properly defined fields are the following:
|
||||||
|
* - Relevant Post ID
|
||||||
|
* - yap category ("changelog", "comment", "build log", etc.)
|
||||||
|
* - yap tag/hash (will e.g. use the Git Commit Hash for changelogs)
|
||||||
|
* - yap creation time
|
||||||
|
* - yap text
|
||||||
|
* - other yap data (free-form :D)
|
||||||
|
*
|
||||||
|
* There will be little to no post-processing by the
|
||||||
|
* database, as a lot is somewhat free-form.
|
||||||
|
* No settings, no caching, etc.
|
||||||
|
*
|
||||||
|
* Instead, searching yaps will be a fair bit more
|
||||||
|
* important, as they act as a lot of the feeds!
|
||||||
|
* RSS/Atom will run over "changelog" yaps, and changelogs
|
||||||
|
* will be displayed in most posts when present.
|
||||||
|
* The system might also serve user comments, as well as
|
||||||
|
* a posting alternative to social media feeds...
|
||||||
|
* As well as a few WebMentions comments ^^'
|
||||||
|
*
|
||||||
|
* Oh yes, Yaps will always be ordered by time, descending.
|
||||||
|
* It's feeds :P
|
||||||
|
* */
|
||||||
|
|
||||||
|
/* Adds a yap, or overwrites/updates an existing one
|
||||||
|
* The yap-data MUST include the following:
|
||||||
|
* - post (string path!)
|
||||||
|
* - tag
|
||||||
|
* - category
|
||||||
|
*
|
||||||
|
* And may include:
|
||||||
|
* - text (which is added to yap_text)
|
||||||
|
* - created_at (if not set, NOW() is used)
|
||||||
|
*
|
||||||
|
* With other fields slapped into yap_metadata
|
||||||
|
*/
|
||||||
|
public function add_yap($yap_data);
|
||||||
|
|
||||||
|
/* Returns a list of yaps for the given filters.
|
||||||
|
* - Category must always be set, but may be a "LIKE" wildcard
|
||||||
|
* - post *may* be set. If set, only yaps for that given post are returned
|
||||||
|
* - $since, which, if set and a valid timestamp, will return yaps after that time
|
||||||
|
* - $limit, which should be obvious
|
||||||
|
*
|
||||||
|
* This function always returns an array of yaps, chronologically ordered.
|
||||||
|
*
|
||||||
|
* The following search args are supported:
|
||||||
|
* - category: String, matched to the Yap Category. Can have a trailing % for LIKE search
|
||||||
|
* - post: String, post path to look for. Can have a trailing % for LIKE search
|
||||||
|
* - tag: Yap tag to look for.
|
||||||
|
* - before: Datetime string. Shows yaps before the given date
|
||||||
|
* - limit: Limit the number of returned yaps
|
||||||
|
*/
|
||||||
|
public function get_yaplist($args);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -36,6 +36,37 @@ switch($API_FUNCTION) {
|
||||||
|
|
||||||
echo $analytics_adapter->pop_analytics($delete = true);
|
echo $analytics_adapter->pop_analytics($delete = true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'db_yaps':
|
||||||
|
echo json_encode($yap_adapter->get_yaplist($_GET));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'add_yap':
|
||||||
|
if( !isset($_POST['path'])
|
||||||
|
or !isset($_POST['yap_category'])
|
||||||
|
or !isset($_POST['yap_text'])) {
|
||||||
|
echo json_encode([
|
||||||
|
'status' => 'Missing paramters (must POST path, category and a text!)'
|
||||||
|
]);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
$yap_data = [
|
||||||
|
'post' => $_POST['path'],
|
||||||
|
'category' => $_POST['yap_category'],
|
||||||
|
'text' => $_POST['yap_text']
|
||||||
|
];
|
||||||
|
|
||||||
|
$yap_data['tag'] = MD5($_POST['path'] . microtime());
|
||||||
|
|
||||||
|
$yap_adapter->add_yap($yap_data);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'status' => '200 OK',
|
||||||
|
'yap' => $yap_data
|
||||||
|
]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case 'upload':
|
case 'upload':
|
||||||
if(!access_can_upload()) {
|
if(!access_can_upload()) {
|
||||||
|
|
|
@ -54,10 +54,15 @@ function render_post($post, $args = []) {
|
||||||
$args);
|
$args);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($REQUEST_PATH == '/upload') {
|
if($REQUEST_PATH == '/upload.php') {
|
||||||
render_root_template('upload.html');
|
render_root_template('upload.html');
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
|
if($REQUEST_PATH == '/yap.php') {
|
||||||
|
render_root_template('_dev_add_yap.html');
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
if($REQUEST_PATH == '/search/') {
|
if($REQUEST_PATH == '/search/') {
|
||||||
$search_results = [];
|
$search_results = [];
|
||||||
$display_type = 'none';
|
$display_type = 'none';
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
require_once 'db_handler/mysql_handler.php';
|
require_once 'db_handler/mysql_handler.php';
|
||||||
require_once 'db_handler/mysql_analytics_handler.php';
|
require_once 'db_handler/mysql_analytics_handler.php';
|
||||||
|
require_once 'db_handler/mysql_yaps_handler.php';
|
||||||
|
|
||||||
require_once 'db_handler/post_handler.php';
|
require_once 'db_handler/post_handler.php';
|
||||||
|
|
||||||
$db_params = $SITE_CONFIG['db'];
|
$db_params = $SITE_CONFIG['db'];
|
||||||
|
@ -32,6 +34,9 @@ $sql_adapter = new MySQLHandler($db_connection,
|
||||||
|
|
||||||
$analytics_adapter = new MySQLAnalyticsHandler($db_connection,
|
$analytics_adapter = new MySQLAnalyticsHandler($db_connection,
|
||||||
$SITE_CONFIG['site_defaults']['uri_prefix']);
|
$SITE_CONFIG['site_defaults']['uri_prefix']);
|
||||||
|
$yap_adapter = new MySQL_YapsHandler($db_connection,
|
||||||
|
$SITE_CONFIG['site_defaults']['uri_prefix'],
|
||||||
|
$db_params['prefix']);
|
||||||
|
|
||||||
$adapter = new PostHandler($sql_adapter);
|
$adapter = new PostHandler($sql_adapter);
|
||||||
|
|
||||||
|
|
36
www/templates/_dev_add_yap.html
Normal file
36
www/templates/_dev_add_yap.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
|
||||||
|
{% extends "root.html" %}
|
||||||
|
|
||||||
|
{% block second_title %}
|
||||||
|
<h2> YAP SOMETHIN' </h2>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{%block main_content%}
|
||||||
|
<article>
|
||||||
|
<form method="post" enctype="multipart/form-data"
|
||||||
|
action="/api/add_yap"
|
||||||
|
hx-target="#yap-result"
|
||||||
|
hx-swap="innerHTML">
|
||||||
|
|
||||||
|
<label for="ACCESS_KEY"> Password: </label>
|
||||||
|
<input type="password" id="ACCESS_KEY" name="ACCESS_KEY"/>
|
||||||
|
|
||||||
|
<label for="path"> Path: </label>
|
||||||
|
<input type="text" id="path" name="path"/>
|
||||||
|
<label for="yap_category"> Yap Category: </label>
|
||||||
|
<input type="text" id="yap_category" name="yap_category"
|
||||||
|
value="yap"/>
|
||||||
|
|
||||||
|
<textarea type="file" id="yap_text" name="yap_text">
|
||||||
|
</textarea>
|
||||||
|
|
||||||
|
<button>Submit</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
Return data:
|
||||||
|
<code id="yap-result">
|
||||||
|
|
||||||
|
</code>
|
||||||
|
</article>
|
||||||
|
{%endblock%}
|
Loading…
Add table
Add a link
Reference in a new issue