Addition of files
This commit is contained in:
30
CHANGELOG.md
Normal file
30
CHANGELOG.md
Normal file
@@ -0,0 +1,30 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 0.1.2 - Alpha
|
||||
---------------------
|
||||
- Bugfix: You can't comment, after you already comment (Error + Auto-Logout).
|
||||
- Bugfix: Translated strings didn't returned correctly (English is returned instead),
|
||||
Thansk to [#10](https://github.com/pytesNET/snicker/issues/10).
|
||||
- Bugfix: The "Terms of Use" checkbox couldn't be disabled.
|
||||
Thanks to [#16](https://github.com/pytesNET/snicker/issues/16).
|
||||
|
||||
Version 0.1.1 - Alpha
|
||||
---------------------
|
||||
- Add: The Persian Translation, many thanks to [abdulhalim](https://github.com/abdulhalim).
|
||||
- Add: The GD-less PureCaptcha library, written by Abbas Naderi, which doesn't use PHP GD.
|
||||
Thanks to [#8](https://github.com/pytesNET/snicker/issues/8)
|
||||
- Add: Reload function to the Captcha Image (Click on the image).
|
||||
Thanks to [#5](https://github.com/pytesNET/snicker/issues/5).
|
||||
- Add: A JS-Snippet which Enables/Disables the Gravatar Select Field.
|
||||
Thanks to [#9](https://github.com/pytesNET/snicker/issues/9).
|
||||
- Add: The Captcha and Terms note aren't shown for logged in users.
|
||||
Thanks to [#7](https://github.com/pytesNET/snicker/issues/7).
|
||||
- Bugfix: The `comments_per_page` option couldn't be set on 0 to disable the limit!
|
||||
Thanks to [#9](https://github.com/pytesNET/snicker/issues/9).
|
||||
- Bugfix: Reload the Captcha, if it was wrong.
|
||||
Thansk to [#5](https://github.com/pytesNET/snicker/issues/5).
|
||||
|
||||
Version 0.1.0 - Alpha
|
||||
---------------------
|
||||
- Initial Release owo/
|
||||
43
README.md
Normal file
43
README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
Snicker
|
||||
=======
|
||||
|
||||
Snicker is the first native FlatFile comment system for the Content Management System
|
||||
[Bludit](https://github.com/bludit/bludit). It allows to write and publish comments using basic
|
||||
HTML Syntax or Markdown. The Plugin also offers an extensive environment, many settings and
|
||||
possibilities and is also completely compliant with the GDPR!
|
||||
|
||||
Features
|
||||
--------
|
||||
- Level-Based, AJAX-enabled Commenting for Guests and Users
|
||||
- Many Configurations and adaptable Strings and Themes
|
||||
- Guest Management for Not-Logged-In Comment Authors
|
||||
- Moderatable Comments (Pending, Approved, Rejected, Spam)
|
||||
- Extensive Backend with many possibilities
|
||||
- Compliant with the European GDPR
|
||||
|
||||
Requirements
|
||||
------------
|
||||
- PHP v5.6.0+
|
||||
- Bludit v3.5.0+
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
- Snicker use the awesome [Captcha PHP Library](https://github.com/Gregwar/Captcha) made by Grégoire Passault
|
||||
- Snicker uses also the [PureCaptcha PHP Library](https://github.com/OWASP/PureCaptcha) as fallback by Abbas Naderi
|
||||
- The Avatars are served per default by [Gravatar](https://de.gravatar.com/), made by Automattic / WordPress
|
||||
- **But** you can also directly use [Identicons](http://identicon.net) instead...
|
||||
- ... where we use the [Identicon PHP Library](https://github.com/yzalis/Identicon) from Benjamin Laugueux
|
||||
- ... and the [Identicon JavaScript Library](https://github.com/stewartlord/identicon.js) from Stewart Lord
|
||||
- ... which itself depends on the [PNG JavaScript Library](https://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/) by Robert Eisele
|
||||
|
||||
Thanks for this awesome packages and projects!
|
||||
|
||||
Installation
|
||||
------------
|
||||
- Download the [Snicker Plugin](https://github.com/pytesNET/snicker/zipball/master)
|
||||
- Upload it to your `bl-plugins` folder of your Bludit Website
|
||||
- Visit the Bludit Administration and enable the "Snicker" Plugin through "Settings" > "Plugins"
|
||||
|
||||
Copyright & License
|
||||
-------------------
|
||||
Published under the MIT-License; Copyright © 2019 SamBrishes, pytesNET
|
||||
16
admin/add.php
Normal file
16
admin/add.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./admin/edit.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!"); }
|
||||
|
||||
// Add Formular 4 Admins
|
||||
|
||||
?>
|
||||
106
admin/css/admin.snicker.css
Normal file
106
admin/css/admin.snicker.css
Normal file
@@ -0,0 +1,106 @@
|
||||
@charset "UTF-8";
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./admin/css/admin.snicker.css
|
||||
| @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>
|
||||
*/
|
||||
|
||||
/* @start GENERAL */
|
||||
.shadow-sm-both{
|
||||
box-shadow: 0 0 0.25rem rgba(0,0,0,.075) !important;
|
||||
}
|
||||
.table-hover-light tr:hover{
|
||||
background-color: #f0f3f6;
|
||||
}
|
||||
/* @end GENERAL */
|
||||
|
||||
/* @start Admin Navigation */
|
||||
.nav.nav-pills .nav-link{
|
||||
color: #343a40;
|
||||
font-weight: 400;
|
||||
font-weight: 600;
|
||||
background-color: #e9ecef;
|
||||
transition: color 142ms linear, background 142ms linear;
|
||||
}
|
||||
.nav.nav-pills .nav-link:hover{
|
||||
background-color: #d9dcdf;
|
||||
}
|
||||
.nav.nav-pills .nav-link.active{
|
||||
color: #ffffff;
|
||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.75);
|
||||
background-color: #343a40;
|
||||
}
|
||||
.nav.nav-pills .nav-link .badge{
|
||||
margin-top: 3px;
|
||||
text-shadow: none;
|
||||
vertical-align: top;
|
||||
}
|
||||
.nav.nav-pills .nav-link.active .badge{
|
||||
text-shadow: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.nav.nav-pills .nav-link.nav-pending{
|
||||
border-top-right-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-pending.active{
|
||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25);
|
||||
background-color: #007bff;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-pending.active .badge{
|
||||
color: #007bff;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-approved{
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-approved.active{
|
||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25);
|
||||
background-color: #28A745;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-rejected{
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-rejected.active{
|
||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25);
|
||||
background-color: #FFC107;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-spam{
|
||||
border-top-left-radius: 0 !important;
|
||||
border-bottom-left-radius: 0 !important;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-spam.active{
|
||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25);
|
||||
background-color: #DC3545;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-spam.active .badge{
|
||||
color: #DC3545;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-config{
|
||||
font-weight: 400;
|
||||
}
|
||||
.nav.nav-pills .nav-link.nav-config .oi{
|
||||
margin-right: 3px;
|
||||
line-height: 1.5;
|
||||
vertical-align: top;
|
||||
}
|
||||
/* @end Admin Navigation */
|
||||
|
||||
/* @start Admin Pagination */
|
||||
.btn-group.btn-group-pagination .btn{
|
||||
width: 34px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 7px;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-group.btn-group-pagination .btn.disabled{
|
||||
cursor: not-allowed;
|
||||
}
|
||||
/* @end Admin Pagination */
|
||||
93
admin/edit.php
Normal file
93
admin/edit.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./admin/edit.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!"); }
|
||||
|
||||
global $login, $pages, $security, $SnickerIndex;
|
||||
|
||||
$data = $SnickerIndex->getComment($_GET["uid"]);
|
||||
$comment = new Comment($_GET["uid"], $data["page_uuid"]);
|
||||
$page = new Page($pages->getByUUID($data["page_uuid"]));
|
||||
|
||||
?><h2 class="mt-0 mb-3">
|
||||
<span class="oi oi-comment-square" style="font-size: 0.7em;"></span> Snicker <?php sn_e("Comments"); ?> / <?php sn_e("Edit"); ?>
|
||||
</h2>
|
||||
<form method="post" action="<?php echo HTML_PATH_ADMIN_ROOT; ?>snicker">
|
||||
<div class="card" style="margin: 1.5rem 0;">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<input type="hidden" id="tokenUser" name="tokenUser" value="<?php echo $login->username(); ?>" />
|
||||
<input type="hidden" id="tokenCSRF" name="tokenCSRF" value="<?php echo $security->getTokenCSRF(); ?>" />
|
||||
<input type="hidden" id="sn-action" name="action" value="snicker" />
|
||||
<input type="hidden" id="sn-snicker" name="snicker" value="edit" />
|
||||
<input type="hidden" id="sn-unique" name="uid" value="<?php echo $comment->uid(); ?>" />
|
||||
<button class="btn btn-primary" name="type" value="edit"><?php sn_e("Update Comment"); ?></button>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 text-right">
|
||||
<button class="btn btn-danger" name="type" value="delete"><?php sn_e("Delete Comment"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<input type="text" name="comment[title]" value="<?php echo $comment->title(); ?>"
|
||||
class="form-control form-control-lg" placeholder="<?php sn_e("Comment Title"); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<textarea name="comment[comment]" class="form-control" placeholder="<?php sn_e("Comment Text"); ?>"
|
||||
style="min-height: 275px;"><?php echo $comment->commentRaw(); ?></textarea>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><?php sn_e("Meta Settings"); ?></div>
|
||||
<div class="card-body">
|
||||
|
||||
<?php if(strpos($comment->getValue("author"), "bludit") === 0){ ?>
|
||||
<p>
|
||||
<input type="text" value="<?php echo $comment->username(); ?>" class="form-control" disabled />
|
||||
</p>
|
||||
<p>
|
||||
<input type="text" value="<?php sn_e("Registered User"); ?>" class="form-control" disabled />
|
||||
</p>
|
||||
<?php } else { ?>
|
||||
<p>
|
||||
<input type="text" name="comment[username]" value="<?php echo $comment->username(); ?>"
|
||||
class="form-control" placeholder="<?php sn_e("Comment Username"); ?>" />
|
||||
</p>
|
||||
<p>
|
||||
<input type="text" name="comment[email]" value="<?php echo $comment->email(); ?>"
|
||||
class="form-control" placeholder="<?php sn_e("Comment eMail"); ?>" />
|
||||
</p>
|
||||
<?php } ?>
|
||||
<p>
|
||||
<select name="comment[status]" class="custom-select">
|
||||
<option value="pending"<?php echo ($comment->isPending())? ' selected="selected"': ''; ?>><?php sn_e("Pending"); ?></option>
|
||||
<option value="approved"<?php echo ($comment->isApproved())? ' selected="selected"': ''; ?>><?php sn_e("Approved"); ?></option>
|
||||
<option value="rejected"<?php echo ($comment->isRejected())? ' selected="selected"': ''; ?>><?php sn_e("Rejected"); ?></option>
|
||||
<option value="spam"<?php echo ($comment->isSpam())? ' selected="selected"': ''; ?>><?php sn_e("Spam"); ?></option>
|
||||
</select>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-4 text-center">
|
||||
<a href="<?php echo $page->permalink(); ?>" target="_blank" class="btn btn-primary"><?php sn_e("View Page"); ?></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
188
admin/index-comments.php
Normal file
188
admin/index-comments.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./admin/index-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!"); }
|
||||
|
||||
global $pages, $security, $Snicker, $SnickerIndex, $SnickerPlugin, $SnickerUsers;
|
||||
|
||||
// Get Data
|
||||
$limit = $SnickerPlugin->getValue("frontend_per_page");
|
||||
if($limit === 0){
|
||||
$limit = 15;
|
||||
}
|
||||
$current = isset($_GET["tab"])? $_GET["tab"]: "pending";
|
||||
|
||||
// Get View
|
||||
$view = "index";
|
||||
if(isset($_GET["view"]) && in_array($_GET["view"], array("search", "single", "uuid", "user"))){
|
||||
$view = $current = $_GET["view"];
|
||||
$tabs = array($view);
|
||||
} else {
|
||||
$tabs = array("pending", "approved", "rejected", "spam");
|
||||
}
|
||||
|
||||
// Render Comemnts Tab
|
||||
foreach($tabs AS $status){
|
||||
if(isset($_GET["tab"]) && $_GET["tab"] === $status){
|
||||
$page = max((isset($_GET["page"])? (int) $_GET["page"]: 1), 1);
|
||||
} else {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
// Get Comments
|
||||
if($view === "index"){
|
||||
$comments = $SnickerIndex->getList($status, $page, $limit);
|
||||
$total = $SnickerIndex->count($status);
|
||||
} else if($view === "search"){
|
||||
$comments = $SnickerIndex->searchComments(isset($_GET["search"])? $_GET["search"]: "");
|
||||
$total = count($comments);
|
||||
} else if($view === "single"){
|
||||
$comments = $SnickerIndex->getListByParent(isset($_GET["single"])? $_GET["single"]: "");
|
||||
$total = count($comments);
|
||||
} else if($view === "uuid"){
|
||||
$comments = $SnickerIndex->getListByUUID(isset($_GET["uuid"])? $_GET["uuid"]: "");
|
||||
$total = count($comments);
|
||||
} else if($view === "user"){
|
||||
$comments = $SnickerIndex->getListByUser(isset($_GET["user"])? $_GET["user"]: "");
|
||||
$total = count($comments);
|
||||
}
|
||||
|
||||
// Render Tab Content
|
||||
$link = DOMAIN_ADMIN . "snicker?page=%d&tab={$status}#{$status}";
|
||||
?>
|
||||
<div id="snicker-<?php echo $status; ?>" class="tab-pane <?php echo($current === $status)? "active": ""; ?>">
|
||||
<div class="card shadow-sm" style="margin: 1.5rem 0;">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<form class="col-sm-6" method="get" action="<?php echo DOMAIN_ADMIN; ?>snicker">
|
||||
<div class="form-row align-items-center">
|
||||
<div class="col-sm-8">
|
||||
<?php $search = isset($_GET["search"])? $_GET["search"]: ""; ?>
|
||||
<input type="text" name="search" value="<?php echo $search; ?>" class="form-control" placeholder="<?php sn_e("Comment Title or Excerpt"); ?>" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<button class="btn btn-primary" name="view" value="search"><?php sn_e("Search Comments"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="col-sm-6 text-right">
|
||||
<?php if($total > $limit){ ?>
|
||||
<div class="btn-group btn-group-pagination">
|
||||
<?php if($page <= 1){ ?>
|
||||
<span class="btn btn-secondary disabled">«</span>
|
||||
<span class="btn btn-secondary disabled">‹</span>
|
||||
<?php } else { ?>
|
||||
<a href="<?php printf($link, 1); ?>" class="btn btn-secondary">«</a>
|
||||
<a href="<?php printf($link, $page-1); ?>" class="btn btn-secondary">‹</a>
|
||||
<?php } ?>
|
||||
<?php if(($page * $limit) < $total){ ?>
|
||||
<a href="<?php printf($link, $page+1); ?>" class="btn btn-secondary">›</a>
|
||||
<a href="<?php printf($link, ceil($total / $limit)); ?>" class="btn btn-secondary">»</a>
|
||||
<?php } else { ?>
|
||||
<span class="btn btn-secondary disabled">›</span>
|
||||
<span class="btn btn-secondary disabled">»</span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php /* No Comments available */ ?>
|
||||
<?php if(count($comments) < 1){ ?>
|
||||
<div class="row justify-content-md-center">
|
||||
<div class="col-sm-6">
|
||||
<div class="card w-100 shadow-sm bg-light">
|
||||
<div class="card-body text-center p-4"><i><?php sn_e("No Comments available"); ?></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php continue; ?>
|
||||
<?php } ?>
|
||||
|
||||
<?php /* Comments Table */ ?>
|
||||
<?php $link = DOMAIN_ADMIN . "snicker?action=snicker&snicker=%s&uid=%s&status=%s&tokenCSRF=" . $security->getTokenCSRF(); ?>
|
||||
<table class="table table-bordered table-hover-light shadow-sm mt-3">
|
||||
<?php foreach(array("thead", "tfoot") AS $tag){ ?>
|
||||
<<?php echo $tag; ?>>
|
||||
<tr class="thead-light">
|
||||
<th width="56%" class="border-0 p-3 text-uppercase text-muted"><?php sn_e("Comment"); ?></th>
|
||||
<th width="22%" class="border-0 p-3 text-uppercase text-muted text-center"><?php sn_e("Author"); ?></th>
|
||||
<th width="22%" class="border-0 p-3 text-uppercase text-muted text-center"><?php sn_e("Actions"); ?></th>
|
||||
</tr>
|
||||
</<?php echo $tag; ?>>
|
||||
<?php } ?>
|
||||
<tbody class="shadow-sm-both">
|
||||
<?php foreach($comments AS $uid){ ?>
|
||||
<?php
|
||||
$data = $SnickerIndex->getComment($uid, $status);
|
||||
if(!(isset($data["page_uuid"]) && is_string($data["page_uuid"]))){
|
||||
continue;
|
||||
}
|
||||
$user = $SnickerUsers->getByString($data["author"]);
|
||||
?>
|
||||
<tr>
|
||||
<td class="pt-3 pb-3 pl-3 pr-3">
|
||||
<?php
|
||||
if($SnickerPlugin->getValue("comment_title") !== "disabled" && !empty($data["title"])){
|
||||
echo '<b class="d-inline-block">' . $data["title"] . '</b>';
|
||||
}
|
||||
echo '<p class="text-muted m-0" style="font-size:12px;">' . (isset($data["excerpt"])? $data["excerpt"]: "") . '</p>';
|
||||
if(!empty($data["parent_uid"]) && $SnickerIndex->exists($data["parent_uid"]) && $view !== "single"){
|
||||
$reply = DOMAIN_ADMIN . "snicker?view=single&single={$uid}";
|
||||
$reply = '<a href="'.$reply.'" title="'.sn__("Show all replies").'">' . $SnickerIndex->getComment($data["parent_uid"])["title"] . '</a>';
|
||||
echo "<div class='text-muted mt-1' style='font-size:12px;'>" . sn__("Reply To") . ": " . $reply . "</div>";
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td class="align-middle pt-2 pb-2 pl-3 pr-3">
|
||||
<span class="d-inline-block"><?php echo $user["username"]; ?></span>
|
||||
<small class='d-block'><?php echo $user["email"]; ?></small>
|
||||
</td>
|
||||
<td class="text-center align-middle pt-2 pb-2 pl-1 pr-1">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-outline-secondary btn-sm dropdown-toggle" data-toggle="dropdown">
|
||||
<?php sn_e("Change"); ?>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item text-primary" href="<?php echo DOMAIN_ADMIN . "snicker/edit/?uid=" . $uid; ?>"><?php sn_e("Edit Comment"); ?></a>
|
||||
<a class="dropdown-item text-danger" href="<?php printf($link, "delete", $uid, "delete"); ?>"><?php sn_e("Delete Comment"); ?></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<?php if($status !== "approved"){ ?>
|
||||
<a class="dropdown-item" href="<?php printf($link, "moderate", $uid, "approved"); ?>"><?php sn_e("Approve Comment"); ?></a>
|
||||
<?php } ?>
|
||||
<?php if($status !== "rejected"){ ?>
|
||||
<a class="dropdown-item" href="<?php printf($link, "moderate", $uid, "rejected"); ?>"><?php sn_e("Reject Comment"); ?></a>
|
||||
<?php } ?>
|
||||
<?php if($status !== "spam"){ ?>
|
||||
<a class="dropdown-item" href="<?php printf($link, "moderate", $uid, "spam"); ?>"><?php sn_e("Mark as Spam"); ?></a>
|
||||
<?php } ?>
|
||||
<?php if($status !== "pending"){ ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="<?php printf($link, "moderate", $uid, "pending"); ?>"><?php sn_e("Back to Pending"); ?></a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php $page = new Page($pages->getByUUID($data["page_uuid"])); ?>
|
||||
<a href="<?php echo $page->permalink(); ?>#comment-<?php echo $uid; ?>" class="btn btn-outline-primary btn-sm" target="_blank"><?php sn_e("View"); ?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
484
admin/index-config.php
Normal file
484
admin/index-config.php
Normal file
@@ -0,0 +1,484 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./admin/index-config.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!"); }
|
||||
|
||||
global $L, $login, $pages, $security, $Snicker, $SnickerPlugin;
|
||||
|
||||
// Get Static Pages
|
||||
$static = $pages->getStaticDB(false);
|
||||
|
||||
?>
|
||||
<div id="snicker-configure" class="tab-pane">
|
||||
<form method="post" action="<?php echo HTML_PATH_ADMIN_ROOT; ?>snicker#configure">
|
||||
<div class="card shadow-sm" style="margin: 1.5rem 0;">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<input type="hidden" id="tokenUser" name="tokenUser" value="<?php echo $login->username(); ?>" />
|
||||
<input type="hidden" id="tokenCSRF" name="tokenCSRF" value="<?php echo $security->getTokenCSRF(); ?>" />
|
||||
<input type="hidden" id="sn-action" name="action" value="snicker" />
|
||||
<button class="btn btn-primary" name="snicker" value="configure"><?php sn_e("Save Settings"); ?></button>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion shadow-sm" id="accordion-settings">
|
||||
<div class="card">
|
||||
<div class="card-header text-uppercase pt-3 pb-3 pl-4 pr-4" data-toggle="collapse" data-target="#accordion-general"><?php sn_e("General Settings"); ?></div>
|
||||
<div id="accordion-general" class="collapse show" data-parent="#accordion-settings">
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label for="sn-moderation" class="col-sm-3 col-form-label"><?php sn_e("Comment Moderation"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-moderation" name="moderation" class="custom-select custom-select-sm w-auto">
|
||||
<option value="true" <?php sn_selected("moderation", true); ?>><?php sn_e("Moderate"); ?></option>
|
||||
<option value="false" <?php sn_selected("moderation", false); ?>><?php sn_e("Pass"); ?></option>
|
||||
</select>
|
||||
<label for="sn-moderation" class="col-form-label-sm ml-2 align-top"><?php sn_e("each Comment") ?></label>
|
||||
|
||||
<div class="custom-control custom-checkbox pl-5 mt-1">
|
||||
<input type="checkbox" id="sn-moderation-loggedin" name="moderation_loggedin" value="true"
|
||||
class="custom-control-input" <?php sn_checked("moderation_loggedin"); ?> />
|
||||
<label class="custom-control-label" for="sn-moderation-loggedin"><?php sn_e("Unless the user is logged in"); ?></label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox pl-5">
|
||||
<input type="checkbox" value="true" class="custom-control-input" checked="checked" disabled="disabled" />
|
||||
<label class="custom-control-label"><?php sn_e("Unless the user is admin or the content author"); ?></label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox pl-5 mb-2">
|
||||
<input type="checkbox" id="sn-moderation-approved" name="moderation_approved" value="true"
|
||||
class="custom-control-input" <?php sn_checked("moderation_approved"); ?> />
|
||||
<label class="custom-control-label" for="sn-moderation-approved"><?php sn_e("Unless the user has an already approved comment"); ?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-comment-title" class="col-sm-3 col-form-label"><?php sn_e("Allow Comments"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" id="sn-comment-on-public" name="comment_on_public" value="true"
|
||||
class="custom-control-input" <?php sn_checked("comment_on_public"); ?> />
|
||||
<label class="custom-control-label" for="sn-comment-on-public"><?php sn_e("... on Public Pages"); ?></label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" id="sn-comment-on-sticky" name="comment_on_sticky" value="true"
|
||||
class="custom-control-input" <?php sn_checked("comment_on_sticky"); ?> />
|
||||
<label class="custom-control-label" for="sn-comment-on-sticky"><?php sn_e("... on Sticky Pages"); ?></label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" id="sn-comment-on-static" name="comment_on_static" value="true"
|
||||
class="custom-control-input" <?php sn_checked("comment_on_static"); ?> />
|
||||
<label class="custom-control-label" for="sn-comment-on-static"><?php sn_e("... on Static Pages"); ?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-comment-title" class="col-sm-3 col-form-label"><?php sn_e("Comment Title"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-comment-title" name="comment_title" class="form-control custom-select">
|
||||
<option value="optional" <?php sn_selected("comment_title", "optional"); ?>><?php sn_e("Enable (Optional)"); ?></option>
|
||||
<option value="required" <?php sn_selected("comment_title", "required"); ?>><?php sn_e("Enable (Required)"); ?></option>
|
||||
<option value="disabled" <?php sn_selected("comment_title", "disabled"); ?>><?php sn_e("Disable"); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-comment-limit" class="col-sm-3 col-form-label"><?php sn_e("Comment Limit"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" id="sn-comment-limit" name="comment_limit" value="<?php echo sn_config("comment_limit"); ?>"
|
||||
class="form-control" min="0" placeholder="<?php sn_e("Use '0' to disable any limit!"); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-comment-depth" class="col-sm-3 col-form-label"><?php sn_e("Comment Depth"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" id="sn-comment-depth" name="comment_depth" value="<?php echo sn_config("comment_depth"); ?>"
|
||||
class="form-control" min="0" placeholder="<?php sn_e("Use '0' to disable any limit!"); ?>" />
|
||||
<small class="form-text text-muted"><?php sn_e("Use '0' to disable any limit!"); ?></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label"><?php sn_e("Comment Markup"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" id="sn-markup-html" name="comment_markup_html" value="true"
|
||||
class="custom-control-input" <?php sn_checked("comment_markup_html"); ?> />
|
||||
<label class="custom-control-label" for="sn-markup-html"><?php sn_e("Allow Basic HTML"); ?></label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" id="sn-markup-markdown" name="comment_markup_markdown" value="true"
|
||||
class="custom-control-input" <?php sn_checked("comment_markup_markdown"); ?> />
|
||||
<label class="custom-control-label" for="sn-markup-markdown"><?php sn_e("Allow Markdown"); ?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label"><?php sn_e("Comment Voting"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<label for="sn-vote-storage" class="col-form-label-sm mr-2 align-top"><?php sn_e("Store Votes made by Guests in the") ?></label>
|
||||
<select id="sn-vote-storage" name="comment_vote_storage" class="custom-select custom-select-sm w-auto">
|
||||
<option value="cookie" <?php sn_selected("comment_vote_storage", "cookie"); ?>><?php sn_e("Cookie Storage"); ?></option>
|
||||
<option value="session" <?php sn_selected("comment_vote_storage", "session"); ?>><?php sn_e("Session Storage"); ?></option>
|
||||
<option value="database" <?php sn_selected("comment_vote_storage", "database"); ?>><?php sn_e("Database Storage"); ?></option>
|
||||
</select>
|
||||
<a href="#" class="ml-2 align-top" data-container="body" data-toggle="popover" data-placement="left"
|
||||
data-trigger="focus" data-target="#help-content">(<?php sn_e("What?"); ?>)</a>
|
||||
<div id="help-content" class="hide d-none" style="width: 100%;">
|
||||
<p>
|
||||
<?php sn_e("The <b>Cookie Storage</b> is located on the Computer of the user. So you don't have the full control AND you require the appropriate permissions from the user."); ?>
|
||||
</p>
|
||||
<p>
|
||||
<?php sn_e("The <b>Session Storage</b> is just stored temporary on the server, it gets cleaned up when the user closes the browser. Therefore you don't need any permissions from the user."); ?>
|
||||
</p>
|
||||
<p>
|
||||
<?php sn_e("The <b>Database Storage</b> generates and stores an anonymized but assignable value of the user, which also requires the appropriate permissions from the user."); ?>
|
||||
</p>
|
||||
<p class="bg-light border-top" style="margin: -.5rem -.75rem;padding: .5rem .75rem;border-radius: 0 0 3px 3px;">
|
||||
<?php sn_e("<b>Please Note:</b> You are responsible for obtaining the appropriate permissions, Snicker just handles the permissions for data send (and stored) via the comment form!"); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" id="sn-like" name="comment_enable_like" value="true"
|
||||
class="custom-control-input" <?php sn_checked("comment_enable_like"); ?> />
|
||||
<label class="custom-control-label" for="sn-like"><?php sn_e("Allow to %s comments", array("<b>".sn__("Like")."</b>")); ?></label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" id="sn-dislike" name="comment_enable_dislike" value="true"
|
||||
class="custom-control-input" <?php sn_checked("comment_enable_dislike"); ?>/>
|
||||
<label class="custom-control-label" for="sn-dislike"><?php sn_e("Allow to %s comments", array("<b>".sn__("Dislike")."</b>")); ?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-uppercase pt-3 pb-3 pl-4 pr-4" data-toggle="collapse" data-target="#accordion-frontend"><?php sn_e("Frontend Settings"); ?></div>
|
||||
<div id="accordion-frontend" class="collapse" data-parent="#accordion-settings">
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label for="sn-filter" class="col-sm-3 col-form-label"><?php sn_e("Page Filter"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-filter" name="frontend_filter" class="form-control custom-select">
|
||||
<option value="disabled" <?php sn_selected("frontend_filter", "disabled"); ?>><?php sn_e("Disable Page Filter"); ?></option>
|
||||
<option value="pageBegin" <?php sn_selected("frontend_filter", "pageBegin"); ?>><?php sn_e("Use 'pageBegin'"); ?></option>
|
||||
<option value="pageEnd" <?php sn_selected("frontend_filter", "pageEnd"); ?>><?php sn_e("Use 'pageEnd'"); ?></option>
|
||||
<option value="siteBodyBegin" <?php sn_selected("frontend_filter", "siteBodyBegin"); ?>><?php sn_e("Use 'siteBodyBegin'"); ?></option>
|
||||
<option value="siteBodyEnd" <?php sn_selected("frontend_filter", "siteBodyEnd"); ?>><?php sn_e("Use 'siteBodyEnd'"); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-captcha" class="col-sm-3 col-form-label"><?php sn_e("Comment Captcha"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-captcha" name="frontend_captcha" class="form-control custom-select">
|
||||
<option value="disabled" <?php sn_selected("frontend_captcha", "disabled"); ?>><?php sn_e("Disable Captcha"); ?></option>
|
||||
<option value="purecaptcha" <?php sn_selected("frontend_captcha", "purecaptcha"); ?>><?php sn_e("Use OWASP's PureCaptcha"); ?></option>
|
||||
<?php if(function_exists("imagettfbbox")){ ?>
|
||||
<option value="gregwar" <?php sn_selected("frontend_captcha", "gregwar"); ?>><?php sn_e("Use Gregway's Captcha"); ?></option>
|
||||
<?php } else { ?>
|
||||
<option disabled="disabled"><?php sn_e("Use Gregway's Captcha (GD library is missing!)"); ?></option>
|
||||
<?php } ?>
|
||||
<option value="recaptcha" <?php sn_selected("frontend_captcha", "recaptcha"); ?> disabled="disabled"><?php sn_e("Use Googles reCaptcha (Not available yet)"); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-template" class="col-sm-3 col-form-label"><?php sn_e("Comment Template"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-template" name="frontend_template" class="form-control custom-select">
|
||||
<?php
|
||||
foreach($Snicker->themes AS $key => $theme){
|
||||
?>
|
||||
<option value="<?php echo $key; ?>" <?php sn_selected("frontend_template", $key); ?>><?php echo $theme::SNICKER_NAME; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-order" class="col-sm-3 col-form-label"><?php sn_e("Comment Order"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-order" name="frontend_order" class="form-control custom-select">
|
||||
<option value="date_desc" <?php sn_selected("frontend_order", "date_desc"); ?>><?php sn_e("Newest Comments First"); ?></option>
|
||||
<option value="date_asc" <?php sn_selected("frontend_order", "date_asc"); ?>><?php sn_e("Oldest Comments First"); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-order" class="col-sm-3 col-form-label"><?php sn_e("Comment Form Position"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-order" name="frontend_form" class="form-control custom-select">
|
||||
<option value="top" <?php sn_selected("frontend_form", "top"); ?>><?php sn_e("Show Comment Form above Comments"); ?></option>
|
||||
<option value="bottom" <?php sn_selected("frontend_form", "bottom"); ?>><?php sn_e("Show Comment Form below Comments"); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-per-page" class="col-sm-3 col-form-label"><?php sn_e("Comments Per Page"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" id="sn-per-page" name="frontend_per_page" value="<?php echo sn_config("frontend_per_page"); ?>"
|
||||
class="form-control" min="0" step="1" placheolder="<?php sn_e("Use '0' to show all available comments!"); ?>" />
|
||||
<small class="form-text text-muted"><?php sn_e("Use '0' to show all available comments!"); ?></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-terms" class="col-sm-3 col-form-label"><?php sn_e("Terms of Use Checkbox"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-terms" name="frontend_terms" class="form-control custom-select">
|
||||
<option value="disabled" <?php sn_selected("frontend_terms", "disabled"); ?>><?php sn_e("Disable this field"); ?></option>
|
||||
<option value="default" <?php sn_selected("frontend_terms", "default"); ?>><?php sn_e("Show Message (See Strings)"); ?></option>
|
||||
|
||||
<?php foreach($static AS $key => $value){ ?>
|
||||
<option value="<?php echo $key; ?>" <?php sn_selected("frontend_terms", $key); ?>><?php sn_e("Page"); ?>: <?php echo $value["title"]; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<small class="form-text text-muted"><?php sn_e("Show the default GDPR Text or Select your own static 'Terms of Use' page!"); ?></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-ajax" class="col-sm-3 col-form-label"><?php sn_e("AJAX Script"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-ajax" name="frontend_ajax" class="form-control custom-select">
|
||||
<option value="true" <?php sn_selected("frontend_ajax", true); ?>><?php sn_e("Embed AJAX Script"); ?></option>
|
||||
<option value="false" <?php sn_selected("frontend_ajax", false); ?>><?php sn_e("Don't use AJAX"); ?></option>
|
||||
</select>
|
||||
<small class="form-text text-muted"><?php sn_e("The AJAX Script hands over the request (comment, like, dislike) directly without reloading the page!"); ?></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr style="margin: 30px -20px;" />
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-avatar" class="col-sm-3 col-form-label"><?php sn_e("Comment Avatar"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-avatar" name="frontend_avatar" class="form-control custom-select">
|
||||
<option value="gravatar" <?php sn_selected("frontend_avatar", "gravatar"); ?>><?php sn_e("Use Gravatar"); ?></option>
|
||||
<option value="identicon" <?php sn_selected("frontend_avatar", "identicon"); ?>><?php sn_e("Use Identicon"); ?></option>
|
||||
<option value="static" <?php sn_selected("frontend_avatar", "static"); ?>><?php sn_e("Use Mystery Men"); ?></option>
|
||||
</select>
|
||||
|
||||
<div class="custom-control custom-checkbox mt-1">
|
||||
<input type="checkbox" id="sn-moderation-users" name="frontend_avatar_users" value="true"
|
||||
class="custom-control-input" <?php sn_checked("frontend_avatar_users"); ?> />
|
||||
<label class="custom-control-label" for="sn-moderation-users"><?php sn_e("Use & Prefer profile pictures on logged-in Users"); ?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-gravatar" class="col-sm-3 col-form-label"><?php sn_e("Comment Gravatar"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-gravatar" name="frontend_gravatar" class="form-control custom-select">
|
||||
<option value="mp" <?php sn_selected("frontend_gravatar", "mp"); ?>><?php sn_e("Show Mystery Person"); ?></option>
|
||||
<option value="identicon" <?php sn_selected("frontend_gravatar", "identicon"); ?>><?php sn_e("Show"); ?> Identicon</option>
|
||||
<option value="monsterid" <?php sn_selected("frontend_gravatar", "monsterid"); ?>><?php sn_e("Show"); ?> Monster ID</option>
|
||||
<option value="wavatar" <?php sn_selected("frontend_gravatar", "wavatar"); ?>><?php sn_e("Show"); ?> WAvatar</option>
|
||||
</select>
|
||||
<small class="form-text text-muted"><?php sn_e("The default Gravatar image, if the user has no Gravatar!"); ?></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-uppercase pt-3 pb-3 pl-4 pr-4" data-toggle="collapse" data-target="#accordion-subscripton"><?php sn_e("Subscription Settings"); ?></div>
|
||||
<div id="accordion-subscripton" class="collapse" data-parent="#accordion-settings">
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info"><?php sn_e("The Subscription system isn't available yet!"); ?> :(</div>
|
||||
<div class="form-group row">
|
||||
<label for="sn-subscription" class="col-sm-3 col-form-label text-muted"><?php sn_e("eMail Subscription"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-subscription" name="subscription" class="form-control custom-select" disabled="disabled">
|
||||
<option value="true" <?php sn_selected("subscription", true); ?> disabled="disabled"><?php sn_e("Enable"); ?></option>
|
||||
<option value="false" <?php sn_selected("subscription", false); ?>><?php sn_e("Disable"); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-subscription-from" class="col-sm-3 col-form-label text-muted"><?php sn_e("eMail 'From' Address"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-subscription-from" name="subscription_from" value="<?php echo sn_config("subscription_from"); ?>"
|
||||
class="form-control" placeholder="<?php sn_e("eMail 'From' Address"); ?>" disabled="disabled" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-subscription-reply" class="col-sm-3 col-form-label text-muted"><?php sn_e("eMail 'ReplyTo' Address"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-subscription-reply" name="subscription_reply" value="<?php echo sn_config("subscription_reply"); ?>"
|
||||
class="form-control" placeholder="<?php sn_e("eMail 'ReplyTo' Address"); ?>" disabled="disabled" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-subscription-optin" class="col-sm-3 col-form-label text-muted"><?php sn_e("eMail Body (Opt-In)"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-subscription-optin" name="subscription_optin" class="form-control custom-select" disabled="disabled">
|
||||
<option value="default" <?php sn_selected("subscription_optin", "default"); ?>><?php sn_e("Use default Subscription eMail"); ?></option>
|
||||
<?php foreach($static AS $key => $value){ ?>
|
||||
<option value="<?php echo $key; ?>" <?php sn_selected("subscription_optin", $key); ?>><?php sn_e("Page"); ?>: <?php echo $value["title"]; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-subscription-ticker" class="col-sm-3 col-form-label text-muted"><?php sn_e("eMail Body (Notification)"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<select id="sn-subscription-ticker" name="subscription_ticker" class="form-control custom-select" disabled="disabled">
|
||||
<option value="default" <?php sn_selected("subscription_ticker", "default"); ?>><?php sn_e("Use default Notification eMail"); ?></option>
|
||||
<?php foreach($static AS $key => $value){ ?>
|
||||
<option value="<?php echo $key; ?>" <?php sn_selected("subscription_ticker", $key); ?>><?php sn_e("Page"); ?>: <?php echo $value["title"]; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<small class="form-text text-muted"><?php sn_e("Read more about a custom Notification eMails %s!", array('<a href="#" target="_blank">'.sn__("here").'</a>')); ?></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-uppercase pt-3 pb-3 pl-4 pr-4" data-toggle="collapse" data-target="#accordion-strings"><?php sn_e("Strings"); ?></div>
|
||||
<div id="accordion-strings" class="collapse" data-parent="#accordion-settings">
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label for="sn-success-1" class="col-sm-3 col-form-label"><?php sn_e("Default Thanks Message"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-success-1" name="string_success_1" value="<?php echo sn_config("string_success_1"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_success_1"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-success-2" class="col-sm-3 col-form-label"><?php sn_e("Thanks Message with Subscription"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-success-2" name="string_success_2" value="<?php echo sn_config("string_success_2"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_success_2"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-success-3" class="col-sm-3 col-form-label"><?php sn_e("Thanks Message for Voting"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-success-3" name="string_success_3" value="<?php echo sn_config("string_success_3"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_success_3"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-error-1" class="col-sm-3 col-form-label"><?php sn_e("Error: Unknown Error, Try again"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-error-1" name="string_error_1" value="<?php echo sn_config("string_error_1"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_error_1"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-error-2" class="col-sm-3 col-form-label"><?php sn_e("Error: Username is invalid"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-error-2" name="string_error_2" value="<?php echo sn_config("string_error_2"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_error_2"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-error-3" class="col-sm-3 col-form-label"><?php sn_e("Error: eMail Address is invalid"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-error-3" name="string_error_3" value="<?php echo sn_config("string_error_3"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_error_3"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-error-4" class="col-sm-3 col-form-label"><?php sn_e("Error: Comment Text is missing"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-error-4" name="string_error_4" value="<?php echo sn_config("string_error_4"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_error_4"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-error-5" class="col-sm-3 col-form-label"><?php sn_e("Error: Comment Title is missing"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-error-5" name="string_error_5" value="<?php echo sn_config("string_error_5"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_error_5"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-error-6" class="col-sm-3 col-form-label"><?php sn_e("Error: Terms not accepted"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-error-6" name="string_error_6" value="<?php echo sn_config("string_error_6"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_error_6"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-error-7" class="col-sm-3 col-form-label"><?php sn_e("Error: Marked as SPAM"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-error-7" name="string_error_7" value="<?php echo sn_config("string_error_7"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_error_7"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-error-8" class="col-sm-3 col-form-label"><?php sn_e("Error: Already Voted"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-error-8" name="string_error_8" value="<?php echo sn_config("string_error_8"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_error_8"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="sn-terms-of-use" class="col-sm-3 col-form-label"><?php sn_e("Terms of Use"); ?></label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="sn-terms-of-use" name="string_terms_of_use" value="<?php echo sn_config("string_terms_of_use"); ?>"
|
||||
class="form-control" placeholder="<?php echo $SnickerPlugin->dbFields["string_terms_of_use"]; ?>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm mt-4 mb-4">
|
||||
<div class="card-body">
|
||||
<button class="btn btn-primary" name="snicker" value="configure"><?php sn_e("Save Settings"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
133
admin/index-users.php
Normal file
133
admin/index-users.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./admin/index-users.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!"); }
|
||||
|
||||
global $SnickerUsers;
|
||||
|
||||
// Get Data
|
||||
$page = max((isset($_GET["page"])? (int) $_GET["page"]: 1), 1);
|
||||
$limit = sn_config("frontend_per_page");
|
||||
$total = count($SnickerUsers->db);
|
||||
|
||||
// Get Users
|
||||
$search = null;
|
||||
if(isset($_GET["view"]) && $_GET["view"] === "users"){
|
||||
$page = 1;
|
||||
$limit = -1;
|
||||
$search = isset($_GET["search"])? $_GET["search"]: null;
|
||||
}
|
||||
$users = $SnickerUsers->getList($search, $page, $limit);
|
||||
|
||||
// Link
|
||||
$link = DOMAIN_ADMIN . "snicker?page=%d&tab=users#users";
|
||||
|
||||
?>
|
||||
<div id="snicker-users" class="tab-pane">
|
||||
<div class="card shadow-sm" style="margin: 1.5rem 0;">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<form class="col-sm-6" method="get" action="<?php echo DOMAIN_ADMIN; ?>snicker#users">
|
||||
<div class="form-row align-items-center">
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="search" value="<?php echo $search; ?>" class="form-control" placeholder="<?php sn_e("Username or eMail Address"); ?>" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<button class="btn btn-primary" name="view" value="users"><?php sn_e("Search Users"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="col-sm-6 text-right">
|
||||
<?php if($total > $limit){ ?>
|
||||
<div class="btn-group btn-group-pagination">
|
||||
<?php if($page <= 1){ ?>
|
||||
<span class="btn btn-secondary disabled">«</span>
|
||||
<span class="btn btn-secondary disabled">‹</span>
|
||||
<?php } else { ?>
|
||||
<a href="<?php printf($link, 1); ?>" class="btn btn-secondary">«</a>
|
||||
<a href="<?php printf($link, $page-1); ?>" class="btn btn-secondary">‹</a>
|
||||
<?php } ?>
|
||||
<?php if(($page * $limit) < $total){ ?>
|
||||
<a href="<?php printf($link, $page+1); ?>" class="btn btn-secondary">›</a>
|
||||
<a href="<?php printf($link, ceil($total / $limit)); ?>" class="btn btn-secondary">»</a>
|
||||
<?php } else { ?>
|
||||
<span class="btn btn-secondary disabled">›</span>
|
||||
<span class="btn btn-secondary disabled">»</span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if(!$users || count($users) === 0){ ?>
|
||||
<div class="row justify-content-md-center">
|
||||
<div class="col-sm-6">
|
||||
<div class="card w-100 shadow-sm bg-light">
|
||||
<div class="card-body text-center p-4"><i><?php sn_e("No Users available"); ?></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<?php $link = DOMAIN_ADMIN . "snicker?action=snicker&snicker=users&uuid=%s&handle=%s&tokenCSRF=" . $security->getTokenCSRF(); ?>
|
||||
<table class="table table-bordered table-hover-light shadow-sm mt-3">
|
||||
<?php foreach(array("thead", "tfoot") AS $tag){ ?>
|
||||
<<?php echo $tag; ?>>
|
||||
<tr class="thead-light">
|
||||
<th width="38%" class="border-0 p-3 text-uppercase text-muted"><?php sn_e("Username"); ?></th>
|
||||
<th width="15%" class="border-0 p-3 text-uppercase text-muted"><?php sn_e("eMail"); ?></th>
|
||||
<th width="22%" class="border-0 p-3 text-uppercase text-muted"><?php sn_e("Comments"); ?></th>
|
||||
<th width="25%" class="border-0 p-3 text-uppercase text-muted text-center"><?php sn_e("Actions"); ?></th>
|
||||
</tr>
|
||||
</<?php echo $tag; ?>>
|
||||
<?php } ?>
|
||||
|
||||
<tbody class="shadow-sm-both">
|
||||
<?php foreach($users AS $uuid => $user){ ?>
|
||||
<tr>
|
||||
<td class="p-3">
|
||||
<?php echo $user["username"]; ?>
|
||||
</td>
|
||||
<td class="p-3">
|
||||
<?php echo $user["email"]; ?>
|
||||
</td>
|
||||
<td class="text-center align-middle pt-2 pb-2 pl-1 pr-1">
|
||||
<a href="<?php echo DOMAIN_ADMIN; ?>snicker?view=user&user=<?php echo $uuid; ?>">
|
||||
<?php echo count(isset($user["comments"])? $user["comments"]: array()); ?>
|
||||
<?php sn_e("Comments"); ?>
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center align-middle pt-2 pb-2 pl-1 pr-1">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-outline-secondary btn-sm dropdown-toggle" data-toggle="dropdown">
|
||||
<?php sn_e("Handle"); ?>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item text-danger" href="<?php printf($link, $uuid, "delete"); ?>&anonymize=true"><?php sn_e("Delete (Anonymize)"); ?></a>
|
||||
<a class="dropdown-item text-danger" href="<?php printf($link, $uuid, "delete"); ?>&anonymize=false"><?php sn_e("Delete (Completely)"); ?></a>
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
<?php if($user["blocked"]){ ?>
|
||||
<a class="dropdown-item" href="<?php printf($link, $uuid, "unblock"); ?>"><?php sn_e("Unblock User"); ?></a>
|
||||
<?php } else { ?>
|
||||
<a class="dropdown-item" href="<?php printf($link, $uuid, "block"); ?>"><?php sn_e("Block User"); ?></a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php } ?>
|
||||
</div>
|
||||
85
admin/index.php
Normal file
85
admin/index.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./admin/index.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!"); }
|
||||
|
||||
global $L, $Snicker;
|
||||
|
||||
// Pending Counter
|
||||
$count = count($Snicker->getIndex("pending"));
|
||||
$count = ($count > 99)? "99+": $count;
|
||||
$spam = count($Snicker->getIndex("spam"));
|
||||
|
||||
// Tab Strings
|
||||
$strings = array(
|
||||
"pending" => sn__("Pending"),
|
||||
"approved" => sn__("Approved"),
|
||||
"rejected" => sn__("Rejected"),
|
||||
"spam" => sn__("Spam"),
|
||||
"search" => sn__("Search Comments"),
|
||||
"single" => sn__("Single Comment"),
|
||||
"uuid" => sn__("Page Comments"),
|
||||
"user" => sn__("User Comments")
|
||||
);
|
||||
|
||||
// Current Tab
|
||||
$view = "index";
|
||||
if(isset($_GET["view"]) && in_array($_GET["view"], array("search", "single", "uuid", "user"))){
|
||||
$view = $current = $_GET["view"];
|
||||
$tabs = array($view);
|
||||
} else {
|
||||
$current = isset($_GET["tab"])? $_GET["tab"]: "pending";
|
||||
$tabs = array("pending", "approved", "rejected", "spam");
|
||||
}
|
||||
?>
|
||||
<h2 class="mt-0 mb-3">
|
||||
<span class="oi oi-comment-square" style="font-size: 0.7em;"></span> Snicker <?php sn_e("Comments"); ?>
|
||||
</h2>
|
||||
|
||||
<ul class="nav nav-pills" data-handle="tabs">
|
||||
<?php foreach($tabs AS $tab){ ?>
|
||||
<?php $class = "nav-link nav-{$tab}" . ($current === $tab? " active": ""); ?>
|
||||
<li class="nav-item">
|
||||
<a id="<?php echo $tab; ?>-tab" href="#snicker-<?php echo $tab; ?>" class="<?php echo $class; ?>" data-toggle="tab">
|
||||
<?php
|
||||
echo $strings[$tab];
|
||||
if($tab === "pending" && !empty($count)){
|
||||
?> <span class="badge badge-primary"><?php echo $count; ?></span><?php
|
||||
}
|
||||
if($tab === "spam" && !empty($spam)){
|
||||
?> <span class="badge badge-danger"><?php echo $spam; ?></span><?php
|
||||
}
|
||||
?>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
|
||||
<li class="nav-item flex-grow-1"></li>
|
||||
|
||||
<li class="nav-item mr-2">
|
||||
<a id="users-tab" href="#snicker-users" class="nav-link nav-config" data-toggle="tab">
|
||||
<span class="oi oi-people"></span> <?php sn_e("Users"); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a id="configure-tab" href="#snicker-configure" class="nav-link nav-config" data-toggle="tab">
|
||||
<span class="oi oi-cog"></span> <?php sn_e("Configuration"); ?>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<?php
|
||||
include "index-comments.php";
|
||||
include "index-users.php";
|
||||
include "index-config.php";
|
||||
?>
|
||||
</div>
|
||||
125
admin/js/admin.snicker.js
Normal file
125
admin/js/admin.snicker.js
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./admin/js/admin.snicker.js
|
||||
| @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>
|
||||
*/
|
||||
;(function(root){
|
||||
"use strict";
|
||||
var w = root, d = root.document;
|
||||
|
||||
/*
|
||||
| HELPER :: LOOP
|
||||
| @since 0.1.0
|
||||
*/
|
||||
var each = function(elements, callback){
|
||||
if(elements instanceof HTMLElement){
|
||||
callback.call(elements, elements);
|
||||
} else if(elements.length && elements.length > 0){
|
||||
for(var l = elements.length, i = 0; i < l; i++){
|
||||
callback.call(elements[i], elements[i], i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Ready?
|
||||
d.addEventListener("DOMContentLoaded", function(){
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
| BOOTSTRAP POPOVER
|
||||
| @since 0.1.0
|
||||
*/
|
||||
jQuery('[data-toggle="popover"]').popover({
|
||||
content: function(){
|
||||
var data = d.querySelector(this.getAttribute("data-target"));
|
||||
return data.innerHTML;
|
||||
},
|
||||
html: true
|
||||
}).click(function(event){
|
||||
event.preventDefault();
|
||||
}).on("inserted.bs.popover", function(event){
|
||||
d.querySelector("#" + this.getAttribute("aria-describedby")).style.width = "410px";
|
||||
d.querySelector("#" + this.getAttribute("aria-describedby")).style.maxWidth = "410px";
|
||||
})
|
||||
|
||||
/*
|
||||
| MAIN MENU LINK HANDLER
|
||||
| @since 0.1.0
|
||||
*/
|
||||
var mainMenu = d.querySelector("[data-handle='tabs']");
|
||||
if(mainMenu){
|
||||
var menuLink = function(link){
|
||||
if(typeof(link) === "undefined"){
|
||||
if(w.location.hash.length == 0){
|
||||
var link = mainMenu.querySelector("li a");
|
||||
} else {
|
||||
var link = mainMenu.querySelector("[href='#snicker-" + w.location.hash.substr(1) + "']");
|
||||
}
|
||||
}
|
||||
if(!(link instanceof Element)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle
|
||||
if(link && !link.classList.contains("active")){
|
||||
link.click();
|
||||
}
|
||||
if(link){
|
||||
w.location.hash = link.getAttribute("href").replace("snicker-", "");
|
||||
}
|
||||
};
|
||||
|
||||
// Current Hash Handler
|
||||
if(w.location.hash.length > 0){
|
||||
menuLink();
|
||||
}
|
||||
|
||||
// Local Hash Handler
|
||||
each(mainMenu.querySelectorAll("li > a"), function(){
|
||||
this.addEventListener("click", function(event){
|
||||
menuLink(this);
|
||||
});
|
||||
});
|
||||
|
||||
// History Hash Handler
|
||||
w.onhashchange = function(event){
|
||||
if(w.location.hash.length == 0){
|
||||
var link = mainMenu.querySelector("li a");
|
||||
} else {
|
||||
var link = mainMenu.querySelector("[href='#snicker-" + w.location.hash.substr(1) + "']");
|
||||
}
|
||||
menuLink(link);
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
| MAIN MENU LINK HANDLER
|
||||
| @since 0.1.1
|
||||
*/
|
||||
var avatar = document.getElementById("sn-avatar"),
|
||||
gravatar = document.getElementById("sn-gravatar");
|
||||
if(avatar && gravatar){
|
||||
var GravatarOption = function(){
|
||||
console.log(avatar.value)
|
||||
|
||||
if(avatar.value == "gravatar"){
|
||||
gravatar.disabled = false;
|
||||
document.querySelector("label[for='sn-gravatar']").classList.remove("text-muted");
|
||||
} else {
|
||||
gravatar.disabled = true;
|
||||
document.querySelector("label[for='sn-gravatar']").classList.add("text-muted");
|
||||
}
|
||||
};
|
||||
|
||||
avatar.addEventListener("change", function(){
|
||||
GravatarOption();
|
||||
});
|
||||
GravatarOption();
|
||||
}
|
||||
});
|
||||
})(window);
|
||||
717
includes/Gregwar/Captcha/CaptchaBuilder.php
Normal file
717
includes/Gregwar/Captcha/CaptchaBuilder.php
Normal file
@@ -0,0 +1,717 @@
|
||||
<?php
|
||||
|
||||
namespace Gregwar\Captcha;
|
||||
|
||||
use \Exception;
|
||||
|
||||
/**
|
||||
* Builds a new captcha image
|
||||
* Uses the fingerprint parameter, if one is passed, to generate the same image
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
|
||||
*/
|
||||
class CaptchaBuilder implements CaptchaBuilderInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fingerprint = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useFingerprint = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $textColor = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $backgroundColor = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $backgroundImages = array();
|
||||
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
protected $contents = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $phrase = null;
|
||||
|
||||
/**
|
||||
* @var PhraseBuilderInterface
|
||||
*/
|
||||
protected $builder;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $distortion = true;
|
||||
|
||||
/**
|
||||
* The maximum number of lines to draw in front of
|
||||
* the image. null - use default algorithm
|
||||
*/
|
||||
protected $maxFrontLines = null;
|
||||
|
||||
/**
|
||||
* The maximum number of lines to draw behind
|
||||
* the image. null - use default algorithm
|
||||
*/
|
||||
protected $maxBehindLines = null;
|
||||
|
||||
/**
|
||||
* The maximum angle of char
|
||||
*/
|
||||
protected $maxAngle = 8;
|
||||
|
||||
/**
|
||||
* The maximum offset of char
|
||||
*/
|
||||
protected $maxOffset = 5;
|
||||
|
||||
/**
|
||||
* Is the interpolation enabled ?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $interpolation = true;
|
||||
|
||||
/**
|
||||
* Ignore all effects
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $ignoreAllEffects = false;
|
||||
|
||||
/**
|
||||
* Allowed image types for the background images
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $allowedBackgroundImageTypes = array('image/png', 'image/jpeg', 'image/gif');
|
||||
|
||||
/**
|
||||
* The image contents
|
||||
*/
|
||||
public function getContents()
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disables the interpolation
|
||||
*
|
||||
* @param $interpolate bool True to enable, false to disable
|
||||
*
|
||||
* @return CaptchaBuilder
|
||||
*/
|
||||
public function setInterpolation($interpolate = true)
|
||||
{
|
||||
$this->interpolation = $interpolate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary dir, for OCR check
|
||||
*/
|
||||
public $tempDir = 'temp/';
|
||||
|
||||
public function __construct($phrase = null, PhraseBuilderInterface $builder = null)
|
||||
{
|
||||
if ($builder === null) {
|
||||
$this->builder = new PhraseBuilder;
|
||||
} else {
|
||||
$this->builder = $builder;
|
||||
}
|
||||
|
||||
$this->phrase = is_string($phrase) ? $phrase : $this->builder->build($phrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting the phrase
|
||||
*/
|
||||
public function setPhrase($phrase)
|
||||
{
|
||||
$this->phrase = (string) $phrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disable distortion
|
||||
*/
|
||||
public function setDistortion($distortion)
|
||||
{
|
||||
$this->distortion = (bool) $distortion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxBehindLines($maxBehindLines)
|
||||
{
|
||||
$this->maxBehindLines = $maxBehindLines;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxFrontLines($maxFrontLines)
|
||||
{
|
||||
$this->maxFrontLines = $maxFrontLines;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxAngle($maxAngle)
|
||||
{
|
||||
$this->maxAngle = $maxAngle;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMaxOffset($maxOffset)
|
||||
{
|
||||
$this->maxOffset = $maxOffset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the captcha phrase
|
||||
*/
|
||||
public function getPhrase()
|
||||
{
|
||||
return $this->phrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given phrase is good
|
||||
*/
|
||||
public function testPhrase($phrase)
|
||||
{
|
||||
return ($this->builder->niceize($phrase) == $this->builder->niceize($this->getPhrase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiation
|
||||
*/
|
||||
public static function create($phrase = null)
|
||||
{
|
||||
return new self($phrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text color to use
|
||||
*/
|
||||
public function setTextColor($r, $g, $b)
|
||||
{
|
||||
$this->textColor = array($r, $g, $b);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the background color to use
|
||||
*/
|
||||
public function setBackgroundColor($r, $g, $b)
|
||||
{
|
||||
$this->backgroundColor = array($r, $g, $b);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ignoreAllEffects value
|
||||
*
|
||||
* @param bool $ignoreAllEffects
|
||||
* @return CaptchaBuilder
|
||||
*/
|
||||
public function setIgnoreAllEffects($ignoreAllEffects)
|
||||
{
|
||||
$this->ignoreAllEffects = $ignoreAllEffects;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of background images to use (one image is randomly selected)
|
||||
*/
|
||||
public function setBackgroundImages(array $backgroundImages)
|
||||
{
|
||||
$this->backgroundImages = $backgroundImages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw lines over the image
|
||||
*/
|
||||
protected function drawLine($image, $width, $height, $tcol = null)
|
||||
{
|
||||
if ($tcol === null) {
|
||||
$tcol = imagecolorallocate($image, $this->rand(100, 255), $this->rand(100, 255), $this->rand(100, 255));
|
||||
}
|
||||
|
||||
if ($this->rand(0, 1)) { // Horizontal
|
||||
$Xa = $this->rand(0, $width/2);
|
||||
$Ya = $this->rand(0, $height);
|
||||
$Xb = $this->rand($width/2, $width);
|
||||
$Yb = $this->rand(0, $height);
|
||||
} else { // Vertical
|
||||
$Xa = $this->rand(0, $width);
|
||||
$Ya = $this->rand(0, $height/2);
|
||||
$Xb = $this->rand(0, $width);
|
||||
$Yb = $this->rand($height/2, $height);
|
||||
}
|
||||
imagesetthickness($image, $this->rand(1, 3));
|
||||
imageline($image, $Xa, $Ya, $Xb, $Yb, $tcol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply some post effects
|
||||
*/
|
||||
protected function postEffect($image)
|
||||
{
|
||||
if (!function_exists('imagefilter')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->backgroundColor != null || $this->textColor != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Negate ?
|
||||
if ($this->rand(0, 1) == 0) {
|
||||
imagefilter($image, IMG_FILTER_NEGATE);
|
||||
}
|
||||
|
||||
// Edge ?
|
||||
if ($this->rand(0, 10) == 0) {
|
||||
imagefilter($image, IMG_FILTER_EDGEDETECT);
|
||||
}
|
||||
|
||||
// Contrast
|
||||
imagefilter($image, IMG_FILTER_CONTRAST, $this->rand(-50, 10));
|
||||
|
||||
// Colorize
|
||||
if ($this->rand(0, 5) == 0) {
|
||||
imagefilter($image, IMG_FILTER_COLORIZE, $this->rand(-80, 50), $this->rand(-80, 50), $this->rand(-80, 50));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the phrase on the image
|
||||
*/
|
||||
protected function writePhrase($image, $phrase, $font, $width, $height)
|
||||
{
|
||||
$length = mb_strlen($phrase);
|
||||
if ($length === 0) {
|
||||
return \imagecolorallocate($image, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Gets the text size and start position
|
||||
$size = $width / $length - $this->rand(0, 3) - 1;
|
||||
$box = \imagettfbbox($size, 0, $font, $phrase);
|
||||
$textWidth = $box[2] - $box[0];
|
||||
$textHeight = $box[1] - $box[7];
|
||||
$x = ($width - $textWidth) / 2;
|
||||
$y = ($height - $textHeight) / 2 + $size;
|
||||
|
||||
if (!$this->textColor) {
|
||||
$textColor = array($this->rand(0, 150), $this->rand(0, 150), $this->rand(0, 150));
|
||||
} else {
|
||||
$textColor = $this->textColor;
|
||||
}
|
||||
$col = \imagecolorallocate($image, $textColor[0], $textColor[1], $textColor[2]);
|
||||
|
||||
// Write the letters one by one, with random angle
|
||||
for ($i=0; $i<$length; $i++) {
|
||||
$symbol = mb_substr($phrase, $i, 1);
|
||||
$box = \imagettfbbox($size, 0, $font, $symbol);
|
||||
$w = $box[2] - $box[0];
|
||||
$angle = $this->rand(-$this->maxAngle, $this->maxAngle);
|
||||
$offset = $this->rand(-$this->maxOffset, $this->maxOffset);
|
||||
\imagettftext($image, $size, $angle, $x, $y + $offset, $col, $font, $symbol);
|
||||
$x += $w;
|
||||
}
|
||||
|
||||
return $col;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to read the code against an OCR
|
||||
*/
|
||||
public function isOCRReadable()
|
||||
{
|
||||
if (!is_dir($this->tempDir)) {
|
||||
@mkdir($this->tempDir, 0755, true);
|
||||
}
|
||||
|
||||
$tempj = $this->tempDir . uniqid('captcha', true) . '.jpg';
|
||||
$tempp = $this->tempDir . uniqid('captcha', true) . '.pgm';
|
||||
|
||||
$this->save($tempj);
|
||||
shell_exec("convert $tempj $tempp");
|
||||
$value = trim(strtolower(shell_exec("ocrad $tempp")));
|
||||
|
||||
@unlink($tempj);
|
||||
@unlink($tempp);
|
||||
|
||||
return $this->testPhrase($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds while the code is readable against an OCR
|
||||
*/
|
||||
public function buildAgainstOCR($width = 150, $height = 40, $font = null, $fingerprint = null)
|
||||
{
|
||||
do {
|
||||
$this->build($width, $height, $font, $fingerprint);
|
||||
} while ($this->isOCRReadable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the image
|
||||
*/
|
||||
public function build($width = 150, $height = 40, $font = null, $fingerprint = null)
|
||||
{
|
||||
if (null !== $fingerprint) {
|
||||
$this->fingerprint = $fingerprint;
|
||||
$this->useFingerprint = true;
|
||||
} else {
|
||||
$this->fingerprint = array();
|
||||
$this->useFingerprint = false;
|
||||
}
|
||||
|
||||
if ($font === null) {
|
||||
$font = __DIR__ . '/Font/captcha'.$this->rand(0, 5).'.ttf';
|
||||
}
|
||||
|
||||
if (empty($this->backgroundImages)) {
|
||||
// if background images list is not set, use a color fill as a background
|
||||
$image = imagecreatetruecolor($width, $height);
|
||||
if ($this->backgroundColor == null) {
|
||||
$bg = imagecolorallocate($image, $this->rand(200, 255), $this->rand(200, 255), $this->rand(200, 255));
|
||||
} else {
|
||||
$color = $this->backgroundColor;
|
||||
$bg = imagecolorallocate($image, $color[0], $color[1], $color[2]);
|
||||
}
|
||||
$this->background = $bg;
|
||||
imagefill($image, 0, 0, $bg);
|
||||
} else {
|
||||
// use a random background image
|
||||
$randomBackgroundImage = $this->backgroundImages[rand(0, count($this->backgroundImages)-1)];
|
||||
|
||||
$imageType = $this->validateBackgroundImage($randomBackgroundImage);
|
||||
|
||||
$image = $this->createBackgroundImageFromType($randomBackgroundImage, $imageType);
|
||||
}
|
||||
|
||||
// Apply effects
|
||||
if (!$this->ignoreAllEffects) {
|
||||
$square = $width * $height;
|
||||
$effects = $this->rand($square/3000, $square/2000);
|
||||
|
||||
// set the maximum number of lines to draw in front of the text
|
||||
if ($this->maxBehindLines != null && $this->maxBehindLines > 0) {
|
||||
$effects = min($this->maxBehindLines, $effects);
|
||||
}
|
||||
|
||||
if ($this->maxBehindLines !== 0) {
|
||||
for ($e = 0; $e < $effects; $e++) {
|
||||
$this->drawLine($image, $width, $height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write CAPTCHA text
|
||||
$color = $this->writePhrase($image, $this->phrase, $font, $width, $height);
|
||||
|
||||
// Apply effects
|
||||
if (!$this->ignoreAllEffects) {
|
||||
$square = $width * $height;
|
||||
$effects = $this->rand($square/3000, $square/2000);
|
||||
|
||||
// set the maximum number of lines to draw in front of the text
|
||||
if ($this->maxFrontLines != null && $this->maxFrontLines > 0) {
|
||||
$effects = min($this->maxFrontLines, $effects);
|
||||
}
|
||||
|
||||
if ($this->maxFrontLines !== 0) {
|
||||
for ($e = 0; $e < $effects; $e++) {
|
||||
$this->drawLine($image, $width, $height, $color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Distort the image
|
||||
if ($this->distortion && !$this->ignoreAllEffects) {
|
||||
$image = $this->distort($image, $width, $height, $bg);
|
||||
}
|
||||
|
||||
// Post effects
|
||||
if (!$this->ignoreAllEffects) {
|
||||
$this->postEffect($image);
|
||||
}
|
||||
|
||||
$this->contents = $image;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Distorts the image
|
||||
*/
|
||||
public function distort($image, $width, $height, $bg)
|
||||
{
|
||||
$contents = imagecreatetruecolor($width, $height);
|
||||
$X = $this->rand(0, $width);
|
||||
$Y = $this->rand(0, $height);
|
||||
$phase = $this->rand(0, 10);
|
||||
$scale = 1.1 + $this->rand(0, 10000) / 30000;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$Vx = $x - $X;
|
||||
$Vy = $y - $Y;
|
||||
$Vn = sqrt($Vx * $Vx + $Vy * $Vy);
|
||||
|
||||
if ($Vn != 0) {
|
||||
$Vn2 = $Vn + 4 * sin($Vn / 30);
|
||||
$nX = $X + ($Vx * $Vn2 / $Vn);
|
||||
$nY = $Y + ($Vy * $Vn2 / $Vn);
|
||||
} else {
|
||||
$nX = $X;
|
||||
$nY = $Y;
|
||||
}
|
||||
$nY = $nY + $scale * sin($phase + $nX * 0.2);
|
||||
|
||||
if ($this->interpolation) {
|
||||
$p = $this->interpolate(
|
||||
$nX - floor($nX),
|
||||
$nY - floor($nY),
|
||||
$this->getCol($image, floor($nX), floor($nY), $bg),
|
||||
$this->getCol($image, ceil($nX), floor($nY), $bg),
|
||||
$this->getCol($image, floor($nX), ceil($nY), $bg),
|
||||
$this->getCol($image, ceil($nX), ceil($nY), $bg)
|
||||
);
|
||||
} else {
|
||||
$p = $this->getCol($image, round($nX), round($nY), $bg);
|
||||
}
|
||||
|
||||
if ($p == 0) {
|
||||
$p = $bg;
|
||||
}
|
||||
|
||||
imagesetpixel($contents, $x, $y, $p);
|
||||
}
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the Captcha to a jpeg file
|
||||
*/
|
||||
public function save($filename, $quality = 90)
|
||||
{
|
||||
imagejpeg($this->contents, $filename, $quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image GD
|
||||
*/
|
||||
public function getGd()
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image contents
|
||||
*/
|
||||
public function get($quality = 90)
|
||||
{
|
||||
ob_start();
|
||||
$this->output($quality);
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTML inline base64
|
||||
*/
|
||||
public function inline($quality = 90)
|
||||
{
|
||||
return 'data:image/jpeg;base64,' . base64_encode($this->get($quality));
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the image
|
||||
*/
|
||||
public function output($quality = 90)
|
||||
{
|
||||
imagejpeg($this->contents, null, $quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFingerprint()
|
||||
{
|
||||
return $this->fingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random number or the next number in the
|
||||
* fingerprint
|
||||
*/
|
||||
protected function rand($min, $max)
|
||||
{
|
||||
if (!is_array($this->fingerprint)) {
|
||||
$this->fingerprint = array();
|
||||
}
|
||||
|
||||
if ($this->useFingerprint) {
|
||||
$value = current($this->fingerprint);
|
||||
next($this->fingerprint);
|
||||
} else {
|
||||
$value = mt_rand($min, $max);
|
||||
$this->fingerprint[] = $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $x
|
||||
* @param $y
|
||||
* @param $nw
|
||||
* @param $ne
|
||||
* @param $sw
|
||||
* @param $se
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function interpolate($x, $y, $nw, $ne, $sw, $se)
|
||||
{
|
||||
list($r0, $g0, $b0) = $this->getRGB($nw);
|
||||
list($r1, $g1, $b1) = $this->getRGB($ne);
|
||||
list($r2, $g2, $b2) = $this->getRGB($sw);
|
||||
list($r3, $g3, $b3) = $this->getRGB($se);
|
||||
|
||||
$cx = 1.0 - $x;
|
||||
$cy = 1.0 - $y;
|
||||
|
||||
$m0 = $cx * $r0 + $x * $r1;
|
||||
$m1 = $cx * $r2 + $x * $r3;
|
||||
$r = (int) ($cy * $m0 + $y * $m1);
|
||||
|
||||
$m0 = $cx * $g0 + $x * $g1;
|
||||
$m1 = $cx * $g2 + $x * $g3;
|
||||
$g = (int) ($cy * $m0 + $y * $m1);
|
||||
|
||||
$m0 = $cx * $b0 + $x * $b1;
|
||||
$m1 = $cx * $b2 + $x * $b3;
|
||||
$b = (int) ($cy * $m0 + $y * $m1);
|
||||
|
||||
return ($r << 16) | ($g << 8) | $b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $image
|
||||
* @param $x
|
||||
* @param $y
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getCol($image, $x, $y, $background)
|
||||
{
|
||||
$L = imagesx($image);
|
||||
$H = imagesy($image);
|
||||
if ($x < 0 || $x >= $L || $y < 0 || $y >= $H) {
|
||||
return $background;
|
||||
}
|
||||
|
||||
return imagecolorat($image, $x, $y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $col
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getRGB($col)
|
||||
{
|
||||
return array(
|
||||
(int) ($col >> 16) & 0xff,
|
||||
(int) ($col >> 8) & 0xff,
|
||||
(int) ($col) & 0xff,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the background image path. Return the image type if valid
|
||||
*
|
||||
* @param string $backgroundImage
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function validateBackgroundImage($backgroundImage)
|
||||
{
|
||||
// check if file exists
|
||||
if (!file_exists($backgroundImage)) {
|
||||
$backgroundImageExploded = explode('/', $backgroundImage);
|
||||
$imageFileName = count($backgroundImageExploded) > 1? $backgroundImageExploded[count($backgroundImageExploded)-1] : $backgroundImage;
|
||||
|
||||
throw new Exception('Invalid background image: ' . $imageFileName);
|
||||
}
|
||||
|
||||
// check image type
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
|
||||
$imageType = finfo_file($finfo, $backgroundImage);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (!in_array($imageType, $this->allowedBackgroundImageTypes)) {
|
||||
throw new Exception('Invalid background image type! Allowed types are: ' . join(', ', $this->allowedBackgroundImageTypes));
|
||||
}
|
||||
|
||||
return $imageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create background image from type
|
||||
*
|
||||
* @param string $backgroundImage
|
||||
* @param string $imageType
|
||||
* @return resource
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function createBackgroundImageFromType($backgroundImage, $imageType)
|
||||
{
|
||||
switch ($imageType) {
|
||||
case 'image/jpeg':
|
||||
$image = imagecreatefromjpeg($backgroundImage);
|
||||
break;
|
||||
case 'image/png':
|
||||
$image = imagecreatefrompng($backgroundImage);
|
||||
break;
|
||||
case 'image/gif':
|
||||
$image = imagecreatefromgif($backgroundImage);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception('Not supported file type for background image!');
|
||||
break;
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
29
includes/Gregwar/Captcha/CaptchaBuilderInterface.php
Normal file
29
includes/Gregwar/Captcha/CaptchaBuilderInterface.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Gregwar\Captcha;
|
||||
|
||||
/**
|
||||
* A Captcha builder
|
||||
*/
|
||||
interface CaptchaBuilderInterface
|
||||
{
|
||||
/**
|
||||
* Builds the code
|
||||
*/
|
||||
public function build($width, $height, $font, $fingerprint);
|
||||
|
||||
/**
|
||||
* Saves the code to a file
|
||||
*/
|
||||
public function save($filename, $quality);
|
||||
|
||||
/**
|
||||
* Gets the image contents
|
||||
*/
|
||||
public function get($quality);
|
||||
|
||||
/**
|
||||
* Outputs the image
|
||||
*/
|
||||
public function output($quality);
|
||||
}
|
||||
BIN
includes/Gregwar/Captcha/Font/captcha0.ttf
Normal file
BIN
includes/Gregwar/Captcha/Font/captcha0.ttf
Normal file
Binary file not shown.
BIN
includes/Gregwar/Captcha/Font/captcha1.ttf
Normal file
BIN
includes/Gregwar/Captcha/Font/captcha1.ttf
Normal file
Binary file not shown.
BIN
includes/Gregwar/Captcha/Font/captcha2.ttf
Normal file
BIN
includes/Gregwar/Captcha/Font/captcha2.ttf
Normal file
Binary file not shown.
BIN
includes/Gregwar/Captcha/Font/captcha3.ttf
Normal file
BIN
includes/Gregwar/Captcha/Font/captcha3.ttf
Normal file
Binary file not shown.
BIN
includes/Gregwar/Captcha/Font/captcha4.ttf
Normal file
BIN
includes/Gregwar/Captcha/Font/captcha4.ttf
Normal file
Binary file not shown.
BIN
includes/Gregwar/Captcha/Font/captcha5.ttf
Normal file
BIN
includes/Gregwar/Captcha/Font/captcha5.ttf
Normal file
Binary file not shown.
105
includes/Gregwar/Captcha/ImageFileHandler.php
Normal file
105
includes/Gregwar/Captcha/ImageFileHandler.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Gregwar\Captcha;
|
||||
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
/**
|
||||
* Handles actions related to captcha image files including saving and garbage collection
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
* @author Jeremy Livingston <jeremy@quizzle.com>
|
||||
*/
|
||||
class ImageFileHandler
|
||||
{
|
||||
/**
|
||||
* Name of folder for captcha images
|
||||
* @var string
|
||||
*/
|
||||
protected $imageFolder;
|
||||
|
||||
/**
|
||||
* Absolute path to public web folder
|
||||
* @var string
|
||||
*/
|
||||
protected $webPath;
|
||||
|
||||
/**
|
||||
* Frequency of garbage collection in fractions of 1
|
||||
* @var int
|
||||
*/
|
||||
protected $gcFreq;
|
||||
|
||||
/**
|
||||
* Maximum age of images in minutes
|
||||
* @var int
|
||||
*/
|
||||
protected $expiration;
|
||||
|
||||
/**
|
||||
* @param $imageFolder
|
||||
* @param $webPath
|
||||
* @param $gcFreq
|
||||
* @param $expiration
|
||||
*/
|
||||
public function __construct($imageFolder, $webPath, $gcFreq, $expiration)
|
||||
{
|
||||
$this->imageFolder = $imageFolder;
|
||||
$this->webPath = $webPath;
|
||||
$this->gcFreq = $gcFreq;
|
||||
$this->expiration = $expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the provided image content as a file
|
||||
*
|
||||
* @param string $contents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function saveAsFile($contents)
|
||||
{
|
||||
$this->createFolderIfMissing();
|
||||
|
||||
$filename = md5(uniqid()) . '.jpg';
|
||||
$filePath = $this->webPath . '/' . $this->imageFolder . '/' . $filename;
|
||||
imagejpeg($contents, $filePath, 15);
|
||||
|
||||
return '/' . $this->imageFolder . '/' . $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly runs garbage collection on the image directory
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function collectGarbage()
|
||||
{
|
||||
if (!mt_rand(1, $this->gcFreq) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->createFolderIfMissing();
|
||||
|
||||
$finder = new Finder();
|
||||
$criteria = sprintf('<= now - %s minutes', $this->expiration);
|
||||
$finder->in($this->webPath . '/' . $this->imageFolder)
|
||||
->date($criteria);
|
||||
|
||||
foreach ($finder->files() as $file) {
|
||||
unlink($file->getPathname());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the folder if it doesn't exist
|
||||
*/
|
||||
protected function createFolderIfMissing()
|
||||
{
|
||||
if (!file_exists($this->webPath . '/' . $this->imageFolder)) {
|
||||
mkdir($this->webPath . '/' . $this->imageFolder, 0755);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
includes/Gregwar/Captcha/PhraseBuilder.php
Normal file
59
includes/Gregwar/Captcha/PhraseBuilder.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Gregwar\Captcha;
|
||||
|
||||
/**
|
||||
* Generates random phrase
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
*/
|
||||
class PhraseBuilder implements PhraseBuilderInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $length;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $charset;
|
||||
/**
|
||||
* Constructs a PhraseBuilder with given parameters
|
||||
*/
|
||||
public function __construct($length = 5, $charset = 'abcdefghijklmnpqrstuvwxyz123456789')
|
||||
{
|
||||
$this->length = $length;
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random phrase of given length with given charset
|
||||
*/
|
||||
public function build($length = null, $charset = null)
|
||||
{
|
||||
if ($length !== null) {
|
||||
$this->length = $length;
|
||||
}
|
||||
if ($charset !== null) {
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
$phrase = '';
|
||||
$chars = str_split($this->charset);
|
||||
|
||||
for ($i = 0; $i < $this->length; $i++) {
|
||||
$phrase .= $chars[array_rand($chars)];
|
||||
}
|
||||
|
||||
return $phrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Niceize" a code
|
||||
*/
|
||||
public function niceize($str)
|
||||
{
|
||||
return strtr(strtolower($str), '01', 'ol');
|
||||
}
|
||||
}
|
||||
21
includes/Gregwar/Captcha/PhraseBuilderInterface.php
Normal file
21
includes/Gregwar/Captcha/PhraseBuilderInterface.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Gregwar\Captcha;
|
||||
|
||||
/**
|
||||
* Interface for the PhraseBuilder
|
||||
*
|
||||
* @author Gregwar <g.passault@gmail.com>
|
||||
*/
|
||||
interface PhraseBuilderInterface
|
||||
{
|
||||
/**
|
||||
* Generates random phrase of given length with given charset
|
||||
*/
|
||||
public function build();
|
||||
|
||||
/**
|
||||
* "Niceize" a code
|
||||
*/
|
||||
public function niceize($str);
|
||||
}
|
||||
250
includes/Identicon/Generator/BaseGenerator.php
Normal file
250
includes/Identicon/Generator/BaseGenerator.php
Normal file
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
namespace Identicon\Generator;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @author Benjamin Laugueux <benjamin@yzalis.com>
|
||||
*/
|
||||
class BaseGenerator
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $generatedImage;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $color;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $backgroundColor;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $size;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $pixelRatio;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $hash;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $arrayOfSquare = [];
|
||||
|
||||
/**
|
||||
* Set the image color.
|
||||
*
|
||||
* @param string|array $color The color in hexa (3 or 6 chars) or rgb array
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setColor($color)
|
||||
{
|
||||
if (null === $color) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->color = $this->convertColor($color);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image background color.
|
||||
*
|
||||
* @param string|array $backgroundColor The color in hexa (3 or 6 chars) or rgb array
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setBackgroundColor($backgroundColor)
|
||||
{
|
||||
if (null === $backgroundColor) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->backgroundColor = $this->convertColor($backgroundColor);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $color
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function convertColor($color)
|
||||
{
|
||||
if (is_array($color)) {
|
||||
return $color;
|
||||
}
|
||||
|
||||
if (preg_match('/^#?([a-z\d])([a-z\d])([a-z\d])$/i', $color, $matches)) {
|
||||
$color = $matches[1].$matches[1];
|
||||
$color .= $matches[2].$matches[2];
|
||||
$color .= $matches[3].$matches[3];
|
||||
}
|
||||
|
||||
preg_match('/#?([a-z\d]{2})([a-z\d]{2})([a-z\d]{2})$/i', $color, $matches);
|
||||
|
||||
return array_map(function ($value) {
|
||||
return hexdec($value);
|
||||
}, array_slice($matches, 1, 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getColor()
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the background color.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBackgroundColor()
|
||||
{
|
||||
return $this->backgroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the hash into an multidimensional array of boolean.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
private function convertHashToArrayOfBoolean()
|
||||
{
|
||||
preg_match_all('/(\w)(\w)/', $this->hash, $chars);
|
||||
|
||||
foreach ($chars[1] as $i => $char) {
|
||||
$index = (int) ($i / 3);
|
||||
$data = $this->convertHexaToBoolean($char);
|
||||
|
||||
$items = [
|
||||
0 => [0, 4],
|
||||
1 => [1, 3],
|
||||
2 => [2],
|
||||
];
|
||||
|
||||
foreach ($items[$i % 3] as $item) {
|
||||
$this->arrayOfSquare[$index][$item] = $data;
|
||||
}
|
||||
|
||||
ksort($this->arrayOfSquare[$index]);
|
||||
}
|
||||
|
||||
$this->color = array_map(function ($data) {
|
||||
return hexdec($data) * 16;
|
||||
}, array_reverse($chars[1]));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an hexadecimal number into a boolean.
|
||||
*
|
||||
* @param string $hexa
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function convertHexaToBoolean($hexa)
|
||||
{
|
||||
return (bool) round(hexdec($hexa) / 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getArrayOfSquare()
|
||||
{
|
||||
return $this->arrayOfSquare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identicon string hash.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHash()
|
||||
{
|
||||
return $this->hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a hash from the original string.
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setString($string)
|
||||
{
|
||||
if (null === $string) {
|
||||
throw new Exception('The string cannot be null.');
|
||||
}
|
||||
|
||||
$this->hash = md5($string);
|
||||
|
||||
$this->convertHashToArrayOfBoolean();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image size.
|
||||
*
|
||||
* @param int $size
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSize($size)
|
||||
{
|
||||
if (null === $size) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->size = $size;
|
||||
$this->pixelRatio = (int) round($size / 5);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image size.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pixel ratio.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPixelRatio()
|
||||
{
|
||||
return $this->pixelRatio;
|
||||
}
|
||||
}
|
||||
90
includes/Identicon/Generator/GdGenerator.php
Normal file
90
includes/Identicon/Generator/GdGenerator.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Identicon\Generator;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @author Benjamin Laugueux <benjamin@yzalis.com>
|
||||
*/
|
||||
class GdGenerator extends BaseGenerator implements GeneratorInterface
|
||||
{
|
||||
/**
|
||||
* GdGenerator constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('gd') && !extension_loaded('ext-gd')) {
|
||||
throw new Exception('GD does not appear to be available in your PHP installation. Please try another generator');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMimeType()
|
||||
{
|
||||
return 'image/png';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
private function generateImage()
|
||||
{
|
||||
// prepare image
|
||||
$this->generatedImage = imagecreatetruecolor($this->getPixelRatio() * 5, $this->getPixelRatio() * 5);
|
||||
|
||||
$rgbBackgroundColor = $this->getBackgroundColor();
|
||||
if (null === $rgbBackgroundColor) {
|
||||
$background = imagecolorallocate($this->generatedImage, 0, 0, 0);
|
||||
imagecolortransparent($this->generatedImage, $background);
|
||||
} else {
|
||||
$background = imagecolorallocate($this->generatedImage, $rgbBackgroundColor[0], $rgbBackgroundColor[1], $rgbBackgroundColor[2]);
|
||||
imagefill($this->generatedImage, 0, 0, $background);
|
||||
}
|
||||
|
||||
// prepare color
|
||||
$rgbColor = $this->getColor();
|
||||
$gdColor = imagecolorallocate($this->generatedImage, $rgbColor[0], $rgbColor[1], $rgbColor[2]);
|
||||
|
||||
// draw content
|
||||
foreach ($this->getArrayOfSquare() as $lineKey => $lineValue) {
|
||||
foreach ($lineValue as $colKey => $colValue) {
|
||||
if (true === $colValue) {
|
||||
imagefilledrectangle($this->generatedImage, $colKey * $this->getPixelRatio(), $lineKey * $this->getPixelRatio(), ($colKey + 1) * $this->getPixelRatio(), ($lineKey + 1) * $this->getPixelRatio(), $gdColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null)
|
||||
{
|
||||
ob_start();
|
||||
imagepng($this->getImageResource($string, $size, $color, $backgroundColor));
|
||||
$imageData = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return $imageData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getImageResource($string, $size = null, $color = null, $backgroundColor = null)
|
||||
{
|
||||
$this
|
||||
->setString($string)
|
||||
->setSize($size)
|
||||
->setColor($color)
|
||||
->setBackgroundColor($backgroundColor)
|
||||
->generateImage();
|
||||
|
||||
return $this->generatedImage;
|
||||
}
|
||||
}
|
||||
43
includes/Identicon/Generator/GeneratorInterface.php
Normal file
43
includes/Identicon/Generator/GeneratorInterface.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Identicon\Generator;
|
||||
|
||||
/**
|
||||
* @author Benjamin Laugueux <benjamin@yzalis.com>
|
||||
*/
|
||||
interface GeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @param string $string
|
||||
* @param int $size
|
||||
* @param array|string $color
|
||||
* @param array|string $backgroundColor
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null);
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
* @param int $size
|
||||
* @param array|string $color
|
||||
* @param array|string $backgroundColor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getImageResource($string, $size = null, $color = null, $backgroundColor = null);
|
||||
|
||||
/**
|
||||
* Return the mime-type of this identicon.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMimeType();
|
||||
|
||||
/**
|
||||
* Return the color of the created identicon.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getColor();
|
||||
}
|
||||
98
includes/Identicon/Generator/ImageMagickGenerator.php
Normal file
98
includes/Identicon/Generator/ImageMagickGenerator.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Identicon\Generator;
|
||||
|
||||
use Exception;
|
||||
use ImagickDraw;
|
||||
use ImagickPixel;
|
||||
|
||||
/**
|
||||
* @author Francis Chuang <francis.chuang@gmail.com>
|
||||
*/
|
||||
class ImageMagickGenerator extends BaseGenerator implements GeneratorInterface
|
||||
{
|
||||
/**
|
||||
* ImageMagickGenerator constructor.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('imagick')) {
|
||||
throw new Exception('ImageMagick does not appear to be avaliable in your PHP installation. Please try another generator');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMimeType()
|
||||
{
|
||||
return 'image/png';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
private function generateImage()
|
||||
{
|
||||
$this->generatedImage = new \Imagick();
|
||||
$rgbBackgroundColor = $this->getBackgroundColor();
|
||||
|
||||
if (null === $rgbBackgroundColor) {
|
||||
$background = 'none';
|
||||
} else {
|
||||
$background = new ImagickPixel("rgb($rgbBackgroundColor[0],$rgbBackgroundColor[1],$rgbBackgroundColor[2])");
|
||||
}
|
||||
|
||||
$this->generatedImage->newImage($this->pixelRatio * 5, $this->pixelRatio * 5, $background, 'png');
|
||||
|
||||
// prepare color
|
||||
$rgbColor = $this->getColor();
|
||||
$color = new ImagickPixel("rgb($rgbColor[0],$rgbColor[1],$rgbColor[2])");
|
||||
|
||||
$draw = new ImagickDraw();
|
||||
$draw->setFillColor($color);
|
||||
|
||||
// draw the content
|
||||
foreach ($this->getArrayOfSquare() as $lineKey => $lineValue) {
|
||||
foreach ($lineValue as $colKey => $colValue) {
|
||||
if (true === $colValue) {
|
||||
$draw->rectangle($colKey * $this->pixelRatio, $lineKey * $this->pixelRatio, ($colKey + 1) * $this->pixelRatio, ($lineKey + 1) * $this->pixelRatio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->generatedImage->drawImage($draw);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null)
|
||||
{
|
||||
ob_start();
|
||||
echo $this->getImageResource($string, $size, $color, $backgroundColor);
|
||||
$imageData = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return $imageData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getImageResource($string, $size = null, $color = null, $backgroundColor = null)
|
||||
{
|
||||
$this
|
||||
->setString($string)
|
||||
->setSize($size)
|
||||
->setColor($color)
|
||||
->setBackgroundColor($backgroundColor)
|
||||
->generateImage();
|
||||
|
||||
return $this->generatedImage;
|
||||
}
|
||||
}
|
||||
88
includes/Identicon/Generator/SvgGenerator.php
Normal file
88
includes/Identicon/Generator/SvgGenerator.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Identicon\Generator;
|
||||
|
||||
/**
|
||||
* @author Grummfy <grummfy@gmail.com>
|
||||
*/
|
||||
class SvgGenerator extends BaseGenerator implements GeneratorInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMimeType()
|
||||
{
|
||||
return 'image/svg+xml';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null)
|
||||
{
|
||||
return $this->getImageResource($string, $size, $color, $backgroundColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getImageResource($string, $size = null, $color = null, $backgroundColor = null)
|
||||
{
|
||||
$this
|
||||
->setString($string)
|
||||
->setSize($size)
|
||||
->setColor($color)
|
||||
->setBackgroundColor($backgroundColor)
|
||||
->_generateImage();
|
||||
|
||||
return $this->generatedImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
protected function _generateImage()
|
||||
{
|
||||
// prepare image
|
||||
$w = $this->getPixelRatio() * 5;
|
||||
$h = $this->getPixelRatio() * 5;
|
||||
$svg = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'.$w.'" height="'.$h.'">';
|
||||
|
||||
$backgroundColor = '#FFFFFF';
|
||||
$rgbBackgroundColor = $this->getBackgroundColor();
|
||||
if (!is_null($rgbBackgroundColor)) {
|
||||
$backgroundColor = $this->_toUnderstandableColor($rgbBackgroundColor);
|
||||
}
|
||||
$svg .= '<rect width="'.$w.'" height="'.$h.'" style="fill:'.$backgroundColor.';stroke-width:1;stroke:'.$backgroundColor.'"/>';
|
||||
|
||||
$rgbColor = $this->_toUnderstandableColor($this->getColor());
|
||||
// draw content
|
||||
foreach ($this->getArrayOfSquare() as $lineKey => $lineValue) {
|
||||
foreach ($lineValue as $colKey => $colValue) {
|
||||
if (true === $colValue) {
|
||||
$svg .= '<rect x="'.$colKey * $this->getPixelRatio().'" y="'.$lineKey * $this->getPixelRatio().'" width="'.($this->getPixelRatio()).'" height="'.$this->getPixelRatio().'" style="fill:'.$rgbColor.';stroke-width:0;"/>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$svg .= '</svg>';
|
||||
|
||||
$this->generatedImage = $svg;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $color
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _toUnderstandableColor($color)
|
||||
{
|
||||
if (is_array($color)) {
|
||||
return 'rgb('.implode(', ', $color).')';
|
||||
}
|
||||
|
||||
return $color;
|
||||
}
|
||||
}
|
||||
123
includes/Identicon/Identicon.php
Normal file
123
includes/Identicon/Identicon.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Identicon;
|
||||
|
||||
use Identicon\Generator\GdGenerator;
|
||||
use Identicon\Generator\GeneratorInterface;
|
||||
|
||||
/**
|
||||
* @author Benjamin Laugueux <benjamin@yzalis.com>
|
||||
*/
|
||||
class Identicon
|
||||
{
|
||||
/**
|
||||
* @var \Identicon\Generator\GeneratorInterface
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
* Identicon constructor.
|
||||
*
|
||||
* @param \Identicon\Generator\GeneratorInterface|null $generator
|
||||
*/
|
||||
public function __construct($generator = null)
|
||||
{
|
||||
if (null === $generator) {
|
||||
$this->generator = new GdGenerator();
|
||||
} else {
|
||||
$this->generator = $generator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image generator.
|
||||
*
|
||||
* @param \Identicon\Generator\GeneratorInterface $generator
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setGenerator(GeneratorInterface $generator)
|
||||
{
|
||||
$this->generator = $generator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display an Identicon image.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $size
|
||||
* @param string $color
|
||||
* @param string $backgroundColor
|
||||
*/
|
||||
public function displayImage($string, $size = 64, $color = null, $backgroundColor = null)
|
||||
{
|
||||
header('Content-Type: '.$this->generator->getMimeType());
|
||||
echo $this->getImageData($string, $size, $color, $backgroundColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an Identicon PNG image data.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $size
|
||||
* @param string $color
|
||||
* @param string $backgroundColor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getImageData($string, $size = 64, $color = null, $backgroundColor = null)
|
||||
{
|
||||
return $this->generator->getImageBinaryData($string, $size, $color, $backgroundColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an Identicon PNG image resource.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $size
|
||||
* @param string $color
|
||||
* @param string $backgroundColor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getImageResource($string, $size = 64, $color = null, $backgroundColor = null)
|
||||
{
|
||||
return $this->generator->getImageResource($string, $size, $color, $backgroundColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an Identicon PNG image data as base 64 encoded.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $size
|
||||
* @param string $color
|
||||
* @param string $backgroundColor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getImageDataUri($string, $size = 64, $color = null, $backgroundColor = null)
|
||||
{
|
||||
return sprintf('data:%s;base64,%s', $this->generator->getMimeType(), base64_encode($this->getImageData($string, $size, $color, $backgroundColor)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color of the Identicon
|
||||
*
|
||||
* Returns an array with RGB values of the Identicon's color. Colors may be NULL if no image has been generated
|
||||
* so far (e.g., when calling the method on a new Identicon()).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getColor()
|
||||
{
|
||||
$colors = $this->generator->getColor();
|
||||
|
||||
return [
|
||||
"r" => $colors[0],
|
||||
"g" => $colors[1],
|
||||
"b" => $colors[2]
|
||||
];
|
||||
}
|
||||
}
|
||||
244
includes/OWASP/PureCaptcha.php
Normal file
244
includes/OWASP/PureCaptcha.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
/**
|
||||
* OWASP PureCaptcha
|
||||
* Generates simple CAPTCHAs without requiring any third party library.
|
||||
* @version 1.1
|
||||
*/
|
||||
|
||||
namespace OWASP;
|
||||
|
||||
class PureCaptcha
|
||||
{
|
||||
protected $charWidth=6;
|
||||
protected $charHeight=13;
|
||||
protected $chars="2346789ABDHKLMNPRTWXYZ"; //do not modify!
|
||||
protected $ascii="eNrtW0FuwyAQ/NIANjbOa3LMG6r8vWrrSonkkt0xDWDvIcGXEYHM7O6s4b
|
||||
p4v3zcFlyuiwu/T/Hn4ba4h49fx7COwzqO3+P964tF+i0kViRWJLaQ4RGJF3PiETnQyFGzzidklC
|
||||
A31znlkMjt7Zzb2+y/kjQ79MwEbEH/+kOftsjxLHKehK7cbYT/qu0KNBcHmosjzcVI63yi1znTyE
|
||||
RHCAd6oZX479uL/yIuBho5SFgs5z8kc0YaOUm4iJfxXxVbEr23Djy0Dv+D1T/iXzvSyKjhop7/r+
|
||||
M/LP5v83+w+qdM/aOP/6LKqXr9r0Lm+Z+H1uH/aPGf87/Y7X8t/jfA/2j8b43/MP/7Pv5P7frfbH
|
||||
YPtKOszn8VcqIrxPrxXwetw/+5n/pfz395/Y8L2//HG+sf1ZwzjUw0Utf/byH+J+N/bf7L47/xv/
|
||||
z7L7QnAFG+7NgAgDYAnRngHgog50wAXAcU9BtgaBqDEz3nTK/zVALw/QiAbwHxFvg/X4G53QJwuw
|
||||
VgR4CCZYBc9wh0BpD3gKDuAVkJVE4Aw5EEgGICcF0JgG+C4vQCGI/QBTqWCT5cCSSDVhJANAH0Kw
|
||||
AzwfsFMB3xHBzEJliFLHwPoMY5OBEy0cj8OWi0mAFmM8G1D0LwHgC0CYbaBIvaBB1mgHRuAajOEB
|
||||
W+CcNnANGcM408UwnkYQLoTwBWApUTgN0F7vAuDNRdILsLvymA+ycgmwSd";
|
||||
function __construct()
|
||||
{
|
||||
$this->ascii=unserialize(gzuncompress(base64_decode(
|
||||
preg_replace('/\s+/', '', $this->ascii))));
|
||||
$this->text=$this->randomText();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the captcha text.
|
||||
* @var string
|
||||
*/
|
||||
public $text;
|
||||
|
||||
/**
|
||||
* Generates random text for use in captcha
|
||||
* @param integer $length
|
||||
* @return string
|
||||
*/
|
||||
protected function randomText($length=5)
|
||||
{
|
||||
$res="";
|
||||
for ($i=0;$i<$length;++$i)
|
||||
$res.=$this->chars[mt_rand(0,strlen($this->chars)-1)];
|
||||
return $res;
|
||||
}
|
||||
/**
|
||||
* returns the index of a char in $chars array
|
||||
*/
|
||||
protected function asciiEntry($char)
|
||||
{
|
||||
for ($i=0;$i<strlen($this->chars);++$i)
|
||||
if ($this->chars[$i]==$char) return $i;
|
||||
return -1;
|
||||
|
||||
}
|
||||
/**
|
||||
* converts a text to a bitmap
|
||||
* which is a 2D array of ones and zeroes denoting the text
|
||||
*/
|
||||
protected function textBitmap($text,$spacing=2)
|
||||
{
|
||||
$width=$this->charWidth;
|
||||
$height=$this->charHeight;
|
||||
$result=array();
|
||||
$baseY=$baseX=0;
|
||||
|
||||
for ($index=0;$index<strlen($text);++$index)
|
||||
{
|
||||
for ($j=0;$j<$height;++$j)
|
||||
{
|
||||
for ($i=0;$i<$width;++$i)
|
||||
$result[$baseY+$j][$baseX+$i]=
|
||||
1-$this->ascii[$this->asciiEntry($text[$index])][$j][$i];
|
||||
for ($i=0;$i<$spacing;++$i)
|
||||
$result[$baseY+$j][$baseX+$width+$i]=0;
|
||||
}
|
||||
$baseX+=$width+$spacing;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* displays a bitmap string on the browser screen
|
||||
*/
|
||||
protected function displayBitmap($bitmap)
|
||||
{
|
||||
header("Content-Type: image/bmp");
|
||||
echo $this->bitmap2bmp($bitmap);
|
||||
}
|
||||
|
||||
protected function inlineBitmap($bitmap)
|
||||
{
|
||||
return base64_encode($this->bitmap2bmp($bitmap));
|
||||
}
|
||||
|
||||
/**
|
||||
* generates a monochrome BMP file
|
||||
* a bitmap needs to be sent to this function
|
||||
* i.e a 2D array with every element being either 1 or 0
|
||||
* @param integer $width
|
||||
* @param integer $height
|
||||
* @param array $bitmap
|
||||
* @return string
|
||||
*/
|
||||
protected function bitmap2bmp($bitmap)
|
||||
{
|
||||
$width=count($bitmap[0]);
|
||||
$height=count($bitmap);
|
||||
$bytemap=$this->bitmap2bytemap($bitmap);
|
||||
|
||||
$rowSize=floor(($width+31)/32)*4;
|
||||
$size=$rowSize*$height + 62; //62 metadata size
|
||||
#bitmap header
|
||||
$data= "BM"; //header
|
||||
$data.= (pack('V',$size)); //bitmap size ,4 bytes unsigned little endian
|
||||
$data.= "RRRR";
|
||||
$data.= (pack('V',14+40+8)); //bitmap data start offset ,
|
||||
//4 bytes unsigned little endian, 14 forced, 40 header, 8 colors
|
||||
|
||||
#info header
|
||||
$data.= pack('V',40); //bitmap header size (min 40),
|
||||
//4 bytes unsigned little-endian
|
||||
$data.= (pack('V',$width)); //bitmap width , 4 bytes signed integer
|
||||
$data.= (pack('V',$height)); //bitmap height , 4 bytes signed integer
|
||||
$data.= (pack('v',1)); //number of colored plains , 2 bytes
|
||||
$data.= (pack('v',1)); //color depth , 2 bytes
|
||||
$data.= (pack('V',0)); //compression algorithm , 4 bytes (0=none, RGB)
|
||||
$data.= (pack('V',0)); //size of raw data, 0 is fine for no compression
|
||||
$data.= (pack('V',11808)); //horizontal resolution (dpi), 4 bytes
|
||||
$data.= (pack('V',11808)); //vertical resolution (dpi), 4 bytes
|
||||
$data.= (pack('V',0)); //number of colors in pallette (0 = all), 4 bytes
|
||||
$data.= (pack('V',0)); //number of important colors (0 = all), 4 bytes
|
||||
|
||||
#color palette
|
||||
$data.= (pack('V',0x00FFFFFF)); //next color, white
|
||||
$data.= (pack('V',0)); //first color, black
|
||||
|
||||
for ($j=$height-1;$j>=0;--$j)
|
||||
for ($i=0;$i<$rowSize/4;++$i)
|
||||
for ($k=0;$k<4;++$k)
|
||||
if (isset($bytemap[$j][$i*4+$k]))
|
||||
$data.= pack('C',$bytemap[$j][$i*4+$k]);
|
||||
else
|
||||
$data.= pack('C',0);
|
||||
return $data;
|
||||
}
|
||||
/**
|
||||
* Converts a bitmap to a bytemap, which is necessary for outputting it
|
||||
*
|
||||
*/
|
||||
protected function bitmap2bytemap($bitmap)
|
||||
{
|
||||
$width=count($bitmap[0]);
|
||||
$height=count($bitmap);
|
||||
$bytemap=array();
|
||||
for ($j=0;$j<$height;++$j)
|
||||
{
|
||||
for ($i=0;$i<$width/8;++$i)
|
||||
{
|
||||
$bitstring="";
|
||||
for ($k=0;$k<8;++$k)
|
||||
if (isset($bitmap[$j][$i*8+$k]))
|
||||
$bitstring.=$bitmap[$j][$i*8+$k];
|
||||
else
|
||||
$bitstring.="0";
|
||||
$bytemap[$j][]=bindec($bitstring);
|
||||
}
|
||||
}
|
||||
return $bytemap;
|
||||
}
|
||||
/**
|
||||
* rotates a bitmap, returning new dimensions with the bitmap
|
||||
* return bitmap
|
||||
*/
|
||||
protected function rotateBitmap($bitmap, $degree)
|
||||
{
|
||||
$c=cos(deg2rad($degree));
|
||||
$s=sin(deg2rad($degree));
|
||||
|
||||
$width=count($bitmap[0]);
|
||||
$height=count($bitmap);
|
||||
$newHeight=round(abs($width*$s)+abs($height*$c));
|
||||
$newWidth=round(abs($width*$c) + abs($height*$s))+1;
|
||||
$x0 = $width/2 - $c*$newWidth/2 - $s*$newHeight/2;
|
||||
$y0 = $height/2 - $c*$newHeight/2 + $s*$newWidth/2;
|
||||
$result=array_fill(0, $newHeight, array_fill(0, $newWidth, 0));
|
||||
for ($j=0;$j<$newHeight;++$j)
|
||||
for ($i=1;$i<$newWidth;++$i)
|
||||
{
|
||||
$y=(int)(-$s*$i+$c*$j+$y0);
|
||||
$x=(int)($c*$i+$s*$j+$x0);
|
||||
if (isset($bitmap[$y][$x]))
|
||||
$result[$j][$i]=$bitmap[$y][$x];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* scales a bitmap to be bigger
|
||||
*/
|
||||
protected function scaleBitmap($bitmap,$scaleX,$scaleY)
|
||||
{
|
||||
$width=count($bitmap[0]);
|
||||
$height=count($bitmap);
|
||||
$newHeight=$height*$scaleY;
|
||||
$newWidth=$width*$scaleX;
|
||||
$result=array_fill(0, $newHeight, array_fill(0, $newWidth, 0));
|
||||
for ($j=0;$j<$newHeight;++$j)
|
||||
for ($i=0;$i<$newWidth;++$i)
|
||||
$result[$j][$i]=$bitmap[(int)($j/$scaleY)]
|
||||
[(int)($i/$scaleX)];
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* adds random noise to the captcha
|
||||
*/
|
||||
protected function distort($bitmap,$noisePercent=5)
|
||||
{
|
||||
for ($j=0;$j<count($bitmap);++$j)
|
||||
for ($i=0;$i<count($bitmap[0]);++$i)
|
||||
if (isset($bitmap[$j][$i]) && mt_rand()%100<$noisePercent)
|
||||
$bitmap[$j][$i]=1;
|
||||
return $bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* draw a captcha to the screen, returning its value
|
||||
*/
|
||||
public function show($distort=true,$scale=2.3)
|
||||
{
|
||||
$bitmap=$this->textBitmap($this->text);
|
||||
$degree=mt_rand(2,4);
|
||||
if (mt_rand()%100<50)
|
||||
$degree=-$degree;
|
||||
$bitmap=$this->rotateBitmap($bitmap,$degree);
|
||||
$bitmap=$this->scaleBitmap($bitmap,$scale,$scale);
|
||||
if ($distort) $bitmap=$this->distort($bitmap);
|
||||
return $this->inlineBitmap($bitmap);
|
||||
}
|
||||
|
||||
}
|
||||
516
includes/PIT/Zip.php
Normal file
516
includes/PIT/Zip.php
Normal file
@@ -0,0 +1,516 @@
|
||||
<?php
|
||||
/*
|
||||
| ZIP A ZipArchive and Plain PKZIP PHP Helper
|
||||
| @file ./PIT/ZIP.php
|
||||
| @author SamBrishes <sam@pytes.net>
|
||||
| @version 0.2.1 [0.2.1] - Beta
|
||||
|
|
||||
| @license X11 / MIT License
|
||||
| @copyright Copyright © 2015 - 2019 SamBrishes, pytesNET <info@pytes.net>
|
||||
*/
|
||||
/*
|
||||
| The following websites contains all required informations, which were unavoidable for the
|
||||
| creation of this class:
|
||||
|
|
||||
| - https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
|
||||
| - https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html
|
||||
| - https://php.net/manual/class.ziparchive.php
|
||||
*/
|
||||
|
||||
namespace PIT;
|
||||
|
||||
class Zip{
|
||||
const FLAGS = "\x00\x00";
|
||||
const VERSION = "\x14\x00";
|
||||
const SIGNATURE = "\x50\x4b";
|
||||
const COMPRESSION = "\x08\x00";
|
||||
|
||||
/*
|
||||
| SETTINGs
|
||||
*/
|
||||
public $zipArchive = false;
|
||||
public $compression = 6;
|
||||
|
||||
/*
|
||||
| ZIP ARCHIVE
|
||||
*/
|
||||
private $zipFilename;
|
||||
private $zipInstance;
|
||||
|
||||
/*
|
||||
| FALLBACK
|
||||
*/
|
||||
private $offset = 0;
|
||||
private $headers = array();
|
||||
private $central = array();
|
||||
private $counter = 0;
|
||||
|
||||
/*
|
||||
| CONSTRUCTOR
|
||||
| @since 0.2.0
|
||||
|
|
||||
| @param bool TRUE to check and use ZipArchive if available,
|
||||
| FALSE to use the PKZIP PHP compression per default.
|
||||
| @return int The compression level between -1 and 9.
|
||||
*/
|
||||
public function __construct($ziparchive = false, $compression = 6){
|
||||
if($ziparchive){
|
||||
$this->zipArchive = class_exists("ZipArchive", false);
|
||||
$this->zipInstance = ($this->zipArchive)? new ZipArchive(): false;
|
||||
}
|
||||
if($this->zipArchive){
|
||||
$this->zipFilename = tempnam(sys_get_temp_dir(), "tzp") . ".zip";
|
||||
$this->zipInstance->open($this->zipFilename, ZipArchive::CREATE);
|
||||
}
|
||||
$this->compression = ($compression >= -1 && $compression <= 9)? $compression: 6;
|
||||
}
|
||||
|
||||
/*
|
||||
| DESTRUCTOR
|
||||
| @since 0.2.0
|
||||
*/
|
||||
public function __destruct(){
|
||||
$this->clear(false);
|
||||
}
|
||||
|
||||
/*
|
||||
| HELPER :: CONVERT UNIX TO DOS TIME
|
||||
| @since 0.2.1
|
||||
|
|
||||
| @param int The respective timestamp as INTEGER.
|
||||
*/
|
||||
protected function msDOSTime($time){
|
||||
$array = getdate((is_int($time) && $time > 0)? $time: time());
|
||||
if($array["year"] < 1980 || $array["year"] > 2107){
|
||||
$array = getdate(time());
|
||||
}
|
||||
|
||||
// Return as DEC
|
||||
return (
|
||||
(($array["year"]-1980 << 25)) |
|
||||
(($array["mon"] << 21)) |
|
||||
(($array["mday"] << 16)) |
|
||||
(($array["hours"] << 11)) |
|
||||
(($array["minutes"] << 5)) |
|
||||
(($array["seconds"] >> 1))
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
| ADD A FILE
|
||||
| @since 0.1.0
|
||||
| @update 0.2.1
|
||||
|
|
||||
| @param string The relative or absolute filepath or the respective file content.
|
||||
| @param string The local path within the archive file.
|
||||
| @param int The timestamp to use.
|
||||
| @param string The optional file comment or just an empty string.
|
||||
|
|
||||
| @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function addFile($data, $path, $time = 0, $comment = ""){
|
||||
if((!is_string($data) && !is_numeric($data)) || !is_string($path)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sanitize Data
|
||||
if(is_string($data) && file_exists($data) && is_file($data)){
|
||||
$data = file_get_contents($data);
|
||||
}
|
||||
$path = trim(str_replace("\\", "/", $path), "/");
|
||||
$time = $this->msDOSTime($time);
|
||||
|
||||
// Zip Archive
|
||||
if($this->zipArchive){
|
||||
return $this->zipInstance->addFromString($path, $data);
|
||||
}
|
||||
|
||||
// Fallback
|
||||
$crcval = crc32($data);
|
||||
$length = strlen($data);
|
||||
if(version_compare(PHP_VERSION, "5.4.0", ">=")){
|
||||
$gzcval = gzcompress($data, $this->compression, ZLIB_ENCODING_DEFLATE);
|
||||
} else {
|
||||
$gzcval = gzcompress($data, $this->compression);
|
||||
}
|
||||
$gzcval = substr($gzcval, 2, strlen($gzcval) - 6); // Fix CRC-32 Bug
|
||||
$gzclen = strlen($gzcval);
|
||||
|
||||
/*
|
||||
| LOCAL FILE HEADER
|
||||
| 01 SIGNATURE
|
||||
| 02 Version needed to extract this archive.
|
||||
| 03 General purpose bit flag.
|
||||
| 04 Compression method.
|
||||
| 05 Last modification DOS datetime.
|
||||
| 06 CRC32 value.
|
||||
| 07 Compressed Filesize.
|
||||
| 08 Uncompressed Filesize.
|
||||
| 09 Length of the filename inside the archive.
|
||||
| 10 Length of the extra fields.
|
||||
| 11 The relative path / filename inside the archive.
|
||||
| 12 The main file data value.
|
||||
*/
|
||||
$this->headers[] =
|
||||
self::SIGNATURE . "\x03\x04" .
|
||||
self::VERSION .
|
||||
self::FLAGS .
|
||||
self::COMPRESSION .
|
||||
pack("V", $time) .
|
||||
pack("V", $crcval) .
|
||||
pack("V", $gzclen) .
|
||||
pack("V", $length) .
|
||||
pack("v", strlen($path)) .
|
||||
pack("v", 0) .
|
||||
$path .
|
||||
$gzcval;
|
||||
|
||||
/*
|
||||
| CENTRAL DIRECTORY RECORD
|
||||
| 01 SIGNATURE
|
||||
| 02 MadeBy Version numbers.
|
||||
| 03 Version needed to extract this archive.
|
||||
| 04 General purpose bit flag.
|
||||
| 05 Compression method.
|
||||
| 06 Last modification DOS datetime.
|
||||
| 07 CRC32 value.
|
||||
| 08 Compressed Filesize.
|
||||
| 09 Uncompressed Filesize.
|
||||
| 10 Length of the filename inside the archive.
|
||||
| 11 Length of the extra fields.
|
||||
| 12 Length of the file comment.
|
||||
| 13 The disk number where the file exists.
|
||||
| 14 Internal file attributes.
|
||||
| 15 External file attributes.
|
||||
| 16 Offset of the local file header.
|
||||
| 17 The relative path / filename inside the archive.
|
||||
| 18 The file comment.
|
||||
*/
|
||||
$this->central[] =
|
||||
self::SIGNATURE . "\x01\x02" .
|
||||
"\x00\x00" .
|
||||
self::VERSION .
|
||||
self::FLAGS .
|
||||
self::COMPRESSION .
|
||||
pack("V", $time) .
|
||||
pack("V", $crcval) .
|
||||
pack("V", $gzclen) .
|
||||
pack("V", $length) .
|
||||
pack("v", strlen($path)) .
|
||||
pack("v", 0) .
|
||||
pack("v", strlen($comment)) .
|
||||
pack("v", 0) .
|
||||
pack("v", 0) .
|
||||
pack("V", 32) .
|
||||
pack("V", $this->offset) .
|
||||
$path .
|
||||
$comment;
|
||||
|
||||
// Count Offset and Return
|
||||
$this->offset += strlen($this->headers[count($this->headers)-1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| ADD MULTIPLE FILES
|
||||
| @since 0.2.0
|
||||
| @update 0.2.1
|
||||
|
|
||||
| @param array Multiple 'local/file/path' => "filepath/or/filecontent" ARRAY pairs.
|
||||
| @param int The timestamp to use for all files.
|
||||
| @param string The optional comment or just an empty string for alle respective files.
|
||||
|
|
||||
| @return int The number of successfully added elements / files.
|
||||
*/
|
||||
public function addFiles($array, $time = 0, $comment = ""){
|
||||
if(!is_array($array)){
|
||||
return false;
|
||||
}
|
||||
foreach($array AS $path => &$data){
|
||||
$data = $this->addFile($data, $path);
|
||||
}
|
||||
return array_filter(array_values($array));
|
||||
}
|
||||
|
||||
/*
|
||||
| ADD FOLDER
|
||||
| @since 0.2.0
|
||||
|
|
||||
| @param string The path to the folder, which should be zipped.
|
||||
| @param string The local path within the zip file.
|
||||
| @param bool TRUE to zip recursive and include all sub directories,
|
||||
| FALSE to just zip all files within the $path folder.
|
||||
| @param bool TRUE to include empty folders on recursive zips.
|
||||
| FALSE to skip empty folders.
|
||||
|
|
||||
| @return multi The number as INT of successfully added elements / files,
|
||||
| FALSE on failure.
|
||||
*/
|
||||
public function addFolder($path, $local = "/", $recursive = false, $empty = false){
|
||||
if(!file_exists($path) || !is_dir($path)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Chech Path
|
||||
$path = str_replace(array("/", "\\"), DIRECTORY_SEPARATOR, realpath($path));
|
||||
if(strpos($path, DIRECTORY_SEPARATOR) !== strlen($path)-1){
|
||||
$path .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
// Check Local
|
||||
if(!is_string($local)){
|
||||
$local = "";
|
||||
}
|
||||
$local = trim(str_replace("\\", "/", $local), "/") . "/";
|
||||
|
||||
// Start Flow
|
||||
$this->counter = 0;
|
||||
$this->addFolderFlow($path, "", $local, !!$recursive, !!$empty);
|
||||
return $this->counter;
|
||||
}
|
||||
|
||||
/*
|
||||
| HELPER :: ADD FOLDER LOOP
|
||||
| @since 0.2.0
|
||||
|
|
||||
| @param string The base path to the folder, which should be zipped.
|
||||
| @param string The further path, within the base path, on recursive calls.
|
||||
| @param string The local path within the zip file.
|
||||
| @param bool TRUE to zip recursive and include all sub directories,
|
||||
| FALSE to just zip all files within the $path folder.
|
||||
| @param bool TRUE to include empty folders on recursive zips.
|
||||
| FALSE to skip empty folders.
|
||||
|
|
||||
| @return int The number of successfully added elements / files.
|
||||
*/
|
||||
private function addFolderFlow($base, $path = "", $local = "", $recursive = false, $empty = false){
|
||||
$path = str_replace(array("/", "\\"), DIRECTORY_SEPARATOR, $path);
|
||||
$path = trim($path, DIRECTORY_SEPARATOR);
|
||||
if(!empty($path)){
|
||||
$path .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$handle = opendir($base . $path);
|
||||
while(($file = readdir($handle)) !== false){
|
||||
if(in_array($file, array(".", ".."))){
|
||||
continue;
|
||||
}
|
||||
if(is_dir($base . $path . $file)){
|
||||
if($recursive){
|
||||
$count = $this->addFolderFlow($base, $path . $file, $local, $recursive, $empty);
|
||||
if($count == 0 && $empty){
|
||||
$this->addEmptyFolder($local . $path . $file);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(is_file($base . $path . $file)){
|
||||
if($this->addFile($base . $path . $file, $local . $path . $file)){
|
||||
$count++;
|
||||
$this->counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
return $count;
|
||||
}
|
||||
|
||||
/*
|
||||
| ADD EMPTY FOLDER
|
||||
| @since 0.2.0
|
||||
| @update 0.2.1
|
||||
|
|
||||
| @param string The local path structure within the zip file.
|
||||
| @param int The timestamp to use.
|
||||
| @param string The optional file comment or just an empty string.
|
||||
|
|
||||
| @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function addEmptyFolder($path, $time = 0, $comment = ""){
|
||||
$path = trim(str_replace("\\", "/", $path), "/") . "/";
|
||||
$time = $this->msDOSTime($time);
|
||||
|
||||
// ZipArchive
|
||||
if($this->zipArchive){
|
||||
return $this->zipInstance->addEmptyDir($path);
|
||||
}
|
||||
|
||||
// Add Header
|
||||
$this->headers[] =
|
||||
self::SIGNATURE . "\x03\x04" .
|
||||
self::VERSION .
|
||||
self::FLAGS .
|
||||
"\x00\x00" .
|
||||
pack("V", $time) .
|
||||
pack("V", 0) .
|
||||
pack("V", 0) .
|
||||
pack("V", 0) .
|
||||
pack("v", strlen($path)) .
|
||||
pack("v", 0) .
|
||||
$path .
|
||||
"";
|
||||
|
||||
// Add Central
|
||||
$this->central[] =
|
||||
self::SIGNATURE . "\x01\x02" .
|
||||
"\x14\x03" .
|
||||
self::VERSION .
|
||||
self::FLAGS .
|
||||
"\x00\x00" .
|
||||
pack("V", $time) .
|
||||
pack("V", 0) .
|
||||
pack("V", 0) .
|
||||
pack("V", 0) .
|
||||
pack("v", strlen($path)) .
|
||||
pack("v", 0) .
|
||||
pack("v", strlen($comment)) .
|
||||
pack("v", 0) .
|
||||
pack("v", 0) .
|
||||
"\x00\x00\xFF\x41" .
|
||||
pack("V", $this->offset) .
|
||||
$path .
|
||||
$comment;
|
||||
|
||||
// Count Offset and Return
|
||||
$this->offset += strlen($this->headers[count($this->headers)-1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| CLEAR DATA STRINGs
|
||||
| @since 0.1.0
|
||||
| @update 0.2.0
|
||||
*/
|
||||
public function clear($new = true){
|
||||
if($this->zipArchive){
|
||||
if(is_a($this->zipInstance, "ZipArchive")){
|
||||
$this->zipInstance->close();
|
||||
}
|
||||
if(strpos($this->zipFilename, sys_get_temp_dir()) === 0 && file_exists($this->zipFilename)){
|
||||
@unlink($this->zipFilename);
|
||||
}
|
||||
if($new){
|
||||
$this->zipFilename = "./temp-".time().".zip";
|
||||
$this->zipInstance = new ZipArchive();
|
||||
$this->zipInstance->open($this->zipFilename, ZipArchive::CREATE);
|
||||
}
|
||||
}
|
||||
$this->offset = 0;
|
||||
$this->headers = array();
|
||||
$this->central = array();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| DUMBS OUT THE FILE
|
||||
| @since 0.1.0
|
||||
| @update 0.2.1
|
||||
*/
|
||||
public function file(){
|
||||
$comment = "PKZipped with https://github.com/SamBrishes/FoxCMS/tree/helpers/zip";
|
||||
|
||||
// ZipArchive
|
||||
if($this->zipArchive){
|
||||
$this->zipInstance->setArchiveComment($comment);
|
||||
$this->zipInstance->close();
|
||||
|
||||
$content = file_get_contents($this->zipFilename);
|
||||
|
||||
$this->zipInstance = new ZipArchive();
|
||||
$this->zipInstance->open($this->zipFilename);
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Fallback
|
||||
$headers = implode("", $this->headers);
|
||||
$central = implode("", $this->central);
|
||||
/*
|
||||
| RETURN
|
||||
| 01 The file header items.
|
||||
| 02 The central directory items.
|
||||
| 03 The signature for the end of the central directory record.
|
||||
| 04 The number of this disk / part.
|
||||
| 05 The number of the disk / part where the central directory starts.
|
||||
| 06 The number of central directoy entries on this disk.
|
||||
| 07 Total number of entries on this disk / part.
|
||||
| 08 Total number of entries in general.
|
||||
| 09 Length of the central directory.
|
||||
| 10 Offset where the central directory starts.
|
||||
| 11 The length of the following comment field.
|
||||
| 12 The archive comment.
|
||||
*/
|
||||
return $headers . $central .
|
||||
self::SIGNATURE . "\x05\x06" .
|
||||
"\x00" .
|
||||
"\x00" .
|
||||
"\x00" .
|
||||
"\x00" .
|
||||
pack("v", count($this->central)) .
|
||||
pack("v", count($this->central)) .
|
||||
pack("V", strlen($central)) .
|
||||
pack("V", strlen($headers)) .
|
||||
pack("v", strlen($comment)) .
|
||||
$comment;
|
||||
}
|
||||
|
||||
/*
|
||||
| STORE THE ZIP FILE
|
||||
| @since 0.1.0
|
||||
| @update 0.2.0
|
||||
|
|
||||
| @param string The filename with the respective path to store the archive.
|
||||
| @param bool TRUE to overwrite existing archives, FALSE to do it not.
|
||||
|
|
||||
| @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function save($filename = "archive.zip", $overwrite = false){
|
||||
if(file_exists($filename) && !$overwrite){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Zip Archive
|
||||
if($this->zipArchive){
|
||||
if(is_a($this->zipInstance, "ZipArchive")){
|
||||
$this->zipInstance->close();
|
||||
}
|
||||
if(@file_put_contents($filename, file_get_contents($this->zipFilename))){
|
||||
@unlink($this->zipFilename);
|
||||
|
||||
$this->zipFilename = $filename;
|
||||
$this->zipInstance = new ZipArchive();
|
||||
return $this->zipInstance->open($this->zipFilename);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return @file_put_contents($filename, $this->file()) !== false;
|
||||
}
|
||||
|
||||
/*
|
||||
| DOWNLOAD THE FILE
|
||||
| @since 0.1.0
|
||||
| @update 0.2.0
|
||||
|
|
||||
| @param string The filename for the archiv.
|
||||
| @param bool TRUE to exit after the execution, FALSE to do it not.
|
||||
|
|
||||
| @return void
|
||||
*/
|
||||
public function download($filename = "archive.zip", $exit = false){
|
||||
$file = $this->file();
|
||||
header("Pragma: public");
|
||||
header("Expires: 0");
|
||||
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
|
||||
header("Cache-Control: private", false);
|
||||
header("Content-Type: application/zip");
|
||||
header("Content-Disposition: attachment; filename={$filename};" );
|
||||
header("Content-Transfer-Encoding: binary");
|
||||
header("Content-Length: " . strlen($file));
|
||||
print ($file);
|
||||
if($exit){
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
23
includes/autoload.php
Normal file
23
includes/autoload.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./includes/autoload.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>
|
||||
*/
|
||||
|
||||
spl_autoload_register(function($class){
|
||||
foreach(array("Gregwar", "Identicon", "PIT", "OWASP") AS $allowed){
|
||||
if(strpos($class, $allowed) !== 0){
|
||||
continue;
|
||||
}
|
||||
$path = dirname(__FILE__) . DIRECTORY_SEPARATOR;
|
||||
$class = str_replace("\\", DIRECTORY_SEPARATOR, $class);
|
||||
require_once $class . ".php";
|
||||
}
|
||||
return false;
|
||||
});
|
||||
BIN
includes/img/default-avatar.jpg
Normal file
BIN
includes/img/default-avatar.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
205
includes/js/identicon.js
Normal file
205
includes/js/identicon.js
Normal file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Identicon.js 2.3.3
|
||||
* http://github.com/stewartlord/identicon.js
|
||||
*
|
||||
* PNGLib required for PNG output
|
||||
* http://www.xarg.org/download/pnglib.js
|
||||
*
|
||||
* Copyright 2018, Stewart Lord
|
||||
* Released under the BSD license
|
||||
* http://www.opensource.org/licenses/bsd-license.php
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var PNGlib;
|
||||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
|
||||
PNGlib = require('./pnglib');
|
||||
} else {
|
||||
PNGlib = window.PNGlib;
|
||||
}
|
||||
|
||||
var Identicon = function(hash, options){
|
||||
if (typeof(hash) !== 'string' || hash.length < 15) {
|
||||
throw 'A hash of at least 15 characters is required.';
|
||||
}
|
||||
|
||||
this.defaults = {
|
||||
background: [240, 240, 240, 255],
|
||||
margin: 0.08,
|
||||
size: 64,
|
||||
saturation: 0.7,
|
||||
brightness: 0.5,
|
||||
format: 'png'
|
||||
};
|
||||
|
||||
this.options = typeof(options) === 'object' ? options : this.defaults;
|
||||
|
||||
// backward compatibility with old constructor (hash, size, margin)
|
||||
if (typeof(arguments[1]) === 'number') { this.options.size = arguments[1]; }
|
||||
if (arguments[2]) { this.options.margin = arguments[2]; }
|
||||
|
||||
this.hash = hash
|
||||
this.background = this.options.background || this.defaults.background;
|
||||
this.size = this.options.size || this.defaults.size;
|
||||
this.format = this.options.format || this.defaults.format;
|
||||
this.margin = this.options.margin !== undefined ? this.options.margin : this.defaults.margin;
|
||||
|
||||
// foreground defaults to last 7 chars as hue at 70% saturation, 50% brightness
|
||||
var hue = parseInt(this.hash.substr(-7), 16) / 0xfffffff;
|
||||
var saturation = this.options.saturation || this.defaults.saturation;
|
||||
var brightness = this.options.brightness || this.defaults.brightness;
|
||||
this.foreground = this.options.foreground || this.hsl2rgb(hue, saturation, brightness);
|
||||
};
|
||||
|
||||
Identicon.prototype = {
|
||||
background: null,
|
||||
foreground: null,
|
||||
hash: null,
|
||||
margin: null,
|
||||
size: null,
|
||||
format: null,
|
||||
|
||||
image: function(){
|
||||
return this.isSvg()
|
||||
? new Svg(this.size, this.foreground, this.background)
|
||||
: new PNGlib(this.size, this.size, 256);
|
||||
},
|
||||
|
||||
render: function(){
|
||||
var image = this.image(),
|
||||
size = this.size,
|
||||
baseMargin = Math.floor(size * this.margin),
|
||||
cell = Math.floor((size - (baseMargin * 2)) / 5),
|
||||
margin = Math.floor((size - cell * 5) / 2),
|
||||
bg = image.color.apply(image, this.background),
|
||||
fg = image.color.apply(image, this.foreground);
|
||||
|
||||
// the first 15 characters of the hash control the pixels (even/odd)
|
||||
// they are drawn down the middle first, then mirrored outwards
|
||||
var i, color;
|
||||
for (i = 0; i < 15; i++) {
|
||||
color = parseInt(this.hash.charAt(i), 16) % 2 ? bg : fg;
|
||||
if (i < 5) {
|
||||
this.rectangle(2 * cell + margin, i * cell + margin, cell, cell, color, image);
|
||||
} else if (i < 10) {
|
||||
this.rectangle(1 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
|
||||
this.rectangle(3 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
|
||||
} else if (i < 15) {
|
||||
this.rectangle(0 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
|
||||
this.rectangle(4 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
},
|
||||
|
||||
rectangle: function(x, y, w, h, color, image){
|
||||
if (this.isSvg()) {
|
||||
image.rectangles.push({x: x, y: y, w: w, h: h, color: color});
|
||||
} else {
|
||||
var i, j;
|
||||
for (i = x; i < x + w; i++) {
|
||||
for (j = y; j < y + h; j++) {
|
||||
image.buffer[image.index(i, j)] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// adapted from: https://gist.github.com/aemkei/1325937
|
||||
hsl2rgb: function(h, s, b){
|
||||
h *= 6;
|
||||
s = [
|
||||
b += s *= b < .5 ? b : 1 - b,
|
||||
b - h % 1 * s * 2,
|
||||
b -= s *= 2,
|
||||
b,
|
||||
b + h % 1 * s,
|
||||
b + s
|
||||
];
|
||||
|
||||
return[
|
||||
s[ ~~h % 6 ] * 255, // red
|
||||
s[ (h|16) % 6 ] * 255, // green
|
||||
s[ (h|8) % 6 ] * 255 // blue
|
||||
];
|
||||
},
|
||||
|
||||
toString: function(raw){
|
||||
// backward compatibility with old toString, default to base64
|
||||
if (raw) {
|
||||
return this.render().getDump();
|
||||
} else {
|
||||
return this.render().getBase64();
|
||||
}
|
||||
},
|
||||
|
||||
isSvg: function(){
|
||||
return this.format.match(/svg/i)
|
||||
}
|
||||
};
|
||||
|
||||
var Svg = function(size, foreground, background){
|
||||
this.size = size;
|
||||
this.foreground = this.color.apply(this, foreground);
|
||||
this.background = this.color.apply(this, background);
|
||||
this.rectangles = [];
|
||||
};
|
||||
|
||||
Svg.prototype = {
|
||||
size: null,
|
||||
foreground: null,
|
||||
background: null,
|
||||
rectangles: null,
|
||||
|
||||
color: function(r, g, b, a){
|
||||
var values = [r, g, b].map(Math.round);
|
||||
values.push((a >= 0) && (a <= 255) ? a/255 : 1);
|
||||
return 'rgba(' + values.join(',') + ')';
|
||||
},
|
||||
|
||||
getDump: function(){
|
||||
var i,
|
||||
xml,
|
||||
rect,
|
||||
fg = this.foreground,
|
||||
bg = this.background,
|
||||
stroke = this.size * 0.005;
|
||||
|
||||
xml = "<svg xmlns='http://www.w3.org/2000/svg'"
|
||||
+ " width='" + this.size + "' height='" + this.size + "'"
|
||||
+ " style='background-color:" + bg + ";'>"
|
||||
+ "<g style='fill:" + fg + "; stroke:" + fg + "; stroke-width:" + stroke + ";'>";
|
||||
|
||||
for (i = 0; i < this.rectangles.length; i++) {
|
||||
rect = this.rectangles[i];
|
||||
if (rect.color == bg) continue;
|
||||
xml += "<rect "
|
||||
+ " x='" + rect.x + "'"
|
||||
+ " y='" + rect.y + "'"
|
||||
+ " width='" + rect.w + "'"
|
||||
+ " height='" + rect.h + "'"
|
||||
+ "/>";
|
||||
}
|
||||
xml += "</g></svg>"
|
||||
|
||||
return xml;
|
||||
},
|
||||
|
||||
getBase64: function(){
|
||||
if ('function' === typeof btoa) {
|
||||
return btoa(this.getDump());
|
||||
} else if (Buffer) {
|
||||
return new Buffer(this.getDump(), 'binary').toString('base64');
|
||||
} else {
|
||||
throw 'Cannot generate base64 output';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
|
||||
module.exports = Identicon;
|
||||
} else {
|
||||
window.Identicon = Identicon;
|
||||
}
|
||||
})();
|
||||
214
includes/js/pnglib.js
Normal file
214
includes/js/pnglib.js
Normal file
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* A handy class to calculate color values.
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Robert Eisele <robert@xarg.org>
|
||||
* @copyright Copyright (c) 2010, Robert Eisele
|
||||
* @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
*
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
// helper functions for that ctx
|
||||
function write(buffer, offs) {
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
for (var j = 0; j < arguments[i].length; j++) {
|
||||
buffer[offs++] = arguments[i].charAt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function byte2(w) {
|
||||
return String.fromCharCode((w >> 8) & 255, w & 255);
|
||||
}
|
||||
|
||||
function byte4(w) {
|
||||
return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255);
|
||||
}
|
||||
|
||||
function byte2lsb(w) {
|
||||
return String.fromCharCode(w & 255, (w >> 8) & 255);
|
||||
}
|
||||
|
||||
// modified from original source to support NPM
|
||||
var PNGlib = function(width,height,depth) {
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
|
||||
// pixel data and row filter identifier size
|
||||
this.pix_size = height * (width + 1);
|
||||
|
||||
// deflate header, pix_size, block headers, adler32 checksum
|
||||
this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4;
|
||||
|
||||
// offsets and sizes of Png chunks
|
||||
this.ihdr_offs = 0; // IHDR offset and size
|
||||
this.ihdr_size = 4 + 4 + 13 + 4;
|
||||
this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size
|
||||
this.plte_size = 4 + 4 + 3 * depth + 4;
|
||||
this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size
|
||||
this.trns_size = 4 + 4 + depth + 4;
|
||||
this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size
|
||||
this.idat_size = 4 + 4 + this.data_size + 4;
|
||||
this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size
|
||||
this.iend_size = 4 + 4 + 4;
|
||||
this.buffer_size = this.iend_offs + this.iend_size; // total PNG size
|
||||
|
||||
this.buffer = new Array();
|
||||
this.palette = new Object();
|
||||
this.pindex = 0;
|
||||
|
||||
var _crc32 = new Array();
|
||||
|
||||
// initialize buffer with zero bytes
|
||||
for (var i = 0; i < this.buffer_size; i++) {
|
||||
this.buffer[i] = "\x00";
|
||||
}
|
||||
|
||||
// initialize non-zero elements
|
||||
write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03");
|
||||
write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE');
|
||||
write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS');
|
||||
write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT');
|
||||
write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND');
|
||||
|
||||
// initialize deflate header
|
||||
var header = ((8 + (7 << 4)) << 8) | (3 << 6);
|
||||
header+= 31 - (header % 31);
|
||||
|
||||
write(this.buffer, this.idat_offs + 8, byte2(header));
|
||||
|
||||
// initialize deflate block headers
|
||||
for (var i = 0; (i << 16) - 1 < this.pix_size; i++) {
|
||||
var size, bits;
|
||||
if (i + 0xffff < this.pix_size) {
|
||||
size = 0xffff;
|
||||
bits = "\x00";
|
||||
} else {
|
||||
size = this.pix_size - (i << 16) - i;
|
||||
bits = "\x01";
|
||||
}
|
||||
write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size));
|
||||
}
|
||||
|
||||
/* Create crc32 lookup table */
|
||||
for (var i = 0; i < 256; i++) {
|
||||
var c = i;
|
||||
for (var j = 0; j < 8; j++) {
|
||||
if (c & 1) {
|
||||
c = -306674912 ^ ((c >> 1) & 0x7fffffff);
|
||||
} else {
|
||||
c = (c >> 1) & 0x7fffffff;
|
||||
}
|
||||
}
|
||||
_crc32[i] = c;
|
||||
}
|
||||
|
||||
// compute the index into a png for a given pixel
|
||||
this.index = function(x,y) {
|
||||
var i = y * (this.width + 1) + x + 1;
|
||||
var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i;
|
||||
return j;
|
||||
}
|
||||
|
||||
// convert a color and build up the palette
|
||||
this.color = function(red, green, blue, alpha) {
|
||||
|
||||
alpha = alpha >= 0 ? alpha : 255;
|
||||
var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue;
|
||||
|
||||
if (typeof this.palette[color] == "undefined") {
|
||||
if (this.pindex == this.depth) return "\x00";
|
||||
|
||||
var ndx = this.plte_offs + 8 + 3 * this.pindex;
|
||||
|
||||
this.buffer[ndx + 0] = String.fromCharCode(red);
|
||||
this.buffer[ndx + 1] = String.fromCharCode(green);
|
||||
this.buffer[ndx + 2] = String.fromCharCode(blue);
|
||||
this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha);
|
||||
|
||||
this.palette[color] = String.fromCharCode(this.pindex++);
|
||||
}
|
||||
return this.palette[color];
|
||||
}
|
||||
|
||||
// output a PNG string, Base64 encoded
|
||||
this.getBase64 = function() {
|
||||
|
||||
var s = this.getDump();
|
||||
|
||||
var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
var c1, c2, c3, e1, e2, e3, e4;
|
||||
var l = s.length;
|
||||
var i = 0;
|
||||
var r = "";
|
||||
|
||||
do {
|
||||
c1 = s.charCodeAt(i);
|
||||
e1 = c1 >> 2;
|
||||
c2 = s.charCodeAt(i+1);
|
||||
e2 = ((c1 & 3) << 4) | (c2 >> 4);
|
||||
c3 = s.charCodeAt(i+2);
|
||||
if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); }
|
||||
if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; }
|
||||
r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4);
|
||||
} while ((i+= 3) < l);
|
||||
return r;
|
||||
}
|
||||
|
||||
// output a PNG string
|
||||
this.getDump = function() {
|
||||
|
||||
// compute adler32 of output pixels + row filter bytes
|
||||
var BASE = 65521; /* largest prime smaller than 65536 */
|
||||
var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
|
||||
var s1 = 1;
|
||||
var s2 = 0;
|
||||
var n = NMAX;
|
||||
|
||||
for (var y = 0; y < this.height; y++) {
|
||||
for (var x = -1; x < this.width; x++) {
|
||||
s1+= this.buffer[this.index(x, y)].charCodeAt(0);
|
||||
s2+= s1;
|
||||
if ((n-= 1) == 0) {
|
||||
s1%= BASE;
|
||||
s2%= BASE;
|
||||
n = NMAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
s1%= BASE;
|
||||
s2%= BASE;
|
||||
write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1));
|
||||
|
||||
// compute crc32 of the PNG chunks
|
||||
function crc32(png, offs, size) {
|
||||
var crc = -1;
|
||||
for (var i = 4; i < size-4; i += 1) {
|
||||
crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff);
|
||||
}
|
||||
write(png, offs+size-4, byte4(crc ^ -1));
|
||||
}
|
||||
|
||||
crc32(this.buffer, this.ihdr_offs, this.ihdr_size);
|
||||
crc32(this.buffer, this.plte_offs, this.plte_size);
|
||||
crc32(this.buffer, this.trns_offs, this.trns_size);
|
||||
crc32(this.buffer, this.idat_offs, this.idat_size);
|
||||
crc32(this.buffer, this.iend_offs, this.iend_size);
|
||||
|
||||
// convert PNG to string
|
||||
return "\x89PNG\r\n\x1a\n"+this.buffer.join('');
|
||||
}
|
||||
}
|
||||
|
||||
// modified from original source to support NPM
|
||||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
|
||||
module.exports = PNGlib;
|
||||
} else {
|
||||
window.PNGlib = PNGlib;
|
||||
}
|
||||
})();
|
||||
6
languages/de.json
Normal file
6
languages/de.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugin-data": {
|
||||
"name": "Snicker",
|
||||
"description": "Ein natives, AJAX-unterstütztes FlatFile Kommentiersystem für Bludit, mit einer Abonnement-Funktion und im Einklang mit der DSGVO!"
|
||||
}
|
||||
}
|
||||
204
languages/de_DE.json
Normal file
204
languages/de_DE.json
Normal file
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"plugin-data": {
|
||||
"name": "Snicker",
|
||||
"description": "Ein natives, AJAX-unterstütztes FlatFile Kommentiersystem für Bludit, mit einer Abonnement-Funktion und im Einklang mit der DSGVO!"
|
||||
},
|
||||
"s18n-a5d491060952aa8ad5fdee071be752de": "Kommentare",
|
||||
"s18n-de95b43bceeb4b998aed4aed5cef1ae7": "Bearbeiten",
|
||||
"s18n-f984023ed3d6df2326c9d59838c29792": "Kommentar bearbeiten",
|
||||
"s18n-86448a506dd93303a72140b9124ee321": "Kommentar entfernen",
|
||||
"s18n-db10a8eb963bc0e5f4483ac9b5dc554c": "Kommentar-Titel",
|
||||
"s18n-335630425567dbe91768a3beffdec752": "Kommentar-Inhalt",
|
||||
"s18n-bd7e63f881c7f787a9a0dce20b7f9e5b": "Meta Einstellungen",
|
||||
"s18n-1acfe725df7bd12195751f0737c4d375": "Registrierter Nutzer",
|
||||
"s18n-e988189db402fab453f72052629c02cd": "Kommentar-Benutzername",
|
||||
"s18n-db84c6236ca6a01b9189504d78c012a5": "Kommentar-eMail",
|
||||
"s18n-7c6c2e5d48ab37a007cbf70d3ea25fa4": "Ausstehend",
|
||||
"s18n-787d5f05953ec39b108869dfdd7733e6": "Genehmigt",
|
||||
"s18n-c7537d6d48ecf261749c09a9f284bd45": "Abgelehnt",
|
||||
"s18n-e09f6a7593f8ae3994ea57e1117f67ec": "Spam",
|
||||
"s18n-7a5115c2c1eb662308decbec83593494": "Seite aufrufen",
|
||||
"s18n-0572a05cd6d6360391993a611099542f": "Kommentar Titel oder Auszug",
|
||||
"s18n-428f14500191b5d53675da4a96bc8bba": "Kommentare suchen",
|
||||
"s18n-e0be71bccdeceb713fba3c222f79a3c5": "Keine Kommentare verfügbar",
|
||||
"s18n-06d4cd63bde972fc66a0aed41d2f5c51": "Kommentar",
|
||||
"s18n-02bd92faa38aaa6cc0ea75e59937a1ef": "Autor",
|
||||
"s18n-ebb67a4271abe715344471b0f16321f6": "Aktionen",
|
||||
"s18n-58566b9a9b2733b0ceacb2186672b5d1": "Zeige alle Antworten",
|
||||
"s18n-5f44c0081bd862a77ba8b24e923cadf1": "Antwort zu",
|
||||
"s18n-eb399bcaca686f8609137153307eecf1": "Ändern",
|
||||
"s18n-2736f4347985da50dc023444c193bfea": "Kommentar bBearbeiten",
|
||||
"s18n-a107bf4b12e36a07161a26d95b03bc81": "Kommentar genehmigen",
|
||||
"s18n-da937abd19cd9e1430470b8a471a41d4": "Kommentar ablehnen",
|
||||
"s18n-32cb4199893d9948cc0853eef244f1fc": "Als Spam markieren",
|
||||
"s18n-a6494adfb72d12d3a4da66855c284ec6": "Zurück zu ausstehend",
|
||||
"s18n-1bda80f2be4d3658e0baa43fbe7ae8c1": "Anzeigen",
|
||||
"s18n-c9ae5a4214e87ad6fbed44a267471eee": "Einstellungen speichern",
|
||||
"s18n-e124d357c3c832434a8676a5e18db842": "Allgemeine Einstellungen",
|
||||
"s18n-b62a9dcd666f3ff44197cf21ac66507d": "Kommentar Moderation",
|
||||
"s18n-7f0217cdcdd58ba86aae84d9d3d79f81": "Moderiere",
|
||||
"s18n-1a1dc91c907325c69271ddf0c944bc72": "Genehmige",
|
||||
"s18n-df6963dc912cde9baeef10343167ba01": "jeden Kommentar",
|
||||
"s18n-39ba4181e212acf183ac965c9b37da89": "Außer der Nutzer ist angemeldet",
|
||||
"s18n-0dac4426a017f0a0370db32776bc30bb": "Außer der Nutzer ist Admin oder der Autor",
|
||||
"s18n-c70aa8b8fd6f2652eda2d5366faf1de5": "Außer der Nutzer hat bereits einen genehmigen Kommentar",
|
||||
"s18n-8b26cab9d444760b4bcc65dc4d8634f8": "Kommentare erlauben",
|
||||
"s18n-67614909bf9de326de71946036de39f1": "... auf Öffentliche Seiten",
|
||||
"s18n-a7c384c1b60785c40bc3a4e4dfc5a108": "... auf Fixierte Seiten",
|
||||
"s18n-9907b78f8745810599dbf6fd29a01364": "... auf Statische Seiten",
|
||||
"s18n-c89cbecce48d04cd76c0b95c8128ad97": "Aktivieren (Optional)",
|
||||
"s18n-e9d51286fdd0ff058650392fc8b6ae30": "Aktivieren (Erforderlich)",
|
||||
"s18n-0aaa87422396fdd678498793b6d5250e": "Deaktivieren",
|
||||
"s18n-8da2c8185edfeb1765526f8e2e4f388d": "Kommentar Limit",
|
||||
"s18n-77d6d185c93549dab24f29ff2e3b25a8": "Nutze '0' um das Limit zu deaktivieren!",
|
||||
"s18n-1ef52691308c8add87723a4103a561c2": "Kommentar-Tiefe",
|
||||
"s18n-d102731a5fedff24f30e24e883ef4636": "Kommentar-Markups",
|
||||
"s18n-6640979a191e66655c26c59d404bf955": "Erlaube generelles HTML",
|
||||
"s18n-915009e874f8bed1845060012f826fcd": "Erlaube Markdown",
|
||||
"s18n-a9a36cb3d8f4f7297ebca99a322d6342": "Kommentar-Bewertungen",
|
||||
"s18n-2123546d1ff8b0cb035df0c0b0d06825": "Speicher Gäste-Bewertungen im",
|
||||
"s18n-dead693ab29895d302fca0e6baad6182": "Cookie Speicher",
|
||||
"s18n-02b68043bdcae159e83199d64a5abd7d": "Session Speicher",
|
||||
"s18n-40bd8791e523e91219886c35622163fc": "Datenbank Speicher",
|
||||
"s18n-8717cfca734e8987971f63b20eeb8024": "Was?",
|
||||
"s18n-5a8cbcf57f5b59f0d4b8ded97d018399": "Der <b>Cookie Speicher<\/b> befindet sich auf dem Computer des Nutzers, So hast du nicht den vollen Zugriff darauf UND benötigst zudem die Erlaubnis des Nutzers!",
|
||||
"s18n-a09dbdf907873e66d9d644cca71970d5": "Der <b>Session Speicher<\/b> legt die Daten temporär auf den Server ab, bis der Nutzer den Browser schließt. Dafür benötigst du allerdings auch keine Berechtigung vom Nutzer.",
|
||||
"s18n-71892ebe01ba92d1f163dc37d818b5ff": "Der <b>Datenbank Speicher<\/b> generiert und speichert einen anonymisierten aber zuweisbaren Wert des Nutzers, welche eine entsprechende Einwilligung benötigt.",
|
||||
"s18n-7c35a0bcb0b0678f0829036eead5ddca": "<b>Bitte beachte:<\/b> Du bist für die Einholung der Einwilligung des Nutzers verantwortlich, Snicker übernimmt dies lediglich für die Speicherung der Daten über das Kommentar-Formular selbst!",
|
||||
"s18n-82e5228061f185ee185bd9f3ecba4ee7": "Erlaube die Bewertung: %s",
|
||||
"s18n-61b58693e0eceeb27ce0cc3b25b3bf31": "Webseiten-Einstellungen",
|
||||
"s18n-b3c1c2c231275878abe58a55966fa9e0": "Seiten Filter",
|
||||
"s18n-9cb1eef8966f93282524929f65c8b9ec": "Deaktiviere den Seiten-Filter",
|
||||
"s18n-e89fd56cefec9baabcbe0db3e5a36962": "Nutze 'pageBegin'",
|
||||
"s18n-3d08b5dcc1e5c3c7e7e2eaf2d0d6a12d": "Nutze 'pageEnd'",
|
||||
"s18n-3771d05b6af4ebb0a303266c47809548": "Nutze 'siteBodyBegin'",
|
||||
"s18n-42438edc41ab83312486009e3122e92b": "Nutze 'siteBodyEnd'",
|
||||
"s18n-948da5199de32c7601a20b6107c31d4d": "Kommentar-Captcha",
|
||||
"s18n-47e5c42fb9bdca3636a5d866a6794101": "Deaktiviere das Captcha",
|
||||
"s18n-eedf6d23d18212016e22428658e17794": "Nutze den lokalen Captcha (von Gregway)",
|
||||
"s18n-ab6ef7ef94efc86db78218c6c265243a": "Nutze OWASPs PureCaptcha",
|
||||
"s18n-a58adce0085cc1a25fc8076e97c29d70": "Nutze Gregways Captcha (Die GD Bibliothek fehlt!)",
|
||||
"s18n-09cddbc3627ea46a8dce692d64273b61": "Nutze Googles reCaptcha (Noch nicht verfügbar)",
|
||||
"s18n-838a13e4fece7c272b960da3fb99f94d": "Kommentar-Template",
|
||||
"s18n-6f95370a28520696b2a0ad34efc54d2d": "Kommentar-Reihenfolge",
|
||||
"s18n-4a8dc1710396b21e7b1da8112c07c4ad": "Die neusten Kommentare zuerst",
|
||||
"s18n-2dab3b12d0b0642c3964b37d675ff24b": "Die ältesten Kommentare zuerst",
|
||||
"s18n-230c71c29590608034b4a590a67ace31": "Kommentar-Formular Position",
|
||||
"s18n-37d988444dec2001c488806fc8401e25": "Zeige das Kommentare-Formular über den Kommentaren",
|
||||
"s18n-16ac6c11951d825826f77a4097a1c2cb": "Zeige das Kommentare-Formular unter den Kommentaren",
|
||||
"s18n-1e98ec9312b69676d5e3fe3caf8ecde1": "Kommentare pro Seite",
|
||||
"s18n-0d46f4389ca5f882e24899fe489bf344": "Nutze '0' um alle Kommentare anzuzeigen!",
|
||||
"s18n-7b6b84fbd65a4b712a5ba0dccce176d5": "Nutzungsbedingungen",
|
||||
"s18n-9853383062a2e308d5aed35fe3da7953": "Deaktiviere dieses Feld",
|
||||
"s18n-352fd1d7225b5ea02b8ddd9fad0d6e34": "Zeige eine Standard-Nachricht (See Zeichenketten)",
|
||||
"s18n-71860c77c6745379b0d44304d66b6a13": "Seite",
|
||||
"s18n-5dcb84333ae70a5bed60bf70d34dcd2b": "Zeige den Standard DSGVO Text oder wähle eine Seite für deie Nutzungsbedingungen!",
|
||||
"s18n-2e3d9327c371afb7489f9b9278198622": "AJAX Script",
|
||||
"s18n-c60cab6330745b41cbee05603eec6691": "AJAX Script einbetten",
|
||||
"s18n-69b11f64af515ef979fbf28c5e06f370": "AJAX Script nicht einbetten",
|
||||
"s18n-9bbec7b57565f06d73522669d3b836dc": "Das AJAX Script übergibt die Anfragen (Kommentare, Bewertungen) direkt ohne den Browser neu zu laden!",
|
||||
"s18n-7c74c0d2d1c28d1568298c89742ce126": "Kommentar-Profilbild",
|
||||
"s18n-aeab7c630dae161d8f6e2898dd83b471": "Nutze Gravatar",
|
||||
"s18n-882e3436da897c055cc3f8bd2598b71a": "Nutze Identicon",
|
||||
"s18n-d92d61ad0c0065170a37a1805ad1bc9e": "Nutze Mystery Men",
|
||||
"s18n-b4a51a35344f9a8fa1139cfd968ab308": "Nutze & Bevorzuge das richtige Profilbild bei Nutzern",
|
||||
"s18n-b50a4a96c25d745d73114af5a4b03145": "Kommentar-Gravatar",
|
||||
"s18n-9ffb941a398ddee8e054eef3292c546e": "Nutze Mystery Person",
|
||||
"s18n-a7dd12b1dab17d25467b0b0a4c8d4a92": "Nutze",
|
||||
"s18n-0d2fc085ee57276417cf380027060760": "Das Standard-Gravatar Bild, wenn der Nutzer keines hat!",
|
||||
"s18n-58e2aacf5792087168cbc62578584ecd": "Abonnement Einstellungen",
|
||||
"s18n-8290ca86b8980a14bd46f34017e03f93": "Das Abonnement System ist noch nicht verfügbar!",
|
||||
"s18n-0bd7ff1b4ac56a9616796bdc05609de2": "eMail Abonnement",
|
||||
"s18n-208f156d4a803025c284bb595a7576b4": "Aktivieren",
|
||||
"s18n-dc985a7c2144c6447674e674aee08441": "eMail 'Von' Adresse",
|
||||
"s18n-f6db5b4db3f9c1ba0ffc091abc561802": "eMail 'Antwort an' Adresse",
|
||||
"s18n-8d868315d258783a95336d1a6ce27e1e": "eMail Inahlt (Opt-In)",
|
||||
"s18n-7721b2a2f2a453cc790e3ac7065e9b65": "Nutze die Standard Abonnements eMail",
|
||||
"s18n-58ff11585a82c73f5117c91c29cb3f63": "eMail Inhalt (Benachrichtigung)",
|
||||
"s18n-bea9ff19efca028c01617da5dce18171": "Nutze die Standard Benachrichtigungs eMail",
|
||||
"s18n-e0024a8886a178d4f70b8c888b701680": "Finde weitere Informationen zu benutzerdefinierten eMails %s!",
|
||||
"s18n-8bcf6629759bd278a5c6266bd9c054f8": "Zeichenketten",
|
||||
"s18n-3979b4205954030810a8a87769348094": "Standard Danke Nachricht",
|
||||
"s18n-620b528248b36bf743d1ad33e35022d6": "Danke Nachricht mit Abonnement",
|
||||
"s18n-e83f6b01d1c81313f6b388281e13aacf": "Danke Nachricht für die Bewertung",
|
||||
"s18n-01b7a6bc6b57783b4fd081085ff3271e": "Fehler: Unbekannter Fehler",
|
||||
"s18n-19678b1419eff34dffd41d6778b1aa89": "Fehler: Benutzername ist ungültig",
|
||||
"s18n-1255e6794d33b443bcee21279d9caa1a": "Fehler: eMail Adresse ist ungültig",
|
||||
"s18n-ebf545757b92b3a553d83ea3db48beca": "Fehler: Kommentar-Text fehlt",
|
||||
"s18n-f1bc53e2456c425578277adbc7c90f3a": "Fehler: Kommentar-Titel fehlt",
|
||||
"s18n-53c8fdd7ed497dbacbef5fd5d4f38f3d": "Fehler: Nutzungsbedingungen fehlen",
|
||||
"s18n-89f8cc478fdfc0a8f36c1b393f39677a": "Fehler: Als SPAM markiert",
|
||||
"s18n-59ad5c9fcee1b28a5d004bcf684a5acd": "Fehler: Bereits abgestimmt",
|
||||
"s18n-56ef2c600b4af0f9f7b35640525967ca": "Nutzungsbedingungen",
|
||||
"s18n-08400c2e0f51197fdb3590461b15b2cc": "Nutzername oder eMail Adresse",
|
||||
"s18n-b79edd2e426f90401c04869346b503c7": "Nutzer suchen",
|
||||
"s18n-ee51fa9d5097c84d2fa6c885bf2d5d84": "Keine Nutzer verfügbar",
|
||||
"s18n-14c4b06b824ec593239362517f538b29": "Benutzername",
|
||||
"s18n-0c83f57c786a0b4a39efab23731c7ebc": "eMail",
|
||||
"s18n-e1260894f59eeae98c8440899de4df8d": "Aktionem",
|
||||
"s18n-4bc61296b766756f1c7296489633bf32": "Entfernen (Anonymisieren)",
|
||||
"s18n-c0f4afd3614929f1c803f3a01414a6c7": "Entfernen (Vollständig)",
|
||||
"s18n-6356f32d0c02c8f90cb59a77e16e8fe2": "Nutzer entblockieren",
|
||||
"s18n-2327a01afbee025fb5913357c9d6b1b3": "Nutzer blockieren",
|
||||
"s18n-76a0f6752a45d8af6343ef3e2b6f522a": "Einzelner Kommentar",
|
||||
"s18n-c426859e50a35617d863cdad2b9c84aa": "Seiten-Kommentare",
|
||||
"s18n-a64d776275f13a51790bb460774b9129": "Nutzer Kommentare",
|
||||
"s18n-9bc65c2abec141778ffaa729489f3e87": "Nutzer",
|
||||
"s18n-ccd1066343c95877b75b79d47c36bebe": "Konfiguration",
|
||||
"s18n-5b49260517622682a058b69f996d06eb": "Vielen Dank für deinen Kommentar!",
|
||||
"s18n-74196a783a6f1707a43cc8117f0d9c83": "Vielen Dank für deinen Kommentar, bitte bestätige noch dein Abonnement über die zugesandte eMail!",
|
||||
"s18n-a939eb542e34cd502b3f7352b2e0f715": "Vielen Dank für deine Bewertung!",
|
||||
"s18n-d2a9677817ee08ed05bf9fd868669756": "Ein unbekannter Fehler ist aufgetreten, bitte lade die Seite neu!",
|
||||
"s18n-05b85714aa8f1b364f930e2539059b5e": "Ein Fehler ist aufgetreten: Der Benutzer ist ungültig (Max. 42 Zeichen)!",
|
||||
"s18n-321e8b481f0ccd62df535256e8e6d2c6": "Ein Fehler ist aufgetreten: Die eMail Adresse ist ungültig!",
|
||||
"s18n-fcd0c3a087c5123ffdecc20fe9015870": "Ein Fehler ist aufgetreten: Der Kommentar-Text fehlt!",
|
||||
"s18n-2afa2d90ce3343fbe188b9f49ad5797d": "Ein Fehler ist aufgetreten: Der Kommentar-Titel fehlt!",
|
||||
"s18n-bee1741efe0c735d2c7180771586faf0": "Ein Fehler ist aufgetreten: Du musst die Nutzungsbedingungen akzeptieren!",
|
||||
"s18n-92fe96d6ccee901f94fad0000369a9b7": "Ein Fehler ist aufgetreten: Deine IP oder eMail Adresse wurde als SPAM markiert!",
|
||||
"s18n-9b264fc6137096f8a40acde68f6ae562": "Ein Fehler ist aufgetreten: Du hast diesen Kommentar bereits bewertet!",
|
||||
"s18n-ca62db4704290ef1a7e65df3ffc7983b": "Ich stimme der Speicherung der Daten (inkl. meiner anonymisierten IP) zu!",
|
||||
"s18n-01c611362e8b046f32650b85ce161559": "Ein unbekannter Fehler ist aufgetreten!",
|
||||
"s18n-c4f94c6995b0376f28276e432bed75fa": "Das Sicherheitstoken fehlt!",
|
||||
"s18n-a573e7b86e41522c7a291846aa109104": "Das Sicherheitstoken ist ungültig!",
|
||||
"s18n-3e8909518ce4728685aa09cdde3caa22": "Du hast nicht die Berechtigung diese Aktion aufzurufen!",
|
||||
"s18n-b60a6a8a529a9f0497134205bab15e77": "Du hast nicht die Berechtigung diese Aktion duchzuführen!",
|
||||
"s18n-3fee90e2f59aeb29b74c1c21648ba712": "Das Captcha Bild konnte erfolgreich erstellt werden!",
|
||||
"s18n-56abae3e615ae5b5609c32852b777d46": "Die gewünschte Aktion ist ungültig doer unvollständig!",
|
||||
"s18n-be5136b4f2b33c80e3afd377ee993acb": "Ein unbekannter Fehler ist aufgetreten!",
|
||||
"s18n-46d4c97e91319867654f7cc80c439ba4": "Diese einzigartige Nutzer-ID existiert nicht!",
|
||||
"s18n-af1ba1dd4eab5562f78c65bc89a0a7e9": "Die Aktion wurde erfolgreich durchgeführt!",
|
||||
"s18n-a5193444ee82c18bac726b35a1704d03": "Die Einstellungen wurden erfolgreich aktualisiert!",
|
||||
"s18n-717c8267d40664ccf7ef25a26ff9cde6": "Das Backup wurde erfolgreich erstellt!",
|
||||
"s18n-a1e2b7401861cee01c172878f104bd8c": "Kommentare deaktivieren",
|
||||
"s18n-15802277ea1cdfcbacd6308fa0c7c30f": "Snicker Plugin deaktivieren",
|
||||
"s18n-877d58f21c87442efa4081112a6cb07a": "Du bist das das <b>Snicker<\/b> Plugin zu deaktivieren, was sämtliche Kommentare löschen würde!",
|
||||
"s18n-a4983c86683f8d8598c0513339550dc0": "Möchtest du die Kommentare vorher sichern?",
|
||||
"s18n-4cce9e52118cc659be5070b2f08cdd91": "Das Backup wird unter %s gespeichert!",
|
||||
"s18n-5f87fd2e0fa992d37c814bb4ca299646": "Ja, Backup erstellen",
|
||||
"s18n-3d414feb412a1f4cd9324f6411c76329": "Nein, einfach deativieren",
|
||||
"s18n-10aec35353f9c4096a71c38654c3d402": "Abbrechen",
|
||||
"s18n-bc6d6a26d44b6f39a0e7b6c7787f3295": "Die Kommentar-Sektion auf dieser Seite wurde deaktiviert!",
|
||||
"s18n-a8e30d73eddea9866cf99ecd6e8467b5": "Es wurden noch keine Kommentare verfasst, sei der erste!",
|
||||
"s18n-b3afbadaa2f1c79f8b3999be7fd9719f": "Die Antwort auf das Captcha wurde nicht übermittelt oder war falsch!",
|
||||
"s18n-fb74aafe8bf1fd4d8f7a6e1ff73028f7": "Die Kommentar ID existiert nicht oder ist ungültig!",
|
||||
"s18n-25dea7cb70f98250b388f6ab0ddf20cb": "Der Kommentar Status existiert nicht oder ist ungültig!",
|
||||
"s18n-2cd68855bdc54ff5e3c191a6333ff75d": "Der neue Kommentar Status konnte nicht gespeichert werden!",
|
||||
"s18n-c0937505b7afa81a053077bc7ae369a5": "Der neue Kommentar Status wurde erfolgreich gespeichert!",
|
||||
"s18n-6b2c8084c67f24bb73d95031bb570ef7": "Der Kommentar konnte nicht erfolgreich gelöscht werden!",
|
||||
"s18n-376388311a80dbde63fde7f6c72081e0": "Der Kommentar konnte erfolgreich gelöscht werden!",
|
||||
"s18n-83bbb9e8745cc95e730fe7b8de9345f1": "Angemeldet als %s (%s)",
|
||||
"s18n-91fb98e1ac4cf76b7a5b8bae09051e2a": "Dein Benutzername",
|
||||
"s18n-31f6da7a30e7acf1f82451bfd1a7f8fa": "Deine eMail Adresse",
|
||||
"s18n-f794080a5a29e35233c82df85f1207eb": "Dein Kommentar...",
|
||||
"s18n-a363b8d13575101a0226e8d0d054f2e7": "Antworten",
|
||||
"s18n-f10db888c5e63b343000cffc038e0a46": "schrieb",
|
||||
"s18n-3cc5bcf15d6b8faed118e2ce72d19a1e": "Ich stimme der %s zu!",
|
||||
"s18n-2af0aab477f402e0f4ad7a27e6c9f952": "Vorherigen Kommentare",
|
||||
"s18n-8538431db22040e2147b363f86a2e2f0": "Nächsten Kommentare",
|
||||
"s18n-ae0dbd5cc42a6db191db5e0083bcb307": "Dieser Kommentare wurde noch nicht moderiert!",
|
||||
"s18n-48df9c3f3cca3fb2b8bcf811633bee06": "Geschrieben von %s",
|
||||
"s18n-81fdc9813cebe0553c55e78dc2b6029f": "am %s",
|
||||
"s18n-be1ab1632e4285edc3733b142935c60b": "Gefällt Mir",
|
||||
"s18n-bc8b79025e4595298669fd21da814941": "Gefällt Mir nicht",
|
||||
"s18n-e84afaab83ecb301b3d97ce4174d2773": "Antworten"
|
||||
}
|
||||
204
languages/en.json
Normal file
204
languages/en.json
Normal file
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"plugin-data": {
|
||||
"name": "Snicker",
|
||||
"description": "A native, AJAX-enabled FlatFile Comment system for bludit, including a comment subscription and completely compliant with the GDPR!"
|
||||
},
|
||||
"s18n-a5d491060952aa8ad5fdee071be752de": "Comments",
|
||||
"s18n-de95b43bceeb4b998aed4aed5cef1ae7": "Edit",
|
||||
"s18n-f984023ed3d6df2326c9d59838c29792": "Update Comment",
|
||||
"s18n-86448a506dd93303a72140b9124ee321": "Delete Comment",
|
||||
"s18n-db10a8eb963bc0e5f4483ac9b5dc554c": "Comment Title",
|
||||
"s18n-335630425567dbe91768a3beffdec752": "Comment Text",
|
||||
"s18n-bd7e63f881c7f787a9a0dce20b7f9e5b": "Meta Settings",
|
||||
"s18n-1acfe725df7bd12195751f0737c4d375": "Registered User",
|
||||
"s18n-e988189db402fab453f72052629c02cd": "Comment Username",
|
||||
"s18n-db84c6236ca6a01b9189504d78c012a5": "Comment Email",
|
||||
"s18n-7c6c2e5d48ab37a007cbf70d3ea25fa4": "Pending",
|
||||
"s18n-787d5f05953ec39b108869dfdd7733e6": "Approved",
|
||||
"s18n-c7537d6d48ecf261749c09a9f284bd45": "Rejected",
|
||||
"s18n-e09f6a7593f8ae3994ea57e1117f67ec": "Spam",
|
||||
"s18n-7a5115c2c1eb662308decbec83593494": "View Page",
|
||||
"s18n-0572a05cd6d6360391993a611099542f": "Comment Title or Excerpt",
|
||||
"s18n-428f14500191b5d53675da4a96bc8bba": "Search Comments",
|
||||
"s18n-e0be71bccdeceb713fba3c222f79a3c5": "No Comments available",
|
||||
"s18n-06d4cd63bde972fc66a0aed41d2f5c51": "Comment",
|
||||
"s18n-02bd92faa38aaa6cc0ea75e59937a1ef": "Author",
|
||||
"s18n-ebb67a4271abe715344471b0f16321f6": "Actions",
|
||||
"s18n-58566b9a9b2733b0ceacb2186672b5d1": "Show all replies",
|
||||
"s18n-5f44c0081bd862a77ba8b24e923cadf1": "Reply To",
|
||||
"s18n-eb399bcaca686f8609137153307eecf1": "Change",
|
||||
"s18n-2736f4347985da50dc023444c193bfea": "Edit Comment",
|
||||
"s18n-a107bf4b12e36a07161a26d95b03bc81": "Approve Comment",
|
||||
"s18n-da937abd19cd9e1430470b8a471a41d4": "Reject Comment",
|
||||
"s18n-32cb4199893d9948cc0853eef244f1fc": "Mark as Spam",
|
||||
"s18n-a6494adfb72d12d3a4da66855c284ec6": "Back to Pending",
|
||||
"s18n-1bda80f2be4d3658e0baa43fbe7ae8c1": "View",
|
||||
"s18n-c9ae5a4214e87ad6fbed44a267471eee": "Save Settings",
|
||||
"s18n-e124d357c3c832434a8676a5e18db842": "General Settings",
|
||||
"s18n-b62a9dcd666f3ff44197cf21ac66507d": "Comment Moderation",
|
||||
"s18n-7f0217cdcdd58ba86aae84d9d3d79f81": "Moderate",
|
||||
"s18n-1a1dc91c907325c69271ddf0c944bc72": "Pass",
|
||||
"s18n-df6963dc912cde9baeef10343167ba01": "each Comment",
|
||||
"s18n-39ba4181e212acf183ac965c9b37da89": "Unless the user is logged in",
|
||||
"s18n-0dac4426a017f0a0370db32776bc30bb": "Unless the user is admin or the content author",
|
||||
"s18n-c70aa8b8fd6f2652eda2d5366faf1de5": "Unless the user has an already approved comment",
|
||||
"s18n-8b26cab9d444760b4bcc65dc4d8634f8": "Allow Comments",
|
||||
"s18n-67614909bf9de326de71946036de39f1": "... on Public Pages",
|
||||
"s18n-a7c384c1b60785c40bc3a4e4dfc5a108": "... on Sticky Pages",
|
||||
"s18n-9907b78f8745810599dbf6fd29a01364": "... on Static Pages",
|
||||
"s18n-c89cbecce48d04cd76c0b95c8128ad97": "Enable (Optional)",
|
||||
"s18n-e9d51286fdd0ff058650392fc8b6ae30": "Enable (Required)",
|
||||
"s18n-0aaa87422396fdd678498793b6d5250e": "Disable",
|
||||
"s18n-8da2c8185edfeb1765526f8e2e4f388d": "Comment Limit",
|
||||
"s18n-77d6d185c93549dab24f29ff2e3b25a8": "Use '0' to disable any limit!",
|
||||
"s18n-1ef52691308c8add87723a4103a561c2": "Comment Depth",
|
||||
"s18n-d102731a5fedff24f30e24e883ef4636": "Comment Markup",
|
||||
"s18n-6640979a191e66655c26c59d404bf955": "Allow Basic HTML",
|
||||
"s18n-915009e874f8bed1845060012f826fcd": "Allow Markdown",
|
||||
"s18n-a9a36cb3d8f4f7297ebca99a322d6342": "Comment Voting",
|
||||
"s18n-2123546d1ff8b0cb035df0c0b0d06825": "Store Votes made by Guests in the",
|
||||
"s18n-dead693ab29895d302fca0e6baad6182": "Cookie Storage",
|
||||
"s18n-02b68043bdcae159e83199d64a5abd7d": "Session Storage",
|
||||
"s18n-40bd8791e523e91219886c35622163fc": "Database Storage",
|
||||
"s18n-8717cfca734e8987971f63b20eeb8024": "What?",
|
||||
"s18n-5a8cbcf57f5b59f0d4b8ded97d018399": "The <b>Cookie Storage<\/b> is located on the Computer of the user. So you don't have the full control AND you require the appropriate permissions from the user.",
|
||||
"s18n-a09dbdf907873e66d9d644cca71970d5": "The <b>Session Storage<\/b> is just stored temporary on the server, it gets cleaned up when the user closes the browser. Therefore you don't need any permissions from the user.",
|
||||
"s18n-71892ebe01ba92d1f163dc37d818b5ff": "The <b>Database Storage<\/b> generates and stores an anonymized but assignable value of the user, which also requires the appropriate permissions from the user.",
|
||||
"s18n-7c35a0bcb0b0678f0829036eead5ddca": "<b>Please Note:<\/b> You are responsible for obtaining the appropriate permissions, Snicker just handles the permissions for data sent (and stored) via the comment form!",
|
||||
"s18n-82e5228061f185ee185bd9f3ecba4ee7": "Allow to %s comments",
|
||||
"s18n-61b58693e0eceeb27ce0cc3b25b3bf31": "Frontend Settings",
|
||||
"s18n-b3c1c2c231275878abe58a55966fa9e0": "Page Filter",
|
||||
"s18n-9cb1eef8966f93282524929f65c8b9ec": "Disable Page Filter",
|
||||
"s18n-e89fd56cefec9baabcbe0db3e5a36962": "Use 'pageBegin'",
|
||||
"s18n-3d08b5dcc1e5c3c7e7e2eaf2d0d6a12d": "Use 'pageEnd'",
|
||||
"s18n-3771d05b6af4ebb0a303266c47809548": "Use 'siteBodyBegin'",
|
||||
"s18n-42438edc41ab83312486009e3122e92b": "Use 'siteBodyEnd'",
|
||||
"s18n-948da5199de32c7601a20b6107c31d4d": "Comment Captcha",
|
||||
"s18n-47e5c42fb9bdca3636a5d866a6794101": "Disable Captcha",
|
||||
"s18n-ab6ef7ef94efc86db78218c6c265243a": "Use OWASP's PureCaptcha",
|
||||
"s18n-a58adce0085cc1a25fc8076e97c29d70": "Use Gregway's Captcha",
|
||||
"s18n-9d2f2ec577e7383b88fd481d6c566c5e": "Use Gregway's Captcha (GD library is missing!)",
|
||||
"s18n-09cddbc3627ea46a8dce692d64273b61": "Use Googles reCaptcha (Not available yet)",
|
||||
"s18n-838a13e4fece7c272b960da3fb99f94d": "Comment Template",
|
||||
"s18n-6f95370a28520696b2a0ad34efc54d2d": "Comment Order",
|
||||
"s18n-4a8dc1710396b21e7b1da8112c07c4ad": "Newest Comments First",
|
||||
"s18n-2dab3b12d0b0642c3964b37d675ff24b": "Oldest Comments First",
|
||||
"s18n-230c71c29590608034b4a590a67ace31": "Comment Form Position",
|
||||
"s18n-37d988444dec2001c488806fc8401e25": "Show Comment Form above Comments",
|
||||
"s18n-16ac6c11951d825826f77a4097a1c2cb": "Show Comment Form below Comments",
|
||||
"s18n-1e98ec9312b69676d5e3fe3caf8ecde1": "Comments Per Page",
|
||||
"s18n-0d46f4389ca5f882e24899fe489bf344": "Use '0' to show all available comments!",
|
||||
"s18n-7b6b84fbd65a4b712a5ba0dccce176d5": "Terms of Use Checkbox",
|
||||
"s18n-9853383062a2e308d5aed35fe3da7953": "Disable this field",
|
||||
"s18n-352fd1d7225b5ea02b8ddd9fad0d6e34": "Show Message (See Strings)",
|
||||
"s18n-71860c77c6745379b0d44304d66b6a13": "Page",
|
||||
"s18n-5dcb84333ae70a5bed60bf70d34dcd2b": "Show the default GDPR Text or Select your own static 'Terms of Use' page!",
|
||||
"s18n-2e3d9327c371afb7489f9b9278198622": "AJAX Script",
|
||||
"s18n-c60cab6330745b41cbee05603eec6691": "Embed AJAX Script",
|
||||
"s18n-69b11f64af515ef979fbf28c5e06f370": "Don't use AJAX",
|
||||
"s18n-9bbec7b57565f06d73522669d3b836dc": "The AJAX Script hands over the request (comment, like, dislike) directly without reloading the page!",
|
||||
"s18n-7c74c0d2d1c28d1568298c89742ce126": "Comment Avatar",
|
||||
"s18n-aeab7c630dae161d8f6e2898dd83b471": "Use Gravatar",
|
||||
"s18n-882e3436da897c055cc3f8bd2598b71a": "Use Identicon",
|
||||
"s18n-d92d61ad0c0065170a37a1805ad1bc9e": "Use Mystery Men",
|
||||
"s18n-b4a51a35344f9a8fa1139cfd968ab308": "Use & Prefer profile pictures on logged-in Users",
|
||||
"s18n-b50a4a96c25d745d73114af5a4b03145": "Comment Gravatar",
|
||||
"s18n-9ffb941a398ddee8e054eef3292c546e": "Show Mystery Person",
|
||||
"s18n-a7dd12b1dab17d25467b0b0a4c8d4a92": "Show",
|
||||
"s18n-0d2fc085ee57276417cf380027060760": "The default Gravatar image, if the user has no Gravatar!",
|
||||
"s18n-58e2aacf5792087168cbc62578584ecd": "Subscription Settings",
|
||||
"s18n-8290ca86b8980a14bd46f34017e03f93": "The Subscription system isn't available yet!",
|
||||
"s18n-0bd7ff1b4ac56a9616796bdc05609de2": "Email Subscription",
|
||||
"s18n-208f156d4a803025c284bb595a7576b4": "Enable",
|
||||
"s18n-dc985a7c2144c6447674e674aee08441": "Email 'From' Address",
|
||||
"s18n-f6db5b4db3f9c1ba0ffc091abc561802": "Email 'ReplyTo' Address",
|
||||
"s18n-8d868315d258783a95336d1a6ce27e1e": "Email Body (Opt-In)",
|
||||
"s18n-7721b2a2f2a453cc790e3ac7065e9b65": "Use default Subscription Email",
|
||||
"s18n-58ff11585a82c73f5117c91c29cb3f63": "Email Body (Notification)",
|
||||
"s18n-bea9ff19efca028c01617da5dce18171": "Use default Notification Email",
|
||||
"s18n-e0024a8886a178d4f70b8c888b701680": "Read more about a custom Notification Emails %s!",
|
||||
"s18n-8bcf6629759bd278a5c6266bd9c054f8": "Strings",
|
||||
"s18n-3979b4205954030810a8a87769348094": "Default Thanks Message",
|
||||
"s18n-620b528248b36bf743d1ad33e35022d6": "Thanks Message with Subscription",
|
||||
"s18n-e83f6b01d1c81313f6b388281e13aacf": "Thanks Message for Voting",
|
||||
"s18n-01b7a6bc6b57783b4fd081085ff3271e": "Error: Unknown Error, Try again",
|
||||
"s18n-19678b1419eff34dffd41d6778b1aa89": "Error: Username is invalid",
|
||||
"s18n-1255e6794d33b443bcee21279d9caa1a": "Error: Email Address is invalid",
|
||||
"s18n-ebf545757b92b3a553d83ea3db48beca": "Error: Comment Text is missing",
|
||||
"s18n-f1bc53e2456c425578277adbc7c90f3a": "Error: Comment Title is missing",
|
||||
"s18n-53c8fdd7ed497dbacbef5fd5d4f38f3d": "Error: Terms not accepted",
|
||||
"s18n-89f8cc478fdfc0a8f36c1b393f39677a": "Error: Marked as SPAM",
|
||||
"s18n-59ad5c9fcee1b28a5d004bcf684a5acd": "Error: Already Voted",
|
||||
"s18n-56ef2c600b4af0f9f7b35640525967ca": "Terms of Use",
|
||||
"s18n-08400c2e0f51197fdb3590461b15b2cc": "Username or Email Address",
|
||||
"s18n-b79edd2e426f90401c04869346b503c7": "Search Users",
|
||||
"s18n-ee51fa9d5097c84d2fa6c885bf2d5d84": "No Users available",
|
||||
"s18n-14c4b06b824ec593239362517f538b29": "Username",
|
||||
"s18n-0c83f57c786a0b4a39efab23731c7ebc": "Email",
|
||||
"s18n-e1260894f59eeae98c8440899de4df8d": "Handle",
|
||||
"s18n-4bc61296b766756f1c7296489633bf32": "Delete (Anonymize)",
|
||||
"s18n-c0f4afd3614929f1c803f3a01414a6c7": "Delete (Completely)",
|
||||
"s18n-6356f32d0c02c8f90cb59a77e16e8fe2": "Unblock User",
|
||||
"s18n-2327a01afbee025fb5913357c9d6b1b3": "Block User",
|
||||
"s18n-76a0f6752a45d8af6343ef3e2b6f522a": "Single Comment",
|
||||
"s18n-c426859e50a35617d863cdad2b9c84aa": "Page Comments",
|
||||
"s18n-a64d776275f13a51790bb460774b9129": "User Comments",
|
||||
"s18n-9bc65c2abec141778ffaa729489f3e87": "Users",
|
||||
"s18n-ccd1066343c95877b75b79d47c36bebe": "Configuration",
|
||||
"s18n-5b49260517622682a058b69f996d06eb": "Thanks for your comment!",
|
||||
"s18n-74196a783a6f1707a43cc8117f0d9c83": "Thanks for your comment, please confirm your subscription via the link we sent to your Email address!",
|
||||
"s18n-a939eb542e34cd502b3f7352b2e0f715": "Thanks for voting this comment!",
|
||||
"s18n-d2a9677817ee08ed05bf9fd868669756": "An unknown error occured, please reload the page and try it again!",
|
||||
"s18n-05b85714aa8f1b364f930e2539059b5e": "An error occured: The passed Username is invalid or too long (42 characters only)!",
|
||||
"s18n-321e8b481f0ccd62df535256e8e6d2c6": "An error occured: The passed Email address is invalid!",
|
||||
"s18n-fcd0c3a087c5123ffdecc20fe9015870": "An error occured: The comment text is missing!",
|
||||
"s18n-2afa2d90ce3343fbe188b9f49ad5797d": "An error occured: The comment title is missing!",
|
||||
"s18n-bee1741efe0c735d2c7180771586faf0": "An error occured: You need to accept the Terms to comment!",
|
||||
"s18n-92fe96d6ccee901f94fad0000369a9b7": "An error occured: Your IP address or Email address has been marked as Spam!",
|
||||
"s18n-9b264fc6137096f8a40acde68f6ae562": "An error occured: You already rated this comment!",
|
||||
"s18n-ca62db4704290ef1a7e65df3ffc7983b": "I agree that my data (incl. my anonymized IP address) gets stored!",
|
||||
"s18n-01c611362e8b046f32650b85ce161559": "An unknown error occured!",
|
||||
"s18n-c4f94c6995b0376f28276e432bed75fa": "The CSRF Token is missing!",
|
||||
"s18n-a573e7b86e41522c7a291846aa109104": "The CSRF Token is invalid!",
|
||||
"s18n-3e8909518ce4728685aa09cdde3caa22": "You don't have the permission to call this action!",
|
||||
"s18n-b60a6a8a529a9f0497134205bab15e77": "You don't have the permission to perform this action!",
|
||||
"s18n-3fee90e2f59aeb29b74c1c21648ba712": "The Captcha image could be successfully created!",
|
||||
"s18n-56abae3e615ae5b5609c32852b777d46": "The passed action is unknown or invalid!",
|
||||
"s18n-be5136b4f2b33c80e3afd377ee993acb": "An unknown error is occured!",
|
||||
"s18n-46d4c97e91319867654f7cc80c439ba4": "A unique user ID does not exist!",
|
||||
"s18n-af1ba1dd4eab5562f78c65bc89a0a7e9": "The action has been performed successfully!",
|
||||
"s18n-a5193444ee82c18bac726b35a1704d03": "The settings has been updated successfully!",
|
||||
"s18n-717c8267d40664ccf7ef25a26ff9cde6": "The backup has been created successfully!",
|
||||
"s18n-a1e2b7401861cee01c172878f104bd8c": "Disallow Comments",
|
||||
"s18n-15802277ea1cdfcbacd6308fa0c7c30f": "Snicker Plugin Deactivation",
|
||||
"s18n-877d58f21c87442efa4081112a6cb07a": "You are about to deactivate the <b>Snicker<\/b> Plugin, which will delete all written comments!",
|
||||
"s18n-a4983c86683f8d8598c0513339550dc0": "Do you want to backup your comments before?",
|
||||
"s18n-4cce9e52118cc659be5070b2f08cdd91": "The backup will be stored in %s!",
|
||||
"s18n-5f87fd2e0fa992d37c814bb4ca299646": "Yes, create a backup",
|
||||
"s18n-3d414feb412a1f4cd9324f6411c76329": "No, just deactivate",
|
||||
"s18n-10aec35353f9c4096a71c38654c3d402": "Cancel",
|
||||
"s18n-bc6d6a26d44b6f39a0e7b6c7787f3295": "The comment section on this page has been disabled!",
|
||||
"s18n-a8e30d73eddea9866cf99ecd6e8467b5": "Currently there are no comments, so be the first!",
|
||||
"s18n-b3afbadaa2f1c79f8b3999be7fd9719f": "The answer to the Captcha hasn't been passed or is wrong!",
|
||||
"s18n-fb74aafe8bf1fd4d8f7a6e1ff73028f7": "The comment UID doesn't exist or is invalid!",
|
||||
"s18n-25dea7cb70f98250b388f6ab0ddf20cb": "The comment status is unknown or invalid!",
|
||||
"s18n-2cd68855bdc54ff5e3c191a6333ff75d": "The new comment status couldn't be updated!",
|
||||
"s18n-c0937505b7afa81a053077bc7ae369a5": "The new comment status has been stored successfully!",
|
||||
"s18n-6b2c8084c67f24bb73d95031bb570ef7": "The comment couldn't deleted!",
|
||||
"s18n-376388311a80dbde63fde7f6c72081e0": "The comment has been deleted!",
|
||||
"s18n-83bbb9e8745cc95e730fe7b8de9345f1": "Logged in as %s (%s)",
|
||||
"s18n-91fb98e1ac4cf76b7a5b8bae09051e2a": "Your Username",
|
||||
"s18n-31f6da7a30e7acf1f82451bfd1a7f8fa": "Your Email Address",
|
||||
"s18n-f794080a5a29e35233c82df85f1207eb": "Your Comment...",
|
||||
"s18n-a363b8d13575101a0226e8d0d054f2e7": "Answer",
|
||||
"s18n-f10db888c5e63b343000cffc038e0a46": "wrote",
|
||||
"s18n-3cc5bcf15d6b8faed118e2ce72d19a1e": "I agree the %s!",
|
||||
"s18n-2af0aab477f402e0f4ad7a27e6c9f952": "Previous Comments",
|
||||
"s18n-8538431db22040e2147b363f86a2e2f0": "Next Comments",
|
||||
"s18n-ae0dbd5cc42a6db191db5e0083bcb307": "This comment hasn't been moderated yet!",
|
||||
"s18n-48df9c3f3cca3fb2b8bcf811633bee06": "Written by %s",
|
||||
"s18n-81fdc9813cebe0553c55e78dc2b6029f": "on %s",
|
||||
"s18n-be1ab1632e4285edc3733b142935c60b": "Like",
|
||||
"s18n-bc8b79025e4595298669fd21da814941": "Dislike",
|
||||
"s18n-e84afaab83ecb301b3d97ce4174d2773": "Reply"
|
||||
}
|
||||
200
languages/es.json
Normal file
200
languages/es.json
Normal file
@@ -0,0 +1,200 @@
|
||||
{
|
||||
"plugin-data": {
|
||||
"name": "Snicker",
|
||||
"description": "Sistema de comentarios para Bludit. Incluye suscripción a comentarios y es completamente compatible con el RGPD (Reglamento General de Protección de datos). Usa tecnologías AJAX y Flatfile. Ligero y seguro." },
|
||||
"s18n-a5d491060952aa8ad5fdee071be752de": "Comentarios",
|
||||
"s18n-de95b43bceeb4b998aed4aed5cef1ae7": "Editar",
|
||||
"s18n-f984023ed3d6df2326c9d59838c29792": "Actualizar",
|
||||
"s18n-86448a506dd93303a72140b9124ee321": "Borrar",
|
||||
"s18n-db10a8eb963bc0e5f4483ac9b5dc554c": "Título de comentario",
|
||||
"s18n-335630425567dbe91768a3beffdec752": "Texto de comentario",
|
||||
"s18n-bd7e63f881c7f787a9a0dce20b7f9e5b": "Configuración de metadatos",
|
||||
"s18n-1acfe725df7bd12195751f0737c4d375": "Registro de usuario",
|
||||
"s18n-e988189db402fab453f72052629c02cd": "Usuario",
|
||||
"s18n-db84c6236ca6a01b9189504d78c012a5": "Email",
|
||||
"s18n-7c6c2e5d48ab37a007cbf70d3ea25fa4": "Pendiente",
|
||||
"s18n-787d5f05953ec39b108869dfdd7733e6": "Aprobado",
|
||||
"s18n-c7537d6d48ecf261749c09a9f284bd45": "Rechazado",
|
||||
"s18n-e09f6a7593f8ae3994ea57e1117f67ec": "Spam",
|
||||
"s18n-7a5115c2c1eb662308decbec83593494": "Ver página",
|
||||
"s18n-0572a05cd6d6360391993a611099542f": "Título de comentario",
|
||||
"s18n-428f14500191b5d53675da4a96bc8bba": "Buscar comentarios",
|
||||
"s18n-e0be71bccdeceb713fba3c222f79a3c5": "No hay comentarios aun",
|
||||
"s18n-06d4cd63bde972fc66a0aed41d2f5c51": "Comentar",
|
||||
"s18n-02bd92faa38aaa6cc0ea75e59937a1ef": "Autor",
|
||||
"s18n-ebb67a4271abe715344471b0f16321f6": "Acciones",
|
||||
"s18n-58566b9a9b2733b0ceacb2186672b5d1": "Mostrar todas las respuestas",
|
||||
"s18n-5f44c0081bd862a77ba8b24e923cadf1": "Responder a",
|
||||
"s18n-eb399bcaca686f8609137153307eecf1": "Cambiar",
|
||||
"s18n-2736f4347985da50dc023444c193bfea": "Editar Comentario",
|
||||
"s18n-a107bf4b12e36a07161a26d95b03bc81": "Aprobar Comentario",
|
||||
"s18n-da937abd19cd9e1430470b8a471a41d4": "Rechazar Comentario",
|
||||
"s18n-32cb4199893d9948cc0853eef244f1fc": "Marcar como espam",
|
||||
"s18n-a6494adfb72d12d3a4da66855c284ec6": "Archivar como pendiente",
|
||||
"s18n-1bda80f2be4d3658e0baa43fbe7ae8c1": "Ver",
|
||||
"s18n-c9ae5a4214e87ad6fbed44a267471eee": "Guardar la configuración",
|
||||
"s18n-e124d357c3c832434a8676a5e18db842": "Configuración General",
|
||||
"s18n-b62a9dcd666f3ff44197cf21ac66507d": "Moderación",
|
||||
"s18n-7f0217cdcdd58ba86aae84d9d3d79f81": "Moderar",
|
||||
"s18n-1a1dc91c907325c69271ddf0c944bc72": "Aprobar",
|
||||
"s18n-df6963dc912cde9baeef10343167ba01": "por defecto",
|
||||
"s18n-39ba4181e212acf183ac965c9b37da89": "Aprobar usuarios identificados",
|
||||
"s18n-0dac4426a017f0a0370db32776bc30bb": "Aprobar roles de admin o autor",
|
||||
"s18n-c70aa8b8fd6f2652eda2d5366faf1de5": "Aprobar si ya tiene algún comentario aprobado",
|
||||
"s18n-8b26cab9d444760b4bcc65dc4d8634f8": "Permitir comentarios",
|
||||
"s18n-67614909bf9de326de71946036de39f1": "... en páginas Publicadas",
|
||||
"s18n-a7c384c1b60785c40bc3a4e4dfc5a108": "... en páginas Ancladas (sticky)",
|
||||
"s18n-9907b78f8745810599dbf6fd29a01364": "... en páginas Estáticas",
|
||||
"s18n-c89cbecce48d04cd76c0b95c8128ad97": "Activo (Opcional)",
|
||||
"s18n-e9d51286fdd0ff058650392fc8b6ae30": "Activo (Requerido)",
|
||||
"s18n-0aaa87422396fdd678498793b6d5250e": "Inactivo",
|
||||
"s18n-8da2c8185edfeb1765526f8e2e4f388d": "Limite de comentarios",
|
||||
"s18n-77d6d185c93549dab24f29ff2e3b25a8": "'0' si no deseas límite!",
|
||||
"s18n-1ef52691308c8add87723a4103a561c2": "Extensión de comentario",
|
||||
"s18n-d102731a5fedff24f30e24e883ef4636": "Formato de comentario",
|
||||
"s18n-6640979a191e66655c26c59d404bf955": "Permitir HTML",
|
||||
"s18n-915009e874f8bed1845060012f826fcd": "Permitir Markdown",
|
||||
"s18n-a9a36cb3d8f4f7297ebca99a322d6342": "Votos de comentarios",
|
||||
"s18n-2123546d1ff8b0cb035df0c0b0d06825": "Los votos se almacenan en",
|
||||
"s18n-dead693ab29895d302fca0e6baad6182": "Cookies",
|
||||
"s18n-02b68043bdcae159e83199d64a5abd7d": "Sesiones",
|
||||
"s18n-40bd8791e523e91219886c35622163fc": "Base de datos",
|
||||
"s18n-8717cfca734e8987971f63b20eeb8024": "Qué?",
|
||||
"s18n-5a8cbcf57f5b59f0d4b8ded97d018399": "Las <b>Cookies<\/b> se localizan en la computadora del usuario. Precisas su consentimiento y no tienes control",
|
||||
"s18n-a09dbdf907873e66d9d644cca71970d5": "Las <b>Sesiones<\/b> se guardan en el servidor temporalmente. Desaparecen cuando caducan o el usuario cierra el navegador. No precisas permiso del usuario.",
|
||||
"s18n-71892ebe01ba92d1f163dc37d818b5ff": "La <b>Base de datos<\/b> se guardan indefinidamente en el servido, pero como identifican al usuario, precisaras de su permiso.",
|
||||
"s18n-7c35a0bcb0b0678f0829036eead5ddca": "<b>Atención!!:<\/b> usted es responsable de obtener los permisos apropiados, Snicker solo maneja los permisos para el envío de datos (y el almacenamiento) a través del formulario de comentarios!",
|
||||
"s18n-82e5228061f185ee185bd9f3ecba4ee7": "Permitir a %s comentarios",
|
||||
"s18n-61b58693e0eceeb27ce0cc3b25b3bf31": "Configuración de la vista de usuario",
|
||||
"s18n-b3c1c2c231275878abe58a55966fa9e0": "Posición en la página",
|
||||
"s18n-9cb1eef8966f93282524929f65c8b9ec": "Sin posición (deberá habilitarse en la plantilla)",
|
||||
"s18n-e89fd56cefec9baabcbe0db3e5a36962": "Usar comienzo de página 'pageBegin'",
|
||||
"s18n-3d08b5dcc1e5c3c7e7e2eaf2d0d6a12d": "Usar final de página 'pageEnd'",
|
||||
"s18n-3771d05b6af4ebb0a303266c47809548": "Usar comienzo de cuerpo html 'siteBodyBegin'",
|
||||
"s18n-42438edc41ab83312486009e3122e92b": "Usar final de cuerpo html 'siteBodyEnd'",
|
||||
"s18n-948da5199de32c7601a20b6107c31d4d": "Captcha",
|
||||
"s18n-47e5c42fb9bdca3636a5d866a6794101": "Deshabilitar Captcha",
|
||||
"s18n-eedf6d23d18212016e22428658e17794": "Usar local Captcha (by Gregway)",
|
||||
"s18n-09cddbc3627ea46a8dce692d64273b61": "Usar Googles reCaptcha (aun no disponible)",
|
||||
"s18n-838a13e4fece7c272b960da3fb99f94d": "Plantilla de comentarios",
|
||||
"s18n-6f95370a28520696b2a0ad34efc54d2d": "Orden de comentarios",
|
||||
"s18n-4a8dc1710396b21e7b1da8112c07c4ad": "Primero recientes",
|
||||
"s18n-2dab3b12d0b0642c3964b37d675ff24b": "Primero antiguos",
|
||||
"s18n-230c71c29590608034b4a590a67ace31": "Posición del formulario",
|
||||
"s18n-37d988444dec2001c488806fc8401e25": "Enseñar comentarios antes del formulario",
|
||||
"s18n-16ac6c11951d825826f77a4097a1c2cb": "Enseñar comentarios tras el formulario",
|
||||
"s18n-1e98ec9312b69676d5e3fe3caf8ecde1": "Comentarios por página",
|
||||
"s18n-0d46f4389ca5f882e24899fe489bf344": "'0' si no deseas límite!",
|
||||
"s18n-7b6b84fbd65a4b712a5ba0dccce176d5": "Casilla de Términos de Uso",
|
||||
"s18n-9853383062a2e308d5aed35fe3da7953": "Desactivar este campo",
|
||||
"s18n-352fd1d7225b5ea02b8ddd9fad0d6e34": "Mostrar mensaje (ver Frases por Defecto)",
|
||||
"s18n-71860c77c6745379b0d44304d66b6a13": "Página",
|
||||
"s18n-5dcb84333ae70a5bed60bf70d34dcd2b": "Mostrar texto por defecto de RGPD (Reglamento General de Protección de datos) o tu propia página!",
|
||||
"s18n-2e3d9327c371afb7489f9b9278198622": "AJAX Script",
|
||||
"s18n-c60cab6330745b41cbee05603eec6691": "AJAX Script disponible",
|
||||
"s18n-69b11f64af515ef979fbf28c5e06f370": "No usar AJAX",
|
||||
"s18n-9bbec7b57565f06d73522669d3b836dc": "Con AJAX acciones como comentar, me gusta, o no me gusta se realizan sin tener que actualizar la página!",
|
||||
"s18n-7c74c0d2d1c28d1568298c89742ce126": "Avatares",
|
||||
"s18n-aeab7c630dae161d8f6e2898dd83b471": "Usar Gravatar",
|
||||
"s18n-882e3436da897c055cc3f8bd2598b71a": "Usar Identicon",
|
||||
"s18n-d92d61ad0c0065170a37a1805ad1bc9e": "Usar Persona Misteriosa",
|
||||
"s18n-b4a51a35344f9a8fa1139cfd968ab308": "Usar imagen de perfil de usuario si está registrado en la web",
|
||||
"s18n-b50a4a96c25d745d73114af5a4b03145": "Configuaración Gravatar",
|
||||
"s18n-9ffb941a398ddee8e054eef3292c546e": "Mostrar Persona Misteriosa",
|
||||
"s18n-a7dd12b1dab17d25467b0b0a4c8d4a92": "Mostrar",
|
||||
"s18n-0d2fc085ee57276417cf380027060760": "Imagen Gravatar por defecto si el usuario no dispone de Gravatar!",
|
||||
"s18n-58e2aacf5792087168cbc62578584ecd": "Configuración de subscripción",
|
||||
"s18n-8290ca86b8980a14bd46f34017e03f93": "El sistema de suscripción aun no está disponible!",
|
||||
"s18n-0bd7ff1b4ac56a9616796bdc05609de2": "Subscripción por Email",
|
||||
"s18n-208f156d4a803025c284bb595a7576b4": "Activo",
|
||||
"s18n-dc985a7c2144c6447674e674aee08441": "Email 'From'",
|
||||
"s18n-f6db5b4db3f9c1ba0ffc091abc561802": "Email 'ReplyTo'",
|
||||
"s18n-8d868315d258783a95336d1a6ce27e1e": "Cuerpo de Email (Opt-In)",
|
||||
"s18n-7721b2a2f2a453cc790e3ac7065e9b65": "Usar el email por defecto para la suscripción",
|
||||
"s18n-58ff11585a82c73f5117c91c29cb3f63": "Cuerpo de Email (Notificación)",
|
||||
"s18n-bea9ff19efca028c01617da5dce18171": "Usar el email por defecto para las notificaciones",
|
||||
"s18n-e0024a8886a178d4f70b8c888b701680": "Conoce más sobre las notificaciones de Email %s!",
|
||||
"s18n-8bcf6629759bd278a5c6266bd9c054f8": "Frases por Defecto",
|
||||
"s18n-3979b4205954030810a8a87769348094": "Mensajes por defecto de agradecimiento",
|
||||
"s18n-620b528248b36bf743d1ad33e35022d6": "Por suscripción",
|
||||
"s18n-e83f6b01d1c81313f6b388281e13aacf": "Por votar",
|
||||
"s18n-01b7a6bc6b57783b4fd081085ff3271e": "Opss!: Algo salió mal. Prueba de nuevo.",
|
||||
"s18n-19678b1419eff34dffd41d6778b1aa89": "Opss!: Nombre de usuario incorrecto",
|
||||
"s18n-1255e6794d33b443bcee21279d9caa1a": "Opss!: Dirección de correo incorrecta",
|
||||
"s18n-ebf545757b92b3a553d83ea3db48beca": "Opss!: Texto de comentario vacío",
|
||||
"s18n-f1bc53e2456c425578277adbc7c90f3a": "Opss!: Título de comentario vacío",
|
||||
"s18n-53c8fdd7ed497dbacbef5fd5d4f38f3d": "Opss!: No has aceptado los términos",
|
||||
"s18n-89f8cc478fdfc0a8f36c1b393f39677a": "Opss!: Se ha marcado como Spam",
|
||||
"s18n-59ad5c9fcee1b28a5d004bcf684a5acd": "Opss!: Ya ha sido votado",
|
||||
"s18n-56ef2c600b4af0f9f7b35640525967ca": "Términos de uso",
|
||||
"s18n-08400c2e0f51197fdb3590461b15b2cc": "Nombre de usuario o Email",
|
||||
"s18n-b79edd2e426f90401c04869346b503c7": "Busqueda de usuarios",
|
||||
"s18n-ee51fa9d5097c84d2fa6c885bf2d5d84": "No hay usuarios disponibles",
|
||||
"s18n-14c4b06b824ec593239362517f538b29": "Nombre de usuarios",
|
||||
"s18n-0c83f57c786a0b4a39efab23731c7ebc": "Email",
|
||||
"s18n-e1260894f59eeae98c8440899de4df8d": "Encargarse de",
|
||||
"s18n-4bc61296b766756f1c7296489633bf32": "Borrar (Anonimizar)",
|
||||
"s18n-c0f4afd3614929f1c803f3a01414a6c7": "Borrar (Completamente)",
|
||||
"s18n-6356f32d0c02c8f90cb59a77e16e8fe2": "Usuario desbloqueado",
|
||||
"s18n-2327a01afbee025fb5913357c9d6b1b3": "Usuario bloqueado",
|
||||
"s18n-76a0f6752a45d8af6343ef3e2b6f522a": "Único comentario",
|
||||
"s18n-c426859e50a35617d863cdad2b9c84aa": "Página de comentarios",
|
||||
"s18n-a64d776275f13a51790bb460774b9129": "Comentarios de usuarios",
|
||||
"s18n-9bc65c2abec141778ffaa729489f3e87": "Usuarios",
|
||||
"s18n-ccd1066343c95877b75b79d47c36bebe": "Configuración",
|
||||
"s18n-5b49260517622682a058b69f996d06eb": "Gracias por comentar!",
|
||||
"s18n-74196a783a6f1707a43cc8117f0d9c83": "Gracias por tu comentario. Revisa tu correo electrónico para confirmar la subscripción!",
|
||||
"s18n-a939eb542e34cd502b3f7352b2e0f715": "Gracias por valorar este comentario!",
|
||||
"s18n-d2a9677817ee08ed05bf9fd868669756": "Opss!: Algo salió mal, actualiza e intentalo de nuevo!",
|
||||
"s18n-05b85714aa8f1b364f930e2539059b5e": "Opss! Nombre de usuario inválido o demasiado largo (hasta 42 caracteres)!",
|
||||
"s18n-321e8b481f0ccd62df535256e8e6d2c6": "Opss!: Dirección de correo inválida!",
|
||||
"s18n-fcd0c3a087c5123ffdecc20fe9015870": "Opss!: No hay texto en tu comentario!",
|
||||
"s18n-2afa2d90ce3343fbe188b9f49ad5797d": "Opss!: Falta el título!",
|
||||
"s18n-bee1741efe0c735d2c7180771586faf0": "Opss!: No aceptaste los términos!",
|
||||
"s18n-92fe96d6ccee901f94fad0000369a9b7": "Opss!: No eres bien recibido, por espamer!",
|
||||
"s18n-9b264fc6137096f8a40acde68f6ae562": "Opss!: Ya votaste este comentario!",
|
||||
"s18n-ca62db4704290ef1a7e65df3ffc7983b": "Conforme con almacenar mis datos (incluida dirección IP)!",
|
||||
"s18n-01c611362e8b046f32650b85ce161559": "Opss!: Algo salió mal",
|
||||
"s18n-c4f94c6995b0376f28276e432bed75fa": "Token perdido!",
|
||||
"s18n-a573e7b86e41522c7a291846aa109104": "Token inválido!",
|
||||
"s18n-3e8909518ce4728685aa09cdde3caa22": "No tienes suficientes permisos!",
|
||||
"s18n-b60a6a8a529a9f0497134205bab15e77": "No tienes suficientes permisos!",
|
||||
"s18n-56abae3e615ae5b5609c32852b777d46": "Acción desconocida o inválida!",
|
||||
"s18n-be5136b4f2b33c80e3afd377ee993acb": "Opss!: Algo salió mal",
|
||||
"s18n-46d4c97e91319867654f7cc80c439ba4": "Usuario sin ID (identificador)!",
|
||||
"s18n-af1ba1dd4eab5562f78c65bc89a0a7e9": "Acción exitosa!",
|
||||
"s18n-a5193444ee82c18bac726b35a1704d03": "Actualizado con éxito!",
|
||||
"s18n-717c8267d40664ccf7ef25a26ff9cde6": "Copia de seguridad realizada con éxito!",
|
||||
"s18n-a1e2b7401861cee01c172878f104bd8c": "Comentarios no disponibles",
|
||||
"s18n-15802277ea1cdfcbacd6308fa0c7c30f": "Desactivación Snicker Plugin",
|
||||
"s18n-877d58f21c87442efa4081112a6cb07a": "Desactivar <b>Snicker<\/b> Plugin, podría borrar todos los comentarios!",
|
||||
"s18n-a4983c86683f8d8598c0513339550dc0": "Deseas realizar una copia de seguridad antes?",
|
||||
"s18n-4cce9e52118cc659be5070b2f08cdd91": "La copia de seguridad será guardad en %s!",
|
||||
"s18n-5f87fd2e0fa992d37c814bb4ca299646": "Si, crear copia de seguridad",
|
||||
"s18n-3d414feb412a1f4cd9324f6411c76329": "No, solo desactiva",
|
||||
"s18n-10aec35353f9c4096a71c38654c3d402": "Cancelar",
|
||||
"s18n-bc6d6a26d44b6f39a0e7b6c7787f3295": "Se han desactivado los comentarios para esta página!",
|
||||
"s18n-a8e30d73eddea9866cf99ecd6e8467b5": "No hay comentarios. Se el primero!",
|
||||
"s18n-b3afbadaa2f1c79f8b3999be7fd9719f": "Captcha incorrecto!",
|
||||
"s18n-fb74aafe8bf1fd4d8f7a6e1ff73028f7": "UID de comentario inexistente o incorrecta!",
|
||||
"s18n-25dea7cb70f98250b388f6ab0ddf20cb": "Estado de comentario inexistente o incorrecto!",
|
||||
"s18n-2cd68855bdc54ff5e3c191a6333ff75d": "No se ha podido actualizar el nuevo estado de comentario!",
|
||||
"s18n-c0937505b7afa81a053077bc7ae369a5": "Estado de comentario actualizado con éxito!",
|
||||
"s18n-6b2c8084c67f24bb73d95031bb570ef7": "No se ha podido borrar el comentario!",
|
||||
"s18n-376388311a80dbde63fde7f6c72081e0": "Comentario borrado con éxito!",
|
||||
"s18n-83bbb9e8745cc95e730fe7b8de9345f1": "Identificado como %s (%s)",
|
||||
"s18n-91fb98e1ac4cf76b7a5b8bae09051e2a": "Nombre de usuario",
|
||||
"s18n-31f6da7a30e7acf1f82451bfd1a7f8fa": "Dirección de correo",
|
||||
"s18n-f794080a5a29e35233c82df85f1207eb": "Comentario...",
|
||||
"s18n-a363b8d13575101a0226e8d0d054f2e7": "Respuestas",
|
||||
"s18n-f10db888c5e63b343000cffc038e0a46": "Escribe",
|
||||
"s18n-3cc5bcf15d6b8faed118e2ce72d19a1e": "De acuerdo con %s!",
|
||||
"s18n-2af0aab477f402e0f4ad7a27e6c9f952": "Comentarios anteriores",
|
||||
"s18n-8538431db22040e2147b363f86a2e2f0": "Comentarios siguientes",
|
||||
"s18n-ae0dbd5cc42a6db191db5e0083bcb307": "Este comentario aun no ha sido moderado!",
|
||||
"s18n-48df9c3f3cca3fb2b8bcf811633bee06": "Escrito por %s",
|
||||
"s18n-81fdc9813cebe0553c55e78dc2b6029f": "sobre %s",
|
||||
"s18n-be1ab1632e4285edc3733b142935c60b": "Me gusta",
|
||||
"s18n-bc8b79025e4595298669fd21da814941": "No me gusta",
|
||||
"s18n-e84afaab83ecb301b3d97ce4174d2773": "Responder"
|
||||
}
|
||||
201
languages/fa_IR.json
Normal file
201
languages/fa_IR.json
Normal file
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"plugin-data": {
|
||||
"name": "ارسال دیدگاه Snicker",
|
||||
"description": "یک سیستم بومی ارسال دیدگاه فلت-فایل آجاکسی برای بلودیت، شامل اشتراک به دیدگاهها و کاملاً سازگار با GDPR!"
|
||||
},
|
||||
"s18n-a5d491060952aa8ad5fdee071be752de": "ارسال دیدگاه",
|
||||
"s18n-de95b43bceeb4b998aed4aed5cef1ae7": "ویرایش",
|
||||
"s18n-f984023ed3d6df2326c9d59838c29792": "بروزرسانی دیدگاه",
|
||||
"s18n-86448a506dd93303a72140b9124ee321": "حذف دیدگاه",
|
||||
"s18n-db10a8eb963bc0e5f4483ac9b5dc554c": "عنوان دیدگاه",
|
||||
"s18n-335630425567dbe91768a3beffdec752": "متن دیدگاه",
|
||||
"s18n-bd7e63f881c7f787a9a0dce20b7f9e5b": "ابر تنظیمات",
|
||||
"s18n-1acfe725df7bd12195751f0737c4d375": "کاربر ثبت شده",
|
||||
"s18n-e988189db402fab453f72052629c02cd": "نام کاربری دیدگاه",
|
||||
"s18n-db84c6236ca6a01b9189504d78c012a5": "ایمیل دیدگاه",
|
||||
"s18n-7c6c2e5d48ab37a007cbf70d3ea25fa4": "در انتظار",
|
||||
"s18n-787d5f05953ec39b108869dfdd7733e6": "تائید شده",
|
||||
"s18n-c7537d6d48ecf261749c09a9f284bd45": "رد شده",
|
||||
"s18n-e09f6a7593f8ae3994ea57e1117f67ec": "هرزنامه",
|
||||
"s18n-7a5115c2c1eb662308decbec83593494": "مشاهده صفحه",
|
||||
"s18n-0572a05cd6d6360391993a611099542f": "عنوان و یا گزیده دیدگاه",
|
||||
"s18n-428f14500191b5d53675da4a96bc8bba": "جستجوی دیدگاهها",
|
||||
"s18n-e0be71bccdeceb713fba3c222f79a3c5": "دیدگاهی در دسترس نیست",
|
||||
"s18n-06d4cd63bde972fc66a0aed41d2f5c51": "دیدگاه",
|
||||
"s18n-02bd92faa38aaa6cc0ea75e59937a1ef": "نویسنده",
|
||||
"s18n-ebb67a4271abe715344471b0f16321f6": "عملیات",
|
||||
"s18n-58566b9a9b2733b0ceacb2186672b5d1": "نمایش تمام پاسخ ها",
|
||||
"s18n-5f44c0081bd862a77ba8b24e923cadf1": "پاسخ به",
|
||||
"s18n-eb399bcaca686f8609137153307eecf1": "تغییر",
|
||||
"s18n-2736f4347985da50dc023444c193bfea": "ویرایش دیدگاه",
|
||||
"s18n-a107bf4b12e36a07161a26d95b03bc81": "تائید دیدگاه",
|
||||
"s18n-da937abd19cd9e1430470b8a471a41d4": "رد دیدگاه",
|
||||
"s18n-32cb4199893d9948cc0853eef244f1fc": "هرزنامه است",
|
||||
"s18n-a6494adfb72d12d3a4da66855c284ec6": "بازگشت به در انتظار",
|
||||
"s18n-1bda80f2be4d3658e0baa43fbe7ae8c1": "مشاهده",
|
||||
"s18n-c9ae5a4214e87ad6fbed44a267471eee": "ذخیره تنظیمات",
|
||||
"s18n-e124d357c3c832434a8676a5e18db842": "تنظیمات عمومی",
|
||||
"s18n-b62a9dcd666f3ff44197cf21ac66507d": "مدیریت دیدگاهها",
|
||||
"s18n-7f0217cdcdd58ba86aae84d9d3d79f81": "مدیریت",
|
||||
"s18n-1a1dc91c907325c69271ddf0c944bc72": "تائید",
|
||||
"s18n-df6963dc912cde9baeef10343167ba01": "هر دیدگاه",
|
||||
"s18n-39ba4181e212acf183ac965c9b37da89": "درصورت ورود به سیستم",
|
||||
"s18n-0dac4426a017f0a0370db32776bc30bb": "در صورت مدیر یا نویسنده بودن",
|
||||
"s18n-c70aa8b8fd6f2652eda2d5366faf1de5": "در صورت داشتن دیدگاه از قبل",
|
||||
"s18n-8b26cab9d444760b4bcc65dc4d8634f8": "پذیرفتن دیدگاه",
|
||||
"s18n-67614909bf9de326de71946036de39f1": "... در نوشته های عمومی",
|
||||
"s18n-a7c384c1b60785c40bc3a4e4dfc5a108": "... در نوشته های چسبنده",
|
||||
"s18n-9907b78f8745810599dbf6fd29a01364": "... در نوشته های استاتیک",
|
||||
"s18n-c89cbecce48d04cd76c0b95c8128ad97": "فعال (اختیاری)",
|
||||
"s18n-e9d51286fdd0ff058650392fc8b6ae30": "فعال (الزامی)",
|
||||
"s18n-0aaa87422396fdd678498793b6d5250e": "غیرفعال",
|
||||
"s18n-8da2c8185edfeb1765526f8e2e4f388d": "محدودیت دیدگاه",
|
||||
"s18n-77d6d185c93549dab24f29ff2e3b25a8": "برای غیرفعال کردن هر محدودیتی از '0' استفاده کنید",
|
||||
"s18n-1ef52691308c8add87723a4103a561c2": "عمق دیدگاه",
|
||||
"s18n-d102731a5fedff24f30e24e883ef4636": "Markup دیدگاه",
|
||||
"s18n-6640979a191e66655c26c59d404bf955": "پذیرفتن HTML ابتدایی",
|
||||
"s18n-915009e874f8bed1845060012f826fcd": "پذیرفتن Markdown",
|
||||
"s18n-a9a36cb3d8f4f7297ebca99a322d6342": "امتیاز به دیدگاه",
|
||||
"s18n-2123546d1ff8b0cb035df0c0b0d06825": "ذخیره امتیازهای مهمانان در",
|
||||
"s18n-dead693ab29895d302fca0e6baad6182": "ذخیره سازی کوکی",
|
||||
"s18n-02b68043bdcae159e83199d64a5abd7d": "ذخیره سازی نشست",
|
||||
"s18n-40bd8791e523e91219886c35622163fc": "ذخیره سازی پایگاه داده",
|
||||
"s18n-8717cfca734e8987971f63b20eeb8024": "توضیحات؟",
|
||||
"s18n-5a8cbcf57f5b59f0d4b8ded97d018399": "<b>ذخیره سازی کوکی</b> بر روی کامپیوتر کاربر قرار دارد. بنابراین شما کنترل کامل نداشته و نیاز به مجوز مناسب از طرف کاربر را دارید.",
|
||||
"s18n-a09dbdf907873e66d9d644cca71970d5": "<b>ذخیره سازی نشست</b> فقط به صورت موقت در سرور ذخیره میشود، زمانی که کاربر مرورگرش را ببندد این اطلاعات هم پاک میشود. بنابراین شما به هیچ مجوزی از طرف کاربر نیازی ندارید.",
|
||||
"s18n-71892ebe01ba92d1f163dc37d818b5ff": "<b>ذخیره سازی پایگاه داده</b> تولید و در یک مقدار ناشناس ذخیره میشود، ولی قابل تعیین توسط کاربر است، همچنین نیاز به مجوزهای مناسب از طرف کاربر میباشد.",
|
||||
"s18n-7c35a0bcb0b0678f0829036eead5ddca": "<b> لطفاً توجه کنید: </b>شما مسئولیت اخذ مجوزهای مناسب هستید، Snicker فقط مجوزهای ارسال (و ذخیره سازی) اطلاعات از طریق فرم دیدگاه را بکار میگیرد!",
|
||||
"s18n-82e5228061f185ee185bd9f3ecba4ee7": "پذیرفتن به %s دیدگاه",
|
||||
"s18n-61b58693e0eceeb27ce0cc3b25b3bf31": "تنظیمات محیط کاربری",
|
||||
"s18n-b3c1c2c231275878abe58a55966fa9e0": "فیلتر صفحه",
|
||||
"s18n-9cb1eef8966f93282524929f65c8b9ec": "غیرفعال کردن فیلتر صفحه",
|
||||
"s18n-e89fd56cefec9baabcbe0db3e5a36962": "استفاده از 'pageBegin'",
|
||||
"s18n-3d08b5dcc1e5c3c7e7e2eaf2d0d6a12d": "استفاده از 'pageEnd'",
|
||||
"s18n-3771d05b6af4ebb0a303266c47809548": "استفاده از 'siteBodyBegin'",
|
||||
"s18n-42438edc41ab83312486009e3122e92b": "استفاده از 'siteBodyEnd'",
|
||||
"s18n-948da5199de32c7601a20b6107c31d4d": "کدامنیتی دیدگاه",
|
||||
"s18n-47e5c42fb9bdca3636a5d866a6794101": "غیرفعال کردن دیدگاه",
|
||||
"s18n-eedf6d23d18212016e22428658e17794": "استفاده از کدامنیتی محلی (مولف Gregway)",
|
||||
"s18n-09cddbc3627ea46a8dce692d64273b61": "استفاده از reCaptcha گوگل (هنوز در دسترس نیست)",
|
||||
"s18n-838a13e4fece7c272b960da3fb99f94d": "قالب دیدگاه",
|
||||
"s18n-6f95370a28520696b2a0ad34efc54d2d": "ترتیب دیدگاه",
|
||||
"s18n-4a8dc1710396b21e7b1da8112c07c4ad": "جدیدترین دیدگاه در ابتدا",
|
||||
"s18n-2dab3b12d0b0642c3964b37d675ff24b": "جدیدترین دیدگاه در انتها",
|
||||
"s18n-230c71c29590608034b4a590a67ace31": "موقعیت فرم دیدگاه",
|
||||
"s18n-37d988444dec2001c488806fc8401e25": "نمایش فرم دیدگاه بالای دیدگاهها",
|
||||
"s18n-16ac6c11951d825826f77a4097a1c2cb": "نمایش فرم دیدگاه پایین دیدگاهها",
|
||||
"s18n-1e98ec9312b69676d5e3fe3caf8ecde1": "دیدگاه در هر صفحه",
|
||||
"s18n-0d46f4389ca5f882e24899fe489bf344": "برای نمایش تمام دیدگاهها از '0' استفاده کنید",
|
||||
"s18n-7b6b84fbd65a4b712a5ba0dccce176d5": "جعبه شرایط استفاده",
|
||||
"s18n-9853383062a2e308d5aed35fe3da7953": "غیرفعال کردن این کادر",
|
||||
"s18n-352fd1d7225b5ea02b8ddd9fad0d6e34": "نمایش پیام (متون جایگزین را ببینید)",
|
||||
"s18n-71860c77c6745379b0d44304d66b6a13": "صفحه",
|
||||
"s18n-5dcb84333ae70a5bed60bf70d34dcd2b": "متن پیشفرض GDPR نمایش داده شود و یا متن صفحه استاتیک 'قوانین استفاده' خود را انتخاب کنید!",
|
||||
"s18n-2e3d9327c371afb7489f9b9278198622": "اسکریپت AJAX",
|
||||
"s18n-c60cab6330745b41cbee05603eec6691": "استفاده از AJAX",
|
||||
"s18n-69b11f64af515ef979fbf28c5e06f370": "از AJAX استفاده نشود",
|
||||
"s18n-9bbec7b57565f06d73522669d3b836dc": "اسکریپت AJAX درخواست های (دیدگاه، like،dislike) را مستقیماً بکار میبرد بدون اینکه صفحه مجدداً بارگذاری شود!",
|
||||
"s18n-7c74c0d2d1c28d1568298c89742ce126": "آواتار دیدگاه",
|
||||
"s18n-aeab7c630dae161d8f6e2898dd83b471": "استفاده از Gravatar",
|
||||
"s18n-882e3436da897c055cc3f8bd2598b71a": "استفاده از Identicon",
|
||||
"s18n-d92d61ad0c0065170a37a1805ad1bc9e": "استفاده از Mystery Men",
|
||||
"s18n-b4a51a35344f9a8fa1139cfd968ab308": "ترجیح استفاده از تصویر پروفایل کاربران وارد شده به سیستم",
|
||||
"s18n-b50a4a96c25d745d73114af5a4b03145": "دیدگاه Gravatar",
|
||||
"s18n-9ffb941a398ddee8e054eef3292c546e": "نمایش Mystery Person",
|
||||
"s18n-a7dd12b1dab17d25467b0b0a4c8d4a92": "نمایش",
|
||||
"s18n-0d2fc085ee57276417cf380027060760": "تصویر پیش فرض Gravatar، در صورتی که کاربر Gravatar ندارد!",
|
||||
"s18n-58e2aacf5792087168cbc62578584ecd": "تنظیمات اشتراک",
|
||||
"s18n-8290ca86b8980a14bd46f34017e03f93": "سیستم اشتراک هنوز در دسترس نیست!",
|
||||
"s18n-0bd7ff1b4ac56a9616796bdc05609de2": "اشتراک ایمیلی",
|
||||
"s18n-208f156d4a803025c284bb595a7576b4": "فعال",
|
||||
"s18n-dc985a7c2144c6447674e674aee08441": "آدرس ایمیل 'From'",
|
||||
"s18n-f6db5b4db3f9c1ba0ffc091abc561802": "آدرس ایمیل 'ReplyTo'",
|
||||
"s18n-8d868315d258783a95336d1a6ce27e1e": "متن ایمیل (Opt-In)",
|
||||
"s18n-7721b2a2f2a453cc790e3ac7065e9b65": "استفاده از ایمیل اشتراک پیش فرض",
|
||||
"s18n-58ff11585a82c73f5117c91c29cb3f63": "متن ایمیل (اطلاع رسانی)",
|
||||
"s18n-bea9ff19efca028c01617da5dce18171": "استفاده از ایمیل اطلاع رسانی پیش فرض",
|
||||
"s18n-e0024a8886a178d4f70b8c888b701680": "درمورد ایمیل اطلاع رسانی های سفارشی %s مطالعه کنید!",
|
||||
"s18n-8bcf6629759bd278a5c6266bd9c054f8": "متون جایگزین",
|
||||
"s18n-3979b4205954030810a8a87769348094": "پیام تشکر پیش فرض",
|
||||
"s18n-620b528248b36bf743d1ad33e35022d6": "پیام تشکر به همراه اشتراک",
|
||||
"s18n-e83f6b01d1c81313f6b388281e13aacf": "پیام تشکر برای امتیازدهی",
|
||||
"s18n-01b7a6bc6b57783b4fd081085ff3271e": "خطا: خطای ناشناخته، دوباره تلاش کنید",
|
||||
"s18n-19678b1419eff34dffd41d6778b1aa89": "خطا: نام کاربری معتبر نیست",
|
||||
"s18n-1255e6794d33b443bcee21279d9caa1a": "خطا: آدرس ایمیل معتبر نیست",
|
||||
"s18n-ebf545757b92b3a553d83ea3db48beca": "خطا: متن دیدگاه خالی است",
|
||||
"s18n-f1bc53e2456c425578277adbc7c90f3a": "خطا: عنوان دیدگاه خالی است",
|
||||
"s18n-53c8fdd7ed497dbacbef5fd5d4f38f3d": "خطا: قوانین پذیرفته نشده",
|
||||
"s18n-89f8cc478fdfc0a8f36c1b393f39677a": "خطا: به عنوان هرزنامه شناخته شد",
|
||||
"s18n-59ad5c9fcee1b28a5d004bcf684a5acd": "خطا: قبلا امتیاز دادید",
|
||||
"s18n-56ef2c600b4af0f9f7b35640525967ca": "قوانین استفاده",
|
||||
"s18n-08400c2e0f51197fdb3590461b15b2cc": "نام کاربری یا آدرس ایمیل",
|
||||
"s18n-b79edd2e426f90401c04869346b503c7": "جستجوی کاربران",
|
||||
"s18n-ee51fa9d5097c84d2fa6c885bf2d5d84": "هیچ کاربری وجود ندارد",
|
||||
"s18n-14c4b06b824ec593239362517f538b29": "نام کاربرری",
|
||||
"s18n-0c83f57c786a0b4a39efab23731c7ebc": "ایمیل",
|
||||
"s18n-e1260894f59eeae98c8440899de4df8d": "بکارگیری",
|
||||
"s18n-4bc61296b766756f1c7296489633bf32": "حذف (ناشناس)",
|
||||
"s18n-c0f4afd3614929f1c803f3a01414a6c7": "حذف (به طور کامل)",
|
||||
"s18n-6356f32d0c02c8f90cb59a77e16e8fe2": "آزاد کردن کاربر",
|
||||
"s18n-2327a01afbee025fb5913357c9d6b1b3": "مسدود کردن کاربر",
|
||||
"s18n-76a0f6752a45d8af6343ef3e2b6f522a": "یک دیدگاه",
|
||||
"s18n-c426859e50a35617d863cdad2b9c84aa": "دیدگاه های نوشته",
|
||||
"s18n-a64d776275f13a51790bb460774b9129": "دیدگاه های کاربر",
|
||||
"s18n-9bc65c2abec141778ffaa729489f3e87": "کاربران",
|
||||
"s18n-ccd1066343c95877b75b79d47c36bebe": "پیکربندی",
|
||||
"s18n-5b49260517622682a058b69f996d06eb": "متشکرم از ارسال دیدگاه!",
|
||||
"s18n-74196a783a6f1707a43cc8117f0d9c83": "متشکرم از ارسال دیدگاه! لطفاً اشتراک خود را از طریق لینکی که به آدرس ایمیل شما ارسال شده را تائید کنید",
|
||||
"s18n-a939eb542e34cd502b3f7352b2e0f715": "متشکرم از اینکه به این دیدگاه امتیاز دادید!",
|
||||
"s18n-d2a9677817ee08ed05bf9fd868669756": "خطای ناشناخته ای رخ داد، لطفا صفحه را مجددا بارگیری کرده و دوباره تلاش کنید!",
|
||||
"s18n-05b85714aa8f1b364f930e2539059b5e": "خطایی رخ داد: نام کاربری ارائه شده نامعتبر و یا بسیار طولانی است(فقط 42 کاراکتر باشد)!",
|
||||
"s18n-321e8b481f0ccd62df535256e8e6d2c6": "خطایی رخ داد: آدرس ایمیل ارائه شده معتبر نیست",
|
||||
"s18n-fcd0c3a087c5123ffdecc20fe9015870": "خطایی رخ داد: متن دیدگاه خالی است!",
|
||||
"s18n-2afa2d90ce3343fbe188b9f49ad5797d": "خطایی رخ داد: عنوان دیدگاه خالی است!",
|
||||
"s18n-bee1741efe0c735d2c7180771586faf0": "خطایی رخ داد: برای ارسال دیدگاه باید قوانین را بپذیرید!",
|
||||
"s18n-92fe96d6ccee901f94fad0000369a9b7": "خطایی رخ داد: آدرس IP و یا آدرس ایمیل شما به عنوان هرزنامه شناخته شده!",
|
||||
"s18n-9b264fc6137096f8a40acde68f6ae562": "خطایی رخ داد: شما قبلا به این دیدگاه رای داده اید!",
|
||||
"s18n-ca62db4704290ef1a7e65df3ffc7983b": "من موافقم که اطلاعات من (شامل آدرس IP ناشناس) من ذخیره شود!",
|
||||
"s18n-01c611362e8b046f32650b85ce161559": "خطای ناشناخته رخ داد!",
|
||||
"s18n-c4f94c6995b0376f28276e432bed75fa": "توکن CSRF وجود ندارد!",
|
||||
"s18n-a573e7b86e41522c7a291846aa109104": "توکن CSRF معتبر نیست!",
|
||||
"s18n-3e8909518ce4728685aa09cdde3caa22": "برای فراخوانی این اقدام مجوز لازم را ندارید!",
|
||||
"s18n-b60a6a8a529a9f0497134205bab15e77": "برای اجرای این اقدام مجوز لازم را ندارید!",
|
||||
"s18n-56abae3e615ae5b5609c32852b777d46": "اقدام انجام شده ناشناخته و یا معتبر نیست!",
|
||||
"s18n-be5136b4f2b33c80e3afd377ee993acb": "خطای ناشناخته رخ داد!",
|
||||
"s18n-46d4c97e91319867654f7cc80c439ba4": "شناسه منحصر به فرد کاربری وجود ندارد!",
|
||||
"s18n-af1ba1dd4eab5562f78c65bc89a0a7e9": "اقدام با موفقیت انجام شد!",
|
||||
"s18n-a5193444ee82c18bac726b35a1704d03": "تنظیمات با موفقیت به روز شد!",
|
||||
"s18n-717c8267d40664ccf7ef25a26ff9cde6": "نسخه پشتیبان با موفقیت تهیه شد!",
|
||||
"s18n-a1e2b7401861cee01c172878f104bd8c": "نپذیرفتن دیدگاه",
|
||||
"s18n-15802277ea1cdfcbacd6308fa0c7c30f": "غیر فعال کردن پلاگین ارسال دیدگاه Snicker",
|
||||
"s18n-877d58f21c87442efa4081112a6cb07a": "شما دارید پلاگین <b>Snicker</b> را غیرفعال میکنید، که تمام دیدگاههای نوشته شده حذف خواهند شد!",
|
||||
"s18n-a4983c86683f8d8598c0513339550dc0": "آیا تمایل دارید قبل از اینکار از دیدگاهها نسخه پشتیبان تهیه کنید؟",
|
||||
"s18n-4cce9e52118cc659be5070b2f08cdd91": "نسخه پشتیبان در %s ذخیره می شود!",
|
||||
"s18n-5f87fd2e0fa992d37c814bb4ca299646": "بله، تهیه پشتیبان",
|
||||
"s18n-3d414feb412a1f4cd9324f6411c76329": "خیر، غیرفعال کردن",
|
||||
"s18n-10aec35353f9c4096a71c38654c3d402": "لغو",
|
||||
"s18n-bc6d6a26d44b6f39a0e7b6c7787f3295": "ارسال دیدگاه در این صفحه غیرفعال است!",
|
||||
"s18n-a8e30d73eddea9866cf99ecd6e8467b5": "اولین نفری باشید که دیدگاهی ارسال می کند!",
|
||||
"s18n-b3afbadaa2f1c79f8b3999be7fd9719f": "پاسخ به کدامنیتی انجام نشده و یا اشتباه وارد کردید!",
|
||||
"s18n-fb74aafe8bf1fd4d8f7a6e1ff73028f7": "شناسه منحصر به فرد دیدگاه وجود نداشته و یا معتبر نیست!",
|
||||
"s18n-25dea7cb70f98250b388f6ab0ddf20cb": "وضعیت دیدگاه ناشناخته و یا معتبر نیست!",
|
||||
"s18n-2cd68855bdc54ff5e3c191a6333ff75d": "وضعیت دیدگاه جدید نمی تواند با موفقیت به روز شود!",
|
||||
"s18n-c0937505b7afa81a053077bc7ae369a5": "وضعیت دیدگاه جدید با موفقیت ذخیره شد!",
|
||||
"s18n-6b2c8084c67f24bb73d95031bb570ef7": "دیدگاه نمی تواند با موفقیت حذف شود!",
|
||||
"s18n-376388311a80dbde63fde7f6c72081e0": "دیدگاه با موفقیت حذف شد!",
|
||||
"s18n-83bbb9e8745cc95e730fe7b8de9345f1": "ورود به نام %s (%s)",
|
||||
"s18n-91fb98e1ac4cf76b7a5b8bae09051e2a": "نام شما",
|
||||
"s18n-31f6da7a30e7acf1f82451bfd1a7f8fa": "آدرس ایمیل شما",
|
||||
"s18n-f794080a5a29e35233c82df85f1207eb": "متن دیدگاه...",
|
||||
"s18n-a363b8d13575101a0226e8d0d054f2e7": "کدامنیتی",
|
||||
"s18n-f10db888c5e63b343000cffc038e0a46": "نوشته:",
|
||||
"s18n-3cc5bcf15d6b8faed118e2ce72d19a1e": "با %s موافقت می کنم!",
|
||||
"s18n-2af0aab477f402e0f4ad7a27e6c9f952": "دیدگاههای قبلی",
|
||||
"s18n-8538431db22040e2147b363f86a2e2f0": "دیدگاههای بعدی",
|
||||
"s18n-ae0dbd5cc42a6db191db5e0083bcb307": "این دیدگاه هنوز تائید نشده!",
|
||||
"s18n-48df9c3f3cca3fb2b8bcf811633bee06": "نوشته %s",
|
||||
"s18n-81fdc9813cebe0553c55e78dc2b6029f": "در مورخه %s",
|
||||
"s18n-be1ab1632e4285edc3733b142935c60b": "Like",
|
||||
"s18n-bc8b79025e4595298669fd21da814941": "Dislike",
|
||||
"s18n-e84afaab83ecb301b3d97ce4174d2773": "پاسخ"
|
||||
}
|
||||
204
languages/nl_NL.json
Normal file
204
languages/nl_NL.json
Normal file
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"plugin-data": {
|
||||
"name": "Snicker",
|
||||
"description": "Een native, AJAX-enabled FlatFile reactiesysteem voor bludit volgens de GDPR-normen en met ondersteuning voor het abonneren op reacties."
|
||||
},
|
||||
"s18n-a5d491060952aa8ad5fdee071be752de": "Reacties",
|
||||
"s18n-de95b43bceeb4b998aed4aed5cef1ae7": "Bewerken",
|
||||
"s18n-f984023ed3d6df2326c9d59838c29792": "Reactie bijwerken",
|
||||
"s18n-86448a506dd93303a72140b9124ee321": "Reactie verwijderen",
|
||||
"s18n-db10a8eb963bc0e5f4483ac9b5dc554c": "Titel",
|
||||
"s18n-335630425567dbe91768a3beffdec752": "Bericht",
|
||||
"s18n-bd7e63f881c7f787a9a0dce20b7f9e5b": "Metadata",
|
||||
"s18n-1acfe725df7bd12195751f0737c4d375": "Geregistreerde gebruiker",
|
||||
"s18n-e988189db402fab453f72052629c02cd": "Gebruikersnaam",
|
||||
"s18n-db84c6236ca6a01b9189504d78c012a5": "E-mail",
|
||||
"s18n-7c6c2e5d48ab37a007cbf70d3ea25fa4": "Wordt beoordeeld",
|
||||
"s18n-787d5f05953ec39b108869dfdd7733e6": "Goedgekeurd",
|
||||
"s18n-c7537d6d48ecf261749c09a9f284bd45": "Afgekeurd",
|
||||
"s18n-e09f6a7593f8ae3994ea57e1117f67ec": "Spam",
|
||||
"s18n-7a5115c2c1eb662308decbec83593494": "Pagina bekijken",
|
||||
"s18n-0572a05cd6d6360391993a611099542f": "Titel of samenvatting reactie",
|
||||
"s18n-428f14500191b5d53675da4a96bc8bba": "Zoeken",
|
||||
"s18n-e0be71bccdeceb713fba3c222f79a3c5": "Geen reacties beschikbaar",
|
||||
"s18n-06d4cd63bde972fc66a0aed41d2f5c51": "Reactie",
|
||||
"s18n-02bd92faa38aaa6cc0ea75e59937a1ef": "Auteur",
|
||||
"s18n-ebb67a4271abe715344471b0f16321f6": "Acties",
|
||||
"s18n-58566b9a9b2733b0ceacb2186672b5d1": "Alle antwoorden tonen",
|
||||
"s18n-5f44c0081bd862a77ba8b24e923cadf1": "Antwoorden op",
|
||||
"s18n-eb399bcaca686f8609137153307eecf1": "Wijzigen",
|
||||
"s18n-2736f4347985da50dc023444c193bfea": "Reactie bewerken",
|
||||
"s18n-a107bf4b12e36a07161a26d95b03bc81": "Reactie goedkeuren",
|
||||
"s18n-da937abd19cd9e1430470b8a471a41d4": "Reactie afkeuren",
|
||||
"s18n-32cb4199893d9948cc0853eef244f1fc": "Markeren als Spam",
|
||||
"s18n-a6494adfb72d12d3a4da66855c284ec6": "Terug naar reacties in afwachting",
|
||||
"s18n-1bda80f2be4d3658e0baa43fbe7ae8c1": "Bekijken",
|
||||
"s18n-c9ae5a4214e87ad6fbed44a267471eee": "Instellingen opslaan",
|
||||
"s18n-e124d357c3c832434a8676a5e18db842": "Algemene instellingen",
|
||||
"s18n-b62a9dcd666f3ff44197cf21ac66507d": "Reactiebeheer",
|
||||
"s18n-7f0217cdcdd58ba86aae84d9d3d79f81": "Modereren",
|
||||
"s18n-1a1dc91c907325c69271ddf0c944bc72": "Goedkeuren",
|
||||
"s18n-df6963dc912cde9baeef10343167ba01": "iedere reactie",
|
||||
"s18n-39ba4181e212acf183ac965c9b37da89": "Tenzij de gebruiker is ingelogd",
|
||||
"s18n-0dac4426a017f0a0370db32776bc30bb": "Tenzij de gebruiker beheerder of auteur van de pagina is",
|
||||
"s18n-c70aa8b8fd6f2652eda2d5366faf1de5": "Tenzij er reeds een eerdere reactie van de gebruiker is goedgekeurd",
|
||||
"s18n-8b26cab9d444760b4bcc65dc4d8634f8": "Reacties toestaan",
|
||||
"s18n-67614909bf9de326de71946036de39f1": "... op gepubliceerde pagina's",
|
||||
"s18n-a7c384c1b60785c40bc3a4e4dfc5a108": "... op vastgezette pagina's",
|
||||
"s18n-9907b78f8745810599dbf6fd29a01364": "... op statische pagina's",
|
||||
"s18n-c89cbecce48d04cd76c0b95c8128ad97": "Inschakelen (optioneel)",
|
||||
"s18n-e9d51286fdd0ff058650392fc8b6ae30": "Inschakelen (vereist)",
|
||||
"s18n-0aaa87422396fdd678498793b6d5250e": "Uitschakelen",
|
||||
"s18n-8da2c8185edfeb1765526f8e2e4f388d": "Limiet voor reacties",
|
||||
"s18n-77d6d185c93549dab24f29ff2e3b25a8": "Voer '0' in om geen limiet toe te passen.",
|
||||
"s18n-1ef52691308c8add87723a4103a561c2": "Diepte reacties",
|
||||
"s18n-d102731a5fedff24f30e24e883ef4636": "Formaat voor reacties",
|
||||
"s18n-6640979a191e66655c26c59d404bf955": "HTML beperkt toestaan",
|
||||
"s18n-915009e874f8bed1845060012f826fcd": "Markdown toestaan",
|
||||
"s18n-a9a36cb3d8f4f7297ebca99a322d6342": "Stemmen op reacties",
|
||||
"s18n-2123546d1ff8b0cb035df0c0b0d06825": "Stemmen door gasten opslaan in",
|
||||
"s18n-dead693ab29895d302fca0e6baad6182": "Cookies",
|
||||
"s18n-02b68043bdcae159e83199d64a5abd7d": "Sessie-opslag",
|
||||
"s18n-40bd8791e523e91219886c35622163fc": "Database",
|
||||
"s18n-8717cfca734e8987971f63b20eeb8024": "Wat betekent dit?",
|
||||
"s18n-5a8cbcf57f5b59f0d4b8ded97d018399": "<b>Cookies<\/b> worden opgeslagen op de computer van de gebruiker; u heeft geen volledige controle EN de gebruiker moet cookies toestaan.",
|
||||
"s18n-a09dbdf907873e66d9d644cca71970d5": "<b>Sessie-opslag<\/b> wordt slechts tijdelijk op de server bewaard en wordt verwijderd wanneer de gebruiker de browser afsluit. Daarom is er g<><67>n toestemming van de gebruiker benodigd.",
|
||||
"s18n-71892ebe01ba92d1f163dc37d818b5ff": "Opslaan in de <b>Database<\/b> genereert een gepseudonimiseerde waarde voor iedere gebruiker die stemt en slaat die op in de database. Daarom is er toestemming van de gebruiker benodigd.",
|
||||
"s18n-7c35a0bcb0b0678f0829036eead5ddca": "<b>Waarschuwing:<\/b> U bent zelf verantwoordelijk voor het verkrijgen van goedkeuring door gebruikers; Snicker behandelt alleen de machtigingen voor de gegevens die verstuurd en/of opgeslagen worden via het reactieformulier!",
|
||||
"s18n-82e5228061f185ee185bd9f3ecba4ee7": "%s toestaan voor reacties",
|
||||
"s18n-61b58693e0eceeb27ce0cc3b25b3bf31": "Instellingen front-end",
|
||||
"s18n-b3c1c2c231275878abe58a55966fa9e0": "Paginafilter",
|
||||
"s18n-9cb1eef8966f93282524929f65c8b9ec": "Paginafilter uitschakelen",
|
||||
"s18n-e89fd56cefec9baabcbe0db3e5a36962": "'pageBegin' gebruiken",
|
||||
"s18n-3d08b5dcc1e5c3c7e7e2eaf2d0d6a12d": "'pageEnd' gebruiken",
|
||||
"s18n-3771d05b6af4ebb0a303266c47809548": "'siteBodyBegin' gebruiken",
|
||||
"s18n-42438edc41ab83312486009e3122e92b": "'siteBodyEnd' gebruiken",
|
||||
"s18n-948da5199de32c7601a20b6107c31d4d": "Captcha",
|
||||
"s18n-47e5c42fb9bdca3636a5d866a6794101": "Captcha uitschakelen",
|
||||
"s18n-ab6ef7ef94efc86db78218c6c265243a": "OWASP's PureCaptcha gebruiken",
|
||||
"s18n-a58adce0085cc1a25fc8076e97c29d70": "Gregway's Captcha gebruiken",
|
||||
"s18n-9d2f2ec577e7383b88fd481d6c566c5e": "Gregway's Captcha gebruiken (GD library niet beschikbaar!)",
|
||||
"s18n-09cddbc3627ea46a8dce692d64273b61": "Googles reCaptcha gebruiken (nog niet beschikbaar)",
|
||||
"s18n-838a13e4fece7c272b960da3fb99f94d": "Sjabloon",
|
||||
"s18n-6f95370a28520696b2a0ad34efc54d2d": "Volgorde",
|
||||
"s18n-4a8dc1710396b21e7b1da8112c07c4ad": "Nieuwste reacties eerst",
|
||||
"s18n-2dab3b12d0b0642c3964b37d675ff24b": "Oudste reacties eerst",
|
||||
"s18n-230c71c29590608034b4a590a67ace31": "Positie reactieformulier",
|
||||
"s18n-37d988444dec2001c488806fc8401e25": "Boven reacties tonen",
|
||||
"s18n-16ac6c11951d825826f77a4097a1c2cb": "Onder reacties tonen",
|
||||
"s18n-1e98ec9312b69676d5e3fe3caf8ecde1": "Reacties per pagina",
|
||||
"s18n-0d46f4389ca5f882e24899fe489bf344": "Voer '0' in om alle reacties te tonen.",
|
||||
"s18n-7b6b84fbd65a4b712a5ba0dccce176d5": "Accepteren gebruiksvoorwaarden",
|
||||
"s18n-9853383062a2e308d5aed35fe3da7953": "Dit veld uitschakelen",
|
||||
"s18n-352fd1d7225b5ea02b8ddd9fad0d6e34": "Bericht tonen (zie Meldingen)",
|
||||
"s18n-71860c77c6745379b0d44304d66b6a13": "Pagina",
|
||||
"s18n-5dcb84333ae70a5bed60bf70d34dcd2b": "Toon de standaardtekst AVG (GDPR) of kies een eigen statische pagina voor de gebruiksvoorwaarden.",
|
||||
"s18n-2e3d9327c371afb7489f9b9278198622": "AJAX-script",
|
||||
"s18n-c60cab6330745b41cbee05603eec6691": "AJAX-script insluiten",
|
||||
"s18n-69b11f64af515ef979fbf28c5e06f370": "AJAX niet gebruiken",
|
||||
"s18n-9bbec7b57565f06d73522669d3b836dc": "The AJAX Script hands over the request (reactie, like, dislike) directly without reloading the page!",
|
||||
"s18n-7c74c0d2d1c28d1568298c89742ce126": "Avatar",
|
||||
"s18n-aeab7c630dae161d8f6e2898dd83b471": "Gravatar gebruiken",
|
||||
"s18n-882e3436da897c055cc3f8bd2598b71a": "Identicon gebruiken",
|
||||
"s18n-d92d61ad0c0065170a37a1805ad1bc9e": "Mystery Men gebruiken",
|
||||
"s18n-b4a51a35344f9a8fa1139cfd968ab308": "Profielfoto's gebruiken voor ingelogde gebruikers",
|
||||
"s18n-b50a4a96c25d745d73114af5a4b03145": "Gravatar",
|
||||
"s18n-9ffb941a398ddee8e054eef3292c546e": "Mystery Person tonen",
|
||||
"s18n-a7dd12b1dab17d25467b0b0a4c8d4a92": "Tonen",
|
||||
"s18n-0d2fc085ee57276417cf380027060760": "Standaardafbeelding van Gravatar als de gebruiker geen eigen Gravatar heeft.",
|
||||
"s18n-58e2aacf5792087168cbc62578584ecd": "Abonneren",
|
||||
"s18n-8290ca86b8980a14bd46f34017e03f93": "De functie Abonneren is nog niet beschikbaar.",
|
||||
"s18n-0bd7ff1b4ac56a9616796bdc05609de2": "Abonneren met e-mail",
|
||||
"s18n-208f156d4a803025c284bb595a7576b4": "Inschakelen",
|
||||
"s18n-dc985a7c2144c6447674e674aee08441": "E-mailadres afzender ('From')",
|
||||
"s18n-f6db5b4db3f9c1ba0ffc091abc561802": "E-mailadres beantwoorden ('ReplyTo')",
|
||||
"s18n-8d868315d258783a95336d1a6ce27e1e": "Inhoud e-mail bij abonneren",
|
||||
"s18n-7721b2a2f2a453cc790e3ac7065e9b65": "Standaardinhoud voor e-mail bij abonneren gebruiken",
|
||||
"s18n-58ff11585a82c73f5117c91c29cb3f63": "Inhoud e-mail bij notificatie",
|
||||
"s18n-bea9ff19efca028c01617da5dce18171": "Standaardinhoud voor e-mail bij notificatie gebruiken",
|
||||
"s18n-e0024a8886a178d4f70b8c888b701680": "Lees meer over eigen inhoud voor e-mail bij notificaties: %s.",
|
||||
"s18n-8bcf6629759bd278a5c6266bd9c054f8": "Meldingen",
|
||||
"s18n-3979b4205954030810a8a87769348094": "Standaardbericht voor bevestiging",
|
||||
"s18n-620b528248b36bf743d1ad33e35022d6": "Bevestiging na abonneren",
|
||||
"s18n-e83f6b01d1c81313f6b388281e13aacf": "Bevestiging na stemmen",
|
||||
"s18n-01b7a6bc6b57783b4fd081085ff3271e": "Fout: onbekende fout, probeer het nogmaals",
|
||||
"s18n-19678b1419eff34dffd41d6778b1aa89": "Fout: ongeldige gebruikersnaam",
|
||||
"s18n-1255e6794d33b443bcee21279d9caa1a": "Fout: ongeldig e-mailadres",
|
||||
"s18n-ebf545757b92b3a553d83ea3db48beca": "Fout: geen bericht ingevoerd",
|
||||
"s18n-f1bc53e2456c425578277adbc7c90f3a": "Fout: geen titel ingevoerd",
|
||||
"s18n-53c8fdd7ed497dbacbef5fd5d4f38f3d": "Fout: gebruiksvoorwaarden niet geaccepteerd",
|
||||
"s18n-89f8cc478fdfc0a8f36c1b393f39677a": "Fout: gemarkeerd als SPAM",
|
||||
"s18n-59ad5c9fcee1b28a5d004bcf684a5acd": "Fout: u heeft reeds gestemd",
|
||||
"s18n-56ef2c600b4af0f9f7b35640525967ca": "Gebruiksvoorwaarden",
|
||||
"s18n-08400c2e0f51197fdb3590461b15b2cc": "Gebruikersnaam of e-mailadres",
|
||||
"s18n-b79edd2e426f90401c04869346b503c7": "Gebruikers zoeken",
|
||||
"s18n-ee51fa9d5097c84d2fa6c885bf2d5d84": "Geen gebruikers gevonden",
|
||||
"s18n-14c4b06b824ec593239362517f538b29": "Gebruikersnaam",
|
||||
"s18n-0c83f57c786a0b4a39efab23731c7ebc": "E-mail",
|
||||
"s18n-e1260894f59eeae98c8440899de4df8d": "Afhandelen",
|
||||
"s18n-4bc61296b766756f1c7296489633bf32": "Verwijderen (pseudonimiseren)",
|
||||
"s18n-c0f4afd3614929f1c803f3a01414a6c7": "Verwijderen (volledig)",
|
||||
"s18n-6356f32d0c02c8f90cb59a77e16e8fe2": "Gebruiker deblokkeren",
|
||||
"s18n-2327a01afbee025fb5913357c9d6b1b3": "Gebruiker blokkeren",
|
||||
"s18n-76a0f6752a45d8af6343ef3e2b6f522a": "Enkele reactie",
|
||||
"s18n-c426859e50a35617d863cdad2b9c84aa": "Reacties op pagina",
|
||||
"s18n-a64d776275f13a51790bb460774b9129": "Reacties van gebruiker",
|
||||
"s18n-9bc65c2abec141778ffaa729489f3e87": "Gebruikers",
|
||||
"s18n-ccd1066343c95877b75b79d47c36bebe": "Configuratie",
|
||||
"s18n-5b49260517622682a058b69f996d06eb": "Bedankt voor uw reactie!",
|
||||
"s18n-74196a783a6f1707a43cc8117f0d9c83": "Bedankt voor uw reactie! Bevestig het abonneren op verdere reacties via de link in het bericht dat u per e-mail ontvangt.",
|
||||
"s18n-a939eb542e34cd502b3f7352b2e0f715": "Bedankt voor het stemmen op deze reactie!",
|
||||
"s18n-d2a9677817ee08ed05bf9fd868669756": "Onbekende fout opgetreden, herlaad de pagina probeer het nogmaals.",
|
||||
"s18n-05b85714aa8f1b364f930e2539059b5e": "Fout opgetreden: de gebruikersnaam is ongeldig of langer dan 42 tekens.",
|
||||
"s18n-321e8b481f0ccd62df535256e8e6d2c6": "Fout opgetreden: het e-mailadres is ongeldig.",
|
||||
"s18n-fcd0c3a087c5123ffdecc20fe9015870": "Fout opgetreden: er is geen bericht ingevoerd.",
|
||||
"s18n-2afa2d90ce3343fbe188b9f49ad5797d": "Fout opgetreden: er is geen titel ingevoerd.",
|
||||
"s18n-bee1741efe0c735d2c7180771586faf0": "Fout opgetreden: de gebruiksvoorwaarden zijn niet geaccepteerd.",
|
||||
"s18n-92fe96d6ccee901f94fad0000369a9b7": "Fout opgetreden: uw IP-adres of e-mailadres is gemarkeerd als SPAM.",
|
||||
"s18n-9b264fc6137096f8a40acde68f6ae562": "Fout opgetreden: u heeft reeds gestemd op deze reactie.",
|
||||
"s18n-ca62db4704290ef1a7e65df3ffc7983b": "Mijn gegevens (inclusief mijn gepseudonimiseerd IP-adres) mogen worden opgeslagen.",
|
||||
"s18n-01c611362e8b046f32650b85ce161559": "Ongeldige fout opgetreden.",
|
||||
"s18n-c4f94c6995b0376f28276e432bed75fa": "Het CSRF-token ontbreekt.",
|
||||
"s18n-a573e7b86e41522c7a291846aa109104": "Het CSRF-token is ongeldig.",
|
||||
"s18n-3e8909518ce4728685aa09cdde3caa22": "U heeft geen machtiging voor het aanroepen van deze functie.",
|
||||
"s18n-b60a6a8a529a9f0497134205bab15e77": "U heeft geen machtiging voor het uitvoeren van deze actie.",
|
||||
"s18n-3fee90e2f59aeb29b74c1c21648ba712": "De Captcha is succesvol gegenereerd.",
|
||||
"s18n-56abae3e615ae5b5609c32852b777d46": "De actie is onbekend of ongeldig.",
|
||||
"s18n-be5136b4f2b33c80e3afd377ee993acb": "Er is een onbekende fout opgetreden.",
|
||||
"s18n-46d4c97e91319867654f7cc80c439ba4": "Geen uniek gebruikers-ID gevonden.",
|
||||
"s18n-af1ba1dd4eab5562f78c65bc89a0a7e9": "Actie is succesvol afgerond.",
|
||||
"s18n-a5193444ee82c18bac726b35a1704d03": "Instellingen zijn opgeslagen.",
|
||||
"s18n-717c8267d40664ccf7ef25a26ff9cde6": "Backup is voltooid.",
|
||||
"s18n-a1e2b7401861cee01c172878f104bd8c": "Reacties niet toestaan",
|
||||
"s18n-15802277ea1cdfcbacd6308fa0c7c30f": "Snicker-plugin uitschakelen",
|
||||
"s18n-877d58f21c87442efa4081112a6cb07a": "Het uitschakelen van de <b>Snicker<\/b>-plugin zal alle reacties verwijderen!",
|
||||
"s18n-a4983c86683f8d8598c0513339550dc0": "Eerst een backup maken van de reacties?",
|
||||
"s18n-4cce9e52118cc659be5070b2f08cdd91": "De backup zal worden opgeslagen in %s.",
|
||||
"s18n-5f87fd2e0fa992d37c814bb4ca299646": "Ja, backup maken",
|
||||
"s18n-3d414feb412a1f4cd9324f6411c76329": "Nee, alleen uitschakelen",
|
||||
"s18n-10aec35353f9c4096a71c38654c3d402": "Annuleren",
|
||||
"s18n-bc6d6a26d44b6f39a0e7b6c7787f3295": "Reacties zijn voor deze pagina uitgeschakeld.",
|
||||
"s18n-a8e30d73eddea9866cf99ecd6e8467b5": "Wees de eerste met een reactie hier!",
|
||||
"s18n-b3afbadaa2f1c79f8b3999be7fd9719f": "Het antwoord op de Captcha is onjuist.",
|
||||
"s18n-fb74aafe8bf1fd4d8f7a6e1ff73028f7": "UID voor de reactie bestaat niet of is ongeldig.",
|
||||
"s18n-25dea7cb70f98250b388f6ab0ddf20cb": "Status van de reactie is onbekend of ongeldig.",
|
||||
"s18n-2cd68855bdc54ff5e3c191a6333ff75d": "De nieuwe status van de reactie kon niet worden opgeslagen.",
|
||||
"s18n-c0937505b7afa81a053077bc7ae369a5": "De nieuwe status van de reactie is opgeslagen.",
|
||||
"s18n-6b2c8084c67f24bb73d95031bb570ef7": "De reactie kon niet worden verwijderd.",
|
||||
"s18n-376388311a80dbde63fde7f6c72081e0": "De reactie is verwijderd.",
|
||||
"s18n-83bbb9e8745cc95e730fe7b8de9345f1": "Ingelogd als %s (%s)",
|
||||
"s18n-91fb98e1ac4cf76b7a5b8bae09051e2a": "Gebruikersnaam",
|
||||
"s18n-31f6da7a30e7acf1f82451bfd1a7f8fa": "E-mailadres",
|
||||
"s18n-f794080a5a29e35233c82df85f1207eb": "Reactie...",
|
||||
"s18n-a363b8d13575101a0226e8d0d054f2e7": "Beantwoorden",
|
||||
"s18n-f10db888c5e63b343000cffc038e0a46": "schreef",
|
||||
"s18n-3cc5bcf15d6b8faed118e2ce72d19a1e": "Ik ga akkoord met de %s!",
|
||||
"s18n-2af0aab477f402e0f4ad7a27e6c9f952": "Vorige",
|
||||
"s18n-8538431db22040e2147b363f86a2e2f0": "Volgende",
|
||||
"s18n-ae0dbd5cc42a6db191db5e0083bcb307": "Deze reactie moet nog worden goedgekeurd.",
|
||||
"s18n-48df9c3f3cca3fb2b8bcf811633bee06": "Geschreven door %s",
|
||||
"s18n-81fdc9813cebe0553c55e78dc2b6029f": "op %s",
|
||||
"s18n-be1ab1632e4285edc3733b142935c60b": "Vind ik leuk",
|
||||
"s18n-bc8b79025e4595298669fd21da814941": "Vind ik niet leuk",
|
||||
"s18n-e84afaab83ecb301b3d97ce4174d2773": "Beantwoorden"
|
||||
}
|
||||
10
metadata.json
Normal file
10
metadata.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"author": "SamBrishes, pytesNET",
|
||||
"email": "sam@pytes.net",
|
||||
"website": "https://www.pytes.net",
|
||||
"version": "0.1.2",
|
||||
"releaseDate": "2019-05-06",
|
||||
"license": "MIT",
|
||||
"compatible": "3.5.0",
|
||||
"notes": ""
|
||||
}
|
||||
893
plugin.php
Normal file
893
plugin.php
Normal file
@@ -0,0 +1,893 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./plugin.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!"); }
|
||||
|
||||
require_once "system/functions.php"; // Load Basic Functions
|
||||
|
||||
class SnickerPlugin extends Plugin{
|
||||
/*
|
||||
| BACKEND VARIABLES
|
||||
*/
|
||||
private $backend = false; // Is Backend
|
||||
private $backendView = null; // Backend View / File
|
||||
private $backendRequest = null; // Backend Request Type ("post", "get", "ajax")
|
||||
|
||||
/*
|
||||
| CONSTRUCTOR
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function __construct(){
|
||||
global $SnickerPlugin;
|
||||
$SnickerPlugin = $this;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
## HELPER METHODs
|
||||
##
|
||||
|
||||
/*
|
||||
| HELPER :: SELECTED
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The respective option key (used in `getValue()`).
|
||||
| @param multi The value to compare with.
|
||||
| @param bool TRUE to print `selected="selected"`, FALSE to return the string.
|
||||
| Use `null` to return as boolean!
|
||||
|
|
||||
| @return multi The respective string, nothing or a BOOLEAN indicator.
|
||||
*/
|
||||
public function selected($field, $value = true, $print = true){
|
||||
if(sn_config($field) == $value){
|
||||
$selected = 'selected="selected"';
|
||||
} else {
|
||||
$selected = '';
|
||||
}
|
||||
if($print === null){
|
||||
return !empty($selected);
|
||||
}
|
||||
if(!$print){
|
||||
return $selected;
|
||||
}
|
||||
print($selected);
|
||||
}
|
||||
|
||||
/*
|
||||
| HELPER :: CHECKED
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The respective option key (used in `getValue()`).
|
||||
| @param multi The value to compare with.
|
||||
| @param bool TRUE to print `checked="checked"`, FALSE to return the string.
|
||||
| Use `null` to return as boolean!
|
||||
|
|
||||
| @return multi The respective string, nothing or a BOOLEAN indicator.
|
||||
*/
|
||||
public function checked($field, $value = true, $print = true){
|
||||
if(sn_config($field) == $value){
|
||||
$checked = 'checked="checked"';
|
||||
} else {
|
||||
$checked = '';
|
||||
}
|
||||
if($print === null){
|
||||
return !empty($checked);
|
||||
}
|
||||
if(!$print){
|
||||
return $checked;
|
||||
}
|
||||
print($checked);
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
## PLUGIN HOOKs
|
||||
##
|
||||
|
||||
/*
|
||||
| PLUGIN :: GET VALUE
|
||||
| @since 0.1.2
|
||||
*/
|
||||
public function getValue($field, $html = true){
|
||||
if(isset($this->db[$field])){
|
||||
$data = strpos($field, "string_") === 0? sn__($this->db[$field]): $this->db[$field];
|
||||
return ($html)? $data: Sanitize::htmlDecode($data);
|
||||
}
|
||||
return isset($this->dbFields[$field])? $this->dbFields[$field]: null;
|
||||
}
|
||||
|
||||
/*
|
||||
| PLUGIN :: INIT
|
||||
| @since 0.1.0
|
||||
| @update 0.1.1
|
||||
*/
|
||||
public function init(){
|
||||
global $url;
|
||||
|
||||
// Init Default Settings
|
||||
$this->dbFields = array(
|
||||
"moderation" => true,
|
||||
"moderation_loggedin" => true,
|
||||
"moderation_approved" => true,
|
||||
"comment_on_public" => true,
|
||||
"comment_on_static" => false,
|
||||
"comment_on_sticky" => true,
|
||||
"comment_title" => "optional",
|
||||
"comment_limit" => 0,
|
||||
"comment_depth" => 3,
|
||||
"comment_markup_html" => true,
|
||||
"comment_markup_markdown" => false,
|
||||
"comment_vote_storage" => "session",
|
||||
"comment_enable_like" => true,
|
||||
"comment_enable_dislike" => true,
|
||||
"frontend_captcha" => function_exists("imagettfbbox")? "gregwar": "purecaptcha",
|
||||
"frontend_recaptcha_public" => "",
|
||||
"frontend_recaptcha_private"=> "",
|
||||
"frontend_terms" => "default",
|
||||
"frontend_filter" => "pageEnd",
|
||||
"frontend_template" => "default",
|
||||
"frontend_order" => "date_desc",
|
||||
"frontend_form" => "top",
|
||||
"frontend_per_page" => 15,
|
||||
"frontend_ajax" => true,
|
||||
"frontend_avatar" => "gravatar",
|
||||
"frontend_avatar_users" => true,
|
||||
"frontend_gravatar" => "mp",
|
||||
"subscription" => false,
|
||||
"subscription_from" => "ticker@{$_SERVER["SERVER_NAME"]}",
|
||||
"subscription_reply" => "noreply@{$_SERVER["SERVER_NAME"]}",
|
||||
"subscription_optin" => "default",
|
||||
"subscription_ticker" => "default",
|
||||
|
||||
// Frontend Messages, can be changed by the user
|
||||
"string_success_1" => sn__("Thanks for your comment!"),
|
||||
"string_success_2" => sn__("Thanks for your comment, please confirm your subscription via the link we sent to your eMail address!"),
|
||||
"string_success_3" => sn__("Thanks for voting this comment!"),
|
||||
"string_error_1" => sn__("An unknown error occured, please reload the page and try it again!"),
|
||||
"string_error_2" => sn__("An error occured: The passed Username is invalid or too long (42 characters only)!"),
|
||||
"string_error_3" => sn__("An error occured: The passed eMail address is invalid!"),
|
||||
"string_error_4" => sn__("An error occured: The comment text is missing!"),
|
||||
"string_error_5" => sn__("An error occured: The comment title is missing!"),
|
||||
"string_error_6" => sn__("An error occured: You need to accept the Terms to comment!"),
|
||||
"string_error_7" => sn__("An error occured: Your IP address or eMail address has been marked as Spam!"),
|
||||
"string_error_8" => sn__("An error occured: You already rated this comment!"),
|
||||
"string_terms_of_use" => sn__("I agree that my data (incl. my anonymized IP address) gets stored!")
|
||||
);
|
||||
|
||||
// Check Backend
|
||||
$this->backend = (trim($url->activeFilter(), "/") == ADMIN_URI_FILTER);
|
||||
}
|
||||
|
||||
/*
|
||||
| PLUGIN :: OVERWRITE INSTALLED
|
||||
| @since 0.1.0
|
||||
| @update 0.1.1
|
||||
*/
|
||||
public function installed(){
|
||||
global $Snicker, // Main Comment Handler
|
||||
$SnickerIndex, // Main Comment Indexer
|
||||
$SnickerUsers, // Main Comment Users
|
||||
$SnickerVotes; // Main Comment Votes
|
||||
|
||||
if(file_exists($this->filenameDb)){
|
||||
if(!defined("SNICKER")){
|
||||
define("SNICKER", true);
|
||||
define("SNICKER_PATH", PATH_PLUGINS . basename(__DIR__) . DS);
|
||||
define("SNICKER_DOMAIN", DOMAIN_PLUGINS . basename(__DIR__) . "/");
|
||||
define("SNICKER_VERSION", "0.1.2");
|
||||
|
||||
// DataBases
|
||||
define("DB_SNICKER_COMMENTS", $this->workspace() . "pages" . DS);
|
||||
define("DB_SNICKER_INDEX", $this->workspace() . "comments-index.php");
|
||||
define("DB_SNICKER_USERS", $this->workspace() . "comments-users.php");
|
||||
define("DB_SNICKER_VOTES", $this->workspace() . "comments-votes.php");
|
||||
|
||||
// Pages Filter
|
||||
if(!file_exists(DB_SNICKER_COMMENTS)){
|
||||
@mkdir(DB_SNICKER_COMMENTS);
|
||||
}
|
||||
|
||||
// Load Plugin
|
||||
require_once "system/abstract.comments-theme.php";
|
||||
require_once "system/class.comment.php";
|
||||
require_once "system/class.comments.php";
|
||||
require_once "system/class.comments-index.php";
|
||||
require_once "system/class.comments-users.php";
|
||||
require_once "system/class.comments-votes.php";
|
||||
require_once "system/class.snicker.php";
|
||||
require_once "includes/autoload.php";
|
||||
} else {
|
||||
$Snicker = new Snicker();
|
||||
$SnickerIndex = new CommentsIndex();
|
||||
$SnickerUsers = new CommentsUsers();
|
||||
$SnickerVotes = new CommentsVotes();
|
||||
$this->request();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
## API METHODs
|
||||
##
|
||||
|
||||
/*
|
||||
| API :: HANDLE RESPONSE
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param array The response data, which MUST contain at least the status:
|
||||
| "error" The error message (required).
|
||||
| "success" The success message (required).
|
||||
|
|
||||
| :: NON-AJAX ONLY
|
||||
| "referer" A referer URL (The current URL is used, if not present)
|
||||
|
|
||||
| :: AJAX-BASED ONLY
|
||||
| :any Any additional data, which should return to the client.
|
||||
|
|
||||
| @return none This method calls the die(); method at any time!
|
||||
*/
|
||||
public function response($data = array(), $key = null){
|
||||
global $url;
|
||||
|
||||
// Validate
|
||||
if(isset($data["success"]) || isset($data["error"])){
|
||||
$status = isset($data["success"]);
|
||||
} else {
|
||||
$status = false;
|
||||
$data["error"] = sn__("An unknown error occured!");
|
||||
}
|
||||
|
||||
// POST Redirect
|
||||
if($this->backendRequest !== "ajax"){
|
||||
if($status){
|
||||
$key = empty($key)? "snicker-success": $key;
|
||||
Alert::set($data["success"], ALERT_STATUS_OK, $key);
|
||||
} else {
|
||||
$key = empty($key)? "snicker-alert": $key;
|
||||
Alert::set($data["error"], ALERT_STATUS_FAIL, $key);
|
||||
}
|
||||
|
||||
if($data["referer"]){
|
||||
Redirect::url($data["referer"]);
|
||||
} else {
|
||||
$action = isset($_GET["snicker"])? $_GET["snicker"]: $_POST["snicker"];
|
||||
Redirect::url(HTML_PATH_ADMIN_ROOT . $url->slug() . "#{$action}");
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
// AJAX Print
|
||||
if(!is_array($data)){
|
||||
$data = array();
|
||||
}
|
||||
$data["status"] = ($status)? "success": "error";
|
||||
$data = json_encode($data);
|
||||
|
||||
header("Content-Type: application/json");
|
||||
header("Content-Length: " . strlen($data));
|
||||
print($data);
|
||||
die();
|
||||
}
|
||||
|
||||
/*
|
||||
| API :: HANDLE REQUESTS
|
||||
| @since 0.1.0
|
||||
| @update 0.1.1
|
||||
*/
|
||||
public function request(){
|
||||
global $login, $security, $url, $Snicker;
|
||||
|
||||
// Get POST/GET Request
|
||||
if(isset($_POST["action"]) && $_POST["action"] === "snicker"){
|
||||
$data = $_POST;
|
||||
$this->backendRequest = "post";
|
||||
} else if(isset($_GET["action"]) && $_GET["action"] === "snicker"){
|
||||
$data = $_GET;
|
||||
$this->backendRequest = "get";
|
||||
}
|
||||
if(!(isset($data) && isset($data["snicker"]))){
|
||||
$this->backendRequest = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get AJAX Request
|
||||
$ajax = "HTTP_X_REQUESTED_WITH";
|
||||
if(strpos($url->slug(), "snicker/ajax") === 0){
|
||||
if(isset($_SERVER[$ajax]) && $_SERVER[$ajax] === "XMLHttpRequest"){
|
||||
$this->backendRequest = "ajax";
|
||||
} else {
|
||||
return Redirect::url(HTML_PATH_ADMIN_ROOT . "snicker/");
|
||||
}
|
||||
} else if(isset($_SERVER[$ajax]) && $_SERVER[$ajax] === "XMLHttpRequest"){
|
||||
print("Invalid AJAX Call"); die();
|
||||
}
|
||||
if($this->backendRequest === "ajax" && !sn_config("frontend_template")){
|
||||
print("AJAX Calls has been disabled"); die();
|
||||
}
|
||||
|
||||
// Start Session
|
||||
if(!Session::started()){
|
||||
Session::start();
|
||||
}
|
||||
|
||||
$key = null;
|
||||
if(in_array($data["snicker"], array("add", "edit", "delete", "config", "users", "backup", "moderate"))){
|
||||
$key = "alert";
|
||||
}
|
||||
|
||||
// Check CSRF Token
|
||||
if(!empty($key)){
|
||||
if(!isset($data["tokenCSRF"])){
|
||||
return $this->response(array(
|
||||
"error" => sn__("The CSRF Token is missing!")
|
||||
));
|
||||
}
|
||||
if(!$security->validateTokenCSRF($data["tokenCSRF"])){
|
||||
return $this->response(array(
|
||||
"error" => sn__("The CSRF Token is invalid!")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Check Permissions
|
||||
if(!empty($key)){
|
||||
if(!is_a($login, "Login")){
|
||||
$login = new Login();
|
||||
}
|
||||
if(!$login->isLogged()){
|
||||
return $this->response(array(
|
||||
"error" => sn__("You don't have the permission to call this action!")
|
||||
));
|
||||
}
|
||||
if($login->role() !== "admin"){
|
||||
return $this->response(array(
|
||||
"error" => sn__("You don't have the permission to perform this action!")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Route
|
||||
switch($data["snicker"]){
|
||||
case "comment": //@fallthrough
|
||||
case "reply": //@fallthrough
|
||||
case "add":
|
||||
return $Snicker->writeComment($data["comment"], $key);
|
||||
/* case "update": */ //@todo User can edit his own comments
|
||||
case "edit":
|
||||
return $Snicker->editComment($data["uid"], $data["comment"], $key);
|
||||
/* case "remove": */ //@todo User can delete his own comments
|
||||
case "delete":
|
||||
return $Snicker->deleteComment($data["uid"], $key);
|
||||
case "moderate":
|
||||
return $Snicker->moderateComment($data["uid"], $data["status"], $key);
|
||||
case "list": //@fallthrough
|
||||
case "get":
|
||||
return $Snicker->renderComment($data);
|
||||
case "rate":
|
||||
return $Snicker->rateComment($data["uid"], $data["type"]);
|
||||
case "users":
|
||||
return $this->user($data);
|
||||
case "configure":
|
||||
return $this->config($data);
|
||||
case "backup":
|
||||
return $this->backup();
|
||||
case "captcha":
|
||||
return $this->response(array(
|
||||
"success" => sn__("The Captcha Image could be successfully created!"),
|
||||
"captcha" => $Snicker->generateCaptcha(150, 40, true)
|
||||
));
|
||||
}
|
||||
return $this->response(array(
|
||||
"error" => sn__("The passed action is unknown or invalid!")
|
||||
), "alert");
|
||||
}
|
||||
|
||||
/*
|
||||
| API :: HANDLE USERs
|
||||
| @since 0.1.0
|
||||
*/
|
||||
private function user($data){
|
||||
global $SnickerIndex, $SnickerUsers;
|
||||
|
||||
// Validate Data
|
||||
if(!isset($data["uuid"]) || !isset($data["handle"])){
|
||||
return $this->response(array(
|
||||
"error" => sn__("An unknown error is occured!")
|
||||
), "alert");
|
||||
}
|
||||
|
||||
// Validata UUID
|
||||
if(!$SnickerUsers->exists($data["uuid"])){
|
||||
return $this->response(array(
|
||||
"error" => sn__("An unique user ID does not exist!")
|
||||
), "alert");
|
||||
}
|
||||
|
||||
// Handle
|
||||
if($data["handle"] === "delete"){
|
||||
$comments = $SnickerUsers->db[$data["uuid"]]["comments"];
|
||||
foreach($comments AS $uid){
|
||||
if(!$SnickerIndex->exists($uid)){
|
||||
continue;
|
||||
}
|
||||
$index = $SnickerIndex->getComment($uid);
|
||||
$comment = new Comments($index["page_uuid"]);
|
||||
|
||||
if(isset($data["anonymize"]) && $data["anonymize"] === "true"){
|
||||
$comment = new Comments($index["page_uuid"]);
|
||||
$comment->edit($uid, array("author" => "anonymous"));
|
||||
} else {
|
||||
$comment = new Comments($index["page_uuid"]);
|
||||
$comment->delete($uid);
|
||||
}
|
||||
}
|
||||
$status = $SnickerUsers->delete($data["uuid"]);
|
||||
} else if($data["handle"] === "block"){
|
||||
$status = $SnickerUsers->edit($data["uuid"], null, null, true);
|
||||
} else if($data["handle"] === "unblock"){
|
||||
$status = $SnickerUsers->edit($data["uuid"], null, null, false);
|
||||
}
|
||||
|
||||
// Redirect
|
||||
if(!isset($status)){
|
||||
return $this->response(array(
|
||||
"error" => sn__("The passed action is unknown or invalid!")
|
||||
), "alert");
|
||||
}
|
||||
if($status === false){
|
||||
return $this->response(array(
|
||||
"error" => sn__("An unknown error is occured!")
|
||||
), "alert");
|
||||
}
|
||||
return $this->response(array(
|
||||
"success" => sn__("The action has been performed successfully!")
|
||||
), "alert");
|
||||
}
|
||||
|
||||
/*
|
||||
| API :: HANDLE CONFIGURATION
|
||||
| @since 0.1.0
|
||||
| @update 0.2.0
|
||||
*/
|
||||
private function config($data){
|
||||
global $pages, $Snicker;
|
||||
$config = array();
|
||||
|
||||
// Validations
|
||||
$text = array("frontend_recaptcha_public", "frontend_recaptcha_private");
|
||||
$numbers = array("comment_limit", "comment_depth", "frontend_per_page");
|
||||
$selects = array(
|
||||
"comment_title" => array("optional", "required", "disabled"),
|
||||
"comment_vote_storage" => array("cookie", "session", "database"),
|
||||
"frontend_captcha" => array("disabled", "purecaptcha", "gregwar", "recaptchav2", "recaptchav3"),
|
||||
"frontend_avatar" => array("gravatar", "identicon", "static", "initials"),
|
||||
"frontend_gravatar" => array("mp", "identicon", "monsterid", "wavatar", "retro", "robohash", "blank"),
|
||||
"frontend_filter" => array("disabled", "pageBegin", "pageEnd", "siteBodyBegin", "siteBodyEnd"),
|
||||
"frontend_order" => array("date_desc", "date_asc"),
|
||||
"frontend_form" => array("top", "bottom")
|
||||
);
|
||||
$emails = array("subscription_from", "subscription_reply");
|
||||
$pageid = array("frontend_terms", "subscription_optin", "subscription_ticker");
|
||||
|
||||
// Loop DB Fields
|
||||
foreach($this->dbFields AS $field => $value){
|
||||
if(!isset($data[$field])){
|
||||
$config[$field] = is_bool($value)? false: "";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize Booleans
|
||||
if(is_bool($value)){
|
||||
$config[$field] = ($data[$field] === "true" || $data[$field] === true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize Numbers
|
||||
if(in_array($field, $numbers)){
|
||||
if($data[$field] < 0 || !is_numeric($data[$field])){
|
||||
$config[$field] = 0;
|
||||
}
|
||||
$config[$field] = (int) $data[$field];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize Selection
|
||||
if(array_key_exists($field, $selects)){
|
||||
if(in_array($data[$field], $selects[$field])){
|
||||
$config[$field] = $data[$field];
|
||||
} else {
|
||||
$config[$field] = $value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize eMails
|
||||
if(in_array($field, $emails)){
|
||||
if(Valid::email($data[$field])){
|
||||
$config[$field] = Sanitize::email($data[$field]);
|
||||
} else {
|
||||
$config[$field] = $value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize Pages
|
||||
if(in_array($field, $pageid)){
|
||||
$default = in_array($data[$field], array("default", "disabled"));
|
||||
if($default || $pages->exists($data[$field])){
|
||||
$config[$field] = $data[$field];
|
||||
} else {
|
||||
$config[$field] = $value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize Template
|
||||
if($field == "frontend_template"){
|
||||
if($Snicker->hasTheme($data[$field])){
|
||||
$config[$field] = $data[$field];
|
||||
} else {
|
||||
$config[$field] = $value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanitize Strings
|
||||
if(strpos($field, "string_") === 0 || in_array($field, $text)){
|
||||
$config[$field] = Sanitize::html(strip_tags($data[$field]));
|
||||
if(empty($config[$field])){
|
||||
$config[$field] = $value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Save & Return
|
||||
$this->db = array_merge($this->db, $config);
|
||||
if(!$this->save()){
|
||||
return $this->response(array(
|
||||
"error" => sn__("An unknown error is occured!")
|
||||
), "alert");
|
||||
}
|
||||
return $this->response(array(
|
||||
"success" => sn__("The settings has been updated successfully!")
|
||||
), "alert");
|
||||
}
|
||||
|
||||
/*
|
||||
| API :: CREATE BACKUP
|
||||
| @since 0.1.0
|
||||
*/
|
||||
private function backup(){
|
||||
$filename = "snicker-backup-" . time() . ".zip";
|
||||
|
||||
// Create Backup
|
||||
$zip = new PIT\Zip();
|
||||
$zip->addFolder($this->workspace(), "/", true, true);
|
||||
$zip->save(PATH_TMP . $filename);
|
||||
|
||||
// Return
|
||||
return $this->response(array(
|
||||
"success" => sn__("The backup has been created successfully!"),
|
||||
"referer" => DOMAIN_ADMIN . "uninstall-plugin/SnickerPlugin"
|
||||
), "alert");
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
## BACKEND HOOKs
|
||||
##
|
||||
|
||||
/*
|
||||
| HOOK :: INIT ADMINISTRATION
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function beforeAdminLoad(){
|
||||
global $url;
|
||||
|
||||
// Check if the current View is the "snicker"
|
||||
if(strpos($url->slug(), "snicker") !== 0){
|
||||
return false;
|
||||
}
|
||||
checkRole(array("admin"));
|
||||
|
||||
// Set Backend View
|
||||
$split = str_replace("snicker", "", trim($url->slug(), "/"));
|
||||
if(!empty($split) && $split !== "/" && isset($_GET["uid"])){
|
||||
$this->backendView = "edit";
|
||||
} else {
|
||||
$this->backendView = "index";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
| HOOK :: LOAD ADMINISTRATION FILES
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function adminHead(){
|
||||
global $page, $security, $url;
|
||||
|
||||
$js = SNICKER_DOMAIN . "admin/js/";
|
||||
$css = SNICKER_DOMAIN . "admin/css/";
|
||||
$slug = explode("/", str_replace(HTML_PATH_ADMIN_ROOT, "", $url->uri()));
|
||||
|
||||
// Admin Header
|
||||
ob_start();
|
||||
if($slug[0] === "new-content" || $slug[0] === "edit-content"){
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
(function(){
|
||||
"use strict";
|
||||
var w = window, d = window.document;
|
||||
|
||||
// Render Field
|
||||
var HANDLE_COMMENTS_FIELD = '<?php echo Bootstrap::formSelectBlock(array(
|
||||
'name' => 'allowComments',
|
||||
'label' => sn__('Page Comments'),
|
||||
'selected' => (!$page)? '1': ($page->allowComments()? '1': '0'),
|
||||
'class' => '',
|
||||
'options' => array(
|
||||
'1' => sn__('Allow Comments'),
|
||||
'0' => sn__('Disallow Comments')
|
||||
)
|
||||
)); ?>';
|
||||
|
||||
// Ready ?
|
||||
d.addEventListener("DOMContentLoaded", function(){
|
||||
if(d.querySelector("#jscategory")){
|
||||
var form = d.querySelector("#jscategory").parentElement;
|
||||
form.insertAdjacentHTML("afterend", HANDLE_COMMENTS_FIELD);
|
||||
}
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
<?php
|
||||
} else if($slug[0] === "snicker"){
|
||||
?>
|
||||
<script type="text/javascript" src="<?php echo $js; ?>admin.snicker.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="<?php echo $css; ?>admin.snicker.css" />
|
||||
<?php
|
||||
} else if($slug[0] === "plugins"){
|
||||
$link = DOMAIN_ADMIN . "snicker?action=snicker&snicker=backup&tokenCSRF=" . $security->getTokenCSRF();
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
var link = document.querySelector("tr#SnickerPlugin td a");
|
||||
if(link){
|
||||
link.addEventListener("click", function(event){
|
||||
event.preventDefault();
|
||||
jQuery("#dialog-deactivate-snicker").modal();
|
||||
});
|
||||
jQuery("#dialog-deactivate-snicker button[data-snicker='backup']").click(function(){
|
||||
console.log("owo");
|
||||
window.location.replace("<?php echo $link; ?>&referer=" + link.href);
|
||||
});
|
||||
jQuery("#dialog-deactivate-snicker button[data-snicker='deactivate']").click(function(){
|
||||
window.location.replace(link.href);
|
||||
});
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $content;
|
||||
}
|
||||
|
||||
/*
|
||||
| HOOK :: BEFORE ADMIN CONTENT
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function adminBodyBegin(){
|
||||
if(!$this->backend || !$this->backendView){
|
||||
return false;
|
||||
}
|
||||
ob_start();
|
||||
}
|
||||
|
||||
/*
|
||||
| HOOK :: AFTER ADMIN CONTENT
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function adminBodyEnd(){
|
||||
global $url, $SnickerPlugin;
|
||||
if(!$this->backend || !$this->backendView){
|
||||
$slug = explode("/", str_replace(HTML_PATH_ADMIN_ROOT, "", $url->uri()));
|
||||
if($slug[0] === "plugins"){
|
||||
?>
|
||||
<div id="dialog-deactivate-snicker" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><?php sn_e("Snicker Plugin Deactivation"); ?></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
<?php sn_e("You are about to deactivate the <b>Snicker</b> Plugin, which will delete all written comments!"); ?>
|
||||
<?php sn_e("Do you want to Backup your comments before?"); ?>
|
||||
</p>
|
||||
<p>
|
||||
<?php sn_e("The Backup will be stored in %s!", array("<code>./bl-content/tmp/</code>")); ?>
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-snicker="backup"><?php sn_e("Yes, create a Backup"); ?></button>
|
||||
<button type="button" class="btn btn-danger" data-snicker="deactivate"><?php sn_e("No, just Deactivate"); ?></button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php sn_e("Cancel"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch Content
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// Snicker Admin Content
|
||||
ob_start();
|
||||
if(file_exists(SNICKER_PATH . "admin" . DS . "{$this->backendView}.php")){
|
||||
require SNICKER_PATH . "admin" . DS . "{$this->backendView}.php";
|
||||
$add = ob_get_contents();
|
||||
}
|
||||
ob_end_clean();
|
||||
|
||||
// Inject Code
|
||||
if(isset($add) && !empty($add)){
|
||||
$regexp = "#(\<div class=\"col-lg-10 pt-3 pb-1 h-100\"\>)(.*?)(\<\/div\>)#s";
|
||||
$content = preg_replace($regexp, "$1{$add}$3", $content);
|
||||
}
|
||||
print($content);
|
||||
}
|
||||
|
||||
/*
|
||||
| HOOK :: SHOW SIDEBAR MENU
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function adminSidebar(){
|
||||
global $SnickerIndex;
|
||||
|
||||
$count = $SnickerIndex->count("pending");
|
||||
$count = ($count > 99)? "99+": $count;
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<a href="<?php echo HTML_PATH_ADMIN_ROOT; ?>snicker" class="nav-link" style="white-space: nowrap;">
|
||||
<span class="oi oi-comment-square"></span>Snicker <?php sn_e("Comments"); ?>
|
||||
<?php if(!empty($count)){ ?>
|
||||
<span class="badge badge-success badge-pill"><?php echo $count; ?></span>
|
||||
<?php } ?>
|
||||
</a>
|
||||
<?php
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
## FRONTEND HOOKs
|
||||
##
|
||||
|
||||
/*
|
||||
| HOOK :: BEFORE FRONTEND LOAD
|
||||
| @since 0.1.0
|
||||
| @update 0.1.2
|
||||
*/
|
||||
public function beforeSiteLoad(){
|
||||
global $comments, $page, $url;
|
||||
|
||||
// Start Session
|
||||
if(!Session::started()){
|
||||
Session::start();
|
||||
}
|
||||
|
||||
// Init Comments
|
||||
if(is_a($page, "Page") && $page->published() && !empty($page->uuid())){
|
||||
$comments = new Comments($page->uuid());
|
||||
} else {
|
||||
$comments = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
| HOOK :: FRONTEND HEADER
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function siteHead(){
|
||||
global $Snicker;
|
||||
|
||||
if(($theme = $Snicker->getTheme()) === false){
|
||||
return false;
|
||||
}
|
||||
if(!empty($theme::SNICKER_JS)){
|
||||
$file = SNICKER_DOMAIN . "themes/" . sn_config("frontend_template") . "/" . $theme::SNICKER_JS;
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
var SNICKER_AJAX = <?php echo sn_config("frontend_ajax")? "true": "false"; ?>;
|
||||
var SNICKER_PATH = "<?php echo HTML_PATH_ADMIN_ROOT ?>snicker/ajax/";
|
||||
</script>
|
||||
<script id="snicker-js" type="text/javascript" src="<?php echo $file; ?>"></script>
|
||||
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(!empty($theme::SNICKER_CSS)){
|
||||
$file = SNICKER_DOMAIN . "themes/" . sn_config("frontend_template") . "/" . $theme::SNICKER_CSS;
|
||||
?>
|
||||
<link id="snicker-css" type="text/css" rel="stylesheet" href="<?php echo $file; ?>" />
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
| HOOK :: FRONTEND CONTENT
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function siteBodyBegin(){
|
||||
global $Snicker;
|
||||
if(sn_config("frontend_filter") !== "siteBodyBegin"){
|
||||
return false; // owo
|
||||
}
|
||||
print($Snicker->render());
|
||||
}
|
||||
|
||||
/*
|
||||
| HOOK :: FRONTEND CONTENT
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function pageBegin(){
|
||||
global $Snicker;
|
||||
if(sn_config("frontend_filter") !== "pageBegin"){
|
||||
return false; // Owo
|
||||
}
|
||||
print($Snicker->render());
|
||||
}
|
||||
|
||||
/*
|
||||
| HOOK :: FRONTEND CONTENT
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function pageEnd(){
|
||||
global $Snicker;
|
||||
if(sn_config("frontend_filter") !== "pageEnd"){
|
||||
return false; // owO
|
||||
}
|
||||
print($Snicker->render());
|
||||
}
|
||||
|
||||
/*
|
||||
| HOOK :: FRONTEND CONTENT
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function siteBodyEnd(){
|
||||
global $Snicker;
|
||||
if(sn_config("frontend_filter") !== "siteBodyEnd"){
|
||||
return false; // OwO
|
||||
}
|
||||
print($Snicker->render());
|
||||
}
|
||||
}
|
||||
46
system/abstract.comments-theme.php
Normal file
46
system/abstract.comments-theme.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./system/abstract.comments-theme.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!"); }
|
||||
|
||||
abstract class CommentsTheme{
|
||||
/*
|
||||
| REQUIRED :: FORM
|
||||
| @note This method renders the comment form used on the frontend.
|
||||
|
|
||||
| @param multi The previously passed username (on errors only)
|
||||
| An `array(username, hash, nickname)` array if the user is logged in.
|
||||
| @param string The previously passed email address (on errors only)!
|
||||
| @param string The previously passed comment title (on errors only)!
|
||||
| @param string The previously passed comment message (on errors only)!
|
||||
*/
|
||||
abstract public function form($username = "", $email = "", $title = "", $message = "");
|
||||
|
||||
/*
|
||||
| REQUIRED :: COMMENT
|
||||
| @note This method renders the single shown comments on the frontend.
|
||||
|
|
||||
| @param object The comment instance.
|
||||
| @param string The unique comment UID.
|
||||
*/
|
||||
abstract public function comment($comment, $uid, $depth);
|
||||
|
||||
/*
|
||||
| REQUIRED :: PAGINATION
|
||||
| @note This method renders the pagination for the comment section.
|
||||
|
|
||||
| @param string The called location: "top" or "bottom".
|
||||
| @param int The current comment page.<, startin with 1.
|
||||
| @param int The number of comments to be shown per page.
|
||||
| @param int The total number of comments for the content page.
|
||||
*/
|
||||
abstract public function pagination($loction, $cpage, $limit, $count);
|
||||
}
|
||||
494
system/class.comment.php
Normal file
494
system/class.comment.php
Normal file
@@ -0,0 +1,494 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./system/class.comment.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 Comment{
|
||||
/*
|
||||
| CONSTRUCTOR
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param multi The unique comment id or FALSE.
|
||||
| @param multi The unique page comment ID.
|
||||
*/
|
||||
public function __construct($uid, $uuid){
|
||||
$this->vars["uid"] = $uid;
|
||||
if($uid === false){
|
||||
$row = array(
|
||||
"type" => "comment",
|
||||
"depth" => 1,
|
||||
"title" => "",
|
||||
"comment" => "",
|
||||
"rating" => [0, 0],
|
||||
"page_uuid" => "",
|
||||
"parent_uid" => "",
|
||||
"author" => "",
|
||||
"subscribe" => false,
|
||||
"date" => "",
|
||||
"dateModified" => "",
|
||||
"dateAudit" => "",
|
||||
"custom" => array()
|
||||
);
|
||||
} else {
|
||||
$comments = new Comments($uuid);
|
||||
if(Text::isEmpty($uid) || !$comments->exists($uid)){
|
||||
// @todo Throw Error
|
||||
}
|
||||
$row = $comments->getCommentDB($uid);
|
||||
}
|
||||
|
||||
// Set Class
|
||||
foreach($row AS $field => $value){
|
||||
if(strpos($field, "date") === 0){
|
||||
$this->vars["{$field}Raw"] = $value;
|
||||
} else {
|
||||
$this->vars[$field] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
| PUBLIC :: GET VALUE
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique field key.
|
||||
| @param multi The default value, which should return if the field key doesnt exist.
|
||||
|
|
||||
| @multi multi The respective field value on success, $default otherwise.
|
||||
*/
|
||||
public function getValue($field, $default = false){
|
||||
if(isset($this->vars[$field])){
|
||||
return $this->vars[$field];
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/*
|
||||
| PUBLIC :: SET FIELD
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique field key.
|
||||
| @param multi The respective field value, which you want to set.
|
||||
|
|
||||
| @return bool TRUE
|
||||
*/
|
||||
public function setField($field, $value = NULL){
|
||||
if(is_array($field)){
|
||||
foreach($field AS $k => $v){
|
||||
$this->setField($k, $v);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
$this->vars[$field] = $value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
| FIELD :: COMMENT RAW
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @return string The (sanitized) raw content on success, FALSE on failure.
|
||||
*/
|
||||
public function commentRaw(){
|
||||
return $this->getValue("comment");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: COMMENT
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @return string The (sanitized) content on success, FALSE on failure.
|
||||
*/
|
||||
public function comment(){
|
||||
$content = $this->getValue("comment");
|
||||
if(sn_config("comment_markup_html")){
|
||||
$content = Sanitize::htmlDecode($content);
|
||||
}
|
||||
if(sn_config("comment_markup_markdown")){
|
||||
$parsedown = new Parsedown();
|
||||
$content = $parsedown->text($content);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET UID
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function uid(){
|
||||
return $this->getValue("uid");
|
||||
}
|
||||
public function key(){
|
||||
return $this->getValue("uid");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET (COMMENT FILE) PATH
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function path(){
|
||||
return PATH_PAGES . $this->getValue("page_key") . DS . "comments";
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET (COMMENT FILE) PATH / FILE
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function file(){
|
||||
return $this->path() . DS . "c_" . $this->getValue("uid") . ".php";
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET TYPE
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function type(){
|
||||
return $this->getValue("type");
|
||||
}
|
||||
public function isComment(){
|
||||
return $this->getValue("type") === "comment";
|
||||
}
|
||||
public function isReply(){
|
||||
return $this->getValue("type") === "reply";
|
||||
}
|
||||
public function isPingback(){
|
||||
return $this->getValue("type") === "pingback";
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET DEPTH
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function depth(){
|
||||
return (int) $this->getValue("depth");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: TITLE
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param bool TRUE to sanitize the content, FALSE to return it plain.
|
||||
|
|
||||
| @return string The respective comment title as STRING.
|
||||
*/
|
||||
public function title($sanitize = true){
|
||||
if($sanitize){
|
||||
return Sanitize::html($this->getValue("title"));
|
||||
}
|
||||
return $this->getValue("title");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET STATUS
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function status(){
|
||||
return $this->getValue("status");
|
||||
}
|
||||
public function isPending(){
|
||||
return $this->getValue("status") === "pending";
|
||||
}
|
||||
public function isPublic(){
|
||||
return $this->getValue("status") === "approved";
|
||||
}
|
||||
public function isApproved(){
|
||||
return $this->getValue("status") === "approved";
|
||||
}
|
||||
public function isRejected(){
|
||||
return $this->getValue("status") === "rejected";
|
||||
}
|
||||
public function isSpam(){
|
||||
return $this->getValue("status") === "spam";
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET RATING
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function rating(){
|
||||
return $this->getValue("rating");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET LIKE
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function like(){
|
||||
$rating = $this->getValue("rating");
|
||||
if(is_array($rating) && count($rating) >= 1){
|
||||
return $rating[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET DISLIKE
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function dislike(){
|
||||
$rating = $this->getValue("rating");
|
||||
if(is_array($rating) && count($rating) >= 2){
|
||||
return $rating[1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET PAGE KEY
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function page_key(){
|
||||
return $this->getValue("page_key");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET PAGE UUID
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function page_uuid(){
|
||||
return $this->getValue("page_uuid");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET PARENT UID
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function parent_uid(){
|
||||
return $this->getValue("parent_uid");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET PARENT
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function parent(){
|
||||
global $comments;
|
||||
if($comments->exists($this->getValue("parent_uid"))){
|
||||
return new Comment($this->getValue("parent_uid"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET CHILDREN
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param multi The single comment status which should return, multiple as ARRAY.
|
||||
| Use `null` to return each children comment.
|
||||
| @param string The return type, which allows the following strings:
|
||||
| "uids" Return just the respective UID / keys
|
||||
| "keys" Return just the respective UID / keys
|
||||
| "objects" Return single Comment instances
|
||||
| "arrays" Return the unformatted DB arrays
|
||||
|
|
||||
| @return multi FALSE on error, the respective array on succes.
|
||||
*/
|
||||
public function children($status = "approved", $return = "objects"){
|
||||
global $comments;
|
||||
|
||||
// Check Parameter
|
||||
if(is_string($status)){
|
||||
$status = array($status);
|
||||
}
|
||||
if(!is_array($status) && $status !== null){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Children
|
||||
$return = array();
|
||||
foreach($this->getDB(false) AS $uid => $value){
|
||||
if($value["parent"] !== $this->getValue("uid")){
|
||||
continue;
|
||||
}
|
||||
if(is_array($status) && !in_array($value["status"], $status)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($return === "uids" || $return == "keys"){
|
||||
$return[] = $uid;
|
||||
} else if($return === "objects"){
|
||||
$return[$uid] = new Comment($uid);
|
||||
} else {
|
||||
$return[$uid] = $value;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET UUID
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function uuid(){
|
||||
return $this->getValue("uuid");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET USERNAME
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function username(){
|
||||
global $L, $users, $SnickerUsers;
|
||||
|
||||
list($type, $id) = array_pad(explode("::", $this->getValue("author"), 2), 2, null);
|
||||
switch($type){
|
||||
case "bludit":
|
||||
if(!$users->exists($id)){
|
||||
break;
|
||||
}
|
||||
$user = new User($id);
|
||||
return $user->nickname();
|
||||
case "guest":
|
||||
if(!$SnickerUsers->exists($id)){
|
||||
break;
|
||||
}
|
||||
return $SnickerUsers->db[$id]["username"];
|
||||
case "unknown":
|
||||
return $L->g("Unknown User");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET EMAIL
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function email(){
|
||||
global $L, $users, $SnickerUsers;
|
||||
|
||||
list($type, $id) = array_pad(explode("::", $this->getValue("author"), 2), 2, null);
|
||||
switch($type){
|
||||
case "bludit":
|
||||
if(!$users->exists($id)){
|
||||
break;
|
||||
}
|
||||
$user = new User($id);
|
||||
return $user->email();
|
||||
case "guest":
|
||||
if(!$SnickerUsers->exists($id)){
|
||||
break;
|
||||
}
|
||||
return $SnickerUsers->db[$id]["email"];
|
||||
case "unknown":
|
||||
return "unknown@" . $_SERVER["SERVER_NAME"];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: SUBSCRIBE
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function subscribe(){
|
||||
return $this->getValue("subscribe");
|
||||
}
|
||||
public function hasSubscribed(){
|
||||
return $this->getValue("subscribe") === true;
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET AVATAR
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function avatar($size = "64"){
|
||||
$user = $this->username();
|
||||
$email = md5(strtolower(trim($this->email())));
|
||||
$avatar = $this->avatar_url($size);
|
||||
|
||||
// Force Profile Picture
|
||||
$force = false;
|
||||
if(sn_config("frontend_avatar_users")){
|
||||
$force = (strpos($avatar, DOMAIN_UPLOADS_PROFILES) !== false);
|
||||
}
|
||||
|
||||
// Return IMG Tag
|
||||
if(sn_config("frontend_avatar") === "identicon" && !$force){
|
||||
return '<img src="'.$avatar.'" width="'.$size.'px" height="'.$size.'px" data-identicon="'.$email.'" alt="'.$user.'" />';
|
||||
}
|
||||
return '<img src="'.$avatar.'" width="'.$size.'px" height="'.$size.'px" alt="'.$user.'" />';
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET AVATAR URL
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function avatar_url($size = "64"){
|
||||
global $users;
|
||||
|
||||
// Return Profile Picture
|
||||
if(sn_config("frontend_avatar_users") && strpos($this->getValue("author"), "bludit") === 0){
|
||||
$username = substr($this->getValue("author"), strlen("bludit::"));
|
||||
if($users->exists($username)){
|
||||
$user = new User($username);
|
||||
if(($avatar = $user->profilePicture()) !== false){
|
||||
return $avatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return Gravatar
|
||||
if(sn_config("frontend_avatar") === "gravatar"){
|
||||
$hash = md5(strtolower(trim($this->email())));
|
||||
return "https://www.gravatar.com/avatar/{$hash}?s={$size}&d=" . sn_config("frontend_gravatar");
|
||||
}
|
||||
|
||||
// Return Identicon
|
||||
if(sn_config("frontend_avatar") === "identicon"){
|
||||
$hash = md5(strtolower(trim($this->email())));
|
||||
$ident = new Identicon\Identicon();
|
||||
return $ident->getImageDataUri($hash, $size);
|
||||
}
|
||||
|
||||
// Return Mystery Man
|
||||
return SNICKER_DOMAIN . "includes/img/default-avatar.jpg";
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET / FORMAT DATE
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The respective format, which should be used for the output.
|
||||
|
|
||||
| @return string The formatted Date Output.
|
||||
*/
|
||||
public function date($format = false, $type = "date"){
|
||||
global $site;
|
||||
$date = $this->getValue("{$type}Raw");
|
||||
return Date::format($date, DB_DATE_FORMAT, ($format? $format: $site->dateFormat()));
|
||||
}
|
||||
public function dateModified($format = false){
|
||||
return $this->date($format, "dateModified");
|
||||
}
|
||||
public function dateAudit($format = false){
|
||||
return $this->date($format, "dateAudit");
|
||||
}
|
||||
|
||||
/*
|
||||
| FIELD :: GET CUSTOM
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The respective custom key, to get the value.
|
||||
| Use `null` to get all custom values.
|
||||
|
|
||||
| @return multi The custom value, all customs as ARRAY or FALSE on failure.
|
||||
*/
|
||||
public function custom($key = NULL){
|
||||
$custom = $this->getValue("custom");
|
||||
if($key !== null){
|
||||
if(array_key_exists($key, $custom)){
|
||||
return $custom[$key];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return $custom;
|
||||
}
|
||||
}
|
||||
476
system/class.comments-index.php
Normal file
476
system/class.comments-index.php
Normal file
@@ -0,0 +1,476 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./system/class.comments-index.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 CommentsIndex extends dbJSON{
|
||||
/*
|
||||
| DATABASE FIELDS
|
||||
*/
|
||||
protected $dbFields = array(
|
||||
"title" => "", // Comment Title
|
||||
"excerpt" => "", // Comment Excerpt (142)
|
||||
"status" => "", // Comment Status
|
||||
"page_uuid" => "", // Comment Page UUID
|
||||
"parent_uid" => "", // Comment Parent UID
|
||||
"author" => "", // Comment Author (bludt::username or guest::uuid)
|
||||
"date" => "" // Comment Date
|
||||
);
|
||||
|
||||
/*
|
||||
| CONSTRUCTOR
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function __construct(){
|
||||
parent::__construct(DB_SNICKER_INDEX);
|
||||
if(!file_exists(DB_SNICKER_INDEX)){
|
||||
$this->db = array();
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
| OVERWRITE :: EXISTS
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function exists($uid){
|
||||
return array_key_exists($uid, $this->db);
|
||||
}
|
||||
|
||||
/*
|
||||
| HELPER :: LIMIT LIST
|
||||
| @since 0.1.0
|
||||
*/
|
||||
private function limitList($list, $page, $limit){
|
||||
if($limit == -1){
|
||||
return $list;
|
||||
}
|
||||
$offset = $limit * (max($page, 1) - 1);
|
||||
$count = min(($offset + $limit - 1), count($list));
|
||||
if($offset < 0 || $offset > $count){
|
||||
return false;
|
||||
}
|
||||
return array_slice($list, $offset, $limit, true);
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: GET PENDING INDEX
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param bool TRUE to just return the keys, FALSE to return the complete array.
|
||||
|
|
||||
| @return array All pending comments with basic comment data as ARRAY.
|
||||
*/
|
||||
public function getPending($keys = false){
|
||||
$db = array();
|
||||
foreach($this->db AS $key => $value){
|
||||
if(!isset($value["status"]) || empty($value["status"])){
|
||||
continue;
|
||||
}
|
||||
if($value["status"] === "pending"){
|
||||
$db[$key] = $value;
|
||||
}
|
||||
}
|
||||
if($keys){
|
||||
return array_keys($db);
|
||||
}
|
||||
return $db;
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: GET APPROVED INDEX
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param bool TRUE to just return the keys, FALSE to return the complete array.
|
||||
|
|
||||
| @return array All approved comments with basic comment data as ARRAY.
|
||||
*/
|
||||
public function getApproved($keys = false){
|
||||
$db = array();
|
||||
foreach($this->db AS $key => $value){
|
||||
if(!isset($value["status"]) || empty($value["status"])){
|
||||
continue;
|
||||
}
|
||||
if($value["status"] === "approved"){
|
||||
$db[$key] = $value;
|
||||
}
|
||||
}
|
||||
if($keys){
|
||||
return array_keys($db);
|
||||
}
|
||||
return $db;
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: GET REJECTED INDEX
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param bool TRUE to just return the keys, FALSE to return the complete array.
|
||||
|
|
||||
| @return array All rejected comments with basic comment data as ARRAY.
|
||||
*/
|
||||
public function getRejected($keys = false){
|
||||
$db = array();
|
||||
foreach($this->db AS $key => $value){
|
||||
if(!isset($value["status"]) || empty($value["status"])){
|
||||
continue;
|
||||
}
|
||||
if($value["status"] === "rejected"){
|
||||
$db[$key] = $value;
|
||||
}
|
||||
}
|
||||
if($keys){
|
||||
return array_keys($db);
|
||||
}
|
||||
return $db;
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: GET SPAM INDEX
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param bool TRUE to just return the keys, FALSE to return the complete array.
|
||||
|
|
||||
| @return array All spam comments with basic comment data as ARRAY.
|
||||
*/
|
||||
public function getSpam($keys = false){
|
||||
$db = array();
|
||||
foreach($this->db AS $key => $value){
|
||||
if(!isset($value["status"]) || empty($value["status"])){
|
||||
continue;
|
||||
}
|
||||
if($value["status"] === "spam"){
|
||||
$db[$key] = $value;
|
||||
}
|
||||
}
|
||||
if($keys){
|
||||
return array_keys($db);
|
||||
}
|
||||
return $db;
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: COUNT COMMENTS
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param multi A single comment status as STRING, multiple as ARRAY.
|
||||
| Use `null` to count all comments.
|
||||
|
|
||||
| @return int The number of comments of the respective index.
|
||||
*/
|
||||
public function count($status = array("approved")){
|
||||
if($status === null){
|
||||
return count($this->db);
|
||||
}
|
||||
if(!is_array($status)){
|
||||
$status = array($status);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
foreach($this->db AS $key => $value){
|
||||
if(!isset($value["status"]) || empty($value["status"])){
|
||||
continue;
|
||||
}
|
||||
if(in_array($value["status"], $status)){
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: GET COMMENT
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The desired comment UID.
|
||||
|
|
||||
| @return multi The comment index array on success, FALSE on failure
|
||||
*/
|
||||
public function getComment($uid){
|
||||
return array_key_exists($uid, $this->db)? $this->db[$uid]: false;
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: LIST COMMENTS
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param multi A single comment status as STRING, multiple as ARRAY.
|
||||
| @param int The current comment page number, starting with 1.
|
||||
| @param int The number of comments to be shown per page.
|
||||
|
|
||||
| @return array The respective unique comment IDs as ARRAY, FALSE on failure.
|
||||
*/
|
||||
public function getList($status = array("approved"), $page = 1, $limit = -1){
|
||||
if($status === null){
|
||||
return count($this->db);
|
||||
}
|
||||
if(!is_array($status)){
|
||||
$status = array($status);
|
||||
}
|
||||
|
||||
// Get List
|
||||
$list = array();
|
||||
foreach($this->db AS $key => $value){
|
||||
if(!isset($value["status"]) || empty($value["status"])){
|
||||
continue;
|
||||
}
|
||||
if(in_array($value["status"], $status)){
|
||||
$list[] = $key;
|
||||
}
|
||||
}
|
||||
return $this->limitList($list, $page, $limit);
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: LIST COMMENTS BY UUID
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param multi A single page UUID as STRING, multiple as ARRAY.
|
||||
| @param int The current comment page number, starting with 1.
|
||||
| @param int The number of comments to be shown per page.
|
||||
|
|
||||
| @return array The respective unique comment IDs as ARRAY, FALSE on failure.
|
||||
*/
|
||||
public function getListByUUID($uuid, $page = 1, $limit = -1){
|
||||
if(!is_array($uuid)){
|
||||
$uuid = array($uuid);
|
||||
}
|
||||
|
||||
// Get List
|
||||
$list = array();
|
||||
foreach($this->db AS $key => $value){
|
||||
if(!isset($value["page_uuid"]) || empty($value["page_uuid"])){
|
||||
continue;
|
||||
}
|
||||
if(in_array($value["page_uuid"], $uuid)){
|
||||
$list[] = $key;
|
||||
}
|
||||
}
|
||||
return $this->limitList($list, $page, $limit);
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: LIST COMMENTS BY PARENT
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param multi A single comment UID as STRING.
|
||||
| @param int The current comment page number, starting with 1.
|
||||
| @param int The number of comments to be shown per page.
|
||||
|
|
||||
| @return array The respective unique comment IDs as ARRAY, FALSE on failure.
|
||||
*/
|
||||
public function getListByParent($uid, $page = 1, $limit = -1){
|
||||
if(!is_string($uid) || !$this->exists($uid)){
|
||||
return array();
|
||||
}
|
||||
|
||||
// Get List
|
||||
$list = array($uid);
|
||||
foreach($this->db AS $key => $value){
|
||||
if(!isset($value["parent_uid"]) || empty($value["parent_uid"])){
|
||||
continue;
|
||||
}
|
||||
if($value["parent_uid"] === $uid){
|
||||
$list[] = $key;
|
||||
}
|
||||
}
|
||||
return $this->limitList($list, $page, $limit);
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: LIST COMMENTS BY USER
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string A single username, unique user id or eMail address.
|
||||
| @param int The current comment page number, starting with 1.
|
||||
| @param int The number of comments to be shown per page.
|
||||
|
|
||||
| @return array The respective unique comment IDs as ARRAY, FALSE on failure.
|
||||
*/
|
||||
public function getListByUser($string, $page = 1, $limit = -1){
|
||||
global $users, $SnickerUsers;
|
||||
|
||||
// Get Member / Guest
|
||||
$guest = false;
|
||||
$member = false;
|
||||
if(Valid::email($string)){
|
||||
if(($user = $users->getByEmail($string)) !== false){
|
||||
$member = "bludit::{$user}";
|
||||
}
|
||||
} else {
|
||||
if($users->exists($string)){
|
||||
$member = "bludit::{$string}";
|
||||
}
|
||||
}
|
||||
if(($user = $SnickerUsers->get($string)) !== false){
|
||||
$guest = "guest::{$user["uuid"]}";
|
||||
}
|
||||
if(!$member && !$guest){
|
||||
return array();
|
||||
}
|
||||
|
||||
// Get List
|
||||
$list = array();
|
||||
foreach($this->db AS $key => $value){
|
||||
if(!isset($value["author"]) || empty($value["author"])){
|
||||
continue;
|
||||
}
|
||||
if($value["author"] == $member || $value["author"] == $guest){
|
||||
$list[] = $key;
|
||||
}
|
||||
}
|
||||
return $this->limitList($list, $page, $limit);
|
||||
}
|
||||
|
||||
/*
|
||||
| DATA :: SEARCH COMMENTS BY TITLE & EXCERPT
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The string to be searched.
|
||||
| @param int The current comment page number, starting with 1.
|
||||
| @param int The number of comments to be shown per page.
|
||||
|
|
||||
| @return array The respective unique comment IDs as ARRAY, FALSE on failure.
|
||||
*/
|
||||
public function searchComments($search, $page = 1, $limit = -1){
|
||||
$list = array();
|
||||
foreach($this->db AS $key => $value){
|
||||
if(isset($value["title"]) && stripos($value["title"], $search) !== false){
|
||||
$list[] = $key;
|
||||
} else if(isset($value["excerpt"]) && stripos($value["excerpt"], $search) !== false){
|
||||
$list[] = $key;
|
||||
}
|
||||
}
|
||||
return $this->limitList($list, $page, $limit);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
| HANDLE :: ADD COMMENT
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique comment ID.
|
||||
| @param array The comment array.
|
||||
|
|
||||
| @return bool TRUE if everything is fluffy, FALSE if not.
|
||||
*/
|
||||
public function add($uid, $comment){
|
||||
$row = array();
|
||||
foreach($this->dbFields AS $field => $value){
|
||||
if(isset($comment[$field])){
|
||||
$final = is_string($comment[$field])? Sanitize::html($comment[$field]): $comment[$field];
|
||||
} else {
|
||||
$final = $value;
|
||||
}
|
||||
settype($final, gettype($value));
|
||||
$row[$field] = $final;
|
||||
}
|
||||
|
||||
// Format Excerpt
|
||||
$row["excerpt"] = strip_tags($comment["comment"]);
|
||||
if(strlen($row["excerpt"]) > 142){
|
||||
$row["excerpt"] = substr($row["excerpt"], 0, 139) . "...";
|
||||
}
|
||||
|
||||
// Insert and Return
|
||||
$this->db[$uid] = $row;
|
||||
$this->sortBy();
|
||||
if($this->save() !== true){
|
||||
Log::set(__METHOD__, "error-update-db");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE :: UPDATE COMMENT
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique comment ID.
|
||||
| @param array The comment array.
|
||||
|
|
||||
| @return bool TRUE if everything is fluffy, FALSE if not.
|
||||
*/
|
||||
public function edit($uid, $comment){
|
||||
if(!$this->exists($uid)){
|
||||
$this->log(__METHOD__, "error-comment-uid", array($uid));
|
||||
return false;
|
||||
}
|
||||
$data = $this->db[$uid];
|
||||
|
||||
// Loop Fields
|
||||
$row = array();
|
||||
foreach($this->dbFields AS $field => $value){
|
||||
if(isset($comment[$field])){
|
||||
$final = is_string($comment[$field])? Sanitize::html($comment[$field]): $comments[$field];
|
||||
} else {
|
||||
$final = $data[$field];
|
||||
}
|
||||
settype($final, gettype($value));
|
||||
$row[$field] = $final;
|
||||
}
|
||||
|
||||
// Format Excerpt
|
||||
$row["excerpt"] = strip_tags($comment["comment"]);
|
||||
if(strlen($row["excerpt"]) > 142){
|
||||
$row["excerpt"] = substr($row["excerpt"], 0, 139) . "...";
|
||||
}
|
||||
|
||||
// Update and Return
|
||||
$this->db[$uid] = $row;
|
||||
if($this->save() !== true){
|
||||
Log::set(__METHOD__, "error-update-db");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE :: DELETE COMMENT
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique comment ID.
|
||||
|
|
||||
| @return bool TRUE if everything is fluffy, FALSE if not.
|
||||
*/
|
||||
public function delete($uid){
|
||||
if(!$this->exists($uid)){
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
392
system/class.comments-users.php
Normal file
392
system/class.comments-users.php
Normal file
@@ -0,0 +1,392 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./system/class.comments-users.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 CommentsUsers extends dbJSON{
|
||||
/*
|
||||
| DATABASE FIELDS
|
||||
*/
|
||||
protected $dbFields = array(
|
||||
"username" => "", // Username
|
||||
"email" => "", // User eMail Address
|
||||
"hash" => "", // Hashed IP + User Agent
|
||||
"blocked" => false, // Blocked?
|
||||
"comments" => array() // Page UIDs => array(CommentUIDs)
|
||||
);
|
||||
|
||||
/*
|
||||
| CONSTRUCTOR
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function __construct(){
|
||||
parent::__construct(DB_SNICKER_USERS);
|
||||
if(!file_exists(DB_SNICKER_USERS)){
|
||||
$this->db = array();
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
| GET COMMENTS BY UNIQUE USER ID
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique user ID as string (or the user eMail address).
|
||||
| @param bool TRUE to just return the keys, FALSE to return it as Comment objects.
|
||||
|
|
||||
| @return multi The comment keys / objects as ARRAY, FALSE on failure.
|
||||
*/
|
||||
public function getComments($uuid, $keys = true){
|
||||
global $Snicker;
|
||||
|
||||
// Validate Data
|
||||
if(Valid::email($uuid) !== false){
|
||||
$uuid = md5(strtolower(Sanitize::email($uuid)));
|
||||
}
|
||||
if(!array_key_exists($uuid, $this->db)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return Keys
|
||||
$data = $this->db[$uuid]["comments"];
|
||||
if($keys === true){
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Return Objects
|
||||
foreach($data AS &$key){
|
||||
$key = $Snicker->getComment($key);
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/*
|
||||
| EXISTS
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function exists($uid){
|
||||
return isset($this->db[$uid]);
|
||||
}
|
||||
|
||||
/*
|
||||
| GET USER BY UUID
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique user ID as string (or the user eMail address).
|
||||
|
|
||||
| @return multi The user database array on success, FALSE on failure.
|
||||
*/
|
||||
public function get($uuid){
|
||||
if(Valid::email($uuid) !== false){
|
||||
$uuid = md5(strtolower(Sanitize::email($uuid)));
|
||||
}
|
||||
if(!array_key_exists($uuid, $this->db)){
|
||||
return false;
|
||||
}
|
||||
$data = $this->db[$uuid];
|
||||
$data["uuid"] = $uuid;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/*
|
||||
| GET CURRENT USER ID
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @return multi The user UUID on success, FALSE on failure.
|
||||
*/
|
||||
public function getCurrent(){
|
||||
global $security;
|
||||
$hash = md5($security->getUserIp() . $_SERVER["HTTP_USER_AGENT"]);
|
||||
foreach($this->db AS $uuid => $fields){
|
||||
if($fields["hash"] === $hash){
|
||||
return $uuid;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
| GET USER
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string Get the user by Comment Author STRING.
|
||||
|
|
||||
| @return multi The user data array on success, FALSE on failure.
|
||||
*/
|
||||
public function getByString($string){
|
||||
global $users;
|
||||
|
||||
// Check User Instance
|
||||
if(strpos($string, "bludit::") === 0){
|
||||
$username = substr($string, strlen("bludit::"));
|
||||
if($users->exists($username)){
|
||||
$user = $users->getUserDB($username);
|
||||
$user["username"] = $user["nickname"];
|
||||
return $user;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check Guest Instance
|
||||
if(strpos($string, "guest::") === 0){
|
||||
$uuid = substr($string, strlen("guest::"));
|
||||
if($this->exists($uuid)){
|
||||
return $this->db[$uuid];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return as Anonymous
|
||||
return array(
|
||||
"username" => "Anonymous",
|
||||
"email" => "anonymous@" . $_SERVER["SERVER_NAME"]
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
| GET LIST
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The string to be searched or NULL.
|
||||
| @param int The current comment page number, starting with 1.
|
||||
| @param int The number of comments to be shown per page.
|
||||
|
|
||||
| @return array The respective user keys with an ARRAY or FALSE on failure.
|
||||
*/
|
||||
public function getList($search = null, $page = 1, $limit = -1){
|
||||
if($search !== null){
|
||||
$list = array();
|
||||
foreach($this->db AS $uuid => $fields){
|
||||
if(stripos($fields["username"], $search) === false){
|
||||
continue;
|
||||
}
|
||||
if(stripos($fields["email"], $search) === false){
|
||||
continue;
|
||||
}
|
||||
$list[$uuid] = $fields;
|
||||
}
|
||||
} else {
|
||||
$list = $this->db;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/*
|
||||
| MAIN USER HANDLER
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The username as STRING.
|
||||
| @param string The email address as STRING.
|
||||
|
|
||||
| @return multi The (new) UUID on success, FALSE on failure.
|
||||
*/
|
||||
public function user($username, $email){
|
||||
global $security;
|
||||
|
||||
// Validate Username
|
||||
$username = Sanitize::html(strip_tags(trim($username)));
|
||||
if(empty($username) || strlen($username) > 42){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate eMail Address
|
||||
$email = strtolower(Sanitize::email($email));
|
||||
if(empty($email) || Valid::email($email) === false){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check User
|
||||
$uuid = md5($email);
|
||||
if(array_key_exists($uuid, $this->db)){
|
||||
return $uuid;
|
||||
}
|
||||
|
||||
// Add User
|
||||
$this->db[$uuid] = array(
|
||||
"username" => $username,
|
||||
"email" => $email,
|
||||
"hash" => md5($security->getUserIp() . $_SERVER["HTTP_USER_AGENT"]),
|
||||
"blocked" => false,
|
||||
"comments" => array()
|
||||
);
|
||||
if(!$this->save()){
|
||||
return false;
|
||||
}
|
||||
return $uuid;
|
||||
}
|
||||
public function add($username, $email, $meta = array()){
|
||||
return $this->user($username, $email, $meta);
|
||||
}
|
||||
|
||||
/*
|
||||
| EDIT USER DATA
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique user ID as string (or the user eMail address).
|
||||
| @param multi The new username (or NULL to keep the existing one).
|
||||
| @param multi The new eMail address (or NULL to keep the existing one).
|
||||
| ATTENTION: The new eMail address CANNOT be used already!
|
||||
| ATTENTION: The new eMail address CHANGES the unique user id (UUID)!
|
||||
| @param multi TRUE to block the user, FALSE to unblock, null to keep the current.
|
||||
|
|
||||
| @return multi The (new) UUID on success, FALSE on failure.
|
||||
*/
|
||||
public function edit($uuid, $username = null, $email = null, $blocked = null){
|
||||
if(Valid::email($uuid) !== false){
|
||||
$uuid = md5(strtolower(Sanitize::email($uuid)));
|
||||
}
|
||||
if(!array_key_exists($uuid, $this->db)){
|
||||
return false;
|
||||
}
|
||||
$data = $this->db[$uuid];
|
||||
|
||||
// Change Username
|
||||
if($username !== null){
|
||||
$username = Sanitize::html(strip_tags(trim($username)));
|
||||
if(empty($username) || strlen($username) > 42){
|
||||
return false;
|
||||
}
|
||||
$data["username"] = $username;
|
||||
}
|
||||
|
||||
// Change eMail
|
||||
if($email !== null){
|
||||
$email = strtolower(Sanitize::email($uuid));
|
||||
if(Valid::email($email) === false){
|
||||
return false;
|
||||
}
|
||||
$data["email"] = $email;
|
||||
$newuuid = md5($email);
|
||||
}
|
||||
|
||||
// Change Blocked
|
||||
if(is_bool($blocked)){
|
||||
$data["blocked"] = $blocked;
|
||||
}
|
||||
|
||||
// Update UUID
|
||||
if(isset($newuuid) && $uuid !== $newuuid){
|
||||
unset($this->db[$uuid]);
|
||||
$uuid = $newuuid;
|
||||
}
|
||||
|
||||
// Store new Data
|
||||
$this->db[$uuid] = $data;
|
||||
if(!$this->save()){
|
||||
return false;
|
||||
}
|
||||
return $uuid;
|
||||
}
|
||||
|
||||
/*
|
||||
| ADD COMMENT ID TO USER
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique user ID as string (or the user eMail address).
|
||||
| @param string The unique comment ID as STRING.
|
||||
|
|
||||
| @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function addComment($uuid, $uid){
|
||||
if(Valid::email($uuid) !== false){
|
||||
$uuid = md5(strtolower(Sanitize::email($uuid)));
|
||||
}
|
||||
if(!array_key_exists($uuid, $this->db)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add Comment UID
|
||||
$user = $this->db[$uuid];
|
||||
if(!isset($user["comments"]) || !is_array($user["comments"])){
|
||||
$user["comments"] = array();
|
||||
}
|
||||
if(!in_array($uid, $user["comments"])){
|
||||
$user["comments"][] = $uid;
|
||||
}
|
||||
|
||||
// Save & Return
|
||||
$this->db[$uuid] = $user;
|
||||
if(!$this->save()){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| DELETE COMMENT ID TO USER
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique user ID as string (or the user eMail address).
|
||||
| @param string The unique comment ID as STRING.
|
||||
|
|
||||
| @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function deleteComment($uuid, $uid){
|
||||
if(Valid::email($uuid) !== false){
|
||||
$uuid = md5(strtolower(Sanitize::email($uuid)));
|
||||
}
|
||||
if(!array_key_exists($uuid, $this->db)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete Comment UID
|
||||
$user = $this->db[$uuid];
|
||||
if(!isset($user["comments"])){
|
||||
$user["comments"] = array();
|
||||
}
|
||||
if(in_array($uid, $user["comments"])){
|
||||
unset($user["comments"][array_search($uid, $user["comments"])]);
|
||||
}
|
||||
|
||||
// Save & Return
|
||||
$this->db[$uuid] = $user;
|
||||
if(!$this->save()){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| DELETE USER
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The unique user ID as string (or the user eMail address).
|
||||
|
|
||||
| @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function delete($uuid){
|
||||
if(Valid::email($uuid) !== false){
|
||||
$uuid = md5(strtolower(Sanitize::email($uuid)));
|
||||
}
|
||||
if(!array_key_exists($uuid, $this->db)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete & Return
|
||||
unset($this->db[$uuid]);
|
||||
if(!$this->save()){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
226
system/class.comments-votes.php
Normal file
226
system/class.comments-votes.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./system/class.comments-votes.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 CommentsVotes extends dbJSON{
|
||||
const KEY = "snicker-ratings";
|
||||
|
||||
/*
|
||||
| DATABASE FIELDS
|
||||
*/
|
||||
protected $dbFields = array( );
|
||||
|
||||
/*
|
||||
| CONSTRUCTOR
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function __construct(){
|
||||
parent::__construct(DB_SNICKER_VOTES);
|
||||
if(!file_exists(DB_SNICKER_VOTES)){
|
||||
$this->db = array();
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE :: CURRENT USER
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function currentUser(){
|
||||
global $login, $security;
|
||||
|
||||
if(!is_a($login, "Login")){
|
||||
$login = new Login();
|
||||
}
|
||||
|
||||
// Get Current User
|
||||
if($login->isLogged()){
|
||||
return "bludit::" . $login->username();
|
||||
}
|
||||
return "guest::" . md5($security->getUserIp() . $_SERVER["HTTP_USER_AGENT"]);
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE :: HAS VOTED
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function hasVoted($uid, $vote = null){
|
||||
$user = $this->currentUser();
|
||||
$config = sn_config("comment_vote_storage");
|
||||
|
||||
// Database Storage
|
||||
$db = strpos($user, "bludit::") === 0 || $config === "database";
|
||||
if($db){
|
||||
if(!array_key_exists($user, $this->db)){
|
||||
return false;
|
||||
}
|
||||
$data = $this->db[$user];
|
||||
} else {
|
||||
$store = ($config == "cookie")? "Cookie": "Session";
|
||||
$data = $store::get(self::KEY);
|
||||
$data = !empty($data)? @unserialize($data): false;
|
||||
if(!is_array($data)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check Data
|
||||
if(!array_key_exists($uid, $data)){
|
||||
return false;
|
||||
}
|
||||
return ($vote === null || $data[$uid] === $vote);
|
||||
}
|
||||
public function hasLiked($uid){
|
||||
return $this->hasVoted($uid, "like");
|
||||
}
|
||||
public function hasDisliked($uid){
|
||||
return $this->hasVoted($uid, "dislike");
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE :: ADD NEW COMMENT VOTING
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function add($uid, $vote = "like"){
|
||||
$user = $this->currentUser();
|
||||
$config = sn_config("comment_vote_storage");
|
||||
|
||||
// Database Storage
|
||||
$db = strpos($user, "bludit::") === 0 || $config === "database";
|
||||
if($db){
|
||||
if(!array_key_exists($user, $this->db)){
|
||||
$this->db[$user] = array();
|
||||
}
|
||||
if(array_key_exists($uid, $this->db[$user])){
|
||||
return false;
|
||||
}
|
||||
$this->db[$user][$uid] = $vote;
|
||||
return $this->save() !== false;
|
||||
}
|
||||
|
||||
// Cookie | Session Storage
|
||||
$store = ($config == "cookie")? "Cookie": "Session";
|
||||
$data = $store::get(self::KEY);
|
||||
$data = !empty($data)? @unserialize($data): false;
|
||||
if(is_array($data)){
|
||||
if(array_key_exists($uid, $data)){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$data = array();
|
||||
}
|
||||
$data[$uid] = $vote;
|
||||
$store::set(self::KEY, serialize($data));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE :: EDIT COMMENT VOTING
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function edit($uid, $vote = "like"){
|
||||
$user = $this->currentUser();
|
||||
$config = sn_config("comment_vote_storage");
|
||||
|
||||
// Database Storage
|
||||
$db = strpos($user, "bludit::") === 0 || $config === "database";
|
||||
if($db){
|
||||
if(!array_key_exists($user, $this->db)){
|
||||
$this->db[$user] = array();
|
||||
}
|
||||
if(array_key_exists($uid, $this->db[$user]) && $this->db[$user][$uid] === $vote){
|
||||
return false;
|
||||
}
|
||||
$this->db[$user][$uid] = $vote;
|
||||
return $this->save() !== false;
|
||||
}
|
||||
|
||||
// Cookie | Session Storage
|
||||
$store = ($config == "cookie")? "Cookie": "Session";
|
||||
$data = $store::get(self::KEY);
|
||||
$data = !empty($data)? @unserialize($data): false;
|
||||
if(is_array($data)){
|
||||
if(array_key_exists($uid, $data) && $data[$uid] === $vote){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$data = array();
|
||||
}
|
||||
$data[$uid] = $vote;
|
||||
$store::set(self::KEY, serialize($data));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE :: DELETE COMMENT VOTING
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function delete($uid){
|
||||
$user = $this->currentUser();
|
||||
$config = sn_config("comment_vote_storage");
|
||||
|
||||
// Database Storage
|
||||
$db = strpos($user, "bludit::") === 0 || $config === "database";
|
||||
if($db){
|
||||
if(!array_key_exists($user, $this->db)){
|
||||
return true;
|
||||
}
|
||||
if(!array_key_exists($uid, $this->db[$user])){
|
||||
return true;
|
||||
}
|
||||
unset($this->db[$user][$uid]);
|
||||
return $this->save() !== false;
|
||||
}
|
||||
|
||||
// Cookie | Session Storage
|
||||
$store = ($config == "cookie")? "Cookie": "Session";
|
||||
$data = $store::get(self::KEY);
|
||||
$data = !empty($data)? @unserialize($data): false;
|
||||
if(!is_array($data)){
|
||||
return true;
|
||||
}
|
||||
if(!array_key_exists($uid, $data)){
|
||||
return true;
|
||||
}
|
||||
unset($data[$uid]);
|
||||
$store::set(self::KEY, serialize($data));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE :: DELETE BY USER
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function deleteByUser($user){
|
||||
$config = sn_config("comment_vote_storage");
|
||||
|
||||
// Database Storage
|
||||
$db = strpos($user, "bludit::") === 0 || $config === "database";
|
||||
if($db){
|
||||
if(!array_key_exists($user, $this->db)){
|
||||
return true;
|
||||
}
|
||||
unset($this->db[$user]);
|
||||
return $this->save() !== false;
|
||||
}
|
||||
|
||||
// Cookie | Session Storage
|
||||
$store = ($config == "cookie")? "Cookie": "Session";
|
||||
$data = $store::get(self::KEY);
|
||||
$data = !empty($data)? @unserialize($data): false;
|
||||
if(!is_array($data)){
|
||||
return true;
|
||||
}
|
||||
$store::set(self::KEY, serialize(array()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
564
system/class.comments.php
Normal file
564
system/class.comments.php
Normal file
@@ -0,0 +1,564 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
1045
system/class.snicker.php
Normal file
1045
system/class.snicker.php
Normal file
File diff suppressed because it is too large
Load Diff
90
system/functions.php
Normal file
90
system/functions.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./system/functions.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!"); }
|
||||
|
||||
/*
|
||||
| S18N :: FORMAT AND GET STRING
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The respective string to translate.
|
||||
| @param array Some additional array for `printf()`.
|
||||
|
|
||||
| @return string The translated and formated string.
|
||||
*/
|
||||
function sn__($string, $args = array()){
|
||||
global $L;
|
||||
$hash = "s18n-" . md5(strtolower($string));
|
||||
$value = $L->g($hash);
|
||||
if($hash === $value){
|
||||
$value = $string;
|
||||
}
|
||||
return (count($args) > 0)? vsprintf($value, $args): $value;
|
||||
}
|
||||
|
||||
/*
|
||||
| S18N :: FORMAT AND PRINT STRING
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The respective string to translate.
|
||||
| @param array Some additional array for `printf()`.
|
||||
|
|
||||
| @return <print>
|
||||
*/
|
||||
function sn_e($string, $args = array()){
|
||||
print(sn__($string, $args));
|
||||
}
|
||||
|
||||
/*
|
||||
| SHORTFUNC :: GET VALUE
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @param string The respective Snicker configuration key.
|
||||
|
|
||||
| @return multi The respective value or FALSE if the option doens't exist.
|
||||
*/
|
||||
function sn_config($key){
|
||||
global $SnickerPlugin;
|
||||
return $SnickerPlugin->getValue($key);
|
||||
}
|
||||
|
||||
/*
|
||||
| SHORTFUNC :: RESPONSE
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @return die();
|
||||
*/
|
||||
function sn_response($data, $key = null){
|
||||
global $SnickerPlugin;
|
||||
return $SnickerPlugin->response($data, $key);
|
||||
}
|
||||
|
||||
/*
|
||||
| SHORTFUNC :: SELECTED
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @return die();
|
||||
*/
|
||||
function sn_selected($field, $value = true, $print = true){
|
||||
global $SnickerPlugin;
|
||||
return $SnickerPlugin->selected($field, $value, $print);
|
||||
}
|
||||
|
||||
/*
|
||||
| SHORTFUNC :: CHECKED
|
||||
| @since 0.1.0
|
||||
|
|
||||
| @return die();
|
||||
*/
|
||||
function sn_checked($field, $value = true, $print = true){
|
||||
global $SnickerPlugin;
|
||||
return $SnickerPlugin->checked($field, $value, $print);
|
||||
}
|
||||
745
themes/default/snicker.css
Normal file
745
themes/default/snicker.css
Normal file
@@ -0,0 +1,745 @@
|
||||
@charset "UTF-8";
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./system/themes/default/snicker.css
|
||||
| @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>
|
||||
*/
|
||||
|
||||
/* @start ANIMATIONs */
|
||||
@keyframes spin{
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
0% {
|
||||
transform: rotate(-359deg);
|
||||
-o-transform: rotate(-359deg);
|
||||
-ms-transform: rotate(-359deg);
|
||||
-moz-transform: rotate(-359deg);
|
||||
-webkit-transform: rotate(-359deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes spin{
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
0% {
|
||||
transform: rotate(-359deg);
|
||||
-o-transform: rotate(-359deg);
|
||||
-ms-transform: rotate(-359deg);
|
||||
-moz-transform: rotate(-359deg);
|
||||
-webkit-transform: rotate(-359deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes spin{
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
-moz-transform: rotate(0deg);
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
0% {
|
||||
transform: rotate(-359deg);
|
||||
-o-transform: rotate(-359deg);
|
||||
-ms-transform: rotate(-359deg);
|
||||
-moz-transform: rotate(-359deg);
|
||||
-webkit-transform: rotate(-359deg);
|
||||
}
|
||||
}
|
||||
/* @end ANIMATIONs */
|
||||
|
||||
/* @start GENERAL */
|
||||
.snicker-comments,
|
||||
.snicker-comments *,
|
||||
.snicker-comments *::before,
|
||||
.snicker-comments *::after{
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
}
|
||||
.snicker-comments{
|
||||
width: 100%;
|
||||
margin: 15px 0 30px 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
.snicker-comments input,
|
||||
.snicker-comments textarea,
|
||||
.snicker-comments button{
|
||||
font-family: "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
.snicker-comments input[type="text"],
|
||||
.snicker-comments input[type="email"],
|
||||
.snicker-comments textarea{
|
||||
color: #404448;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 7px 14px;
|
||||
z-index: 10;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
line-height: 1.5em;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #a0a4a8;
|
||||
background-color: #ffffff;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
.snicker-comments textarea{
|
||||
height: 150px;
|
||||
min-height: 150px;
|
||||
padding: 15px;
|
||||
resize: vertical;
|
||||
}
|
||||
.snicker-comments input[type="text"]:hover,
|
||||
.snicker-comments input[type="email"]:hover,
|
||||
.snicker-comments textarea:hover{
|
||||
color: #343A40;
|
||||
border-color: #343A40;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.snicker-comments input[type="text"]:focus,
|
||||
.snicker-comments input[type="email"]:focus,
|
||||
.snicker-comments textarea:focus{
|
||||
color: #343A40;
|
||||
border-color: #343A40;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 0 0 3px rgba(52,58,64, 0.35);
|
||||
-moz-box-shadow: 0 0 0 3px rgba(52,58,64, 0.35);
|
||||
-webkit-box-shadow: 0 0 0 3px rgba(52,58,64, 0.35);
|
||||
}
|
||||
.snicker-comments input[type="checkbox"]{
|
||||
display: none;
|
||||
}
|
||||
.snicker-comments input[type="checkbox"]+label{
|
||||
color: #606468;
|
||||
cursor: pointer;
|
||||
height: auto;
|
||||
margin: 10px 5px;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
line-height: 22px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.snicker-comments input[type="checkbox"]+label:hover,
|
||||
.snicker-comments input[type="checkbox"]:checked+label{
|
||||
color: #404448;
|
||||
}
|
||||
.snicker-comments input[type="checkbox"]+label:before{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
content: "";
|
||||
margin: 0 9px 0 0;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #a0a4a8;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
transition: all 142ms linear;
|
||||
-moz-transition: all 142ms linear;
|
||||
-webkit-transition: all 142ms linear;
|
||||
}
|
||||
.snicker-comments input[type="checkbox"]+label:hover:before{
|
||||
border-color: #343A40;
|
||||
}
|
||||
.snicker-comments input[type="checkbox"]:checked+label:before{
|
||||
background-color: #343A40;
|
||||
border-color: #343A40;
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC\
|
||||
9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDEyIDE2Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJNM\
|
||||
TIgNWwtOCA4LTQtNCAxLjUtMS41TDQgMTBsNi41LTYuNUwxMiA1eiIvPjwvc3ZnPg==");
|
||||
}
|
||||
.snicker-comments button{
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
padding: 10px 15px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
line-height: 1.5em;
|
||||
vertical-align: top;
|
||||
background-color: #343A40;
|
||||
border: 1px solid #343A40;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
transition: background 142ms linear;
|
||||
-moz-transition: background 142ms linear;
|
||||
-webkit-transition: background 142ms linear;
|
||||
}
|
||||
.snicker-comments button:hover{
|
||||
background-color: rgb(14, 16, 19);
|
||||
}
|
||||
.snicker-comments button:active{
|
||||
background-color: #242A30;
|
||||
}
|
||||
.snicker-comments button:disabled,
|
||||
.snicker-comments button.disabled{
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
cursor: not-allowed;
|
||||
background-color: #040A10;
|
||||
}
|
||||
.snicker-comments button.loading:before{
|
||||
top: 8px;
|
||||
left: -36px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
background-size: 18px;
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC\
|
||||
9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDEyIDE2Ij48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZ\
|
||||
GQiIGQ9Ik0xMC4yNCA3LjRhNC4xNSA0LjE1IDAgMCAxLTEuMiAzLjYgNC4zNDYgNC4zNDYgMCAwIDEtNS40MS41NEw0Ljgg\
|
||||
MTAuNC41IDkuOGwuNiA0LjIgMS4zMS0xLjI2YzIuMzYgMS43NCA1LjcgMS41NyA3Ljg0LS41NGE1Ljg3NiA1Ljg3NiAwIDA\
|
||||
gMCAxLjc0LTQuNDZsLTEuNzUtLjM0ek0yLjk2IDVhNC4zNDYgNC4zNDYgMCAwIDEgNS40MS0uNTRMNy4yIDUuNmw0LjMuNi\
|
||||
0uNi00LjItMS4zMSAxLjI2Yy0yLjM2LTEuNzQtNS43LTEuNTctNy44NS41NEMuNSA1LjAzLS4wNiA2LjY1LjAxIDguMjZsM\
|
||||
S43NS4zNUE0LjE3IDQuMTcgMCAwIDEgMi45NiA1eiIvPjwvc3ZnPg==");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
animation: spin 2s linear 0ms infinite;
|
||||
-moz-animation: spin 2s linear 0ms infinite;
|
||||
-webkit-animation: spin 2s linear 0ms infinite;
|
||||
}
|
||||
.snicker-comments .table{
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: table;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.snicker-comments .table .table-cell{
|
||||
display: table-cell;
|
||||
padding: 5px 10px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.snicker-comments .align-left{
|
||||
text-align: left;
|
||||
}
|
||||
.snicker-comments .align-right{
|
||||
text-align: right;
|
||||
}
|
||||
.snicker-comments .align-center{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Main Elements */
|
||||
.snicker-comments .snicker-comments-form,
|
||||
.snicker-comments .snicker-comments-list{
|
||||
width: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
border: 1px solid#c0c4c8;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
.snicker-comments .snicker-comments-list{
|
||||
border-width: 0;
|
||||
}
|
||||
.snicker-comments .no-comments,
|
||||
.snicker-comments .disabled-comments{
|
||||
width: 100%;
|
||||
margin: 15px 0;
|
||||
padding: 15px 0;
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
line-height: 50px;
|
||||
}
|
||||
.snicker-comments .no-comments{
|
||||
background-color: #f4f8fa;
|
||||
border: 1px solid #c0c4c8;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
.snicker-comments .comment-alert{
|
||||
width: 100%;
|
||||
margin: 0 0 15px 0;
|
||||
padding: 10px 15px;
|
||||
display: block;
|
||||
font-size: 85%;
|
||||
font-family: "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
background-color: #f4f8fa;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
.snicker-comments .comment-alert.alert-error{
|
||||
color: #ffffff;
|
||||
background-color: #DC3545;
|
||||
}
|
||||
.snicker-comments .comment-alert.alert-success{
|
||||
color: #ffffff;
|
||||
background-color: #28A745;
|
||||
}
|
||||
/* @end GENERAL */
|
||||
|
||||
/* @start COMMENT FORM */
|
||||
form.comment-form{
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
}
|
||||
form.comment-form .comment-header,
|
||||
form.comment-form .comment-article,
|
||||
form.comment-form .comment-footer{
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
display: block;
|
||||
}
|
||||
form.comment-form .comment-header{
|
||||
font-size: 16px;
|
||||
background-color: #f4f6f8;
|
||||
border-bottom: 1px solid #d0d4d8;
|
||||
border-radius: 5px 5px 0 0;
|
||||
-moz-border-radius: 5px 5px 0 0;
|
||||
-webkit-border-radius: 5px 5px 0 0;
|
||||
}
|
||||
form.comment-form .comment-header .inner{
|
||||
padding: 7px 10px;
|
||||
}
|
||||
form.comment-form .comment-article{
|
||||
padding: 15px 20px;
|
||||
}
|
||||
form.comment-form .comment-footer{
|
||||
background-color: #f4f6f8;
|
||||
border-top: 1px solid #d0d4d8;
|
||||
border-radius: 0 0 5px 5px;
|
||||
-moz-border-radius: 0 0 5px 5px;
|
||||
-webkit-border-radius: 0 0 5px 5px;
|
||||
}
|
||||
form.comment-form .comment-captcha{
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
form.comment-form .comment-captcha input,
|
||||
form.comment-form .comment-captcha input:hover,
|
||||
form.comment-form .comment-captcha input:focus{
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
padding: 5px 10px;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
line-height: 22px;
|
||||
vertical-align: top;
|
||||
border: 0;
|
||||
background-color: #e0e4e8;
|
||||
box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
}
|
||||
form.comment-form .comment-captcha input:hover{
|
||||
background-color: #f0f4f8;
|
||||
}
|
||||
form.comment-form .comment-captcha input:focus{
|
||||
background-color: #e0e4e8;
|
||||
}
|
||||
form.comment-form .comment-captcha a{
|
||||
width: auto;
|
||||
height: 40px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
form.comment-form .comment-captcha a:before{
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
content: "";
|
||||
z-index: 20;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.0);
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
transition: background 142ms linear;
|
||||
-moz-transition: background 142ms linear;
|
||||
-webkit-transition: background 142ms linear;
|
||||
}
|
||||
form.comment-form .comment-captcha a.reload:before{
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
form.comment-form .comment-captcha a:after{
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: -12px 0 0 -12px;
|
||||
padding: 2px;
|
||||
content: "";
|
||||
z-index: 25;
|
||||
opacity: 0;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
background-size: 20px;
|
||||
background-color: #000;
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC\
|
||||
9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDEyIDE2Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJNM\
|
||||
TAuMjQgNy40YTQuMTUgNC4xNSAwIDAgMS0xLjIgMy42IDQuMzQ2IDQuMzQ2IDAgMCAxLTUuNDEuNTRMNC44IDEwLjQuNSA5\
|
||||
LjhsLjYgNC4yIDEuMzEtMS4yNmMyLjM2IDEuNzQgNS43IDEuNTcgNy44NC0uNTRhNS44NzYgNS44NzYgMCAwIDAgMS43NC0\
|
||||
0LjQ2bC0xLjc1LS4zNHpNMi45NiA1YTQuMzQ2IDQuMzQ2IDAgMCAxIDUuNDEtLjU0TDcuMiA1LjZsNC4zLjYtLjYtNC4yLT\
|
||||
EuMzEgMS4yNmMtMi4zNi0xLjc0LTUuNy0xLjU3LTcuODUuNTRDLjUgNS4wMy0uMDYgNi42NS4wMSA4LjI2bDEuNzUuMzVBN\
|
||||
C4xNyA0LjE3IDAgMCAxIDIuOTYgNXoiLz48L3N2Zz4=");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
border-radius: 50%;
|
||||
-moz-border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
transition: opacity 142ms linear;
|
||||
-moz-transition: opacity 142ms linear;
|
||||
-webkit-transition: opacity 142ms linear;
|
||||
}
|
||||
form.comment-form .comment-captcha a.reload:after{
|
||||
opacity: 1;
|
||||
animation: spin 2s linear 0ms infinite;
|
||||
-moz-animation: spin 2s linear 0ms infinite;
|
||||
-webkit-animation: spin 2s linear 0ms infinite;
|
||||
}
|
||||
form.comment-form .comment-captcha img{
|
||||
max-width: none;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
form.comment-form .comment-reply{
|
||||
margin: 20px 0 0 0;
|
||||
padding: 10px 15px;
|
||||
display: block;
|
||||
position: relative;
|
||||
font-size: 80%;
|
||||
background-color: #f4f8fa;
|
||||
border-width: 1px 1px 0 1px;
|
||||
border-style: solid;
|
||||
border-color: #c0c4c8;
|
||||
border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
}
|
||||
form.comment-form .comment-reply + p textarea{
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
-moz-border-raidus-topleft: 0;
|
||||
-moz-border-raidus-topright: 0;
|
||||
-webkit-border-top-left-radius: 0;
|
||||
-webkit-border-top-right-radius: 0;
|
||||
}
|
||||
form.comment-form .comment-reply .reply-cancel{
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
opacity: 0.5;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
background-size: 14px;
|
||||
background-color: #555;
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC\
|
||||
9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDEyIDE2Ij48cGF0aCBmaWxsPSIjZmZmZmZmIiBkP\
|
||||
SJNNy40OCA4bDMuNzUgMy43NS0xLjQ4IDEuNDhMNiA5LjQ4bC0zLjc1IDMuNzUtMS40OC0xLjQ4TDQuNTIgOCAuNzcgNC4y\
|
||||
NWwxLjQ4LTEuNDhMNiA2LjUybDMuNzUtMy43NSAxLjQ4IDEuNDhMNy40OCA4eiIvPjwvc3ZnPg==");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
transition: all 142ms linear;
|
||||
-moz-transition: all 142ms linear;
|
||||
-webkit-transition: all 142ms linear;
|
||||
}
|
||||
form.comment-form .comment-reply .reply-cancel:hover{
|
||||
opacity: 1;
|
||||
background-color: #DC3545;
|
||||
}
|
||||
form.comment-form .comment-reply .reply-title{
|
||||
margin: 0 0 5px 0;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
font-weight: 600;
|
||||
}
|
||||
/* @end COMMENT FORM */
|
||||
|
||||
/* @start COMMENT LIST */
|
||||
.snicker-comments-list .comment{
|
||||
width: auto;
|
||||
margin: 15px 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
border: 1px solid #c0c4c8;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
.snicker-comments-list .comment.new-comment{
|
||||
border-color: #007bff;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-avatar{
|
||||
width: 110px;
|
||||
padding: 15px 10px;
|
||||
position: relative;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-avatar img{
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-avatar .avatar-role{
|
||||
top: 15px;
|
||||
right: 10px;
|
||||
color: #ffffff;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
padding: 3px 5px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
font-size: 11px;
|
||||
font-family: "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
line-height: 16px;
|
||||
font-weight: bold;
|
||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25);
|
||||
background-color: #007bff;
|
||||
border-radius: 0 4px 0 3px;
|
||||
-moz-border-radius: 0 4px 0 3px;
|
||||
-webkit-border-radius: 0 4px 0 3px;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-content{
|
||||
padding: 10px;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-content .comment-title{
|
||||
color: #707478;
|
||||
margin: 0;
|
||||
padding: 5px 5px 2px 5px;
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: bold;
|
||||
font-weight: 600;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-content .comment-moderation{
|
||||
color: #fff;
|
||||
margin-left: 10px;
|
||||
padding: 2px 6px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-weight: normal;
|
||||
font-family: "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
vertical-align: top;
|
||||
background-color: #dc3545;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-content .comment-meta{
|
||||
color: #707478;
|
||||
margin: 0;
|
||||
padding: 1px 6px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
line-height: 1.25em;
|
||||
background-color: #e0e4e8;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-content .comment-comment{
|
||||
color: #303438;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
display: block;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
background-color: #f4f6f8;
|
||||
border-top: 1px solid #d0d4d8;
|
||||
border-radius: 0 0 4px 4px;
|
||||
-moz-border-radius: 0 0 4px 4px;
|
||||
-webkit-border-radius: 0 0 4px 4px;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action a{
|
||||
color: #606468;
|
||||
margin: 2px 0;
|
||||
padding: 2px 7px;
|
||||
opacity: 0.65;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-family: "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
line-height: 18px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #c0c4c8;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
transition: color 142ms linear, background 142ms linear, border 142ms linear;
|
||||
-moz-transition: color 142ms linear, background 142ms linear, border 142ms linear;
|
||||
-webkit-transition: color 142ms linear, background 142ms linear, border 142ms linear;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action a.action-like{
|
||||
color: #28a745;
|
||||
border-color: #28a745;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action a.action-dislike{
|
||||
color: #dc3545;
|
||||
border-color: #dc3545;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action a.action-reply{
|
||||
color: #007bff;
|
||||
border-color: #007bff;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action a:hover,
|
||||
.snicker-comments-list .comment .comment-action a.active{
|
||||
color: #ffffff;
|
||||
opacity: 0.9;
|
||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action a.action-like:hover,
|
||||
.snicker-comments-list .comment .comment-action a.action-like.active{
|
||||
background-color: #28a745;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action a.action-dislike:hover,
|
||||
.snicker-comments-list .comment .comment-action a.action-dislike.active{
|
||||
background-color: #dc3545;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action a.action-reply:hover,
|
||||
.snicker-comments-list .comment .comment-action a.action-reply.active{
|
||||
background-color: #007bff;
|
||||
}
|
||||
.snicker-comments-list .comment .comment-action a span{
|
||||
margin: 0 3px 0 2px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Pagination */
|
||||
.snicker-comments-list .pagination{
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
}
|
||||
.snicker-comments-list .pagination .pagination-button{
|
||||
color: #ffffff;
|
||||
width: auto;
|
||||
min-width: 30px;
|
||||
height: 32px;
|
||||
margin: 0 3px;
|
||||
padding: 5px 10px;
|
||||
display: inline-block;
|
||||
position: static;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
line-height: 22px;
|
||||
vertical-align: top;
|
||||
background-color: #007bff;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
transition: background 142ms linear;
|
||||
-moz-transition: background 142ms linear;
|
||||
-webkit-transition: background 142ms linear;
|
||||
}
|
||||
.snicker-comments-list .pagination .pagination-button:hover,
|
||||
.snicker-comments-list .pagination .pagination-button.hover{
|
||||
background-color: #0062cc;
|
||||
}
|
||||
.snicker-comments-list .pagination .pagination-button:active,
|
||||
.snicker-comments-list .pagination .pagination-button.active{
|
||||
background-color: #004999;
|
||||
}
|
||||
.snicker-comments-list .pagination .pagination-button:disabled,
|
||||
.snicker-comments-list .pagination .pagination-button.disabled{
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
background-color: #555;
|
||||
}
|
||||
.snicker-comments-list .pagination.pagination-top .pagination-button.button-next{
|
||||
float: right;
|
||||
}
|
||||
.snicker-comments-list .pagination.pagination-bottom{
|
||||
text-align: center;
|
||||
}
|
||||
.snicker-comments-list .pagination.pagination-bottom:after{
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
.snicker-comments-list .pagination.pagination-bottom .pagination-inner{
|
||||
margin: 0 auto;
|
||||
display: inline-block;
|
||||
}
|
||||
.snicker-comments-list .pagination.pagination-bottom .pagination-button{
|
||||
float: left;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
}
|
||||
.snicker-comments-list .pagination.pagination-bottom .pagination-button.button-first,
|
||||
.snicker-comments-list .pagination.pagination-bottom .pagination-button.button-previous,
|
||||
.snicker-comments-list .pagination.pagination-bottom .pagination-button.button-next,
|
||||
.snicker-comments-list .pagination.pagination-bottom .pagination-button.button-last{
|
||||
font-size: 20px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.snicker-comments-list .pagination.pagination-bottom .pagination-button:first-child{
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
-moz-border-radius-topleft: 3px;
|
||||
-moz-border-radius-bottomleft: 3px;
|
||||
-webkit-border-top-left-radius: 3px;
|
||||
-webkit-border-bottom-left-radius: 3px;
|
||||
}
|
||||
.snicker-comments-list .pagination.pagination-bottom .pagination-button:last-child{
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
-moz-border-radius-right: 3px;
|
||||
-moz-border-radius-right: 3px;
|
||||
-webkit-border-top-right-radius: 3px;
|
||||
-webkit-border-bottom-right-radius: 3px;
|
||||
}
|
||||
/* @end COMMENT LIST */
|
||||
281
themes/default/snicker.js
Normal file
281
themes/default/snicker.js
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./system/themes/default/snicker.js
|
||||
| @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>
|
||||
*/
|
||||
;(function(root){
|
||||
"use strict";
|
||||
var w = root, d = root.document;
|
||||
|
||||
/*
|
||||
| AJAX HELPER
|
||||
*/
|
||||
function ajax(url, type, data, callback, self){
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function(){
|
||||
if(this.readyState == 4){
|
||||
callback.call((self? self: this), this.responseText, this);
|
||||
}
|
||||
};
|
||||
xhttp.open(type, url, true);
|
||||
xhttp.setRequestHeader("Cache-Control", "no-cache");
|
||||
xhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
if(type == "POST"){
|
||||
if(!(data instanceof FormData)){
|
||||
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
}
|
||||
xhttp.send(data);
|
||||
} else {
|
||||
xhttp.send();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
| ALERT HELPER
|
||||
*/
|
||||
function showAlert(type, message, parent){
|
||||
var alert = d.createElement("DIV");
|
||||
alert.className = "comment-alert alert-" + type;
|
||||
alert.innerText = message;
|
||||
|
||||
// Remove Existing Status
|
||||
var alerts = parent.querySelectorAll(".comment-alert");
|
||||
if(alerts.length > 0){
|
||||
Array.prototype.forEach.call(alerts, function(item){
|
||||
item.parentElement.removeChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
// Add Status
|
||||
if(parent.children.length > 0){
|
||||
parent.insertBefore(alert, parent.children[0]);
|
||||
} else {
|
||||
parent.appendChild(alert);
|
||||
}
|
||||
}
|
||||
|
||||
// Ready?
|
||||
d.addEventListener("DOMContentLoaded", function(){
|
||||
"use strict";
|
||||
|
||||
// Main Elements
|
||||
var form = d.querySelector("form.comment-form"),
|
||||
list = d.querySelector(".snicker-comments-list"),
|
||||
captcha = d.querySelector("a[data-captcha='reload']");
|
||||
|
||||
/*
|
||||
| HANDLE COMMENT FORM
|
||||
*/
|
||||
if(form){
|
||||
form.addEventListener("submit", function(event){
|
||||
event.preventDefault();
|
||||
if(typeof(FormData) !== "function" || !SNICKER_AJAX){
|
||||
return true;
|
||||
}
|
||||
var data = new FormData(this), self = this;
|
||||
|
||||
// Check Button
|
||||
var btn = this.querySelector("[name='snicker']");
|
||||
if(btn.disabled){
|
||||
return true;
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
// AJAX Call
|
||||
btn.disabled = true;
|
||||
btn.classList.add("loading");
|
||||
data.append("snicker", btn.value);
|
||||
ajax(SNICKER_PATH, "POST", data, function(json){
|
||||
var data = JSON.parse(json);
|
||||
|
||||
// Add Comment
|
||||
if(list && data.status == "success" && "comment" in data){
|
||||
if(list.querySelector(".comment")){
|
||||
list.querySelector(".comment").insertAdjacentHTML("beforebegin", data.comment);
|
||||
} else {
|
||||
list.insertAdjacentHTML("afterbegin", data.comment);
|
||||
}
|
||||
list.querySelector(".comment").classList.add("new-comment");
|
||||
list.querySelector(".comment").scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
|
||||
// Empty Form
|
||||
var field = self.querySelectorAll("#comment-title,#comment-text");
|
||||
for(var i = 0, l = field.length; i < l; i++){
|
||||
if(field[i].tagName == "SELECT"){
|
||||
field[i].options[0].selected = true;
|
||||
} else if(field[i].getAttribute("type") === "checkbox"){
|
||||
field[i].checked = false;
|
||||
} else {
|
||||
field[i].value = "";
|
||||
}
|
||||
}
|
||||
|
||||
showAlert("success", data.success, form.querySelector(".comment-article"));
|
||||
} else if(data.status === "error"){
|
||||
showAlert("error", data.error, form.querySelector(".comment-article"));
|
||||
|
||||
if(captcha && "captcha" in data){
|
||||
d.querySelector("input[name='comment[captcha]']").value = "";
|
||||
captcha.querySelector("img").src = data.captcha;
|
||||
}
|
||||
}
|
||||
|
||||
// Re-Enable Button
|
||||
btn.disabled = false;
|
||||
btn.classList.remove("loading");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE CAPTCHA RELOAD
|
||||
*/
|
||||
if(captcha){
|
||||
captcha.addEventListener("click", function(event){
|
||||
if(!SNICKER_AJAX){
|
||||
return false;
|
||||
}
|
||||
event.preventDefault();
|
||||
captcha.classList.add("reload");
|
||||
|
||||
var data = "action=snicker&snicker=captcha&tokenCSRF=";
|
||||
var token = d.querySelector("input[name='tokenCSRF']").value;
|
||||
ajax(SNICKER_PATH, "POST", data + token, function(json){
|
||||
var data = JSON.parse(json);
|
||||
if(data.status !== "success"){
|
||||
window.location.replace(captcha.getAttribute("href"));
|
||||
}
|
||||
captcha.querySelector("img").src = data.captcha;
|
||||
captcha.classList.remove("reload");
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE COMMENT REPLY
|
||||
*/
|
||||
if(list){
|
||||
list.addEventListener("click", function(event){
|
||||
if(event.target.tagName != "A" || !form){
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check Link
|
||||
var href = event.target.getAttribute("href");
|
||||
if(href.indexOf("snicker=reply") < 0){
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle Reply
|
||||
event.preventDefault();
|
||||
var comment = (function getComment(element){
|
||||
var parent = element.parentElement;
|
||||
return (parent.classList.contains("comment"))? parent: getComment(parent);
|
||||
})(event.target);
|
||||
|
||||
// Create Elements
|
||||
var reply = d.createElement("DIV");
|
||||
reply.className = "comment-reply";
|
||||
reply.innerHTML = '<a href="' + window.location.href + '" class="reply-cancel"></a>'
|
||||
+ '<div class="reply-title">' + comment.querySelector(".author-username").innerText + ' wrotes:</div>'
|
||||
+ '<div class="reply-content">' + comment.querySelector(".comment-comment").innerHTML + '</div>';
|
||||
var parent = d.createElement("INPUT");
|
||||
parent.type = "hidden";
|
||||
parent.name = "comment[parent_uid]";
|
||||
parent.value = comment.id.replace("comment-", "");
|
||||
|
||||
// Append Cancel
|
||||
reply.querySelector(".reply-cancel").addEventListener("click", function(event){
|
||||
event.preventDefault();
|
||||
|
||||
// Remove Elements
|
||||
reply.parentElement.removeChild(reply);
|
||||
parent.parentElement.removeChild(parent);
|
||||
|
||||
// Switch Button Text
|
||||
var old = form.querySelector("button").innerText;
|
||||
form.querySelector("button").value = "comment";
|
||||
form.querySelector("button").innerText = form.querySelector("button").getAttribute("data-string");
|
||||
form.querySelector("button").setAttribute("data-string", old);
|
||||
});
|
||||
|
||||
// Inject Elements
|
||||
var art = form.querySelector(".comment-article");
|
||||
if(art.querySelector(".comment-reply")){
|
||||
art.replaceChild(reply, art.querySelector(".comment-reply"));
|
||||
} else {
|
||||
art.insertBefore(reply, form.querySelector("textarea").parentElement);
|
||||
}
|
||||
|
||||
var foo = form.querySelector(".comment-footer");
|
||||
if(foo.querySelector("input[name='comment[parent_uid]']")){
|
||||
foo.replaceChild(parent, foo.querySelector("input[name='comment[parent_uid]']"));
|
||||
} else {
|
||||
foo.appendChild(parent);
|
||||
}
|
||||
|
||||
// Switch Button Text
|
||||
var old = form.querySelector("button").innerText;
|
||||
form.querySelector("button").value = "reply";
|
||||
form.querySelector("button").innerText = form.querySelector("button").getAttribute("data-string");
|
||||
form.querySelector("button").setAttribute("data-string", old);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
| HANDLE COMMENT RATING
|
||||
*/
|
||||
if(list){
|
||||
list.addEventListener("click", function(event){
|
||||
if(event.target.tagName != "A" || !SNICKER_AJAX){
|
||||
return true;
|
||||
}
|
||||
if(event.target.classList.contains("disabled")){
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check Link
|
||||
var href = event.target.getAttribute("href");
|
||||
if(href.indexOf("&type=like") < 0 && href.indexOf("&type=dislike") < 0){
|
||||
return true;
|
||||
}
|
||||
|
||||
// Event Handler
|
||||
event.preventDefault();
|
||||
event.target.classList.add("disabled");
|
||||
var comment = (function getComment(element){
|
||||
var parent = element.parentElement;
|
||||
return (parent.classList.contains("comment"))? parent: getComment(parent);
|
||||
})(event.target), self = event.target;
|
||||
href = href.split("?");
|
||||
|
||||
// AJAX REQUEST
|
||||
ajax(SNICKER_PATH, "POST", href[1], function(json){
|
||||
var data = JSON.parse(json);
|
||||
|
||||
if(data.status === "success" && "rating" in data){
|
||||
var like = comment.querySelector("[data-snicker='like']");
|
||||
if(like){
|
||||
like.innerText = String(data.rating[0]);
|
||||
}
|
||||
like.parentElement.classList[(like.parentElement == self? "add": "remove")]("active");
|
||||
|
||||
var dislike = comment.querySelector("[data-snicker='dislike']");
|
||||
if(dislike){
|
||||
dislike.innerText = String(data.rating[1]);
|
||||
}
|
||||
dislike.parentElement.classList[(dislike.parentElement == self? "add": "remove")]("active");
|
||||
}
|
||||
|
||||
// Re-Enable Button
|
||||
self.classList.remove("disabled");
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
})(window);
|
||||
311
themes/default/snicker.php
Normal file
311
themes/default/snicker.php
Normal file
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
/*
|
||||
| Snicker The first native FlatFile Comment Plugin 4 Bludit
|
||||
| @file ./system/themes/default/snicker.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 Default_SnickerTemplate extends CommentsTheme{
|
||||
const SNICKER_NAME = "Default Theme";
|
||||
const SNICKER_JS = "snicker.js";
|
||||
const SNICKER_CSS = "snicker.css";
|
||||
|
||||
/*
|
||||
| RENDER :: COMMENT FORM
|
||||
| @since 0.1.0
|
||||
| @update 0.1.1
|
||||
*/
|
||||
public function form($username = "", $email = "", $title = "", $message = ""){
|
||||
global $comments, $login, $page, $security, $Snicker;
|
||||
|
||||
// User Logged In
|
||||
if(!is_a($login, "Login")){
|
||||
$login = new Login;
|
||||
}
|
||||
$user = $login->isLogged();
|
||||
|
||||
// Get Data
|
||||
if(empty($security->getTokenCSRF())){
|
||||
$security->generateTokenCSRF();
|
||||
}
|
||||
$captcha = ($user)? "disabled": sn_config("frontend_captcha");
|
||||
$terms = ($user)? "disabled": sn_config("frontend_terms");
|
||||
|
||||
// Is Reply
|
||||
$reply = isset($_GET["snicker"]) && $_GET["snicker"] == "reply";
|
||||
if($reply && isset($_GET["uid"]) && $comments->exists($_GET["uid"])){
|
||||
$reply = new Comment($_GET["uid"], $page->uuid());
|
||||
}
|
||||
?>
|
||||
<form class="comment-form" method="post" action="<?php echo $page->permalink(); ?>?snicker=comment#snicker">
|
||||
<?php if(is_array($username)){ ?>
|
||||
<div class="comment-header">
|
||||
<input type="hidden" id="comment-user" name="comment[user]" value="<?php echo $username[0]; ?>" />
|
||||
<input type="hidden" id="comment-token" name="comment[token]" value="<?php echo $username[1]; ?>" />
|
||||
<div class="inner">
|
||||
<?php sn_e("Logged in as %s (%s)", array("<b>" . $username[2] . "</b>", $username[0])); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<div class="comment-header">
|
||||
<div class="table">
|
||||
<div class="table-cell align-left">
|
||||
<input type="text" id="comment-user" name="comment[username]" value="<?php echo $username; ?>" placeholder="<?php sn_e("Your Username"); ?>" />
|
||||
</div>
|
||||
<div class="table-cell align-right">
|
||||
<input type="email" id="comment-mail" name="comment[email]" value="<?php echo $email; ?>" placeholder="<?php sn_e("Your eMail address"); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="comment-article">
|
||||
<?php if(Alert::get("snicker-alert") !== false){ ?>
|
||||
<div class="comment-alert alert-error">
|
||||
<?php Alert::p("snicker-alert"); ?>
|
||||
</div>
|
||||
<?php } else if(Alert::get("snicker-success") !== false){ ?>
|
||||
<div class="comment-alert alert-success">
|
||||
<?php Alert::p("snicker-success"); ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if($title !== false){ ?>
|
||||
<p>
|
||||
<input type="text" id="comment-title" name="comment[title]" value="<?php echo $title; ?>" placeholder="<?php sn_e("Comment Title"); ?>" />
|
||||
</p>
|
||||
<?php } ?>
|
||||
<p>
|
||||
<textarea id="comment-text" name="comment[comment]" placeholder="<?php sn_e("Your Comment..."); ?>"><?php echo $message; ?></textarea>
|
||||
</p>
|
||||
<?php if($captcha !== "disabled"){ ?>
|
||||
<div class="comment-captcha">
|
||||
<input type="text" name="comment[captcha]" value="" placeholder="<?php sn_e("Answer"); ?>" />
|
||||
|
||||
<a href="<?php echo $page->permalink(); ?>#snicker-comment-form" data-captcha="reload">
|
||||
<?php echo $Snicker->generateCaptcha(); ?>
|
||||
</a>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if(is_a($reply, "Comment")){ ?>
|
||||
<div class="comment-reply">
|
||||
<a href="<?php echo $page->permalink(); ?>" class="reply-cancel"></a>
|
||||
<div class="reply-title">
|
||||
<?php echo $reply->username(); ?> <?php sn_e("wrotes"); ?>:
|
||||
</div>
|
||||
<div class="reply-content">
|
||||
<?php echo $reply->comment(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<div class="comment-footer">
|
||||
<div class="table">
|
||||
<div class="table-cell align-left">
|
||||
<?php if($terms === "default"){ ?>
|
||||
<div class="terms-of-use">
|
||||
<input type="checkbox" id="comment-terms" name="comment[terms]" value="1" />
|
||||
<label for="comment-terms">
|
||||
<?php echo sn_config("string_terms_of_use"); ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php } else if($terms !== "disabled"){ ?>
|
||||
<div class="terms-of-use">
|
||||
<input type="checkbox" id="comment-terms" name="comment[terms]" value="1" />
|
||||
<label for="comment-terms">
|
||||
<?php sn_e("I agree the %s!", array('<a href="" target="_blank">'.sn__("Terms of Use").'</a>')); ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="table-cell align-right">
|
||||
<input type="hidden" name="tokenCSRF" value="<?php echo $security->getTokenCSRF(); ?>" />
|
||||
<input type="hidden" name="comment[page_uuid]" value="<?php echo $page->uuid(); ?>" />
|
||||
<input type="hidden" name="action" value="snicker" />
|
||||
<?php if(is_a($reply, "Comment")){ ?>
|
||||
<input type="hidden" name="comment[parent_uid]" value="<?php echo $reply->uid(); ?>" />
|
||||
<button name="snicker" value="reply" data-string="<?php sn_e("Comment"); ?>"><?php sn_e("Answer"); ?></button>
|
||||
<?php } else { ?>
|
||||
<button name="snicker" value="comment" data-string="<?php sn_e("Answer"); ?>"><?php sn_e("Comment"); ?></button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
|
||||
unset($_SESSION["s_snicker-alert"]); // Remove Snicker Alerts
|
||||
unset($_SESSION["s_snicker-success"]); // Remove Snicker Success
|
||||
}
|
||||
|
||||
/*
|
||||
| RENDER :: PAGINATION
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function pagination($location, $cpage, $limit, $count){
|
||||
global $url;
|
||||
|
||||
// Data
|
||||
$link = DOMAIN . $url->uri() . "?cpage=%d#snicker-comments-list";
|
||||
$maxpages = (int) ceil($count / $limit);
|
||||
$prev = ($cpage === 1)? false: $cpage - 1;
|
||||
$next = ($cpage === $maxpages)? false: $cpage + 1;
|
||||
|
||||
// Top Position
|
||||
if($location === "top"){
|
||||
?>
|
||||
<div class="pagination pagination-top">
|
||||
<?php if($cpage === 1){ ?>
|
||||
<span class="pagination-button button-previous disabled"><?php sn_e("Previous Comments"); ?></span>
|
||||
<?php } else { ?>
|
||||
<a href="<?php printf($link, $prev); ?>" class="pagination-button button-previous"><?php sn_e("Previous Comments"); ?></a>
|
||||
<?php } ?>
|
||||
|
||||
<?php if($cpage < $maxpages){ ?>
|
||||
<a href="<?php printf($link, $next); ?>" class="pagination-button button-next"><?php sn_e("Next Comments"); ?></a>
|
||||
<?php } else { ?>
|
||||
<span class="pagination-button button-next disabled"><?php sn_e("Next Comments"); ?></span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// Bottom Position
|
||||
if($location === "bottom"){
|
||||
?>
|
||||
<div class="pagination pagination-bottom">
|
||||
<div class="pagination-inner">
|
||||
<?php if($prev === false){ ?>
|
||||
<span class="pagination-button button-first disabled">«</span>
|
||||
<span class="pagination-button button-previous disabled">‹</span>
|
||||
<?php } else { ?>
|
||||
<a href="<?php printf($link, 1); ?>" class="pagination-button button-first">«</a>
|
||||
<a href="<?php printf($link, $prev); ?>" class="pagination-button button-previous">‹</a>
|
||||
<?php } ?>
|
||||
|
||||
<?php
|
||||
if($maxpages < 6){
|
||||
$start = 1;
|
||||
$stop = $maxpages;
|
||||
} else {
|
||||
$start = ($cpage > 3)? $cpage - 3: $cpage;
|
||||
$stop = ($cpage + 3 < $maxpages)? $cpage + 3: $maxpages;
|
||||
}
|
||||
|
||||
if($start > 1){
|
||||
?><span class="pagination-button button-sep disabled">...</span><?php
|
||||
}
|
||||
for($i = $start; $i <= $stop; $i++){
|
||||
$active = ($i == $cpage)? "active": "";
|
||||
?>
|
||||
<a href="<?php printf($link, $i); ?>" class="pagination-button button-number <?php echo $active; ?>"><?php echo $i; ?></a>
|
||||
<?php
|
||||
}
|
||||
if($stop < $maxpages){
|
||||
?><span class="pagination-button button-sep disabled">...</span><?php
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if($next !== false){ ?>
|
||||
<a href="<?php printf($link, $next); ?>" class="pagination-button button-next">›</a>
|
||||
<a href="<?php printf($link, $maxpages); ?>" class="pagination-button button-last">»</a>
|
||||
<?php } else { ?>
|
||||
<span class="pagination-button button-next disabled">›</span>
|
||||
<span class="pagination-button button-last disabled">»</span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
| RENDER :: COMMENT
|
||||
| @since 0.1.0
|
||||
*/
|
||||
public function comment($comment, $uid, $depth){
|
||||
global $users, $security, $Snicker, $SnickerUsers;
|
||||
|
||||
// Get Page
|
||||
$page = new Page($comment->page_key());
|
||||
$user = $SnickerUsers->getByString($comment->getValue("author"));
|
||||
|
||||
// Render
|
||||
$token = $security->getTokenCSRF();
|
||||
$maxdepth = (int) sn_config("comment_depth");
|
||||
$url = $page->permalink() . "?action=snicker&snicker=rate&&uid=%s&tokenCSRF=%s";
|
||||
$url = sprintf($url, $comment->uid(), $token);
|
||||
?>
|
||||
<div id="comment-<?php echo $comment->uid(); ?>" class="comment" style="margin-left: <?php echo (15 * ($depth - 1)); ?>px;">
|
||||
<div class="table">
|
||||
<div class="table-cell comment-avatar">
|
||||
<?php echo $comment->avatar(90); ?>
|
||||
<?php
|
||||
if(isset($user["role"]) && $user["username"] === $page->username()){
|
||||
echo '<span class="avatar-role">Author</span>';
|
||||
} else if(isset($user["role"]) && $user["role"] === "admin"){
|
||||
echo '<span class="avatar-role">Admin</span>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div class="table-cell comment-content">
|
||||
<?php if(sn_config("comment_title") !== "disabled" && !empty($comment->title())){ ?>
|
||||
<div class="comment-title">
|
||||
<?php echo $comment->title(); ?>
|
||||
<?php if($comment->status() === "pending"){ ?>
|
||||
<span class="comment-moderation"><?php sn_e("This comment hasn't been moderated yet!"); ?></span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } else if($comment->status() === "pending"){ ?>
|
||||
<div class="comment-moderation"><?php sn_e("This comment hasn't been moderated yet!"); ?></div>
|
||||
<?php } ?>
|
||||
<div class="comment-meta">
|
||||
<span class="meta-author">
|
||||
<?php sn_e("Written by %s", array('<span class="author-username">'.$user["username"].'</span>')); ?>
|
||||
</span>
|
||||
<span class="meta-date">
|
||||
<?php sn_e("on %s", array($comment->date())); ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="comment-comment">
|
||||
<?php echo $comment->comment(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comment-action">
|
||||
<div class="table">
|
||||
<div class="table-cell align-left">
|
||||
<?php if(sn_config("comment_enable_like")){ ?>
|
||||
<a href="<?php echo $url; ?>&type=like" class="action-like <?php echo ($Snicker->hasLiked($comment->uid())? "active": ""); ?>">
|
||||
<?php sn_e("Like"); ?> <span data-snicker="like"><?php echo $comment->like(); ?></span>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php if(sn_config("comment_enable_dislike")){ ?>
|
||||
<a href="<?php echo $url; ?>&type=dislike" class="action-dislike <?php echo ($Snicker->hasDisliked($comment->uid())? "active": ""); ?>">
|
||||
<?php sn_e("Dislike"); ?> <span data-snicker="dislike"><?php echo $comment->dislike(); ?></span>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="table-cell align-right">
|
||||
<?php if($maxdepth === 0 || $maxdepth > $comment->depth()){ ?>
|
||||
<a href="<?php echo $page->permalink(); ?>?snicker=reply&uid=<?php echo $comment->key(); ?>#snicker-comments-form" class="action-reply">
|
||||
<?php sn_e("Reply"); ?>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user