You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
564 lines
20 KiB
564 lines
20 KiB
<?php
|
|
/*
|
|
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
|
| @file ./system/class.comments.php
|
|
| @author SamBrishes <sam@pytes.net>
|
|
| @version 0.1.2 [0.1.0] - Alpha
|
|
|
|
|
| @website https://github.com/pytesNET/snicker
|
|
| @license X11 / MIT License
|
|
| @copyright Copyright © 2019 SamBrishes, pytesNET <info@pytes.net>
|
|
*/
|
|
if(!defined("BLUDIT")){ die("Go directly to Jail. Do not pass Go. Do not collect 200 Cookies!"); }
|
|
|
|
class Comments extends dbJSON{
|
|
/*
|
|
| PAGE UUID
|
|
*/
|
|
protected $uuid;
|
|
|
|
/*
|
|
| DATABASE FIELDS
|
|
*/
|
|
protected $dbFields = array(
|
|
"type" => "comment", // Comment Type ("comment", "reply", "pingback")
|
|
"depth" => 1, // Comment Depth (starting with 1)
|
|
"title" => "", // Comment Title
|
|
"status" => "", // Comment Status ("pending", "approved", "rejected", "spam")
|
|
"comment" => "", // Comment Content
|
|
"rating" => [0, 0], // Comment Rating
|
|
"page_uuid" => "", // Comment Page UUID
|
|
"parent_uid" => "", // Comment Parent UID
|
|
|
|
"author" => "", // Comment Author (bludt::username or guest::uuid)
|
|
"subscribe" => false, // eMail Subscription
|
|
|
|
"date" => "", // Date Comment Written
|
|
"dateModified" => "", // Date Comment Modified
|
|
"dateAudit" => "", // Date Comment Audit
|
|
"custom" => array(), // Custom Data
|
|
);
|
|
|
|
/*
|
|
| CONSTRUCTOR
|
|
| @since 0.1.0
|
|
|
|
|
| @param string The UUID of the respective page.
|
|
*/
|
|
public function __construct($uuid){
|
|
global $pages;
|
|
|
|
// Get Page
|
|
if($pages->getByUUID($uuid) === false){
|
|
$error = "The Page UUID couldn't be found in the database [{$uuid}]";
|
|
Log::set(__METHOD__ . LOG_SEP . $error);
|
|
throw new Exception($error);
|
|
}
|
|
$this->uuid = $uuid;
|
|
parent::__construct(DB_SNICKER_COMMENTS . "comments-{$uuid}.php");
|
|
}
|
|
|
|
/*
|
|
| HELPER :: FILL LOG FILE
|
|
| @since 0.1.0
|
|
|
|
|
| @param string The respective method for the log (Use __METHOD__)
|
|
| @param string The respective error message to be logged.
|
|
| @param array Additional values AS array for the `vsprintf` function.
|
|
*/
|
|
private function log($method, $string, $args){
|
|
$strings = array(
|
|
"error-comment-uid" => "The comment UID is invalid or does not exist [%s]",
|
|
"error-page-uuid" => "The page uuid is invalid or does not exist [%s]",
|
|
"error-create-dir" => "The comment directory could not be created [%s]",
|
|
"error-create-file" => "The comment file could not be created [%s]",
|
|
"error-comment-file" => "The comment file does not exist [%s]",
|
|
"error-comment-update" => "The comment file could not be updated [%s]",
|
|
"error-comment-remove" => "The comment file could not be deleted [%s]",
|
|
"error-update-db" => "The comment database could not be updated"
|
|
);
|
|
if(array_key_exists($string, $strings)){
|
|
$string = $strings[$string];
|
|
}
|
|
Log::set($method . LOG_SEP . vsprintf("Error occured: {$string}", $args), LOG_TYPE_ERROR);
|
|
}
|
|
|
|
/*
|
|
| HELPER :: GENERATE UNIQUE COMMENT ID
|
|
| @since 0.1.0
|
|
*/
|
|
private function generateUID(){
|
|
if(function_exists("random_bytes")){
|
|
return md5(bin2hex(random_bytes(16)) . time());
|
|
} else if(function_exists("openssl_random_pseudo_bytes")){
|
|
return md5(bin2hex(openssl_random_pseudo_bytes(16)) . time());
|
|
}
|
|
return md5(uniqid() . time());
|
|
}
|
|
|
|
|
|
/*
|
|
| PUBLIC :: GET DEFAULT FIELDS
|
|
| @since 0.1.0
|
|
|
|
|
| @return array An array with all default fields and values per entry.
|
|
*/
|
|
public function getDefaultFields(){
|
|
return $this->dbFields;
|
|
}
|
|
|
|
|
|
/*
|
|
| DATA :: GET DATABASE
|
|
| @since 0.1.0
|
|
|
|
|
| @param bool TRUE to just return the keys, FALSE to return the complete DB.
|
|
|
|
|
| @return array The complete database entries (or keys) within an ARRAY.
|
|
*/
|
|
public function getDB($keys = true){
|
|
return ($keys)? array_keys($this->db): $this->db;
|
|
}
|
|
|
|
/*
|
|
| DATA :: CHECK IF COMMENT ITEM EXISTS
|
|
| @since 0.1.0
|
|
|
|
|
| @param string The unique comment ID.
|
|
|
|
|
| @return bool TRUE if the comment ID exists, FALSE if not.
|
|
*/
|
|
public function exists($uid){
|
|
return isset($this->db[$uid]);
|
|
}
|
|
|
|
/*
|
|
| DATA :: GET COMMENT ITEM
|
|
| @since 0.1.0
|
|
|
|
|
| @param string The unique comment ID.
|
|
|
|
|
| @return array The comment data array on success, FALSE on failure.
|
|
*/
|
|
public function getCommentDB($uid){
|
|
return ($this->exists($uid))? $this->db[$uid]: false;
|
|
}
|
|
|
|
/*
|
|
| DATA :: LIST COMMENTS
|
|
| @since 0.1.0
|
|
|
|
|
| @param int The current comment page number, starting with 1.
|
|
| @param int The number of comments to be shown per page.
|
|
| @param multi The desired comment type as STRING, multiple as ARRAY.
|
|
| Pass `null` to get each comment type.
|
|
| @param multi The desired comment status as STRING, multiple as ARRAY.
|
|
| Pass `null` to get each comment status.
|
|
|
|
|
| @return array The respective database keys with an ARRAY or FALSE on failure.
|
|
*/
|
|
public function getList($page, $limit, $type = array("comment", "reply"), $status = array("approved")){
|
|
$type = is_string($type)? array($type): $type;
|
|
if(!is_array($type)){
|
|
$type = null;
|
|
}
|
|
|
|
$status = is_string($status)? array($status): $status;
|
|
if(!is_array($status)){
|
|
$type = null;
|
|
}
|
|
|
|
// Format List
|
|
$list = array();
|
|
foreach($this->db AS $key => $fields){
|
|
if($type !== null && !in_array($fields["type"], $type)){
|
|
continue;
|
|
}
|
|
if($status !== null && !in_array($fields["status"], $status)){
|
|
continue;
|
|
}
|
|
array_push($list, $key);
|
|
}
|
|
|
|
// Limit
|
|
if($limit == -1){
|
|
return $list;
|
|
}
|
|
|
|
// Offset
|
|
$offset = $limit * ($page - 1);
|
|
$count = min(($offset + $limit - 1), count($list));
|
|
if($offset < 0 || $offset > $count){
|
|
return false;
|
|
}
|
|
return array_slice($list, $offset, $limit, true);
|
|
}
|
|
|
|
/*
|
|
| DATA :: GENERATE A DEPTH LIST
|
|
| @since 0.1.0
|
|
|
|
|
| @param int The current comment page number, starting with 1.
|
|
| @param int The number of comments to be shown per page.
|
|
| @param multi The desired comment type as STRING, multiple as ARRAY.
|
|
| Pass `null` to get each comment type.
|
|
| @param multi The desired comment status as STRING, multiple as ARRAY.
|
|
| Pass `null` to get each comment status.
|
|
|
|
|
| @return array The respective database keys with an ARRAY or FALSE on failure.
|
|
*/
|
|
public function getDepthList($page, $limit, $type = array("comment", "reply"), $status = array("approved")){
|
|
global $login, $SnickerUsers;
|
|
$this->sortBy();
|
|
|
|
// Validate Parameters
|
|
$type = is_string($type)? array($type): $type;
|
|
if(!is_array($type)){
|
|
$type = null;
|
|
}
|
|
$status = is_string($status)? array($status): $status;
|
|
if(!is_array($status)){
|
|
$type = null;
|
|
}
|
|
|
|
// Get User Pending
|
|
if(in_array("pending", $status)){
|
|
if(!is_a($login, "Login")){
|
|
$login = new Login();
|
|
}
|
|
if($login->isLogged()){
|
|
$user = "bludit::" . $login->username();
|
|
} else {
|
|
if(($user = $SnickerUsers->getCurrent()) !== false){
|
|
$user = "guest::" . $user;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Format List
|
|
$list = array();
|
|
$children = array();
|
|
foreach($this->db AS $key => $fields){
|
|
if($type !== null && !in_array($fields["type"], $type)){
|
|
continue;
|
|
}
|
|
if($status !== null && !in_array($fields["status"], $status)){
|
|
continue;
|
|
}
|
|
if($fields["status"] === "pending" && $fields["author"] !== $user){
|
|
continue;
|
|
}
|
|
|
|
if(!empty($fields["parent_uid"])){
|
|
if(!array_key_exists($fields["parent_uid"], $children)){
|
|
$children[$fields["parent_uid"]] = array();
|
|
}
|
|
array_push($children[$fields["parent_uid"]], $key);
|
|
} else {
|
|
array_push($list, $key);
|
|
}
|
|
}
|
|
|
|
// Offset
|
|
$count = 0;
|
|
$offset = $limit * ($page - 1);
|
|
for(; $count < $offset ;){
|
|
$key = array_shift($list);
|
|
|
|
$count++;
|
|
if(array_key_exists($key, $children)){
|
|
$count += count($children[$key]);
|
|
unset($children[$key]);
|
|
}
|
|
}
|
|
|
|
// Generator
|
|
$count = 0;
|
|
foreach($list AS $key){
|
|
if($count >= $limit){
|
|
break;
|
|
}
|
|
|
|
$count++;
|
|
yield $key;
|
|
if(!array_key_exists($key, $children)){
|
|
continue;
|
|
}
|
|
|
|
$loop = $key;
|
|
$depth = array();
|
|
while(true){
|
|
if(empty($depth) && empty($children[$key])){
|
|
break;
|
|
}
|
|
if(array_key_exists($loop, $children) && count($children[$loop]) > 0){
|
|
array_push($depth, $loop);
|
|
$loop = array_shift($children[$loop]);
|
|
$count++;
|
|
yield $loop;
|
|
} else {
|
|
$loop = array_pop($depth);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
| DATA :: COUNT COMMENTS
|
|
| @since 0.1.0
|
|
|
|
|
| @param multi The desired comment type as STRING, multiple as ARRAY.
|
|
| Pass `null` to get each comment type.
|
|
|
|
|
| @return int The total number of comments.
|
|
*/
|
|
public function count($type = array("comment", "reply")){
|
|
$type = is_string($type)? array($type): $type;
|
|
if(!is_array($type)){
|
|
$type = null;
|
|
}
|
|
|
|
// Count All
|
|
if($type === null){
|
|
return count($this->db);
|
|
}
|
|
|
|
// Count
|
|
$count = 0;
|
|
foreach($this->db AS $key => $fields){
|
|
if(!in_array($fields["type"], $type)){
|
|
continue;
|
|
}
|
|
$count++;
|
|
}
|
|
return $count;
|
|
}
|
|
|
|
|
|
/*
|
|
| HANDLE :: ADD A NEW COMMENT
|
|
| @since 0.1.0
|
|
|
|
|
| @param array The respective comment array.
|
|
|
|
|
| @return multi The comment UID on success, FALSE on failure.
|
|
*/
|
|
public function add($args){
|
|
global $SnickerIndex, $SnickerUsers;
|
|
|
|
// Loop Default Fields
|
|
$row = array();
|
|
foreach($this->dbFields AS $field => $value){
|
|
if(isset($args[$field])){
|
|
$final = $args[$field];
|
|
} else {
|
|
$final = $value;
|
|
}
|
|
settype($final, gettype($value));
|
|
$row[$field] = $final;
|
|
}
|
|
|
|
// Create (U)UIDs
|
|
$uid = $this->generateUID();
|
|
$row["page_uuid"] = $this->uuid;
|
|
|
|
// Validate Parent UID
|
|
if(!empty($row["parent_uid"]) && !$this->exists($row["parent_uid"])){
|
|
$row["parent_uid"] = null;
|
|
}
|
|
|
|
// Validate Type and Depth
|
|
if(!empty($row["parent_uid"])){
|
|
$row["type"] = "reply";
|
|
$row["depth"] = $this->db[$row["parent_uid"]]["depth"] + 1;
|
|
} else {
|
|
$row["type"] = "comment";
|
|
$row["depth"] = 1;
|
|
}
|
|
|
|
// Validata Status
|
|
if(!in_array($row["status"], array("pending", "approved", "rejected", "spam"))){
|
|
$row["status"] = "pending";
|
|
}
|
|
|
|
// Sanitize Strings
|
|
$row["title"] = Sanitize::html(strip_tags($row["title"]));
|
|
$row["author"] = Sanitize::html($row["author"]);
|
|
|
|
// Sanitize Comment
|
|
$allowed = "<a><b><strong><i><em><u><del><ins><s><strike><p><br><br/><br />";
|
|
$allowed .= "<mark><abbr><acronym><dfn><ul><ol><li><dl><dt><dd><hr><hr/><hr />";
|
|
if(sn_config("comment_markup_html")){
|
|
$row["comment"] = strip_tags($row["comment"], $allowed);
|
|
} else {
|
|
$row["comment"] = strip_tags($row["comment"]);
|
|
}
|
|
$row["comment"] = Sanitize::html($row["comment"]);
|
|
|
|
// Validate Comment
|
|
$limit = sn_config("comment_limit");
|
|
if($limit > 0 && strlen($row["comment"]) > $limit){
|
|
$row["comment"] = substr($row["comment"], 0, $limit);
|
|
}
|
|
|
|
// Set Static Data
|
|
$row["rating"] = array(0, 0);
|
|
$row["subscribe"] = $row["subscribe"] === true;
|
|
$row["date"] = Date::current(DB_DATE_FORMAT);
|
|
$row["dateModified"] = "";
|
|
if($row["status"] !== "pending"){
|
|
$row["dateAudit"] = Date::current(DB_DATE_FORMAT);
|
|
}
|
|
|
|
// Add Index
|
|
if(!is_a($SnickerIndex, "CommentsIndex")){
|
|
$SnickerIndex = new CommentsIndex();
|
|
}
|
|
if(!$SnickerIndex->add($uid, $row)){
|
|
return false;
|
|
}
|
|
if(strpos($row["author"], "guest::") === 0){
|
|
$SnickerUsers->addComment(substr($row["author"], strlen("guest::")), $uid);
|
|
}
|
|
|
|
// Insert Comment
|
|
$this->db[$uid] = $row;
|
|
$this->sortBy();
|
|
if($this->save() !== true){
|
|
Log::set(__METHOD__, "error-update-db");
|
|
return false;
|
|
}
|
|
return $uid;
|
|
}
|
|
|
|
/*
|
|
| HANDLE :: EDIT AN EXISTING COMMENT
|
|
| @since 0.1.0
|
|
|
|
|
| @param string The unique comment ID as STRING.
|
|
| @param array The respective comment data, whcih you want to update.
|
|
|
|
|
| @return multi The comment UID on success, FALSE on failure.
|
|
*/
|
|
public function edit($uid, $args){
|
|
global $SnickerIndex;
|
|
|
|
// Loop Default Fields
|
|
$row = array();
|
|
foreach($this->dbFields AS $field => $value){
|
|
if(isset($args[$field])){
|
|
$final = is_string($args[$field])? Sanitize::html($args[$field]): $args[$field];
|
|
} else {
|
|
$final = $this->db[$uid][$field];
|
|
}
|
|
settype($final, gettype($value));
|
|
$row[$field] = $final;
|
|
}
|
|
|
|
// Create / Check (U)UIDs
|
|
if(!$this->exists($uid)){
|
|
$this->log(__METHOD__, "error-comment-uid", array($uid));
|
|
return false;
|
|
}
|
|
$row["page_uuid"] = $this->uuid;
|
|
|
|
// Validate Parent UID
|
|
if(!empty($row["parent_uid"]) && !$this->exists($row["parent_uid"])){
|
|
$row["parent_uid"] = $data["parent_uid"];
|
|
}
|
|
|
|
// Validate Type and Depth
|
|
if(!empty($row["parent_uid"])){
|
|
$row["type"] = "reply";
|
|
$row["depth"] = $this->db[$row["parent_uid"]]["depth"] + 1;
|
|
} else {
|
|
$row["type"] = "comment";
|
|
$row["depth"] = 1;
|
|
}
|
|
|
|
// Validata Status
|
|
if(!in_array($row["status"], array("pending", "approved", "rejected", "spam"))){
|
|
$row["status"] = $this->db[$uid]["status"];
|
|
}
|
|
|
|
// Sanitize Strings
|
|
$row["title"] = Sanitize::html($row["title"]);
|
|
$row["comment"] = Sanitize::html($row["comment"]);
|
|
$row["author"] = Sanitize::html($row["author"]);
|
|
|
|
// Set Static Data
|
|
$row["subscribe"] = $row["subscribe"] === true;
|
|
$row["dateModified"] = Date::current(DB_DATE_FORMAT);
|
|
if($row["status"] !== $this->db[$uid]["status"]){
|
|
$row["dateAudit"] = Date::current(DB_DATE_FORMAT);
|
|
}
|
|
|
|
// Update Index
|
|
if(!$SnickerIndex->edit($uid, $row)){
|
|
return false;
|
|
}
|
|
|
|
// Update and Return
|
|
$this->db[$uid] = $row;
|
|
$this->sortBy();
|
|
if($this->save() !== true){
|
|
Log::set(__METHOD__, "error-update-db");
|
|
return false;
|
|
}
|
|
return $uid;
|
|
}
|
|
|
|
/*
|
|
| HANDLE :: DELETE AN EXISTING COMMENT
|
|
| @since 0.1.0
|
|
|
|
|
| @param array The respective comment UID to delete.
|
|
|
|
|
| @return bool TRUE on success, FALSE on failure.
|
|
*/
|
|
public function delete($uid){
|
|
global $SnickerIndex, $SnickerUsers;
|
|
if(!isset($this->db[$uid])){
|
|
return false;
|
|
}
|
|
$row = $this->db[$uid];
|
|
|
|
// Remove Index
|
|
if(!$SnickerIndex->delete($uid)){
|
|
return false;
|
|
}
|
|
if(strpos($row["author"], "guest::") === 0){
|
|
$SnickerUsers->deleteComment(substr($row["author"], strlen("guest::")), $uid);
|
|
}
|
|
|
|
// Remove Database Item
|
|
unset($this->db[$uid]);
|
|
if($this->save() !== true){
|
|
Log::set(__METHOD__, "error-update-db");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
| INTERNAL :: SORT COMMENTS
|
|
| @since 0.1.0
|
|
|
|
|
| @return bool TRUE
|
|
*/
|
|
public function sortBy(){
|
|
global $SnickerPlugin;
|
|
|
|
if($SnickerPlugin->getValue("frontend_order") === "date_asc"){
|
|
uasort($this->db, function($a, $b){
|
|
return $a["date"] > $b["date"];
|
|
});
|
|
} else if($SnickerPlugin->getValue("frontend_order") === "date_desc"){
|
|
uasort($this->db, function($a, $b){
|
|
return $a["date"] < $b["date"];
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|