52 changed files with 10720 additions and 0 deletions
@ -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/ |
@ -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 |
@ -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 |
||||
|
|
||||
|
?> |
@ -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 */ |
@ -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> |
@ -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 |
||||
|
} |
@ -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> |
@ -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> |
@ -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> |
@ -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); |
@ -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; |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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'); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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] |
||||
|
]; |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
|
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
}); |
After Width: | Height: | Size: 5.9 KiB |
@ -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; |
||||
|
} |
||||
|
})(); |
@ -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; |
||||
|
} |
||||
|
})(); |
@ -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!" |
||||
|
} |
||||
|
} |
@ -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" |
||||
|
} |
@ -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" |
||||
|
} |
@ -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" |
||||
|
} |
@ -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": "پاسخ" |
||||
|
} |
@ -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éé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" |
||||
|
} |
@ -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": "" |
||||
|
} |
@ -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()); |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
File diff suppressed because it is too large
@ -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); |
||||
|
} |
@ -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 */ |
@ -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); |
@ -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 |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue