bayes 1.0: cleanup and code update version
This commit is contained in:
parent
8c917ab315
commit
f669be9b2b
26 changed files with 1730 additions and 3149 deletions
|
@ -1,4 +1,11 @@
|
||||||
0.5.6: fix possible SQL exposure in email field of comment
|
1.0:
|
||||||
|
* Update from the forked old b8 version to a current
|
||||||
|
version of that library
|
||||||
|
* Code cleanup
|
||||||
|
* Remove seldomly used functions and legacy code, including
|
||||||
|
most of the backend UI
|
||||||
|
* Rewrite javascript used in the backend (for marking spam
|
||||||
|
and ham comments) to be a minimal block of jQuery code
|
||||||
|
|
||||||
0.5.5: Translation fixes (German).
|
0.5.5: Translation fixes (German).
|
||||||
|
|
||||||
|
|
|
@ -1,195 +0,0 @@
|
||||||
#bayesNav {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
#bayesNav a {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
#bayesNav li:last-child a {
|
|
||||||
padding-right: 0.3em;
|
|
||||||
}
|
|
||||||
#bayesNav ul {
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 1px solid;
|
|
||||||
}
|
|
||||||
#bayesNav li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.5em;
|
|
||||||
border-right: 1px solid;
|
|
||||||
}
|
|
||||||
#bayesNav h3 {
|
|
||||||
display: inline;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
#bayesContent {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
#bayesControls * {
|
|
||||||
margin-left: 1em;
|
|
||||||
margin-right: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#bayesLearnTable {
|
|
||||||
padding-top: 1em;
|
|
||||||
margin-bottom: 0.8em;
|
|
||||||
}
|
|
||||||
#bayesLearnTable td {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
#bayesControls {
|
|
||||||
float: right;
|
|
||||||
border: 1px solid;
|
|
||||||
border-top: 0;
|
|
||||||
-moz-border-radius-bottomleft: 5px;
|
|
||||||
-webkit-border-bottom-left-radius: 5px;
|
|
||||||
-moz-border-radius-bottomright: 5px;
|
|
||||||
-webkit-border-bottom-right-radius: 5px;
|
|
||||||
max-width: 21%;
|
|
||||||
padding-top: 1em;
|
|
||||||
}
|
|
||||||
#bayesControls form {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
#bayesDatabase {
|
|
||||||
padding-top: 2em;
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
#bayesSavedValues {
|
|
||||||
padding-top: 2em;
|
|
||||||
}
|
|
||||||
#bayesDatabaseTable th {
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
border: 1px solid;
|
|
||||||
}
|
|
||||||
caption {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
#bayesSavedValuesTable td {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#bayesRecyclerTable {
|
|
||||||
padding-top: 2em;
|
|
||||||
width: 78%;
|
|
||||||
table-layout: fixed;
|
|
||||||
|
|
||||||
}
|
|
||||||
#bayesRecyclerTable td {
|
|
||||||
padding-top: 1em;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
#bayesRecyclerTable th.select {
|
|
||||||
text-align: center;
|
|
||||||
width: 2em;
|
|
||||||
}
|
|
||||||
#bayesRecyclerTable td.select {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.ratingBox {
|
|
||||||
border-bottom: 1px solid grey;
|
|
||||||
}
|
|
||||||
.commentPart {
|
|
||||||
margin-left: 5em;
|
|
||||||
}
|
|
||||||
.rating {
|
|
||||||
margin-left: 5em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.commentType {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.finalRating {
|
|
||||||
padding-left: 3.3em;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.5em;
|
|
||||||
border-bottom: 1px solid grey;
|
|
||||||
}
|
|
||||||
#bayesAnalysisList li {
|
|
||||||
margin: 1em;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.serendipityIconLinkRight {
|
|
||||||
left: 90%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
#bayesControls label, #bayesControls input {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
input[type="submit"] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.bayesTrojaButtons {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
fieldset {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
#trojaImport {
|
|
||||||
margin-left: 1.1em;
|
|
||||||
}
|
|
||||||
#bayesDatabaseTablePagination {
|
|
||||||
font-size: 0.8em;
|
|
||||||
margin-top: 0.2em;
|
|
||||||
}
|
|
||||||
a.curpage {
|
|
||||||
border: 1px dotted black !important;
|
|
||||||
}
|
|
||||||
#bayesDatabaseTablePagination a {
|
|
||||||
border: 1px solid black;
|
|
||||||
padding: 1px;
|
|
||||||
}
|
|
||||||
#bayesRecycler, #bayesAnalysis {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
#bayesRecycler {
|
|
||||||
max-width: 80%;
|
|
||||||
}
|
|
||||||
.bayesRecyclerSummary {
|
|
||||||
display: inline;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.bayesRecyclerSummary td{
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.bayesRecyclerItem {
|
|
||||||
padding-top: 1em;
|
|
||||||
}
|
|
||||||
.bayesRecyclerList {
|
|
||||||
margin-left: 3em;
|
|
||||||
}
|
|
||||||
.bayesRecyclerList dt {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.bayesRecyclerList dt:after {
|
|
||||||
content: ":";
|
|
||||||
}
|
|
||||||
.bayesRecyclerList dt+dd {
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
.bayesRecyclerTableNavigation {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
.bayesAnalysisTableNavigation {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
#bayesAnalysisButton {
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
summary {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
406
serendipity_event_spamblock_bayes/b8/b8.php
Normal file
406
serendipity_event_spamblock_bayes/b8/b8.php
Normal file
|
@ -0,0 +1,406 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/* Copyright (C) 2006-2019 Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
|
||||||
|
b8 - A statistical ("Bayesian") spam filter written in PHP
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation in version 2.1 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software Foundation,
|
||||||
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The b8 spam filter library
|
||||||
|
*
|
||||||
|
* @license LGPL 2.1
|
||||||
|
* @package b8
|
||||||
|
* @author Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
* @author Oliver Lillie <ollie@buggedcom.co.uk> (original PHP 5 port)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace b8;
|
||||||
|
|
||||||
|
spl_autoload_register(
|
||||||
|
function ($class) {
|
||||||
|
$parts = explode('\\', $class);
|
||||||
|
if ($parts[1]) {
|
||||||
|
require_once __DIR__ . DIRECTORY_SEPARATOR . $parts[1] . DIRECTORY_SEPARATOR . $parts[2] . '.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
class b8
|
||||||
|
{
|
||||||
|
const DBVERSION = 3;
|
||||||
|
|
||||||
|
const SPAM = 'spam';
|
||||||
|
const HAM = 'ham';
|
||||||
|
const LEARN = 'learn';
|
||||||
|
const UNLEARN = 'unlearn';
|
||||||
|
|
||||||
|
const CLASSIFIER_TEXT_MISSING = 'CLASSIFIER_TEXT_MISSING';
|
||||||
|
|
||||||
|
const TRAINER_TEXT_MISSING = 'TRAINER_TEXT_MISSING';
|
||||||
|
const TRAINER_CATEGORY_MISSING = 'TRAINER_CATEGORY_MISSING';
|
||||||
|
const TRAINER_CATEGORY_FAIL = 'TRAINER_CATEGORY_FAIL';
|
||||||
|
|
||||||
|
const INTERNALS_TEXTS = 'b8*texts';
|
||||||
|
const INTERNALS_DBVERSION = 'b8*dbversion';
|
||||||
|
|
||||||
|
const KEY_DB_VERSION = 'dbversion';
|
||||||
|
const KEY_COUNT_HAM = 'count_ham';
|
||||||
|
const KEY_COUNT_SPAM = 'count_spam';
|
||||||
|
const KEY_TEXTS_HAM = 'texts_ham';
|
||||||
|
const KEY_TEXTS_SPAM = 'texts_spam';
|
||||||
|
|
||||||
|
private $config = [ 'lexer' => 'standard',
|
||||||
|
'degenerator' => 'standard',
|
||||||
|
'storage' => 'dba',
|
||||||
|
'use_relevant' => 15,
|
||||||
|
'min_dev' => 0.2,
|
||||||
|
'rob_s' => 0.3,
|
||||||
|
'rob_x' => 0.5 ];
|
||||||
|
|
||||||
|
private $storage = null;
|
||||||
|
private $lexer = null;
|
||||||
|
private $degenerator = null;
|
||||||
|
private $token_data = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs b8
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param array b8's configuration: [ 'lexer' => string,
|
||||||
|
'degenerator' => string,
|
||||||
|
'storage' => string,
|
||||||
|
'use_relevant' => int,
|
||||||
|
'min_dev' => float,
|
||||||
|
'rob_s' => float,
|
||||||
|
'rob_x' => float ]
|
||||||
|
* @param array The storage backend's config (depending on the backend used)
|
||||||
|
* @param array The lexer's config (depending on the lexer used)
|
||||||
|
* @param array The degenerator's config (depending on the degenerator used)
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function __construct(array $config = [],
|
||||||
|
array $config_storage = [],
|
||||||
|
array $config_lexer = [],
|
||||||
|
array $config_degenerator = [])
|
||||||
|
{
|
||||||
|
// Validate config data
|
||||||
|
foreach ($config as $name => $value) {
|
||||||
|
switch ($name) {
|
||||||
|
case 'min_dev':
|
||||||
|
case 'rob_s':
|
||||||
|
case 'rob_x':
|
||||||
|
$this->config[$name] = (float) $value;
|
||||||
|
break;
|
||||||
|
case 'use_relevant':
|
||||||
|
$this->config[$name] = (int) $value;
|
||||||
|
break;
|
||||||
|
case 'lexer':
|
||||||
|
case 'degenerator':
|
||||||
|
case 'storage':
|
||||||
|
$this->config[$name] = (string) $value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \Exception(b8::class . ": Unknown configuration key: \"$name\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the degenerator class
|
||||||
|
$class = '\\b8\\degenerator\\' . $this->config['degenerator'];
|
||||||
|
$this->degenerator = new $class($config_degenerator);
|
||||||
|
|
||||||
|
// Setup the lexer class
|
||||||
|
$class = '\\b8\\lexer\\' . $this->config['lexer'];
|
||||||
|
$this->lexer = new $class($config_lexer);
|
||||||
|
|
||||||
|
// Setup the storage backend
|
||||||
|
$class = '\\b8\\storage\\' . $this->config['storage'];
|
||||||
|
$this->storage = new $class($config_storage, $this->degenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classifies a text
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string The text to classify
|
||||||
|
* @return mixed float The rating between 0 (ham) and 1 (spam) or an error code
|
||||||
|
*/
|
||||||
|
public function classify(string $text = null)
|
||||||
|
{
|
||||||
|
// Let's first see if the user called the function correctly
|
||||||
|
if ($text === null) {
|
||||||
|
return \b8\b8::CLASSIFIER_TEXT_MISSING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the internal database variables, containing the number of ham and spam texts so the
|
||||||
|
// spam probability can be calculated in relation to them
|
||||||
|
$internals = $this->storage->get_internals();
|
||||||
|
|
||||||
|
// Calculate the spaminess of all tokens
|
||||||
|
|
||||||
|
// Get all tokens we want to rate
|
||||||
|
$tokens = $this->lexer->get_tokens($text);
|
||||||
|
|
||||||
|
// Check if the lexer failed (if so, $tokens will be a lexer error code, if not, $tokens
|
||||||
|
// will be an array)
|
||||||
|
if (! is_array($tokens)) {
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch all available data for the token set from the database
|
||||||
|
$this->token_data = $this->storage->get(array_keys($tokens));
|
||||||
|
|
||||||
|
// Calculate the spaminess and importance for each token (or a degenerated form of it)
|
||||||
|
|
||||||
|
$word_count = [];
|
||||||
|
$rating = [];
|
||||||
|
$importance = [];
|
||||||
|
|
||||||
|
foreach ($tokens as $word => $count) {
|
||||||
|
$word_count[$word] = $count;
|
||||||
|
|
||||||
|
// Although we only call this function only here ... let's do the calculation stuff in a
|
||||||
|
// function to make this a bit less confusing ;-)
|
||||||
|
$rating[$word] = $this->get_probability($word, $internals);
|
||||||
|
$importance[$word] = abs(0.5 - $rating[$word]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order by importance
|
||||||
|
arsort($importance);
|
||||||
|
reset($importance);
|
||||||
|
|
||||||
|
// Get the most interesting tokens (use all if we have less than the given number)
|
||||||
|
$relevant = [];
|
||||||
|
for ($i = 0; $i < $this->config['use_relevant']; $i++) {
|
||||||
|
if ($token = key($importance)) {
|
||||||
|
// Important tokens remain
|
||||||
|
|
||||||
|
// If the token's rating is relevant enough, use it
|
||||||
|
if (abs(0.5 - $rating[$token]) > $this->config['min_dev']) {
|
||||||
|
// Tokens that appear more than once also count more than once
|
||||||
|
for ($x = 0, $l = $word_count[$token]; $x < $l; $x++) {
|
||||||
|
array_push($relevant, $rating[$token]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We have less words as we want to use, so we already use what we have and can
|
||||||
|
// break here
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
next($importance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the spaminess of the text (thanks to Mr. Robinson ;-)
|
||||||
|
|
||||||
|
// We set both haminess and spaminess to 1 for the first multiplying
|
||||||
|
$haminess = 1;
|
||||||
|
$spaminess = 1;
|
||||||
|
|
||||||
|
// Consider all relevant ratings
|
||||||
|
foreach ($relevant as $value) {
|
||||||
|
$haminess *= (1.0 - $value);
|
||||||
|
$spaminess *= $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no token was good for calculation, we really don't know how to rate this text, so
|
||||||
|
// we can return 0.5 without further calculations.
|
||||||
|
if ($haminess == 1 && $spaminess == 1) {
|
||||||
|
return 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the combined rating
|
||||||
|
|
||||||
|
// Get the number of relevant ratings
|
||||||
|
$n = count($relevant);
|
||||||
|
|
||||||
|
// The actual haminess and spaminess
|
||||||
|
$haminess = 1 - pow($haminess, (1 / $n));
|
||||||
|
$spaminess = 1 - pow($spaminess, (1 / $n));
|
||||||
|
|
||||||
|
// Calculate the combined indicator
|
||||||
|
$probability = ($haminess - $spaminess) / ($haminess + $spaminess);
|
||||||
|
|
||||||
|
// We want a value between 0 and 1, not between -1 and +1, so ...
|
||||||
|
$probability = (1 + $probability) / 2;
|
||||||
|
|
||||||
|
// Alea iacta est
|
||||||
|
return $probability;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the spaminess of a single token also considering "degenerated" versions
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string The word to rate
|
||||||
|
* @param array The "internals" array
|
||||||
|
* @return float The word's rating
|
||||||
|
*/
|
||||||
|
private function get_probability(string $word, array $internals)
|
||||||
|
{
|
||||||
|
// Let's see what we have!
|
||||||
|
if (isset($this->token_data['tokens'][$word])) {
|
||||||
|
// The token is in the database, so we can use it's data as-is and calculate the
|
||||||
|
// spaminess of this token directly
|
||||||
|
return $this->calculate_probability($this->token_data['tokens'][$word], $internals);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The token was not found, so do we at least have similar words?
|
||||||
|
if (isset($this->token_data['degenerates'][$word])) {
|
||||||
|
// We found similar words, so calculate the spaminess for each one and choose the most
|
||||||
|
// important one for the further calculation
|
||||||
|
|
||||||
|
// The default rating is 0.5 simply saying nothing
|
||||||
|
$rating = 0.5;
|
||||||
|
|
||||||
|
foreach ($this->token_data['degenerates'][$word] as $degenerate => $count) {
|
||||||
|
// Calculate the rating of the current degenerated token
|
||||||
|
$rating_tmp = $this->calculate_probability($count, $internals);
|
||||||
|
|
||||||
|
// Is it more important than the rating of another degenerated version?
|
||||||
|
if(abs(0.5 - $rating_tmp) > abs(0.5 - $rating)) {
|
||||||
|
$rating = $rating_tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rating;
|
||||||
|
} else {
|
||||||
|
// The token is really unknown, so choose the default rating for completely unknown
|
||||||
|
// tokens. This strips down to the robX parameter so we can cheap out the freaky math
|
||||||
|
// ;-)
|
||||||
|
return $this->config['rob_x'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do the actual spaminess calculation of a single token
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param array The token's data [ \b8\b8::KEY_COUNT_HAM => int,
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => int ]
|
||||||
|
* @param array The "internals" array
|
||||||
|
* @return float The rating
|
||||||
|
*/
|
||||||
|
private function calculate_probability(array $data, array $internals)
|
||||||
|
{
|
||||||
|
// Calculate the basic probability as proposed by Mr. Graham
|
||||||
|
|
||||||
|
// But: consider the number of ham and spam texts saved instead of the number of entries
|
||||||
|
// where the token appeared to calculate a relative spaminess because we count tokens
|
||||||
|
// appearing multiple times not just once but as often as they appear in the learned texts.
|
||||||
|
|
||||||
|
$rel_ham = $data[\b8\b8::KEY_COUNT_HAM];
|
||||||
|
$rel_spam = $data[\b8\b8::KEY_COUNT_SPAM];
|
||||||
|
|
||||||
|
if ($internals[\b8\b8::KEY_TEXTS_HAM] > 0) {
|
||||||
|
$rel_ham = $data[\b8\b8::KEY_COUNT_HAM] / $internals[\b8\b8::KEY_TEXTS_HAM];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($internals[\b8\b8::KEY_TEXTS_SPAM] > 0) {
|
||||||
|
$rel_spam = $data[\b8\b8::KEY_COUNT_SPAM] / $internals[\b8\b8::KEY_TEXTS_SPAM];
|
||||||
|
}
|
||||||
|
|
||||||
|
$rating = $rel_spam / ($rel_ham + $rel_spam);
|
||||||
|
|
||||||
|
// Calculate the better probability proposed by Mr. Robinson
|
||||||
|
$all = $data[\b8\b8::KEY_COUNT_HAM] + $data[\b8\b8::KEY_COUNT_SPAM];
|
||||||
|
return (($this->config['rob_s'] * $this->config['rob_x']) + ($all * $rating))
|
||||||
|
/ ($this->config['rob_s'] + $all);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the validity of the category of a request
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string The category
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function check_category(string $category)
|
||||||
|
{
|
||||||
|
return $category === \b8\b8::HAM || $category === \b8\b8::SPAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Learn a reference text
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string The text to learn
|
||||||
|
* @param string Either b8::SPAM or b8::HAM
|
||||||
|
* @return mixed void or an error code
|
||||||
|
*/
|
||||||
|
public function learn(string $text = null, string $category = null)
|
||||||
|
{
|
||||||
|
// Let's first see if the user called the function correctly
|
||||||
|
if ($text === null) {
|
||||||
|
return \b8\b8::TRAINER_TEXT_MISSING;
|
||||||
|
}
|
||||||
|
if ($category === null) {
|
||||||
|
return \b8\b8::TRAINER_CATEGORY_MISSING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->process_text($text, $category, \b8\b8::LEARN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlearn a reference text
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string The text to unlearn
|
||||||
|
* @param string Either b8::SPAM or b8::HAM
|
||||||
|
* @return mixed void or an error code
|
||||||
|
*/
|
||||||
|
public function unlearn(string $text = null, string $category = null)
|
||||||
|
{
|
||||||
|
// Let's first see if the user called the function correctly
|
||||||
|
if ($text === null) {
|
||||||
|
return \b8\b8::TRAINER_TEXT_MISSING;
|
||||||
|
}
|
||||||
|
if ($category === null) {
|
||||||
|
return \b8\b8::TRAINER_CATEGORY_MISSING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->process_text($text, $category, \b8\b8::UNLEARN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the actual interaction with the storage backend for learning or unlearning texts
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string The text to process
|
||||||
|
* @param string Either b8::SPAM or b8::HAM
|
||||||
|
* @param string Either b8::LEARN or b8::UNLEARN
|
||||||
|
* @return mixed void or an error code
|
||||||
|
*/
|
||||||
|
private function process_text(string $text, string $category, string $action)
|
||||||
|
{
|
||||||
|
// Look if the request is okay
|
||||||
|
if (! $this->check_category($category)) {
|
||||||
|
return \b8\b8::TRAINER_CATEGORY_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all tokens from $text
|
||||||
|
$tokens = $this->lexer->get_tokens($text);
|
||||||
|
|
||||||
|
// Check if the lexer failed (if so, $tokens will be a lexer error code, if not, $tokens
|
||||||
|
// will be an array)
|
||||||
|
if (! is_array($tokens)) {
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the tokens and what to do with it to the storage backend
|
||||||
|
return $this->storage->process_text($tokens, $category, $action);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
176
serendipity_event_spamblock_bayes/b8/degenerator/standard.php
Normal file
176
serendipity_event_spamblock_bayes/b8/degenerator/standard.php
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/* Copyright (C) 2006-2019 Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
|
||||||
|
This file is part of the b8 package
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation in version 2.1 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software Foundation,
|
||||||
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class to derive simplified tokens
|
||||||
|
*
|
||||||
|
* @license LGPL 2.1
|
||||||
|
* @package b8
|
||||||
|
* @author Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace b8\degenerator;
|
||||||
|
|
||||||
|
class standard
|
||||||
|
{
|
||||||
|
public $config = [ 'multibyte' => true,
|
||||||
|
'encoding' => 'UTF-8' ];
|
||||||
|
|
||||||
|
public $degenerates = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the degenerator.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param array $config The configuration: [ 'multibyte' => bool,
|
||||||
|
'encoding' => string ]
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $config)
|
||||||
|
{
|
||||||
|
// Validate config data
|
||||||
|
foreach ($config as $name => $value) {
|
||||||
|
switch($name) {
|
||||||
|
case 'multibyte':
|
||||||
|
$this->config[$name] = (bool) $value;
|
||||||
|
break;
|
||||||
|
case 'encoding':
|
||||||
|
$this->config[$name] = (string) $value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \Exception(standard::class . ": Unknown configuration key: "
|
||||||
|
. "\"$name\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a list of "degenerated" words for a list of words.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param array $words The words to degenerate
|
||||||
|
* @return array An array containing an array of degenerated tokens for each token
|
||||||
|
*/
|
||||||
|
public function degenerate(array $words)
|
||||||
|
{
|
||||||
|
$degenerates = [];
|
||||||
|
|
||||||
|
foreach ($words as $word) {
|
||||||
|
$degenerates[$word] = $this->degenerate_word($word);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $degenerates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove duplicates from a list of degenerates of a word.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string $word The word
|
||||||
|
* @param array $list The list to process
|
||||||
|
* @return array The list without duplicates
|
||||||
|
*/
|
||||||
|
private function delete_duplicates(string $word, array $list)
|
||||||
|
{
|
||||||
|
$list_processed = [];
|
||||||
|
|
||||||
|
// Check each upper/lower version
|
||||||
|
foreach ($list as $alt_word) {
|
||||||
|
if ($alt_word != $word) {
|
||||||
|
array_push($list_processed, $alt_word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list_processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a list of "degenerated" versions of a word.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string $word The word
|
||||||
|
* @return array An array of degenerated words
|
||||||
|
*/
|
||||||
|
private function degenerate_word(string $word)
|
||||||
|
{
|
||||||
|
// Check for any stored words so the process doesn't have to repeat
|
||||||
|
if (isset($this->degenerates[$word]) === true) {
|
||||||
|
return $this->degenerates[$word];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create different versions of upper and lower case
|
||||||
|
if ($this->config['multibyte'] === false) {
|
||||||
|
// The standard upper/lower versions
|
||||||
|
$lower = strtolower($word);
|
||||||
|
$upper = strtoupper($word);
|
||||||
|
$first = substr($upper, 0, 1) . substr($lower, 1, strlen($word));
|
||||||
|
} elseif ($this->config['multibyte'] === true) {
|
||||||
|
// The multibyte upper/lower versions
|
||||||
|
$lower = mb_strtolower($word, $this->config['encoding']);
|
||||||
|
$upper = mb_strtoupper($word, $this->config['encoding']);
|
||||||
|
$first = mb_substr($upper, 0, 1, $this->config['encoding'])
|
||||||
|
. mb_substr($lower, 1, mb_strlen($word), $this->config['encoding']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the versions
|
||||||
|
$upper_lower = [];
|
||||||
|
array_push($upper_lower, $lower);
|
||||||
|
array_push($upper_lower, $upper);
|
||||||
|
array_push($upper_lower, $first);
|
||||||
|
|
||||||
|
// Delete duplicate upper/lower versions
|
||||||
|
$degenerate = $this->delete_duplicates($word, $upper_lower);
|
||||||
|
|
||||||
|
// Append the original word
|
||||||
|
array_push($degenerate, $word);
|
||||||
|
|
||||||
|
// Degenerate all versions
|
||||||
|
foreach ($degenerate as $alt_word) {
|
||||||
|
// Look for stuff like !!! and ???
|
||||||
|
if (preg_match('/[!?]$/', $alt_word) > 0) {
|
||||||
|
// Add versions with different !s and ?s
|
||||||
|
if (preg_match('/[!?]{2,}$/', $alt_word) > 0) {
|
||||||
|
$tmp = preg_replace('/([!?])+$/', '$1', $alt_word);
|
||||||
|
array_push($degenerate, $tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmp = preg_replace('/([!?])+$/', '', $alt_word);
|
||||||
|
array_push($degenerate, $tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for "..." at the end of the word
|
||||||
|
$alt_word_int = $alt_word;
|
||||||
|
while (preg_match('/[\.]$/', $alt_word_int) > 0) {
|
||||||
|
$alt_word_int = substr($alt_word_int, 0, strlen($alt_word_int) - 1);
|
||||||
|
array_push($degenerate, $alt_word_int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some degenerates are the same as the original word. These don't have to be fetched, so we
|
||||||
|
// create a new array with only new tokens
|
||||||
|
$degenerate = $this->delete_duplicates($word, $degenerate);
|
||||||
|
|
||||||
|
// Store the list of degenerates for the token to prevent unnecessary re-processing
|
||||||
|
$this->degenerates[$word] = $degenerate;
|
||||||
|
|
||||||
|
return $degenerate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
267
serendipity_event_spamblock_bayes/b8/lexer/standard.php
Normal file
267
serendipity_event_spamblock_bayes/b8/lexer/standard.php
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/* Copyright (C) 2006-2019 Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
|
||||||
|
This file is part of the b8 package
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation in version 2.1 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software Foundation,
|
||||||
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class to disassemble a text to tokens
|
||||||
|
*
|
||||||
|
* @license LGPL 2.1
|
||||||
|
* @package b8
|
||||||
|
* @author Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
* @author Oliver Lillie <ollie@buggedcom.co.uk> (original PHP 5 port)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace b8\lexer;
|
||||||
|
|
||||||
|
class standard
|
||||||
|
{
|
||||||
|
const LEXER_TEXT_NOT_STRING = 'LEXER_TEXT_NOT_STRING';
|
||||||
|
const LEXER_TEXT_EMPTY = 'LEXER_TEXT_EMPTY';
|
||||||
|
|
||||||
|
const LEXER_NO_TOKENS = 'b8*no_tokens';
|
||||||
|
|
||||||
|
private $config = [ 'min_size' => 3,
|
||||||
|
'max_size' => 30,
|
||||||
|
'get_uris' => true,
|
||||||
|
'get_html' => true,
|
||||||
|
'get_bbcode' => false,
|
||||||
|
'allow_numbers' => false ];
|
||||||
|
|
||||||
|
private $tokens = null;
|
||||||
|
private $processed_text = null;
|
||||||
|
|
||||||
|
// The regular expressions we use to split the text to tokens
|
||||||
|
private $regexp = [ 'raw_split' => '/[\s,\.\/"\:;\|<>\-_\[\]{}\+=\)\(\*\&\^%]+/',
|
||||||
|
'ip' => '/([A-Za-z0-9\_\-\.]+)/',
|
||||||
|
'uris' => '/([A-Za-z0-9\_\-]*\.[A-Za-z0-9\_\-\.]+)/',
|
||||||
|
'html' => '/(<.+?>)/',
|
||||||
|
'bbcode' => '/(\[.+?\])/',
|
||||||
|
'tagname' => '/(.+?)\s/',
|
||||||
|
'numbers' => '/^[0-9]+$/' ];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the lexer.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param array $config The configuration: [ 'min_size' => int,
|
||||||
|
* 'max_size' => int,
|
||||||
|
* 'get_uris' => bool,
|
||||||
|
* 'get_html' => bool,
|
||||||
|
* 'get_bbcode' => bool,
|
||||||
|
* 'allow_numbers' => bool ]
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function __construct(array $config)
|
||||||
|
{
|
||||||
|
// Validate config data
|
||||||
|
foreach ($config as $name=>$value) {
|
||||||
|
switch ($name) {
|
||||||
|
case 'min_size':
|
||||||
|
case 'max_size':
|
||||||
|
$this->config[$name] = (int) $value;
|
||||||
|
break;
|
||||||
|
case 'allow_numbers':
|
||||||
|
case 'get_uris':
|
||||||
|
case 'get_html':
|
||||||
|
case 'get_bbcode':
|
||||||
|
$this->config[$name] = (bool) $value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \Exception(standard::class . ": Unknown configuration key: "
|
||||||
|
. "\"$name\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a text to tokens.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $text The text to disassemble
|
||||||
|
* @return mixed Returns a list of tokens or an error code
|
||||||
|
*/
|
||||||
|
public function get_tokens(string $text)
|
||||||
|
{
|
||||||
|
// Check if we actually have a string ...
|
||||||
|
if (is_string($text) === false) {
|
||||||
|
return self::LEXER_TEXT_NOT_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and if it's empty
|
||||||
|
if (empty($text) === true) {
|
||||||
|
return self::LEXER_TEXT_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-convert the text to the original characters coded in UTF-8, as they have been coded in
|
||||||
|
// html entities during the post process
|
||||||
|
$this->processed_text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
|
||||||
|
|
||||||
|
// Reset the token list
|
||||||
|
$this->tokens = array();
|
||||||
|
|
||||||
|
if ($this->config['get_uris'] === true) {
|
||||||
|
// Get URIs
|
||||||
|
$this->get_uris($this->processed_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->config['get_html'] === true) {
|
||||||
|
// Get HTML
|
||||||
|
$this->get_markup($this->processed_text, $this->regexp['html']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->config['get_bbcode'] === true) {
|
||||||
|
// Get BBCode
|
||||||
|
$this->get_markup($this->processed_text, $this->regexp['bbcode']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We always want to do a raw split of the (remaining) text, so:
|
||||||
|
$this->raw_split($this->processed_text);
|
||||||
|
|
||||||
|
// Be sure not to return an empty array
|
||||||
|
if (count($this->tokens) == 0) {
|
||||||
|
$this->tokens[self::LEXER_NO_TOKENS] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a list of all found tokens
|
||||||
|
return $this->tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates a token.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string $token The token string
|
||||||
|
* @return bool Returns true if the token is valid, otherwise returns false.
|
||||||
|
*/
|
||||||
|
private function is_valid(string $token)
|
||||||
|
{
|
||||||
|
// Just to be sure that the token's name won't collide with b8's internal variables
|
||||||
|
if (substr($token, 0, 3) == 'b8*') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the size of the token
|
||||||
|
$len = strlen($token);
|
||||||
|
if ($len < $this->config['min_size'] || $len > $this->config['max_size']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We may want to exclude pure numbers
|
||||||
|
if ($this->config['allow_numbers'] === false
|
||||||
|
&& preg_match($this->regexp['numbers'], $token) > 0) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token is okay
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the validity of a token and adds it to the token list if it's valid.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string $token
|
||||||
|
* @param string $word_to_remove Word to remove from the processed string
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function add_token(string $token, string $word_to_remove = null)
|
||||||
|
{
|
||||||
|
// Check the validity of the token
|
||||||
|
if (! $this->is_valid($token)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add it to the list or increase it's counter
|
||||||
|
if (! isset($this->tokens[$token])) {
|
||||||
|
$this->tokens[$token] = 1;
|
||||||
|
} else {
|
||||||
|
$this->tokens[$token] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If requested, remove the word or it's original version from the text
|
||||||
|
if ($word_to_remove !== null) {
|
||||||
|
$this->processed_text = str_replace($word_to_remove, '', $this->processed_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets URIs.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string $text
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function get_uris(string $text)
|
||||||
|
{
|
||||||
|
// Find URIs
|
||||||
|
preg_match_all($this->regexp['uris'], $text, $raw_tokens);
|
||||||
|
foreach ($raw_tokens[1] as $word) {
|
||||||
|
// Remove a possible trailing dot
|
||||||
|
$word = rtrim($word, '.');
|
||||||
|
// Try to add the found tokens to the list
|
||||||
|
$this->add_token($word, $word);
|
||||||
|
// Also process the parts of the found URIs
|
||||||
|
$this->raw_split($word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets HTML or BBCode markup, depending on the regexp used.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string $text
|
||||||
|
* @param string $regexp
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function get_markup(string $text, string $regexp)
|
||||||
|
{
|
||||||
|
// Search for the markup
|
||||||
|
preg_match_all($regexp, $text, $raw_tokens);
|
||||||
|
foreach ($raw_tokens[1] as $word) {
|
||||||
|
$actual_word = $word;
|
||||||
|
|
||||||
|
// If the tag has parameters, just use the tag itself
|
||||||
|
if (strpos($word, ' ') !== false) {
|
||||||
|
preg_match($this->regexp['tagname'], $word, $match);
|
||||||
|
$actual_word = $match[1];
|
||||||
|
$word = "$actual_word..." . substr($word, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to add the found tokens to the list
|
||||||
|
$this->add_token($word, $actual_word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does a raw split.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string $text
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function raw_split(string $text)
|
||||||
|
{
|
||||||
|
foreach (preg_split($this->regexp['raw_split'], $text) as $word) {
|
||||||
|
// Check the word and add it to the token list if it's valid
|
||||||
|
$this->add_token($word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
105
serendipity_event_spamblock_bayes/b8/storage/dba.php
Normal file
105
serendipity_event_spamblock_bayes/b8/storage/dba.php
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/* Copyright (C) 2006-2019 Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
|
||||||
|
This file is part of the b8 package
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation in version 2.1 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software Foundation,
|
||||||
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace b8\storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Berkeley DB (DBA) storage backend
|
||||||
|
*
|
||||||
|
* @license LGPL 2.1
|
||||||
|
* @package b8
|
||||||
|
* @author Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
class dba extends storage_base
|
||||||
|
{
|
||||||
|
|
||||||
|
private $db = null;
|
||||||
|
|
||||||
|
protected function setup_backend(array $config)
|
||||||
|
{
|
||||||
|
if (! isset($config['resource'])
|
||||||
|
|| gettype($config['resource']) !== 'resource'
|
||||||
|
|| get_resource_type($config['resource']) !== 'dba') {
|
||||||
|
|
||||||
|
throw new \Exception(dba::class . ": No valid DBA resource passed");
|
||||||
|
}
|
||||||
|
$this->db = $config['resource'];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function fetch_token_data(array $tokens)
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
// Try to the raw data in the format "count_ham count_spam"
|
||||||
|
$count = dba_fetch($token, $this->db);
|
||||||
|
|
||||||
|
if ($count !== false) {
|
||||||
|
// Split the data by space characters
|
||||||
|
$split_data = explode(' ', $count);
|
||||||
|
|
||||||
|
// As an internal variable may have just one single value, we have to check for this
|
||||||
|
$count_ham = isset($split_data[0]) ? (int) $split_data[0] : null;
|
||||||
|
$count_spam = isset($split_data[1]) ? (int) $split_data[1] : null;
|
||||||
|
|
||||||
|
// Append the parsed data
|
||||||
|
$data[$token] = [ \b8\b8::KEY_COUNT_HAM => $count_ham,
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => $count_spam ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assemble_count_value(array $count)
|
||||||
|
{
|
||||||
|
// Assemble the count data string
|
||||||
|
$count_value = $count[\b8\b8::KEY_COUNT_HAM] . ' ' . $count[\b8\b8::KEY_COUNT_SPAM];
|
||||||
|
// Remove whitespace from data of the internal variables
|
||||||
|
return(rtrim($count_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function add_token(string $token, array $count)
|
||||||
|
{
|
||||||
|
return dba_insert($token, $this->assemble_count_value($count), $this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function update_token(string $token, array $count)
|
||||||
|
{
|
||||||
|
return dba_replace($token, $this->assemble_count_value($count), $this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function delete_token(string $token)
|
||||||
|
{
|
||||||
|
return dba_delete($token, $this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function start_transaction()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function finish_transaction()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
110
serendipity_event_spamblock_bayes/b8/storage/mysql.php
Normal file
110
serendipity_event_spamblock_bayes/b8/storage/mysql.php
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/* Copyright (C) 2019 Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
|
||||||
|
This file is part of the b8 package
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation in version 2.1 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software Foundation,
|
||||||
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace b8\storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A MySQL storage backend
|
||||||
|
*
|
||||||
|
* @license LGPL 2.1
|
||||||
|
* @package b8
|
||||||
|
* @author Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
class mysql extends storage_base
|
||||||
|
{
|
||||||
|
|
||||||
|
private $mysql = null;
|
||||||
|
private $table = null;
|
||||||
|
|
||||||
|
protected function setup_backend(array $config)
|
||||||
|
{
|
||||||
|
if (! isset($config['resource'])
|
||||||
|
|| get_class($config['resource']) !== 'mysqli') {
|
||||||
|
|
||||||
|
throw new \Exception(mysql::class . ": No valid mysqli object passed");
|
||||||
|
}
|
||||||
|
$this->mysql = $config['resource'];
|
||||||
|
|
||||||
|
if (! isset($config['table'])) {
|
||||||
|
throw new \Exception(mysql::class . ": No b8 wordlist table name passed");
|
||||||
|
}
|
||||||
|
$this->table = $config['table'];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function fetch_token_data(array $tokens)
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
$escaped = [];
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
$escaped[] = $this->mysql->real_escape_string($token);
|
||||||
|
}
|
||||||
|
$result = $this->mysql->query('SELECT token, count_ham, count_spam'
|
||||||
|
. ' FROM ' . $this->table
|
||||||
|
. ' WHERE token IN '
|
||||||
|
. "('" . implode("','", $escaped) . "')");
|
||||||
|
|
||||||
|
while ($row = $result->fetch_row()) {
|
||||||
|
$data[$row[0]] = [ \b8\b8::KEY_COUNT_HAM => $row[1],
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => $row[2] ];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result->free_result();
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function add_token(string $token, array $count)
|
||||||
|
{
|
||||||
|
$query = $this->mysql->prepare('INSERT INTO ' . $this->table
|
||||||
|
. '(token, count_ham, count_spam) VALUES(?, ?, ?)');
|
||||||
|
$query->bind_param('sii', $token, $count[\b8\b8::KEY_COUNT_HAM],
|
||||||
|
$count[\b8\b8::KEY_COUNT_SPAM]);
|
||||||
|
$query->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function update_token(string $token, array $count)
|
||||||
|
{
|
||||||
|
$query = $this->mysql->prepare('UPDATE ' . $this->table
|
||||||
|
. ' SET count_ham = ?, count_spam = ? WHERE token = ?');
|
||||||
|
$query->bind_param('iis', $count[\b8\b8::KEY_COUNT_HAM], $count[\b8\b8::KEY_COUNT_SPAM],
|
||||||
|
$token);
|
||||||
|
$query->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function delete_token(string $token)
|
||||||
|
{
|
||||||
|
$query = $this->mysql->prepare('DELETE FROM ' . $this->table . ' WHERE token = ?');
|
||||||
|
$query->bind_param('s', $token);
|
||||||
|
$query->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function start_transaction()
|
||||||
|
{
|
||||||
|
$this->mysql->begin_transaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function finish_transaction()
|
||||||
|
{
|
||||||
|
$this->mysql->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
108
serendipity_event_spamblock_bayes/b8/storage/sqlite.php
Normal file
108
serendipity_event_spamblock_bayes/b8/storage/sqlite.php
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/* Copyright (C) 2019 Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
|
||||||
|
This file is part of the b8 package
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation in version 2.1 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software Foundation,
|
||||||
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace b8\storage;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sqlite storage backend
|
||||||
|
*
|
||||||
|
* @license LGPL 2.1
|
||||||
|
* @package b8
|
||||||
|
* @author Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
class sqlite extends storage_base
|
||||||
|
{
|
||||||
|
|
||||||
|
private $sqlite = null;
|
||||||
|
private $table = null;
|
||||||
|
|
||||||
|
protected function setup_backend(array $config)
|
||||||
|
{
|
||||||
|
$this->sqlite = $config['resource'];
|
||||||
|
|
||||||
|
if (! isset($config['table'])) {
|
||||||
|
$config['table'] = 'b8_wordlist';
|
||||||
|
}
|
||||||
|
$this->table = $config['table'];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function fetch_token_data(array $tokens)
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
$escaped = [];
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
$escaped[] = $this->sqlite->quote($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->sqlite->query('SELECT token, count_ham, count_spam'
|
||||||
|
. ' FROM ' . $this->table
|
||||||
|
. ' WHERE token IN '
|
||||||
|
. "(" . implode(",", $escaped) . ")");
|
||||||
|
|
||||||
|
while ($row = $result->fetch()) {
|
||||||
|
$data[$row[0]] = [ \b8\b8::KEY_COUNT_HAM => $row[1],
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => $row[2] ];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function add_token(string $token, array $count)
|
||||||
|
{
|
||||||
|
$query = $this->sqlite->prepare('INSERT INTO ' . $this->table
|
||||||
|
. '(token, count_ham, count_spam) VALUES(?, ?, ?)');
|
||||||
|
$query->bindParam(1, $token, PDO::PARAM_STR);
|
||||||
|
$query->bindParam(2, $count[\b8\b8::KEY_COUNT_HAM], PDO::PARAM_INT);
|
||||||
|
$query->bindParam(3, $count[\b8\b8::KEY_COUNT_SPAM], PDO::PARAM_INT);
|
||||||
|
|
||||||
|
$query->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function update_token(string $token, array $count)
|
||||||
|
{
|
||||||
|
$query = $this->sqlite->prepare('UPDATE ' . $this->table
|
||||||
|
. ' SET count_ham = ?, count_spam = ? WHERE token = ?');
|
||||||
|
$query->bindParam(1, $count[\b8\b8::KEY_COUNT_HAM], PDO::PARAM_INT);
|
||||||
|
$query->bindParam(2, $count[\b8\b8::KEY_COUNT_SPAM], PDO::PARAM_INT);
|
||||||
|
$query->bindParam(3, $token, PDO::PARAM_STR);
|
||||||
|
$query->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function delete_token(string $token)
|
||||||
|
{
|
||||||
|
$query = $this->sqlite->prepare('DELETE FROM ' . $this->table . ' WHERE token = ?');
|
||||||
|
$query->bindParam(1, $token, PDO::PARAM_STR);
|
||||||
|
$query->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function start_transaction()
|
||||||
|
{
|
||||||
|
$this->sqlite->beginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function finish_transaction()
|
||||||
|
{
|
||||||
|
$this->sqlite->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
316
serendipity_event_spamblock_bayes/b8/storage/storage_base.php
Normal file
316
serendipity_event_spamblock_bayes/b8/storage/storage_base.php
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/* Copyright (C) 2006-2019 Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
|
||||||
|
This file is part of the b8 package
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation in version 2.1 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program; if not, write to the Free Software Foundation,
|
||||||
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for storage backends
|
||||||
|
*
|
||||||
|
* @license LGPL 2.1
|
||||||
|
* @package b8
|
||||||
|
* @author Tobias Leupold <tobias.leupold@gmx.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace b8\storage;
|
||||||
|
|
||||||
|
abstract class storage_base
|
||||||
|
{
|
||||||
|
protected $degenerator = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the backend
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param array The configuration for the respective backend
|
||||||
|
*/
|
||||||
|
abstract protected function setup_backend(array $config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the actual interaction with the database when fetching data
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @param array $tokens List of token names to fetch
|
||||||
|
* @return mixed Returns an array of the returned data in the format array(token => data)
|
||||||
|
or an empty array if there was no data.
|
||||||
|
*/
|
||||||
|
abstract protected function fetch_token_data(array $tokens);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a new token to the database
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @param string $token The token's name
|
||||||
|
* @param array $count The ham and spam counters [ \b8\b8::KEY_COUNT_HAM => int,
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => int ]
|
||||||
|
* @return bool true on success or false on failure
|
||||||
|
*/
|
||||||
|
abstract protected function add_token(string $token, array $count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing token
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @param string $token The token's name
|
||||||
|
* @param array $count The ham and spam counters [ \b8\b8::KEY_COUNT_HAM => int,
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => int ]
|
||||||
|
* @return bool true on success or false on failure
|
||||||
|
*/
|
||||||
|
abstract protected function update_token(string $token, array $count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a token from the database
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @param string $token The token's name
|
||||||
|
* @return bool true on success or false on failure
|
||||||
|
*/
|
||||||
|
abstract protected function delete_token(string $token);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a transaction (if the underlying database supports/needs this)
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
abstract protected function start_transaction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finishes a transaction (if the underlying database supports/needs this)
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
abstract protected function finish_transaction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passes the degenerator to the instance and calls the backend setup
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param array The respective backen's configuration
|
||||||
|
* @param object The degenerator to use
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $config, object $degenerator)
|
||||||
|
{
|
||||||
|
$this->degenerator = $degenerator;
|
||||||
|
$this->setup_backend($config);
|
||||||
|
|
||||||
|
$internals = $this->get_internals();
|
||||||
|
if (! isset($internals[\b8\b8::KEY_DB_VERSION])
|
||||||
|
|| $internals[\b8\b8::KEY_DB_VERSION] !== \b8\b8::DBVERSION) {
|
||||||
|
|
||||||
|
throw new \Exception(storage_base::class . ': The connected database is not a b8 v'
|
||||||
|
. \b8\b8::DBVERSION . ' database.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the database's internal variables.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return array Returns an array of all internals.
|
||||||
|
*/
|
||||||
|
public function get_internals()
|
||||||
|
{
|
||||||
|
$internals = $this->fetch_token_data([ \b8\b8::INTERNALS_TEXTS,
|
||||||
|
\b8\b8::INTERNALS_DBVERSION ]);
|
||||||
|
|
||||||
|
// Just in case this is called by check_database() and it's not yet clear if we actually
|
||||||
|
// have a b8 database
|
||||||
|
$texts_ham = null;
|
||||||
|
$texts_spam = null;
|
||||||
|
$dbversion = null;
|
||||||
|
if(isset($internals[\b8\b8::INTERNALS_TEXTS][\b8\b8::KEY_COUNT_HAM])) {
|
||||||
|
$texts_ham = (int) $internals[\b8\b8::INTERNALS_TEXTS][\b8\b8::KEY_COUNT_HAM];
|
||||||
|
}
|
||||||
|
if(isset($internals[\b8\b8::INTERNALS_TEXTS][\b8\b8::KEY_COUNT_SPAM])) {
|
||||||
|
$texts_spam = (int) $internals[\b8\b8::INTERNALS_TEXTS][\b8\b8::KEY_COUNT_SPAM];
|
||||||
|
}
|
||||||
|
if(isset($internals[\b8\b8::INTERNALS_DBVERSION][\b8\b8::KEY_COUNT_HAM])) {
|
||||||
|
$dbversion = (int) $internals[\b8\b8::INTERNALS_DBVERSION][\b8\b8::KEY_COUNT_HAM];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ \b8\b8::KEY_TEXTS_HAM => $texts_ham,
|
||||||
|
\b8\b8::KEY_TEXTS_SPAM => $texts_spam,
|
||||||
|
\b8\b8::KEY_DB_VERSION => $dbversion ];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all data about a list of tokens from the database.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param array The tokens list
|
||||||
|
* @return mixed Returns False on failure, otherwise returns array of returned data
|
||||||
|
in the format [ 'tokens' => [ token => count ],
|
||||||
|
'degenerates' => [ token => [ degenerate => count ] ] ].
|
||||||
|
*/
|
||||||
|
public function get(array $tokens)
|
||||||
|
{
|
||||||
|
// First we see what we have in the database
|
||||||
|
$token_data = $this->fetch_token_data($tokens);
|
||||||
|
|
||||||
|
// Check if we have to degenerate some tokens
|
||||||
|
$missing_tokens = array();
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
if (! isset($token_data[$token])) {
|
||||||
|
$missing_tokens[] = $token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($missing_tokens) > 0) {
|
||||||
|
// We have to degenerate some tokens
|
||||||
|
$degenerates_list = [];
|
||||||
|
|
||||||
|
// Generate a list of degenerated tokens for the missing tokens ...
|
||||||
|
$degenerates = $this->degenerator->degenerate($missing_tokens);
|
||||||
|
|
||||||
|
// ... and look them up
|
||||||
|
foreach ($degenerates as $token => $token_degenerates) {
|
||||||
|
$degenerates_list = array_merge($degenerates_list, $token_degenerates);
|
||||||
|
}
|
||||||
|
|
||||||
|
$token_data = array_merge($token_data, $this->fetch_token_data($degenerates_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here, we have all available data in $token_data.
|
||||||
|
|
||||||
|
$return_data_tokens = [];
|
||||||
|
$return_data_degenerates = [];
|
||||||
|
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
if (isset($token_data[$token])) {
|
||||||
|
// The token was found in the database
|
||||||
|
$return_data_tokens[$token] = $token_data[$token];
|
||||||
|
} else {
|
||||||
|
// The token was not found, so we look if we can return data for degenerated tokens
|
||||||
|
foreach ($this->degenerator->degenerates[$token] as $degenerate) {
|
||||||
|
if (isset($token_data[$degenerate])) {
|
||||||
|
// A degenertaed version of the token way found in the database
|
||||||
|
$return_data_degenerates[$token][$degenerate] = $token_data[$degenerate];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, all token data directly found in the database is in $return_data_tokens and all
|
||||||
|
// data for degenerated versions is in $return_data_degenerates, so
|
||||||
|
return [ 'tokens' => $return_data_tokens,
|
||||||
|
'degenerates' => $return_data_degenerates ];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores or deletes a list of tokens from the given category.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param array The tokens list
|
||||||
|
* @param string Either \b8\b8::HAM or \b8\b8::SPAM
|
||||||
|
* @param string Either \b8\b8::LEARN or \b8\b8::UNLEARN
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function process_text(array $tokens, string $category, string $action)
|
||||||
|
{
|
||||||
|
// No matter what we do, we first have to check what data we have.
|
||||||
|
|
||||||
|
// First get the internals, including the ham texts and spam texts counter
|
||||||
|
$internals = $this->get_internals();
|
||||||
|
// Then, fetch all data for all tokens we have
|
||||||
|
$token_data = $this->fetch_token_data(array_keys($tokens));
|
||||||
|
|
||||||
|
$this->start_transaction();
|
||||||
|
|
||||||
|
// Process all tokens to learn/unlearn
|
||||||
|
foreach ($tokens as $token => $count) {
|
||||||
|
if (isset($token_data[$token])) {
|
||||||
|
// We already have this token, so update it's data
|
||||||
|
|
||||||
|
// Get the existing data
|
||||||
|
$count_ham = $token_data[$token][\b8\b8::KEY_COUNT_HAM];
|
||||||
|
$count_spam = $token_data[$token][\b8\b8::KEY_COUNT_SPAM];
|
||||||
|
|
||||||
|
// Increase or decrease the right counter
|
||||||
|
if ($action === \b8\b8::LEARN) {
|
||||||
|
if ($category === \b8\b8::HAM) {
|
||||||
|
$count_ham += $count;
|
||||||
|
} elseif ($category === \b8\b8::SPAM) {
|
||||||
|
$count_spam += $count;
|
||||||
|
}
|
||||||
|
} elseif ($action == \b8\b8::UNLEARN) {
|
||||||
|
if ($category === \b8\b8::HAM) {
|
||||||
|
$count_ham -= $count;
|
||||||
|
} elseif ($category === \b8\b8::SPAM) {
|
||||||
|
$count_spam -= $count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't want to have negative values
|
||||||
|
if ($count_ham < 0) {
|
||||||
|
$count_ham = 0;
|
||||||
|
}
|
||||||
|
if ($count_spam < 0) {
|
||||||
|
$count_spam = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now let's see if we have to update or delete the token
|
||||||
|
if ($count_ham != 0 or $count_spam != 0) {
|
||||||
|
$this->update_token($token, [ \b8\b8::KEY_COUNT_HAM => $count_ham,
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => $count_spam ]);
|
||||||
|
} else {
|
||||||
|
$this->delete_token($token);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We don't have the token. If we unlearn a text, we can't delete it as we don't
|
||||||
|
// have it anyway, so just do something if we learn a text
|
||||||
|
if ($action === \b8\b8::LEARN) {
|
||||||
|
if ($category === \b8\b8::HAM) {
|
||||||
|
$this->add_token($token, [ \b8\b8::KEY_COUNT_HAM => $count,
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => 0 ]);
|
||||||
|
} elseif ($category === \b8\b8::SPAM) {
|
||||||
|
$this->add_token($token, [ \b8\b8::KEY_COUNT_HAM => 0,
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => $count ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, all token have been processed, so let's update the right text
|
||||||
|
if ($action === \b8\b8::LEARN) {
|
||||||
|
if ($category === \b8\b8::HAM) {
|
||||||
|
$internals[\b8\b8::KEY_TEXTS_HAM]++;
|
||||||
|
} elseif ($category === \b8\b8::SPAM) {
|
||||||
|
$internals[\b8\b8::KEY_TEXTS_SPAM]++;
|
||||||
|
}
|
||||||
|
} elseif ($action === \b8\b8::UNLEARN) {
|
||||||
|
if ($category === \b8\b8::HAM) {
|
||||||
|
if ($internals[\b8\b8::KEY_TEXTS_HAM] > 0) {
|
||||||
|
$internals[\b8\b8::KEY_TEXTS_HAM]--;
|
||||||
|
}
|
||||||
|
} elseif ($category === \b8\b8::SPAM) {
|
||||||
|
if ($internals[\b8\b8::KEY_TEXTS_SPAM] > 0) {
|
||||||
|
$internals[\b8\b8::KEY_TEXTS_SPAM]--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->update_token(\b8\b8::INTERNALS_TEXTS,
|
||||||
|
[ \b8\b8::KEY_COUNT_HAM => $internals[\b8\b8::KEY_TEXTS_HAM],
|
||||||
|
\b8\b8::KEY_COUNT_SPAM => $internals[\b8\b8::KEY_TEXTS_SPAM] ]);
|
||||||
|
|
||||||
|
$this->finish_transaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
<div id="bayesContent">
|
|
||||||
{foreach from=$comments item=comment}
|
|
||||||
<h3>{$CONST.COMMENT} #{$comment.id}</h3>
|
|
||||||
|
|
||||||
<ul class="plainList bayesAnalysis">
|
|
||||||
{foreach from=$types item=type}
|
|
||||||
<li class="ratingBox">
|
|
||||||
<div class="commentType">{$type}</div>
|
|
||||||
<div class="commentPart">{$comment.$type|escape:"html"}</div>
|
|
||||||
<div class="rating">
|
|
||||||
{if $comment.ratings.$type != "-"}
|
|
||||||
{$comment.ratings.$type|regex_replace:"/\..*/":""}%
|
|
||||||
{else}
|
|
||||||
{$comment.ratings.$type}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{/foreach}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="finalRating">{$comment.rating|regex_replace:"/\..*/":""}%</div>
|
|
||||||
{/foreach}
|
|
||||||
|
|
||||||
<script src="{$path}jquery.excerpt.js" type="text/javascript"></script>
|
|
||||||
<script>shortenAll("commentPart", 1)
|
|
||||||
colorize()</script>
|
|
||||||
</div>
|
|
|
@ -1,73 +0,0 @@
|
||||||
<div id="bayesContent">
|
|
||||||
<div id="bayesAnalysis">
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<div class="bayesAnalysisTableNavigation">
|
|
||||||
{else}
|
|
||||||
<ul class="bayesAnalysisTableNavigation plainList clearfix">
|
|
||||||
{/if}
|
|
||||||
{if $commentpage > 0}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<a class="serendipityIconLink" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=4&serendipity[commentpage]={$commentpage-1}"><img src="{serendipity_getFile file="admin/img/previous.png"}"/>{$CONST.PREVIOUS}</a>
|
|
||||||
{else}
|
|
||||||
<li class="prev"><a class="button_link" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=4&serendipity[commentpage]={$commentpage-1}" title="{$CONST.PREVIOUS}"><span class="icon-left-dir" aria-hidden="true"></span><span class="visuallyhidden"> {$CONST.PREVIOUS}</span></a></li>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{if $comments|@count > 20}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<a class="serendipityIconLinkRight" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=4&serendipity[commentpage]={$commentpage+1}">{$CONST.NEXT} <img src="{serendipity_getFile file="admin/img/next.png"}"/></a>
|
|
||||||
{else}
|
|
||||||
<li class="next"><a class="button_link" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=4&serendipity[commentpage]={$commentpage+1}" title="{$CONST.NEXT}"><span class="visuallyhidden">{$CONST.NEXT} </span><span class="icon-right-dir" aria-hidden="true"></span></a></li>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
</div>
|
|
||||||
{else}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<form action="{$serendipityBaseURL}index.php?/plugin/bayesAnalyse" method="post">
|
|
||||||
<ul id="bayesAnalysisList" class="plainList">
|
|
||||||
{foreach from=$comments item=comment }
|
|
||||||
<li>
|
|
||||||
<input type="checkbox" id="{$comment.id}" name="comments[{$comment.id}]" />
|
|
||||||
<label for="{$comment.id}">{$comment.id}:</label>
|
|
||||||
<div class="bayesComments">
|
|
||||||
{$comment.author|escape:"html"}, {$comment.body|escape:"html"}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{/foreach}
|
|
||||||
<input type="submit" class="serendipityPrettyButton input_button" id="bayesAnalysisButton" value="{$CONST.GO}" />
|
|
||||||
</ul>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script src="{$path}jquery.excerpt.js" type="text/javascript"></script>
|
|
||||||
<script>shortenAll("bayesComments", 1)</script>
|
|
||||||
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<div class="bayesAnalysisTableNavigation">
|
|
||||||
{else}
|
|
||||||
<ul class="bayesAnalysisTableNavigation plainList clearfix">
|
|
||||||
{/if}
|
|
||||||
{if $commentpage > 0}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<a class="serendipityIconLink" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=4&serendipity[commentpage]={$commentpage-1}"><img src="{serendipity_getFile file="admin/img/previous.png"}"/>{$CONST.PREVIOUS}</a>
|
|
||||||
{else}
|
|
||||||
<li class="prev"><a class="button_link" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=4&serendipity[commentpage]={$commentpage-1}" title="{$CONST.PREVIOUS}"><span class="icon-left-dir" aria-hidden="true"></span><span class="visuallyhidden"> {$CONST.PREVIOUS}</span></a></li>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{if $comments|@count > 20}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<a class="serendipityIconLinkRight" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=4&serendipity[commentpage]={$commentpage+1}">{$CONST.NEXT} <img src="{serendipity_getFile file="admin/img/next.png"}"/></a>
|
|
||||||
{else}
|
|
||||||
<li class="next"><a class="button_link" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=4&serendipity[commentpage]={$commentpage+1}" title="{$CONST.NEXT}"><span class="visuallyhidden">{$CONST.NEXT} </span><span class="icon-right-dir" aria-hidden="true"></span></a></li>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
</div>
|
|
||||||
{else}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,112 +0,0 @@
|
||||||
<div id="bayesContent">
|
|
||||||
<div id="bayesControls">
|
|
||||||
<form action="{$serendipityBaseURL}index.php?/plugin/bayesSetupDatabase" method="post">
|
|
||||||
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_CREATEDB}" name="submit"/>
|
|
||||||
</form>
|
|
||||||
<form action="{$serendipityBaseURL}index.php?/plugin/bayesLearnFromOld" method="post">
|
|
||||||
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_LEARNOLD}" name="submit"/>
|
|
||||||
</form>
|
|
||||||
<form id="bayesDeleteDB" action="{$serendipityBaseURL}index.php?/plugin/bayesDeleteDatabase" method="post">
|
|
||||||
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_ERASEDB}" name="submit"/>
|
|
||||||
</form>
|
|
||||||
<form action="{$serendipityBaseURL}index.php?/plugin/bayesExportDatabase" method="post">
|
|
||||||
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_EXPORTDB}" name="submit"/>
|
|
||||||
</form>
|
|
||||||
<form action="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=5" method="post">
|
|
||||||
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_IMPORTDB}" name="submit"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="bayesDatabase">
|
|
||||||
<table id="bayesDatabaseTable">
|
|
||||||
<caption>{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_MENU_DATABASE}</caption>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>token</th>
|
|
||||||
<th>ham</th>
|
|
||||||
<th>spam</th>
|
|
||||||
<th>type</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{foreach from=$bayesTable item=row}
|
|
||||||
<tr>
|
|
||||||
{foreach from=$row item=value}
|
|
||||||
<td>
|
|
||||||
{$value}
|
|
||||||
</td>
|
|
||||||
{/foreach}
|
|
||||||
</tr>
|
|
||||||
{/foreach}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{if $pages > 1}
|
|
||||||
<div id="bayesDatabaseTablePagination">
|
|
||||||
{if $curpage > 2}
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=2&serendipity[commentpage]=0" title="{$CONST.Page}">1</a>
|
|
||||||
...
|
|
||||||
{elseif $curpage > 1}
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=2&serendipity[commentpage]=0" title="{$CONST.Page}">1</a>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{section name=page start=1 loop=$pages+1}
|
|
||||||
{if $curpage == $smarty.section.page.index -1}
|
|
||||||
<a class="curpage" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=2&serendipity[commentpage]={$smarty.section.page.index-1}" title="{$CONST.Page}">{$smarty.section.page.index}</a>
|
|
||||||
{/if}
|
|
||||||
{if $curpage == $smarty.section.page.index -2 || $curpage == $smarty.section.page.index}
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=2&serendipity[commentpage]={$smarty.section.page.index-1}" title="{$CONST.Page}">{$smarty.section.page.index}</a>
|
|
||||||
{/if}
|
|
||||||
{/section}
|
|
||||||
|
|
||||||
{if $curpage < $pages -3}
|
|
||||||
...
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=2&serendipity[commentpage]={$pages-1}" title="{$CONST.Page}">{$pages}</a>
|
|
||||||
{elseif $curpage < $pages -2}
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=2&serendipity[commentpage]={$pages-1}" title="{$CONST.Page}">{$pages}</a>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div id="bayesSavedValues">
|
|
||||||
<table id="bayesSavedValuesTable">
|
|
||||||
<caption>{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_SAVEDVALUES}</caption>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th colspan="2">{$CONST.NAME}</th>
|
|
||||||
<th colspan="2">{$CONST.HOMEPAGE}</th>
|
|
||||||
<th colspan="2">{$CONST.EMAIL}</th>
|
|
||||||
<th colspan="2">{$CONST.IP}</th>
|
|
||||||
<th colspan="2">{$CONST.REFERER}</th>
|
|
||||||
<th colspan="2">{$CONST.COMMENT}</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
{section name=i loop=6 start=0}
|
|
||||||
<th>{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_HAM}</th>
|
|
||||||
<th>{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_SPAM}</th>
|
|
||||||
{/section}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>{$author_ham}</td>
|
|
||||||
<td>{$author_spam}</td>
|
|
||||||
<td>{$url_ham}</td>
|
|
||||||
<td>{$url_spam}</td>
|
|
||||||
<td>{$email_ham}</td>
|
|
||||||
<td>{$email_spam}</td>
|
|
||||||
<td>{$ip_ham}</td>
|
|
||||||
<td>{$ip_spam}</td>
|
|
||||||
<td>{$referer_ham}</td>
|
|
||||||
<td>{$referer_spam}</td>
|
|
||||||
<td>{$body_ham}</td>
|
|
||||||
<td>{$body_spam}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="{$path}jquery.heatcolor.js" type="text/javascript"></script>
|
|
||||||
<script src="{$path}jquery.tablesorter.js" type="text/javascript"></script>
|
|
||||||
<script>$("#bayesDatabaseTable").tablesorter();
|
|
||||||
sortwithcolor(2);</script>
|
|
||||||
</div>
|
|
|
@ -1,31 +0,0 @@
|
||||||
<div id="bayesContent">
|
|
||||||
{if $s9ybackend == 1}<p>{else}<span class="msg_hint"><span class="icon-help-circled" aria-hidden="true"></span> {/if}{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_IMPORT_EXPLANATION}{if $s9ybackend == 1}</p>{else}</span>{/if}
|
|
||||||
|
|
||||||
<form enctype="multipart/form-data" action="{$serendipityBaseURL}index.php?/plugin/spamblock_bayes_import" method="post">
|
|
||||||
{if $s9ybackend != 1}
|
|
||||||
<div class="form_field">
|
|
||||||
{/if}
|
|
||||||
<input name="importcsv" type="file" />
|
|
||||||
<input class="serendipityPrettyButton input_button" type="submit" value="{$CONST.GO}" />
|
|
||||||
{if $s9ybackend != 1}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<h3>{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_TROJA}</h3>
|
|
||||||
|
|
||||||
{if $s9ybackend == 1}<p>{else}<span class="msg_hint"><span class="icon-help-circled" aria-hidden="true"></span> {/if}{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_TROJA_EXPLANATION}{if $s9ybackend == 1}</p>{else}</span>{/if}
|
|
||||||
|
|
||||||
<form{if $s9ybackend != 1} class="bayesTrojaButtons"{/if} action="{$serendipityBaseURL}index.php?/plugin/bayesTrojaRequestDB" method="post">
|
|
||||||
<input id="trojaImport" class="serendipityPrettyButton input_button" type="submit" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_TROJA_IMPORT}" />
|
|
||||||
</form>
|
|
||||||
{if $trojaRegistered}
|
|
||||||
<form class="bayesTrojaButtons" action="{$serendipityBaseURL}index.php?/plugin/bayesTrojaRemove" method="post">
|
|
||||||
<input class="serendipityPrettyButton input_button" type="submit" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_TROJA_REMOVE}" />
|
|
||||||
</form>
|
|
||||||
{else}
|
|
||||||
<form class="bayesTrojaButtons" action="{$serendipityBaseURL}index.php?/plugin/bayesTrojaRegister" method="post">
|
|
||||||
<input class="serendipityPrettyButton input_button" type="submit" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_TROJA_REGISTER}" />
|
|
||||||
</form>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
|
@ -1,86 +0,0 @@
|
||||||
<div id="bayesContent">
|
|
||||||
<form id="bayesLearnForm" action="{$serendipityBaseURL}index.php?/plugin/bayesMenuLearn" method="post">
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<table id="bayesLearnTable">
|
|
||||||
<tr>
|
|
||||||
<td><label for="bayesCommentName">{$CONST.NAME}</label></td>
|
|
||||||
<td><input type="text" id="bayesCommentName" name="author"></input></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><label for="bayesCommentUrl">{$CONST.HOMEPAGE}</label></td>
|
|
||||||
<td><input type="text" id="bayesCommentUrl" name="url"></input></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><label for="bayesCommentEmail">{$CONST.EMAIL}</label></td>
|
|
||||||
<td><input type="text" id="bayesCommentEmail" name="email"></input></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><label for="bayesCommentIp">IP</label></td>
|
|
||||||
<td><input type="text" id="bayesCommentIp" name="ip"></input></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><label for="bayesCommentReferrer">{$CONST.REFERER}</label></td>
|
|
||||||
<td><input type="text" id="bayesCommentReferrer" name="referrer"></input></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><label for="bayesCommentBody">{$CONST.COMMENT}</label></td>
|
|
||||||
<td><textarea rows="10" cols="40" id="bayesCommentBody" name="body"></textarea></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td><label for="bayesCommentHam">{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_HAM}</label>
|
|
||||||
<input class="direction_ltr input_radio" type="radio" id="bayesCommentHam" name="ham" value="true" checked="" title="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_HAM}">
|
|
||||||
<label for="bayesCommentSpam">{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_SPAM}</label>
|
|
||||||
<input class="direction_ltr input_radio" type="radio" id="bayesCommentSpam" name="ham" value="false" title="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_SPAM}"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.SAVE}" name="submit"/>
|
|
||||||
{else}
|
|
||||||
<div class="form_field">
|
|
||||||
<label for="bayesCommentName">{$CONST.NAME}</label>
|
|
||||||
<input type="text" id="bayesCommentName" name="author">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form_field">
|
|
||||||
<label for="bayesCommentUrl">{$CONST.HOMEPAGE}</label>
|
|
||||||
<input type="text" id="bayesCommentUrl" name="url">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form_field">
|
|
||||||
<label for="bayesCommentEmail">{$CONST.EMAIL}</label>
|
|
||||||
<input type="text" id="bayesCommentEmail" name="email">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form_field">
|
|
||||||
<label for="bayesCommentIp">IP</label>
|
|
||||||
<input type="text" id="bayesCommentIp" name="ip">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form_field">
|
|
||||||
<label for="bayesCommentReferrer">{$CONST.REFERER}</label>
|
|
||||||
<input type="text" id="bayesCommentReferrer" name="referrer">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form_area">
|
|
||||||
<label for="bayesCommentBody">{$CONST.COMMENT}</label>
|
|
||||||
<textarea rows="10" id="bayesCommentBody" name="body"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="clearfix">
|
|
||||||
<div class="form_radio">
|
|
||||||
<label for="bayesCommentHam">{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_HAM}</label>
|
|
||||||
<input class="direction_ltr" type="radio" id="bayesCommentHam" name="ham" value="true" checked="">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form_radio">
|
|
||||||
<label for="bayesCommentSpam">{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_SPAM}</label>
|
|
||||||
<input class="direction_ltr" type="radio" id="bayesCommentSpam" name="ham" value="false">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form_buttons">
|
|
||||||
<input type="submit" value="{$CONST.SAVE}" name="submit">
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
|
@ -1,32 +0,0 @@
|
||||||
{if $jquery_needed == true}
|
|
||||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript"></script>
|
|
||||||
{/if}
|
|
||||||
<style> {$css} </style>
|
|
||||||
<script src="{$path}serendipity_event_spamblock_bayes.js" type="text/javascript"></script>
|
|
||||||
{if $s9ybackend != 1}
|
|
||||||
<h2>{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_NAME}</h2>
|
|
||||||
{/if}
|
|
||||||
<div id="bayesNav">
|
|
||||||
<ul>
|
|
||||||
<li{if $subpage == 1 && $s9ybackend != 1} class="current"{/if}>{if $subpage == 1 && $s9ybackend == 1}<h3>{/if}
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=1">{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_MENU_RECYCLER}</a>
|
|
||||||
{if $subpage == 1 && $s9ybackend == 1}</h3>{/if}
|
|
||||||
</li>
|
|
||||||
<li{if $subpage == 2 && $s9ybackend != 1} class="current"{/if}>{if $subpage == 2 && $s9ybackend == 1}<h3>{/if}
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=2">{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_MENU_DATABASE}</a>
|
|
||||||
{if $subpage == 2 && $s9ybackend == 1}</h3>{/if}
|
|
||||||
</li>
|
|
||||||
<li{if $subpage == 3 && $s9ybackend != 1} class="current"{/if}>{if $subpage == 3 && $s9ybackend == 1}<h3>{/if}
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=3">{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_MENU_LEARN}</a>
|
|
||||||
{if $subpage == 3 && $s9ybackend == 1}</h3>{/if}
|
|
||||||
</li>
|
|
||||||
<li{if $subpage == 4 && $s9ybackend != 1} class="current"{/if}>{if $subpage == 4 && $s9ybackend == 1}<h3>{/if}
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=4">{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_MENU_ANALYSIS}</a>
|
|
||||||
{if $subpage == 4 && $s9ybackend == 1}</h3>{/if}
|
|
||||||
</li>
|
|
||||||
<li{if $subpage == 5 && $s9ybackend != 1} class="current"{/if}>{if $subpage == 5 && $s9ybackend == 1}<h3>{/if}
|
|
||||||
<a href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=5">{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_MENU_IMPORT}</a>
|
|
||||||
{if $subpage == 5 && $s9ybackend == 1}</h3>{/if}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
|
@ -1,121 +1,39 @@
|
||||||
|
<h2>{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_NAME}</h2>
|
||||||
|
|
||||||
<div id="bayesContent">
|
<div id="bayesContent">
|
||||||
<form action="{$serendipityBaseUrl}index.php?/plugin/bayesRecycler" method="post">
|
<form action="{$serendipityBaseUrl}index.php?/plugin/bayes_recycle" method="post">
|
||||||
<div id="bayesControls">
|
<div id="bayesControls">
|
||||||
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_RESTORE}" name="restore"/>
|
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_RESTORE}" name="restore"/>
|
||||||
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_RECYCLER_EMPTY}" name="empty" />
|
<input type="submit" class="serendipityPrettyButton input_button" value="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_RECYCLER_EMPTY}" name="empty" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="bayesRecycler">
|
<div id="bayesRecycler">
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<div class="bayesRecyclerTableNavigation">
|
|
||||||
{else}
|
|
||||||
<ul class="bayesRecyclerTableNavigation plainList clearfix">
|
|
||||||
{/if}
|
|
||||||
{if $commentpage > 0}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<a class="serendipityIconLink" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=1&serendipity[commentpage]={$commentpage-1}"><img src="{serendipity_getFile file="admin/img/previous.png"}"/>{$CONST.PREVIOUS}</a>
|
|
||||||
{else}
|
|
||||||
<li class="prev"><a class="button_link" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=1&serendipity[commentpage]={$commentpage-1}" title="{$CONST.PREVIOUS}"><span class="icon-left-dir" aria-hidden="true"></span><span class="visuallyhidden"> {$CONST.PREVIOUS}</span></a></li>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{if ($commentpage+1)*20 < $comments|@count}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<a class="serendipityIconLinkRight" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=1&serendipity[commentpage]={$commentpage+1}">{$CONST.NEXT} <img src="{serendipity_getFile file="admin/img/next.png"}"/></a>
|
|
||||||
{else}
|
|
||||||
<li class="next"><a class="button_link" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=1&serendipity[commentpage]={$commentpage+1}" title="{$CONST.NEXT}"><span class="visuallyhidden">{$CONST.NEXT} </span><span class="icon-right-dir" aria-hidden="true"></span></a></li>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
</div>
|
|
||||||
{else}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
{if $s9ybackend != 1}
|
|
||||||
<ul id="serendipity_comments_list" class="clearfix plainList zebra_list">
|
|
||||||
{/if}
|
|
||||||
{foreach from=$comments item=comment}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<details class="bayesRecyclerItem">
|
|
||||||
<summary>
|
|
||||||
<input type="checkbox" class="bayesRecyclerSelectBox" name="serendipity[selected][{$comment.id}]" />
|
|
||||||
<input type="hidden" name="serendipity[comments][{$comment.id}]" />
|
|
||||||
<table class="bayesRecyclerSummary">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{$CONST.AUTHOR}</th>
|
|
||||||
<th>{$CONST.COMMENT}</th>
|
|
||||||
<th>{$CONST.DATE}</th>
|
|
||||||
<th>{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_RATING}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>{$comment.author|truncate:20:"..."|escape:"html"}</td>
|
|
||||||
<td>{$comment.body|truncate:20:"..."|escape:"html"}</td>
|
|
||||||
<td>{$comment.timestamp|date_format:"%d.%m.%y, %R"}</td>
|
|
||||||
<td>{$comment.rating|regex_replace:"/\..*/":""}%</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</summary>
|
|
||||||
<dl class="bayesRecyclerList">
|
|
||||||
{foreach from=$types item=type}
|
|
||||||
<dt>{$type}</dt>
|
|
||||||
<dd>{$comment.$type|escape:"html"}</dd>
|
|
||||||
{/foreach}
|
|
||||||
<dt>{$CONST.Article}</dt>
|
|
||||||
<dd><a href="{$comment.article_link}" target="_blank">{$comment.article_title}</a></dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
</details>
|
<ul id="serendipity_comments_list" class="clearfix plainList zebra_list">
|
||||||
{else}
|
{foreach from=$comments item=comment}
|
||||||
<li id="comment_{$comment.id}" class="clearfix {cycle values="odd,even"}">
|
<li id="comment_{$comment.id}" class="clearfix {cycle values="odd,even"}">
|
||||||
<input type="hidden" name="serendipity[comments][{$comment.id}]">
|
<input type="hidden" name="serendipity[comments][{$comment.id}]">
|
||||||
<div class="form_check">
|
<div class="form_check">
|
||||||
<input id="serendipity[selected][{$comment.id}]" type="checkbox" class="bayesRecyclerSelectBox" name="serendipity[selected][{$comment.id}]">
|
<input id="serendipity[selected][{$comment.id}]" type="checkbox" class="bayesRecyclerSelectBox" name="serendipity[selected][{$comment.id}]">
|
||||||
<label for="serendipity[selected][{$comment.id}]" class="visuallyhidden">{$CONST.TOGGLE_SELECT}</label>
|
<label for="serendipity[selected][{$comment.id}]" class="visuallyhidden">{$CONST.TOGGLE_SELECT}</label>
|
||||||
</div>
|
</div>
|
||||||
<h4 id="c{$comment.id}">{$comment.author|truncate:20:"..."|escape:"html"} {$CONST.IN_REPLY_TO} <a href="{$comment.article_link}" target="_blank">{$comment.article_title}</a> {$CONST.ON} {$comment.timestamp|date_format:"%d.%m.%y, %R"} – <span title="{$CONST.PLUGIN_EVENT_SPAMBLOCK_BAYES_RATING}">{$comment.rating|regex_replace:"/\..*/":""}%</span> <button class="toggle_info button_link" type="button" data-href="#comment_data_{$comment.id}"><span class="icon-info-circled" aria-hidden="true"></span><span class="visuallyhidden"> More</span></button></h4>
|
<h4 id="c{$comment.id}">{$comment.author|truncate:20:"..."|escape:"html"} {$CONST.IN_REPLY_TO} <a href="{$comment.article_link}" target="_blank">{$comment.article_title}</a> {$CONST.ON} {$comment.timestamp|date_format:"%d.%m.%y, %R"} <button class="toggle_info button_link" type="button" data-href="#comment_data_{$comment.id}"><span class="icon-info-circled" aria-hidden="true"></span><span class="visuallyhidden"> More</span></button></h4>
|
||||||
|
|
||||||
<div id="comment_data_{$comment.id}" class="additional_info">
|
<div id="comment_data_{$comment.id}" class="additional_info">
|
||||||
<dl class="comment_data clearfix">
|
<dl class="comment_data clearfix">
|
||||||
{foreach from=$types item=type}
|
<dt>name</dt>
|
||||||
<dt>{$type}</dt>
|
<dd>{$comment.author|escape:"html"}</dd>
|
||||||
<dd>{$comment.$type|escape:"html"}</dd>
|
<dt>email</dt>
|
||||||
{/foreach}
|
<dd>{$comment.email|escape:"html"}</dd>
|
||||||
|
<dt>url</dt>
|
||||||
|
<dd>{$comment.url|escape:"html"}</dd>
|
||||||
|
<dt>comment</dt>
|
||||||
|
<dd>{$comment.body|escape:"html"}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
|
||||||
{/foreach}
|
{/foreach}
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<div class="bayesRecyclerTableNavigation">
|
|
||||||
{else}
|
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="bayesRecyclerTableNavigation plainList clearfix">
|
|
||||||
{/if}
|
|
||||||
{if $commentpage > 0}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<a class="serendipityIconLink" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=1&serendipity[commentpage]={$commentpage-1}"><img src="{serendipity_getFile file="admin/img/previous.png"}"/>{$CONST.PREVIOUS}</a>
|
|
||||||
{else}
|
|
||||||
<li class="prev"><a class="button_link" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=1&serendipity[commentpage]={$commentpage-1}" title="{$CONST.PREVIOUS}"><span class="icon-left-dir" aria-hidden="true"></span><span class="visuallyhidden"> {$CONST.PREVIOUS}</span></a></li>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{if ($commentpage+1)*20 < $comments|@count}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<a class="serendipityIconLinkRight" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=1&serendipity[commentpage]={$commentpage+1}">{$CONST.NEXT} <img src="{serendipity_getFile file="admin/img/next.png"}"/></a>
|
|
||||||
{else}
|
|
||||||
<li class="next"><a class="button_link" href="?serendipity[adminModule]=event_display&serendipity[adminAction]=spamblock_bayes&serendipity[subpage]=1&serendipity[commentpage]={$commentpage+1}" title="{$CONST.NEXT}"><span class="visuallyhidden">{$CONST.NEXT} </span><span class="icon-right-dir" aria-hidden="true"></span></a></li>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{if $s9ybackend == 1}
|
|
||||||
</div>
|
|
||||||
{else}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{if $s9ybackend == 1}
|
|
||||||
<script src="{$path}details.polyfill.min.js" type="text/javascript"></script>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
|
@ -1,302 +1,11 @@
|
||||||
var httpRequest;
|
function spam(commentID) {
|
||||||
var lastID;
|
$.post(learncommentPath, {id: commentID, category: 'spam'}).done(function() {
|
||||||
|
location.reload();
|
||||||
function ham(id) {
|
});
|
||||||
if (window.XMLHttpRequest) { // Mozilla, Safari, Opera, IE7
|
|
||||||
httpRequest = new XMLHttpRequest();
|
|
||||||
} else if (window.ActiveXObject) { // IE6, IE5
|
|
||||||
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
|
|
||||||
}
|
|
||||||
var copyId = id;
|
|
||||||
httpRequest.onreadystatechange = function() {
|
|
||||||
setMessage(copyId);
|
|
||||||
}
|
|
||||||
lastID = id;
|
|
||||||
// Method, url, Async = true / Sync = false
|
|
||||||
httpRequest.open('POST', learncommentPath, true);
|
|
||||||
httpRequest.setRequestHeader('content-Type', 'application/x-www-form-urlencoded; charset='+bayesCharset);
|
|
||||||
if (id.constructor == Array) {
|
|
||||||
var length = id.length
|
|
||||||
for (var i=0;i<length;i++) {
|
|
||||||
setLoadIndicator(id[i]);
|
|
||||||
}
|
|
||||||
id = id.join(';');
|
|
||||||
} else {
|
|
||||||
setLoadIndicator(id);
|
|
||||||
}
|
|
||||||
httpRequest.send('id='+id+'&category=ham'); // Start request
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function spam(id) {
|
function ham(commentID) {
|
||||||
if (window.XMLHttpRequest) { // Mozilla, Safari, Opera, IE7
|
$.post(learncommentPath, {id: commentID, category: 'ham'}).done(function() {
|
||||||
httpRequest = new XMLHttpRequest();
|
location.reload();
|
||||||
} else if (window.ActiveXObject) { // IE6, IE5
|
});
|
||||||
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
|
|
||||||
}
|
}
|
||||||
//ids will be changed later on if an array, but we want the array
|
|
||||||
var copyId = id;
|
|
||||||
httpRequest.onreadystatechange = function() {
|
|
||||||
deleteComment(copyId);
|
|
||||||
}
|
|
||||||
lastID = id;
|
|
||||||
httpRequest.open('POST', learncommentPath, true);
|
|
||||||
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset='+bayesCharset);
|
|
||||||
if (id.constructor == Array) {
|
|
||||||
var length = id.length
|
|
||||||
for (var i=0;i<length;i++) {
|
|
||||||
setLoadIndicator(id[i]);
|
|
||||||
}
|
|
||||||
id = id.join(';');
|
|
||||||
} else {
|
|
||||||
setLoadIndicator(id);
|
|
||||||
}
|
|
||||||
httpRequest.send('id='+id+'&category=spam'); // Start request
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLoadIndicator(id) {
|
|
||||||
var imgElement = document.createElement("img");
|
|
||||||
imgElement.src=bayesLoadIndicator;
|
|
||||||
document.getElementById('comment_'+id).appendChild(imgElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMessage(ids) {
|
|
||||||
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
|
|
||||||
getAllRatings();
|
|
||||||
if (! (ids.constructor == Array)) {
|
|
||||||
//without the use of id, the new ids would be undefined
|
|
||||||
var id = ids;
|
|
||||||
ids = new Array();
|
|
||||||
//push as workaround, Array(id) produces errors
|
|
||||||
ids.push(id);
|
|
||||||
}
|
|
||||||
var length = ids.length;
|
|
||||||
for (var i=0;i<length;i++) {
|
|
||||||
var textElement = document.createElement("p");
|
|
||||||
textElement.classList.add('bayes_done');
|
|
||||||
textElement.appendChild(document.createTextNode(bayesDone));
|
|
||||||
//update ratings to make the effect visible
|
|
||||||
getAllRatings();
|
|
||||||
//remove load-indicator:
|
|
||||||
var comment = document.getElementById('comment_'+ids[i]);
|
|
||||||
comment.removeChild(comment.lastChild);
|
|
||||||
comment.appendChild(textElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteComment(ids) {
|
|
||||||
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
|
|
||||||
if (! (ids.constructor == Array)) {
|
|
||||||
//without the use of id, the new ids would be undefined
|
|
||||||
var id = ids;
|
|
||||||
ids = new Array();
|
|
||||||
//push as workaround, Array(id) produces errors
|
|
||||||
ids.push(id);
|
|
||||||
}
|
|
||||||
var length = ids.length;
|
|
||||||
for (var i=0;i<length;i++) {
|
|
||||||
var comment = document.getElementById('comment_'+ids[i]);
|
|
||||||
//by default, there is a hr below the comment and a message above
|
|
||||||
var divider = nextObject(comment.parentNode);
|
|
||||||
var message = previousObject(comment.parentNode);
|
|
||||||
|
|
||||||
remove(comment)
|
|
||||||
remove(message);
|
|
||||||
remove(divider);
|
|
||||||
getAllRatings();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//help selecting the various commentelements
|
|
||||||
function nextObject (n) {
|
|
||||||
do
|
|
||||||
n = n.nextSibling;
|
|
||||||
while (n && n.nodeType != 1);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
function previousObject (p) {
|
|
||||||
do
|
|
||||||
p = p.previousSibling;
|
|
||||||
while (p && p.nodeType != 1);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(p) {
|
|
||||||
p.parentNode.removeChild(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Rating-display*/
|
|
||||||
|
|
||||||
function getRating(id) {
|
|
||||||
var httpRequest;
|
|
||||||
if (window.XMLHttpRequest) { // Mozilla, Safari, Opera, IE7
|
|
||||||
httpRequest = new XMLHttpRequest();
|
|
||||||
} else if (window.ActiveXObject) { // IE6, IE5
|
|
||||||
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
|
|
||||||
}
|
|
||||||
|
|
||||||
//define the callback here to make it possible to have a local httpRequest
|
|
||||||
//which is needed for the multiple requests we are sending
|
|
||||||
httpRequest.onreadystatechange = function() {
|
|
||||||
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
|
|
||||||
var response = httpRequest.responseText.split(';');
|
|
||||||
var length = response.length;
|
|
||||||
for (var i=0; i < length; i=i+2) {
|
|
||||||
updateRating(response[i+1], response[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
httpRequest.open('POST', ratingPath, true);
|
|
||||||
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset='+bayesCharset);
|
|
||||||
|
|
||||||
//fetch all ratings of all ids in one step to be faster
|
|
||||||
if (id.constructor == Array) {
|
|
||||||
id = id.join(';');
|
|
||||||
}
|
|
||||||
httpRequest.send('id='+id); // Start request
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateRating(id, rating) {
|
|
||||||
if (typeof id == 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var ratingElement = document.getElementById(id+'_rating');
|
|
||||||
if (ratingElement != null) {
|
|
||||||
ratingElement.innerHTML = rating;
|
|
||||||
} else {
|
|
||||||
var textElement = document.createElement('span');
|
|
||||||
textElement.setAttribute('class', 'spamblockBayesRating');
|
|
||||||
ratingElement = document.createElement('span');
|
|
||||||
ratingElement.setAttribute('id', id+'_rating');
|
|
||||||
ratingElement.appendChild(document.createTextNode(rating));
|
|
||||||
textElement.appendChild(ratingElement);
|
|
||||||
var help = document.createElement('img');
|
|
||||||
help.setAttribute('src', bayesHelpImage);
|
|
||||||
textElement.setAttribute('title', bayesHelpTitle);
|
|
||||||
textElement.appendChild(help);
|
|
||||||
//id_mail only will exist in s9y 1.6
|
|
||||||
var ratingLocation = document.getElementById(id+'_mail');
|
|
||||||
if (ratingLocation == null) {
|
|
||||||
ratingLocation = document.getElementById('comment_'+id);
|
|
||||||
}
|
|
||||||
if (ratingLocation != null) {
|
|
||||||
ratingLocation.appendChild(textElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeRatings() {
|
|
||||||
//reduce number of searched elements via expensive getElementByClass
|
|
||||||
var form = document.getElementById('formMultiDelete');
|
|
||||||
var ratings = getElementByClass('spamblockBayesRating', form);
|
|
||||||
length = ratings.length
|
|
||||||
for (var i=0; i < length; i++) {
|
|
||||||
ratings[i].parentNode.removeChild(ratings[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAllRatings() {
|
|
||||||
var form = document.getElementById('formMultiDelete');
|
|
||||||
//We need them to fetch the comment-ids
|
|
||||||
var controls = getElementByClass('spamblockBayesControls', form, 'a');
|
|
||||||
var length = controls.length;
|
|
||||||
var ids = new Array()
|
|
||||||
for (var i=0; i < length; i++) {
|
|
||||||
if (controls[i].getAttribute('id').indexOf('spam') != -1) {
|
|
||||||
ids.push(controls[i].getAttribute('id').replace('spam', ''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
length = ids.length;
|
|
||||||
//giving 10000s of comments to classify() will probably
|
|
||||||
//produce a timeout - so split it in smaller pieces
|
|
||||||
var size = 100;
|
|
||||||
var blocks = Math.ceil(length / size);
|
|
||||||
for (i=0; i < blocks; i++) {
|
|
||||||
getRating(ids.slice(i*size, (i*size)+size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Something like this don't exist in Javascript
|
|
||||||
function getElementByClass(className, node, tag) {
|
|
||||||
if (node == null) {
|
|
||||||
node = document;
|
|
||||||
}
|
|
||||||
if (tag == null) {
|
|
||||||
tag = "*";
|
|
||||||
}
|
|
||||||
var allHTMLTags = node.getElementsByTagName(tag);
|
|
||||||
var classes = new Array();
|
|
||||||
var length = allHTMLTags.length;
|
|
||||||
for (var i=0; i < length; i++) {
|
|
||||||
//multiple classes are in the same string, so search carefully
|
|
||||||
if (allHTMLTags[i].className.indexOf(className) != -1) {
|
|
||||||
classes.push(allHTMLTags[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return classes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function placeSpambutton() {
|
|
||||||
var groupBayes = document.createElement('fieldset');
|
|
||||||
|
|
||||||
var legendBayes = document.createElement('legend');
|
|
||||||
legendBayes.innerHTML = bayesPlugin;
|
|
||||||
|
|
||||||
var buttonSpam = document.createElement('input');
|
|
||||||
buttonSpam.setAttribute('type', 'button');
|
|
||||||
buttonSpam.setAttribute('name', 'toogle');
|
|
||||||
buttonSpam.setAttribute('value', bayesSpambutton);
|
|
||||||
buttonSpam.setAttribute('class', 'serendipityPrettyButton input_button');
|
|
||||||
buttonSpam.setAttribute('onclick', 'markAllSpam()');
|
|
||||||
|
|
||||||
var buttonHam = document.createElement('input');
|
|
||||||
buttonHam.setAttribute('type', 'button');
|
|
||||||
buttonHam.setAttribute('name', 'toogle');
|
|
||||||
buttonHam.setAttribute('value', bayesHambutton);
|
|
||||||
buttonHam.setAttribute('class', 'serendipityPrettyButton input_button');
|
|
||||||
buttonHam.setAttribute('onclick', 'markAllHam()');
|
|
||||||
|
|
||||||
groupBayes.appendChild(legendBayes);
|
|
||||||
groupBayes.appendChild(buttonHam);
|
|
||||||
groupBayes.appendChild(buttonSpam);
|
|
||||||
|
|
||||||
document.querySelector('.invert_selection').parentNode.appendChild(groupBayes);
|
|
||||||
}
|
|
||||||
|
|
||||||
function markAllSpam() {
|
|
||||||
var ids = getChecked();
|
|
||||||
var length = ids.length;
|
|
||||||
spam(ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
function markAllHam() {
|
|
||||||
var ids = getChecked();
|
|
||||||
var length = ids.length;
|
|
||||||
ham(ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getChecked() {
|
|
||||||
var form = document.getElementById('formMultiDelete');
|
|
||||||
var checkboxes = getElementByClass('input_checkbox', form, 'input');
|
|
||||||
var length = checkboxes.length;
|
|
||||||
if (length == 0) {
|
|
||||||
// the classes changed in 2.0 backend, so we use another selector there
|
|
||||||
checkboxes = form.querySelectorAll('.multidelete');
|
|
||||||
length = checkboxes.length;
|
|
||||||
}
|
|
||||||
var ids = new Array()
|
|
||||||
for (var i=0; i < length; i++) {
|
|
||||||
if (checkboxes[i].checked) {
|
|
||||||
var id = checkboxes[i].name.split('[')[2];
|
|
||||||
id = id.substr(0, id.length-1);
|
|
||||||
ids.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
addLoadEvent(placeSpambutton);
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
jQuery(function(a){(function(){var b=this;this.hideDetailChildren=function(c){var d=c instanceof jQuery?c[0].childNodes:c.childNodes,e=d.length;a(c).attr("open",!1);if(a.browser.safari==!0)for(var f=0;f<e;f++)if(d[f].nodeType==3&&d[f].textContent!=""){var g=a("<span />");g.text(d[f].textContent).hide(),a(d[f]).after(g),d[f].textContent="",e++}a.each(d,function(d,e){if(a(e)[0].nodeType==1&&e==a(e).parent().find("> summary:first-of-type")[0])a(e).data("processed")!=!0&&(a(e).css({display:"block",cursor:"pointer"}).data("processed",!0).addClass("detailHidden").bind("click",function(){b.toggleDetailChildren(a(this))}),a(c).prepend(a(e)));else if(a(e)[0].nodeType==3&&!e.isElementContentWhitespace&&!!a.browser.safari==!1){var f=a("<span />");f.text(e.textContent).hide(),a(e).after(f),e.textContent=""}else if(a(c).find("> summary").length==0){var g=a("<summary />").text("Details").css({display:"block",cursor:"pointer"}).data("processed",!0).addClass("detailHidden").bind("click",function(){b.toggleDetailChildren(a(this))});a(c).prepend(g)}a(c).find("> :visible:not(summary:first-child)").hide()})},this.showDetailChildren=function(b){a(b).attr("open",!0),a.each(a(b).find("> *"),function(b,c){a(c).show()})},this.toggleDetailChildren=function(a){a.hasClass("detailHidden")?(a.removeClass("detailHidden"),b.showDetailChildren(a.parents("details")[0])):(a.addClass("detailHidden"),b.hideDetailChildren(a.parents("details")[0]))};var c=function(a){var b=a.createElement("details"),c,d,e;return"open"in b?(d=a.body||function(){var b=a.documentElement;return c=!0,b.insertBefore(a.createElement("body"),b.firstElementChild||b.firstChild)}(),b.innerHTML="<summary>a</summary>b",b.style.display="block",d.appendChild(b),e=b.offsetHeight,b.open=!0,e=e!=b.offsetHeight,d.removeChild(b),c&&d.parentNode.removeChild(d),e):!1}(document);if(c==!1){if(a("details").length!==0){var d=a("<style />").text('summary {-webkit-text-size-adjust: none;} details > summary:first-child:before {content: "\u25bc"; font-size:.9em;padding-right:6px;font-family:"Courier New";} details > summary.detailHidden:first-child:before {content: "\u25ba";font-size:.9em;padding-right:6px;font-family:"Courier New";}');a("head").append(d)}a.each(a("details"),function(a,c){b.hideDetailChildren(c)})}})()})
|
|
|
@ -1,166 +0,0 @@
|
||||||
(function($) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ensures an element's text is cut off at a certain maximum number of lines.
|
|
||||||
*
|
|
||||||
* The element must have a nonzero width when empty. (Most commonly a block
|
|
||||||
* element, such as a <p>, will fit this criterion.) The contained, HTML-free
|
|
||||||
* text will be truncated to fit the width along with an "end", e.g., '…'.
|
|
||||||
* Truncation will only occur along whitespace.
|
|
||||||
*
|
|
||||||
* Assumptions:
|
|
||||||
* - The element is empty or contains only a single text node.
|
|
||||||
*
|
|
||||||
* Guarantees:
|
|
||||||
* - The displayed text will never surpass the requested number of lines.
|
|
||||||
* - If truncation occurs and the end string fits within the width of the
|
|
||||||
* element, the end string will be displayed.
|
|
||||||
* - As many words in the element's text will be displayed as possible.
|
|
||||||
*
|
|
||||||
* Options:
|
|
||||||
* end: (default '…') String to append to the end when truncating. May also
|
|
||||||
* be a DOM node.
|
|
||||||
* always_end: String or DOM node which must always be appended, whether or
|
|
||||||
* not we truncate. (This may actually cause truncation which
|
|
||||||
* would otherwise not occur.)
|
|
||||||
* lines: (default 1) Number of lines of text to display.
|
|
||||||
*
|
|
||||||
* --
|
|
||||||
* Bodacity JavaScript Utilities
|
|
||||||
* http://adamhooper.com/bodacity
|
|
||||||
* Public Domain (no licensing restrictions)
|
|
||||||
*/
|
|
||||||
function Excerpt(elem, options) {
|
|
||||||
this.$elem = $(elem);
|
|
||||||
this.options = $.extend({
|
|
||||||
end: '…',
|
|
||||||
always_end: undefined,
|
|
||||||
lines: 1
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
this.original_text = this.$elem.text();
|
|
||||||
|
|
||||||
if (typeof(this.options.end) != 'string') {
|
|
||||||
// Assume it's a DOM element or jQuery object
|
|
||||||
this.$end_node = $(this.options.end);
|
|
||||||
this.end_string = this.$end_node.text();
|
|
||||||
} else {
|
|
||||||
this.end_string = this.options.end;
|
|
||||||
this.$end_node = $(document.createTextNode(this.end_string));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.always_end) {
|
|
||||||
if (typeof(this.options.always_end) != 'string') {
|
|
||||||
this.$always_end_node = $(this.options.always_end);
|
|
||||||
this.always_end_string = this.$always_end_node.text();
|
|
||||||
} else {
|
|
||||||
this.always_end_string = this.options.always_end;
|
|
||||||
this.$always_end_node = $(document.createTextNode(this.always_end_string));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._attach();
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
$.extend(Excerpt.prototype, {
|
|
||||||
_attach: function() {
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Resets the element based on its original text, such that it only the
|
|
||||||
* desired number of lines are shown and there is no overflow.
|
|
||||||
*/
|
|
||||||
refresh: function() {
|
|
||||||
if (!this.$elem[0].firstChild) return; // It's already empty
|
|
||||||
|
|
||||||
var wh = this._calculate_desired_width_height();
|
|
||||||
var w = wh[0];
|
|
||||||
var h = wh[1];
|
|
||||||
|
|
||||||
var s = this.original_text.replace(/\s+/, ' ');
|
|
||||||
|
|
||||||
var spaces = []; // Array of indices to space characters
|
|
||||||
spaces.push(0);
|
|
||||||
for (var i = 1; i < s.length; i++) {
|
|
||||||
if (s.charAt(i) == ' ') {
|
|
||||||
spaces.push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spaces.push(s.length);
|
|
||||||
|
|
||||||
var lbound = 0;
|
|
||||||
var rbound = spaces.length - 1;
|
|
||||||
|
|
||||||
var cur = 0;
|
|
||||||
var cutoff = 100;
|
|
||||||
while (lbound < rbound && cutoff) {
|
|
||||||
cur = Math.floor(lbound + (rbound - lbound) / 2);
|
|
||||||
if (cur == lbound) cur += 1;
|
|
||||||
|
|
||||||
var sub = this._substring(s, spaces[cur]);
|
|
||||||
if (this._is_string_small_enough(sub, w, h)) {
|
|
||||||
lbound = cur;
|
|
||||||
} else {
|
|
||||||
rbound = cur - 1;
|
|
||||||
}
|
|
||||||
cutoff -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$elem[0].firstChild.nodeValue = this._substring(s, spaces[lbound], true);
|
|
||||||
if (s.length != spaces[lbound]) {
|
|
||||||
this.$elem.append(this.$end_node.clone());
|
|
||||||
}
|
|
||||||
if (this.$always_end_node) {
|
|
||||||
this.$elem.append(this.$always_end_node.clone());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_substring: function(s, length, exclude_end_string) {
|
|
||||||
if (length == s.length) return s;
|
|
||||||
var substr = s.substr(0, length);
|
|
||||||
if (exclude_end_string) {
|
|
||||||
return substr;
|
|
||||||
} else {
|
|
||||||
return substr + this.end_string + (this.always_end_string || '');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_is_string_small_enough: function(s, w, h) {
|
|
||||||
var node = this.$elem[0];
|
|
||||||
|
|
||||||
node.firstChild.nodeValue = s;
|
|
||||||
return node.offsetHeight <= h && node.offsetWidth <= w;
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns the desired [width, height] in px.
|
|
||||||
*
|
|
||||||
* Modifies this.$elem contents as a side-effect.
|
|
||||||
*/
|
|
||||||
_calculate_desired_width_height: function() {
|
|
||||||
var node = this.$elem[0];
|
|
||||||
|
|
||||||
var s = ' ';
|
|
||||||
for (var i = 0; i < this.options.lines - 1; i++) {
|
|
||||||
s += "<br /> ";
|
|
||||||
}
|
|
||||||
|
|
||||||
node.innerHTML = s;
|
|
||||||
|
|
||||||
var w = node.offsetWidth;
|
|
||||||
var h = node.offsetHeight;
|
|
||||||
|
|
||||||
node.innerHTML = ' '; // anything non-empty
|
|
||||||
|
|
||||||
return [w, h];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$.fn.excerpt = function(options) {
|
|
||||||
return $(this).each(function() {
|
|
||||||
new Excerpt(this, options);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
})(jQuery);
|
|
|
@ -1,7 +0,0 @@
|
||||||
/*
|
|
||||||
HeatColor, by Josh Nathanson
|
|
||||||
A plugin for jQuery
|
|
||||||
Complete documentation at http://www.jnathanson.com/blog/client/jquery/heatcolor/index.cfm
|
|
||||||
*/
|
|
||||||
|
|
||||||
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('p.V.10=9(j,k){4 l={M:9(){v p(8)},q:0,t:0,N:0.Z,F:\'E\',y:X};u(k){p.W(l,k)};4 m={K:9(a,c,d){4 e=(a-c)/(d-c);4 f=l.F==\'E\'?0.5*e+1.7*(1-e):e+0.2+5.5*(1-e);4 h=Y;4 i=2*6.A;4 x=f+e*i;x=l.F!=\'E\'?-x:x;4 r=8.w(6.z((6.D(x)+1)*h));4 g=8.w(6.z((6.D(x+6.A/2)+1)*h));4 b=8.w(6.z((6.D(x+6.A)+1)*h));v\'#\'+r+g+b},w:9(a){4 n=6.z(a+l.N*(U-a));4 s=n.T(S);s=s.C==1?\'0\'+s:s;v s},J:9(c){4 d=[];c.I(9(){d.R(j.B(p(8)))});d=d.Q(9(a,b){v a-b});l.t=!l.y?d[d.C-1]:d[0];l.q=!l.y?d[0]:d[d.C-1]}};u(!l.q&&!l.t)m.J(p(8));H u(l.y){4 o=l.q;l.q=l.t;l.t=o}p(8).I(9(){4 a=p(8);4 b=j.B(a);4 c=m.K(b,l.q,l.t);4 d=l.M.B(a);u(d[0].L==1)d.O("P-G",c);H u(d[0].L==3)d.O("G",c)});v(8)}',62,63,'||||var||Math||this|function||||||||||||||||jQuery|minval|||maxval|if|return|process||reverseOrder|floor|PI|apply|length|cos|roygbiv|colorStyle|color|else|each|setMaxAndMin|findcolor|nodeType|elementFunction|lightness|css|background|sort|push|16|toString|256|fn|extend|false|128|75|heatcolor'.split('|'),0,{}))
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 673 B |
|
@ -1,6 +0,0 @@
|
||||||
-----BEGIN PUBLIC KEY-----
|
|
||||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyUQQGNlUmGwwPWz11ZGndNyrv
|
|
||||||
xxg/V7ldOlUSw3kNuhhAbNk+DT05soHsEXPKQHudE9P2WrHv8UtgdQBiQkbpzNGY
|
|
||||||
vFePrT3gvvm/YfMYOkfhphBm/6ZHdg0l5HN+YRQ0+5oaZD7s3tLRROnucAY5qSeb
|
|
||||||
j0fKR1EfeOIO/r4tTQIDAQAB
|
|
||||||
-----END PUBLIC KEY-----
|
|
|
@ -1,138 +0,0 @@
|
||||||
#bayesNav {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
#bayesNav a {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
#bayesNav li:last-child a {
|
|
||||||
padding-right: 0.3em;
|
|
||||||
}
|
|
||||||
#bayesNav ul {
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 1px solid;
|
|
||||||
}
|
|
||||||
#bayesNav li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.5em;
|
|
||||||
border-right: 1px solid;
|
|
||||||
}
|
|
||||||
#bayesNav h3 {
|
|
||||||
display: inline;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
#bayesContent {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
#bayesControls * {
|
|
||||||
margin-left: 1em;
|
|
||||||
margin-right: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#bayesLearnTable {
|
|
||||||
padding-top: 1em;
|
|
||||||
margin-bottom: 0.8em;
|
|
||||||
}
|
|
||||||
#bayesLearnTable td {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
#bayesControls {
|
|
||||||
float: right;
|
|
||||||
border: 1px solid;
|
|
||||||
border-top: 0;
|
|
||||||
-moz-border-radius-bottomleft: 5px;
|
|
||||||
-webkit-border-bottom-left-radius: 5px;
|
|
||||||
-moz-border-radius-bottomright: 5px;
|
|
||||||
-webkit-border-bottom-right-radius: 5px;
|
|
||||||
max-width: 21%;
|
|
||||||
padding-top: 1em;
|
|
||||||
}
|
|
||||||
#bayesControls form {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
#bayesDatabase {
|
|
||||||
padding-top: 2em;
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
#bayesSavedValues {
|
|
||||||
padding-top: 2em;
|
|
||||||
}
|
|
||||||
#bayesDatabaseTable th {
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
border: 1px solid;
|
|
||||||
}
|
|
||||||
caption {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
#bayesSavedValuesTable td {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#bayesRecyclerTable {
|
|
||||||
padding-top: 2em;
|
|
||||||
width: 78%;
|
|
||||||
table-layout: fixed;
|
|
||||||
|
|
||||||
}
|
|
||||||
#bayesRecyclerTable td {
|
|
||||||
padding-top: 1em;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
#bayesRecyclerTable th.select {
|
|
||||||
text-align: center;
|
|
||||||
width: 2em;
|
|
||||||
}
|
|
||||||
#bayesRecyclerTable td.select {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.ratingBox {
|
|
||||||
border-bottom: 1px solid grey;
|
|
||||||
}
|
|
||||||
.commentPart {
|
|
||||||
margin-left: 5em;
|
|
||||||
}
|
|
||||||
.rating {
|
|
||||||
margin-left: 5em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.commentType {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.finalRating {
|
|
||||||
padding-left: 3.3em;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.5em;
|
|
||||||
border-bottom: 1px solid grey;
|
|
||||||
}
|
|
||||||
#bayesAnalysisList li {
|
|
||||||
margin: 1em;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.serendipityIconLinkRight {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
#bayesControls label {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
input[type="submit"] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.bayesTrojaButtons {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
fieldset {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
#trojaImport {
|
|
||||||
margin-left: 1.1em;
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
|
|
||||||
function sortwithcolor(column) {
|
|
||||||
$("#bayesDatabaseTable > tbody > tr").heatcolor(
|
|
||||||
function() { return $("td:nth-child(" + column + ")", this).text(); },
|
|
||||||
{ colorStyle: 'greentored' }
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
var checked = false;
|
|
||||||
$("#bayesDeleteDB").submit(function(event) {
|
|
||||||
if (! checked) {
|
|
||||||
event.preventDefault()
|
|
||||||
var answer = confirm("Completely delete the database?")
|
|
||||||
if (answer){
|
|
||||||
window.location.href = $("#bayesDeleteDB").attr('action');
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$("th").click(function() {
|
|
||||||
$(this).siblings().css("background-color","#cccccc").end().css("background-color","#dd0000");
|
|
||||||
sortwithcolor( $(this).parent().children().index( this ) + 1 );
|
|
||||||
});
|
|
||||||
|
|
||||||
function shortenAll(textclass, lines) {
|
|
||||||
$.each( $('.'+textclass), function() {
|
|
||||||
shorten($(this), lines);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function shorten($element, lines) {
|
|
||||||
var o = $element.text();
|
|
||||||
$r = $('<a href="#" style="padding-left: 5px;">... show</a>');
|
|
||||||
$element.excerpt({ lines: lines, end: $r});
|
|
||||||
$element.find('a').click(function(e){
|
|
||||||
e.preventDefault();
|
|
||||||
var $cell = $(this).parent();
|
|
||||||
var $link = $('<a></a>')
|
|
||||||
.attr('href', '#')
|
|
||||||
.text('shorten')
|
|
||||||
.css('padding-left', '5px');
|
|
||||||
$link.click( function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$link.remove();
|
|
||||||
shorten($cell);
|
|
||||||
});
|
|
||||||
$cell.text(o);
|
|
||||||
$cell.append($link);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function colorize() {
|
|
||||||
$ratings = $(".ratingBox").children(".rating");
|
|
||||||
$.each($ratings, function() {
|
|
||||||
var rating = parseInt($(this).text().replace("%",""));
|
|
||||||
if (rating > 70) {
|
|
||||||
$(this).parent().css('background', 'rgba(249, 199, 199, 0.5)');
|
|
||||||
} else if ( rating > 10) {
|
|
||||||
$(this).parent().css('background', 'rgba(248, 246, 137, 0.5)');
|
|
||||||
} else if (rating >= 0){
|
|
||||||
$(this).parent().css('background', 'rgba(202, 248, 199, 0.5)');
|
|
||||||
} else {
|
|
||||||
/*detect those without a rating*/
|
|
||||||
$(this).parent().css('background', 'rgba(165, 165, 165, 0.5)');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue