Antispam Bee - Version 2.9.1

Version Description

Download this release

Release Info

Developer websupporter
Plugin Icon 128x128 Antispam Bee
Version 2.9.1
Comparing to
See all releases

Code changes from version 2.9.0 to 2.9.1

Files changed (7) hide show
  1. LICENSE.txt +0 -0
  2. antispam_bee.php +101 -27
  3. css/styles.css +38 -24
  4. css/styles.min.css +1 -1
  5. js/dashboard.js +8 -10
  6. js/dashboard.min.js +5 -5
  7. readme.txt +18 -6
LICENSE.txt CHANGED
File without changes
antispam_bee.php CHANGED
@@ -9,7 +9,7 @@
9
  * Domain Path: /lang
10
  * License: GPLv2 or later
11
  * License URI: http://www.gnu.org/licenses/gpl-2.0.html
12
- * Version: 2.9.0
13
  *
14
  * @package Antispam Bee
15
  **/
@@ -51,6 +51,13 @@ class Antispam_Bee {
51
  */
52
  public static $defaults;
53
 
 
 
 
 
 
 
 
54
  /**
55
  * The base.
56
  *
@@ -65,7 +72,6 @@ class Antispam_Bee {
65
  */
66
  private static $_salt;
67
 
68
-
69
  /**
70
  * The spam reason.
71
  *
@@ -182,6 +188,13 @@ class Antispam_Bee {
182
  'init_plugin_sources',
183
  )
184
  );
 
 
 
 
 
 
 
185
 
186
  } elseif ( self::_current_page( 'admin-post' ) ) {
187
  require_once dirname( __FILE__ ) . '/inc/gui.class.php';
@@ -802,7 +815,7 @@ class Antispam_Bee {
802
 
803
  $html .= "<tfoot><tr>\n";
804
  foreach ( $items as $date => $count ) {
805
- $html .= '<th>' . $date . "</th>\n";
806
  }
807
  $html .= "</tr></tfoot>\n";
808
 
@@ -1151,7 +1164,9 @@ class Antispam_Bee {
1151
  (?P<between3>[^>]*)
1152
  )
1153
  (?P<after>[^>]*) (?# match any additional optional attributes )
1154
- ><\/textarea> (?# the closing of the textarea )
 
 
1155
  )/x',
1156
  array( 'Antispam_Bee', 'replace_comment_field_callback' ),
1157
  $data,
@@ -1183,12 +1198,13 @@ class Antispam_Bee {
1183
  $id_script = '';
1184
  if ( ! empty( $matches['id1'] ) || ! empty( $matches['id2'] ) ) {
1185
  $output .= 'id="' . self::get_secret_id_for_post( self::$_current_post_id ) . '" ';
1186
- $id_script = '<script type="text/javascript">document.getElementById("comment").setAttribute( "id", "' . esc_js( md5( time() ) ) . '" );document.getElementById("' . esc_js( self::get_secret_id_for_post( self::$_current_post_id ) ) . '").setAttribute( "id", "comment" );</script>';
1187
  }
1188
 
1189
  $output .= ' name="' . esc_attr( self::get_secret_name_for_post( self::$_current_post_id ) ) . '" ';
1190
  $output .= $matches['between1'] . $matches['between2'] . $matches['between3'];
1191
  $output .= $matches['after'] . '>';
 
1192
  $output .= '</textarea><textarea id="comment" aria-hidden="true" name="comment" autocomplete="nope" style="padding:0;clip:rect(1px, 1px, 1px, 1px);position:absolute !important;white-space:nowrap;height:1px;width:1px;overflow:hidden;" tabindex="-1"></textarea>';
1193
 
1194
  $output .= $id_script;
@@ -1621,32 +1637,16 @@ class Antispam_Bee {
1621
  private static function _is_db_spam( $ip, $url = '', $email = '' ) {
1622
  global $wpdb;
1623
 
1624
- // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
1625
- $sql = '
1626
- select
1627
- meta_value as ip
1628
- from
1629
- ' . $wpdb->commentmeta . ' as meta,
1630
- ' . $wpdb->comments . ' as comments
1631
- where
1632
- comments.comment_ID = meta.comment_id
1633
- AND meta.meta_key = "antispam_bee_iphash"
1634
- AND comments.comment_approved="spam"';
1635
- $hashed_ips = $wpdb->get_col( $sql );
1636
- if ( ! empty( $hashed_ips ) ) {
1637
- foreach ( $hashed_ips as $hash ) {
1638
- if ( wp_check_password( $ip, $hash ) ) {
1639
- return true;
1640
- }
1641
- }
1642
- }
1643
-
1644
  $params = array();
1645
  $filter = array();
1646
  if ( ! empty( $url ) ) {
1647
  $filter[] = '`comment_author_url` = %s';
1648
  $params[] = wp_unslash( $url );
1649
  }
 
 
 
 
1650
 
1651
  if ( ! empty( $email ) ) {
1652
  $filter[] = '`comment_author_email` = %s';
@@ -1656,8 +1656,8 @@ class Antispam_Bee {
1656
  return false;
1657
  }
1658
 
 
1659
  // phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
1660
- // ToDo: Have a closer look on this SQL Query.
1661
  $filter_sql = implode( ' OR ', $filter );
1662
 
1663
  $result = $wpdb->get_var(
@@ -1851,7 +1851,25 @@ class Antispam_Bee {
1851
  return ! in_array( $detected_lang, $allowed_lang, true );
1852
  }
1853
 
1854
- if ( mb_strlen( $comment_text ) < 10 ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1855
  return false;
1856
  }
1857
 
@@ -2073,6 +2091,7 @@ class Antispam_Bee {
2073
  'ave' => 'ae',
2074
  'aym' => 'ay',
2075
  'aze' => 'az',
 
2076
  );
2077
 
2078
  if ( array_key_exists( $franc_code, $codes ) ) {
@@ -2665,6 +2684,8 @@ class Antispam_Bee {
2665
  $secret = substr( sha1( md5( 'comment-id' . self::$_salt . (int) $post_id ) ), 0, 10 );
2666
  }
2667
 
 
 
2668
  /**
2669
  * Filters the secret for a post, which is used in the textarea name attribute.
2670
  *
@@ -2696,6 +2717,8 @@ class Antispam_Bee {
2696
  $secret = substr( sha1( md5( 'comment-id' . self::$_salt . (int) $post_id ) ), 0, 10 );
2697
  }
2698
 
 
 
2699
  /**
2700
  * Filters the secret for a post, which is used in the textarea id attribute.
2701
  *
@@ -2711,6 +2734,23 @@ class Antispam_Bee {
2711
  );
2712
  }
2713
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2714
  /**
2715
  * Returns 'spam'
2716
  *
@@ -2738,6 +2778,40 @@ class Antispam_Bee {
2738
  $parts = wp_parse_url( $url );
2739
  return ( is_array( $parts ) && isset( $parts[ $component ] ) ) ? $parts[ $component ] : '';
2740
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2741
  }
2742
 
2743
 
9
  * Domain Path: /lang
10
  * License: GPLv2 or later
11
  * License URI: http://www.gnu.org/licenses/gpl-2.0.html
12
+ * Version: 2.9.1
13
  *
14
  * @package Antispam Bee
15
  **/
51
  */
52
  public static $defaults;
53
 
54
+ /**
55
+ * Which internal datastructure version we are running on.
56
+ *
57
+ * @var int
58
+ */
59
+ private static $db_version = 1;
60
+
61
  /**
62
  * The base.
63
  *
72
  */
73
  private static $_salt;
74
 
 
75
  /**
76
  * The spam reason.
77
  *
188
  'init_plugin_sources',
189
  )
190
  );
191
+ add_action(
192
+ 'admin_init',
193
+ array(
194
+ __CLASS__,
195
+ 'update_database',
196
+ )
197
+ );
198
 
199
  } elseif ( self::_current_page( 'admin-post' ) ) {
200
  require_once dirname( __FILE__ ) . '/inc/gui.class.php';
815
 
816
  $html .= "<tfoot><tr>\n";
817
  foreach ( $items as $date => $count ) {
818
+ $html .= '<th>' . date_i18n( 'j. F Y', $date ) . "</th>\n";
819
  }
820
  $html .= "</tr></tfoot>\n";
821
 
1164
  (?P<between3>[^>]*)
1165
  )
1166
  (?P<after>[^>]*) (?# match any additional optional attributes )
1167
+ > (?# the closing of the textarea opening tag )
1168
+ (?s)(?P<content>.*?) (?# any textarea content )
1169
+ <\/textarea> (?# the closing textarea tag )
1170
  )/x',
1171
  array( 'Antispam_Bee', 'replace_comment_field_callback' ),
1172
  $data,
1198
  $id_script = '';
1199
  if ( ! empty( $matches['id1'] ) || ! empty( $matches['id2'] ) ) {
1200
  $output .= 'id="' . self::get_secret_id_for_post( self::$_current_post_id ) . '" ';
1201
+ $id_script = '<script type="text/javascript">document.getElementById("comment").setAttribute( "id", "a' . substr( esc_js( md5( time() ) ), 0, 31 ) . '" );document.getElementById("' . esc_js( self::get_secret_id_for_post( self::$_current_post_id ) ) . '").setAttribute( "id", "comment" );</script>';
1202
  }
1203
 
1204
  $output .= ' name="' . esc_attr( self::get_secret_name_for_post( self::$_current_post_id ) ) . '" ';
1205
  $output .= $matches['between1'] . $matches['between2'] . $matches['between3'];
1206
  $output .= $matches['after'] . '>';
1207
+ $output .= $matches['content'];
1208
  $output .= '</textarea><textarea id="comment" aria-hidden="true" name="comment" autocomplete="nope" style="padding:0;clip:rect(1px, 1px, 1px, 1px);position:absolute !important;white-space:nowrap;height:1px;width:1px;overflow:hidden;" tabindex="-1"></textarea>';
1209
 
1210
  $output .= $id_script;
1637
  private static function _is_db_spam( $ip, $url = '', $email = '' ) {
1638
  global $wpdb;
1639
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1640
  $params = array();
1641
  $filter = array();
1642
  if ( ! empty( $url ) ) {
1643
  $filter[] = '`comment_author_url` = %s';
1644
  $params[] = wp_unslash( $url );
1645
  }
1646
+ if ( ! empty( $ip ) ) {
1647
+ $filter[] = '`comment_author_IP` = %s';
1648
+ $params[] = wp_unslash( $ip );
1649
+ }
1650
 
1651
  if ( ! empty( $email ) ) {
1652
  $filter[] = '`comment_author_email` = %s';
1656
  return false;
1657
  }
1658
 
1659
+ // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
1660
  // phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
 
1661
  $filter_sql = implode( ' OR ', $filter );
1662
 
1663
  $result = $wpdb->get_var(
1851
  return ! in_array( $detected_lang, $allowed_lang, true );
1852
  }
1853
 
1854
+ $word_count = 0;
1855
+ $text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $comment_text ), ' ' );
1856
+
1857
+ /*
1858
+ * translators: If your word count is based on single characters (e.g. East Asian characters),
1859
+ * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
1860
+ * Do not translate into your own language.
1861
+ */
1862
+ if ( strpos( _x( 'words', 'Word count type. Do not translate!' ), 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
1863
+ preg_match_all( '/./u', $text, $words_array );
1864
+ if ( isset( $words_array[0] ) ) {
1865
+ $word_count = count( $words_array[0] );
1866
+ }
1867
+ } else {
1868
+ $words_array = preg_split( "/[\n\r\t ]+/", $text, -1, PREG_SPLIT_NO_EMPTY );
1869
+ $word_count = count( $words_array );
1870
+ }
1871
+
1872
+ if ( $word_count < 10 ) {
1873
  return false;
1874
  }
1875
 
2091
  'ave' => 'ae',
2092
  'aym' => 'ay',
2093
  'aze' => 'az',
2094
+ 'nds' => 'de',
2095
  );
2096
 
2097
  if ( array_key_exists( $franc_code, $codes ) ) {
2684
  $secret = substr( sha1( md5( 'comment-id' . self::$_salt . (int) $post_id ) ), 0, 10 );
2685
  }
2686
 
2687
+ $secret = self::ensure_secret_starts_with_letter( $secret );
2688
+
2689
  /**
2690
  * Filters the secret for a post, which is used in the textarea name attribute.
2691
  *
2717
  $secret = substr( sha1( md5( 'comment-id' . self::$_salt . (int) $post_id ) ), 0, 10 );
2718
  }
2719
 
2720
+ $secret = self::ensure_secret_starts_with_letter( $secret );
2721
+
2722
  /**
2723
  * Filters the secret for a post, which is used in the textarea id attribute.
2724
  *
2734
  );
2735
  }
2736
 
2737
+ /**
2738
+ * Ensures that the secret starts with a letter.
2739
+ *
2740
+ * @param string $secret The secret.
2741
+ *
2742
+ * @return string
2743
+ */
2744
+ public static function ensure_secret_starts_with_letter( $secret ) {
2745
+
2746
+ $first_char = substr( $secret, 0, 1 );
2747
+ if ( is_numeric( $first_char ) ) {
2748
+ return chr( $first_char + 97 ) . substr( $secret, 1 );
2749
+ } else {
2750
+ return $secret;
2751
+ }
2752
+ }
2753
+
2754
  /**
2755
  * Returns 'spam'
2756
  *
2778
  $parts = wp_parse_url( $url );
2779
  return ( is_array( $parts ) && isset( $parts[ $component ] ) ) ? $parts[ $component ] : '';
2780
  }
2781
+
2782
+ /**
2783
+ * Updates the database structure if necessary
2784
+ */
2785
+ public static function update_database() {
2786
+ if ( self::db_version_is_current() ) {
2787
+ return;
2788
+ }
2789
+
2790
+ global $wpdb;
2791
+
2792
+ /**
2793
+ * In Version 2.9 the IP of the commenter was saved as a hash. We reverted this solution.
2794
+ * Therefore, we need to delete this unused data.
2795
+ */
2796
+ //phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
2797
+ $sql = 'delete from `' . $wpdb->commentmeta . '` where `meta_key` IN ("antispam_bee_iphash")';
2798
+ $wpdb->query( $sql );
2799
+ //phpcs:enable WordPress.WP.PreparedSQL.NotPrepared
2800
+
2801
+ update_option( 'antispambee_db_version', self::$db_version );
2802
+ }
2803
+
2804
+ /**
2805
+ * Whether the database structure is up to date.
2806
+ *
2807
+ * @return bool
2808
+ */
2809
+ private static function db_version_is_current() {
2810
+
2811
+ $current_version = absint( get_option( 'antispambee_db_version', 0 ) );
2812
+ return $current_version === self::$db_version;
2813
+
2814
+ }
2815
  }
2816
 
2817
 
css/styles.css CHANGED
@@ -8,13 +8,12 @@
8
  padding: 0;
9
  outline: 0;
10
  -webkit-box-sizing: border-box;
11
- -moz-box-sizing: border-box;
12
- box-sizing: border-box;
13
  }
14
 
15
  /* @end group */
16
 
17
-
18
  /* @group Browse Happy */
19
 
20
  .browsehappy {
@@ -26,7 +25,6 @@
26
 
27
  /* @end group */
28
 
29
-
30
  /* @group Columns */
31
 
32
  .ab-wrap {
@@ -34,6 +32,7 @@
34
  padding: 20px 0 0 0;
35
  text-rendering: optimizeLegibility;
36
  }
 
37
  .ab-column {
38
  float: left;
39
  margin: 0 0 0 10px;
@@ -42,16 +41,16 @@
42
 
43
  /* @end group */
44
 
45
-
46
  /* @group Headlines + Icons */
47
 
48
  .ab-column h3 {
49
  margin: 0;
50
- font-size: 14px;
51
  font-weight: normal;
52
  line-height: 20px;
53
- text-transform: uppercase;
54
  }
 
55
  .ab-column h3.icon:before {
56
  font: normal 30px/30px Dashicons;
57
  top: 4px;
@@ -64,51 +63,57 @@
64
  -webkit-font-smoothing: antialiased;
65
  -moz-osx-font-smoothing: grayscale;
66
  }
 
67
  .ab-column.ab-arrow h3.icon:before {
68
  content: '\f536';
69
  }
 
70
  .ab-column.ab-join h3.icon:before {
71
  content: '\f108';
72
  }
 
73
  .ab-column.ab-diff h3.icon:before {
74
  content: '\f237';
75
  }
76
 
77
  .ab-column h6 {
78
  clear: both;
79
- color: #8e959c;
80
  margin: 0 0 20px;
81
- font-size: 10px;
82
  font-weight: normal;
83
- text-transform: uppercase;
84
  }
85
 
86
  /* @end group */
87
 
88
-
89
  /* @group Form */
90
 
91
  .ab-column input[type="text"],
92
  .ab-column input[type="number"],
93
  .ab-column select {
94
- font-size: 12px;
95
  text-align: center;
96
  background: #f8f8f9;
97
  }
 
98
  .ab-column input[type="number"] {
99
  padding: 0;
100
  }
 
101
  .ab-column select[multiple] {
102
  width: 175px;
103
  min-height: 60px;
104
  }
 
105
  .ab-column input.ab-mini-field {
106
  width: 40px;
107
  }
 
108
  .ab-column .ab-medium-field {
109
  width: 100%;
110
- max-width :285px;
111
  }
 
112
  .ab-column input[type="text"] + label,
113
  .ab-column select + label {
114
  color: #8e959c;
@@ -119,15 +124,16 @@
119
 
120
  /* @end group */
121
 
122
-
123
  /* @group Column contents */
124
 
125
  .ab-column > ul {
126
  padding: 0 20px 0 0;
127
  }
 
128
  .ab-column:last-of-type > ul {
129
  border: 0;
130
  }
 
131
  .ab-column > ul > li {
132
  width: 330px;
133
  margin: 0 0 36px;
@@ -137,9 +143,9 @@
137
  }
138
 
139
  .ab-column > ul > li a {
140
- text-decoration: none;
141
- border-bottom: 1px dotted #e0e5e9;
142
  }
 
143
  .ab-column > ul > li a:hover {
144
  border-color: inherit;
145
  }
@@ -147,19 +153,21 @@
147
  .ab-column > ul > li label {
148
  cursor: default;
149
  display: inline-block;
150
- line-height: 24px;
151
  max-width: 286px;
 
152
  }
 
153
  .ab-column > ul > li label span {
154
- color: #8e959c;
155
  display: block;
156
- font-size: 12px;
157
  line-height: 16px;
 
158
  }
159
 
160
  /* @end group */
161
 
162
-
163
  /* @group Separator */
164
 
165
  .ab-column > ul > li:after,
@@ -175,10 +183,12 @@
175
  border-width: 10px 10px 0;
176
  border-style: solid;
177
  }
 
178
  .ab-column.ab-arrow > ul > li:before {
179
  bottom: -24px;
180
  border-color: #fff transparent;
181
  }
 
182
  .ab-column.ab-arrow > ul > li:after {
183
  bottom: -22px;
184
  border-color: #f1f1f1 transparent;
@@ -201,27 +211,29 @@
201
 
202
  /* @end group */
203
 
204
-
205
  /* @group Submit & Service */
206
 
207
  .ab-column--submit-service {
208
  width: 342px;
209
  margin-top: 20px;
210
- padding-right:20px;
211
-
212
  }
 
213
  .ab-column--submit-service p {
214
  padding: 5px 0;
215
  margin: 0;
216
  text-align: center;
217
  width: 100%;
218
  }
 
219
  .ab-column--submit-service p:first-of-type {
220
  border-top: 1px solid #e0e5e9;
221
  }
 
222
  .ab-column--submit-service p:last-of-type {
223
  border-bottom: 1px solid #e0e5e9;
224
  }
 
225
  .ab-column--submit-service .button {
226
  width: 100%;
227
  margin: 35px 0 10px;
@@ -229,7 +241,6 @@
229
 
230
  /* @end group */
231
 
232
-
233
  /* @group 2nd level */
234
 
235
  .ab-column > ul > li:last-of-type:after,
@@ -241,12 +252,15 @@
241
  margin: 10px 10px 0 26px;
242
  display: none;
243
  }
 
244
  .ab-column > ul > li > ul li {
245
  padding: 2px 0;
246
  }
 
247
  .ab-column > ul > li > ul label {
248
  margin: 0 0 0 7px;
249
  }
 
250
  .ab-column > ul > li > input[type="checkbox"]:checked ~ ul {
251
  display: block;
252
  }
8
  padding: 0;
9
  outline: 0;
10
  -webkit-box-sizing: border-box;
11
+ -moz-box-sizing: border-box;
12
+ box-sizing: border-box;
13
  }
14
 
15
  /* @end group */
16
 
 
17
  /* @group Browse Happy */
18
 
19
  .browsehappy {
25
 
26
  /* @end group */
27
 
 
28
  /* @group Columns */
29
 
30
  .ab-wrap {
32
  padding: 20px 0 0 0;
33
  text-rendering: optimizeLegibility;
34
  }
35
+
36
  .ab-column {
37
  float: left;
38
  margin: 0 0 0 10px;
41
 
42
  /* @end group */
43
 
 
44
  /* @group Headlines + Icons */
45
 
46
  .ab-column h3 {
47
  margin: 0;
48
+ font-size: 18px;
49
  font-weight: normal;
50
  line-height: 20px;
51
+ color: #23282d;
52
  }
53
+
54
  .ab-column h3.icon:before {
55
  font: normal 30px/30px Dashicons;
56
  top: 4px;
63
  -webkit-font-smoothing: antialiased;
64
  -moz-osx-font-smoothing: grayscale;
65
  }
66
+
67
  .ab-column.ab-arrow h3.icon:before {
68
  content: '\f536';
69
  }
70
+
71
  .ab-column.ab-join h3.icon:before {
72
  content: '\f108';
73
  }
74
+
75
  .ab-column.ab-diff h3.icon:before {
76
  content: '\f237';
77
  }
78
 
79
  .ab-column h6 {
80
  clear: both;
81
+ color: #555d66;
82
  margin: 0 0 20px;
 
83
  font-weight: normal;
84
+ font-size: 13px;
85
  }
86
 
87
  /* @end group */
88
 
 
89
  /* @group Form */
90
 
91
  .ab-column input[type="text"],
92
  .ab-column input[type="number"],
93
  .ab-column select {
94
+ font-size: 13px;
95
  text-align: center;
96
  background: #f8f8f9;
97
  }
98
+
99
  .ab-column input[type="number"] {
100
  padding: 0;
101
  }
102
+
103
  .ab-column select[multiple] {
104
  width: 175px;
105
  min-height: 60px;
106
  }
107
+
108
  .ab-column input.ab-mini-field {
109
  width: 40px;
110
  }
111
+
112
  .ab-column .ab-medium-field {
113
  width: 100%;
114
+ max-width: 285px;
115
  }
116
+
117
  .ab-column input[type="text"] + label,
118
  .ab-column select + label {
119
  color: #8e959c;
124
 
125
  /* @end group */
126
 
 
127
  /* @group Column contents */
128
 
129
  .ab-column > ul {
130
  padding: 0 20px 0 0;
131
  }
132
+
133
  .ab-column:last-of-type > ul {
134
  border: 0;
135
  }
136
+
137
  .ab-column > ul > li {
138
  width: 330px;
139
  margin: 0 0 36px;
143
  }
144
 
145
  .ab-column > ul > li a {
146
+ text-decoration: underline;
 
147
  }
148
+
149
  .ab-column > ul > li a:hover {
150
  border-color: inherit;
151
  }
153
  .ab-column > ul > li label {
154
  cursor: default;
155
  display: inline-block;
156
+ font-size: 14px;
157
  max-width: 286px;
158
+ color: #23282d;
159
  }
160
+
161
  .ab-column > ul > li label span {
162
+ color: #555d66;
163
  display: block;
164
+ font-size: 13px;
165
  line-height: 16px;
166
+ margin-top: 5px;
167
  }
168
 
169
  /* @end group */
170
 
 
171
  /* @group Separator */
172
 
173
  .ab-column > ul > li:after,
183
  border-width: 10px 10px 0;
184
  border-style: solid;
185
  }
186
+
187
  .ab-column.ab-arrow > ul > li:before {
188
  bottom: -24px;
189
  border-color: #fff transparent;
190
  }
191
+
192
  .ab-column.ab-arrow > ul > li:after {
193
  bottom: -22px;
194
  border-color: #f1f1f1 transparent;
211
 
212
  /* @end group */
213
 
 
214
  /* @group Submit & Service */
215
 
216
  .ab-column--submit-service {
217
  width: 342px;
218
  margin-top: 20px;
219
+ padding-right: 20px;
 
220
  }
221
+
222
  .ab-column--submit-service p {
223
  padding: 5px 0;
224
  margin: 0;
225
  text-align: center;
226
  width: 100%;
227
  }
228
+
229
  .ab-column--submit-service p:first-of-type {
230
  border-top: 1px solid #e0e5e9;
231
  }
232
+
233
  .ab-column--submit-service p:last-of-type {
234
  border-bottom: 1px solid #e0e5e9;
235
  }
236
+
237
  .ab-column--submit-service .button {
238
  width: 100%;
239
  margin: 35px 0 10px;
241
 
242
  /* @end group */
243
 
 
244
  /* @group 2nd level */
245
 
246
  .ab-column > ul > li:last-of-type:after,
252
  margin: 10px 10px 0 26px;
253
  display: none;
254
  }
255
+
256
  .ab-column > ul > li > ul li {
257
  padding: 2px 0;
258
  }
259
+
260
  .ab-column > ul > li > ul label {
261
  margin: 0 0 0 7px;
262
  }
263
+
264
  .ab-column > ul > li > input[type="checkbox"]:checked ~ ul {
265
  display: block;
266
  }
css/styles.min.css CHANGED
@@ -1 +1 @@
1
- .ab-main *,.ab-main :after,.ab-main :before{border:0;margin:0;padding:0;outline:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.browsehappy{margin:0 0 20px;padding:10px;border:1px solid #e66f00;text-align:center}.ab-wrap{margin:0 0 0 -10px;padding:20px 0 0;text-rendering:optimizeLegibility}.ab-column{float:left;margin:0 0 0 10px;position:relative}.ab-column h3{margin:0;font-size:14px;font-weight:400;line-height:20px;text-transform:uppercase}.ab-column h3.icon:before{font:normal 30px/30px Dashicons;top:4px;right:20px;speak:none;width:1em;color:#8e959c;position:absolute;text-align:center;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ab-column.ab-arrow h3.icon:before{content:'\f536'}.ab-column.ab-join h3.icon:before{content:'\f108'}.ab-column.ab-diff h3.icon:before{content:'\f237'}.ab-column h6{clear:both;color:#8e959c;margin:0 0 20px;font-size:10px;font-weight:400;text-transform:uppercase}.ab-column input[type="text"],.ab-column input[type="number"],.ab-column select{font-size:12px;text-align:center;background:#f8f8f9}.ab-column input[type="number"]{padding:0}.ab-column select[multiple]{width:175px;min-height:60px}.ab-column input.ab-mini-field{width:40px}.ab-column .ab-medium-field{width:100%;max-width:285px}.ab-column input[type="text"] + label,.ab-column select + label{color:#8e959c;margin:0 0 0 7px;display:inline-block;text-transform:uppercase}.ab-column > ul{padding:0 20px 0 0}.ab-column:last-of-type > ul{border:0}.ab-column > ul > li{width:330px;margin:0 0 36px;padding:10px 0 12px 12px;position:relative;background:#fff}.ab-column > ul > li a{text-decoration:none;border-bottom:1px dotted #e0e5e9}.ab-column > ul > li a:hover{border-color:inherit}.ab-column > ul > li label{cursor:default;display:inline-block;line-height:24px;max-width:286px}.ab-column > ul > li label span{color:#8e959c;display:block;font-size:12px;line-height:16px}.ab-column > ul > li:after,.ab-column > ul > li:before{width:0;content:'';position:absolute}.ab-column.ab-arrow > ul > li:before,.ab-column.ab-arrow > ul > li:after{left:157px;border-width:10px 10px 0;border-style:solid}.ab-column.ab-arrow > ul > li:before{bottom:-24px;border-color:#fff transparent}.ab-column.ab-arrow > ul > li:after{bottom:-22px;border-color:#f1f1f1 transparent}.ab-column.ab-join > ul > li:before{left:171px;bottom:-27px;height:18px;border-right:2px solid #fff}.ab-column.ab-diff > ul > li:before{left:162px;bottom:-19px;width:18px;height:0;border-bottom:2px solid #fff}.ab-column--submit-service{width:342px;margin-top:20px;padding-right:20px}.ab-column--submit-service p{padding:5px 0;margin:0;text-align:center;width:100%}.ab-column--submit-service p:first-of-type{border-top:1px solid #e0e5e9}.ab-column--submit-service p:last-of-type{border-bottom:1px solid #e0e5e9}.ab-column--submit-service .button{width:100%;margin:35px 0 10px}.ab-column > ul > li:last-of-type:after,.ab-column > ul > li:last-of-type:before{display:none}.ab-column > ul > li > ul{margin:10px 10px 0 26px;display:none}.ab-column > ul > li > ul li{padding:2px 0}.ab-column > ul > li > ul label{margin:0 0 0 7px}.ab-column > ul > li > input[type="checkbox"]:checked ~ ul{display:block}
1
+ .ab-main *,.ab-main :after,.ab-main :before{border:0;margin:0;padding:0;outline:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.browsehappy{margin:0 0 20px;padding:10px;border:1px solid #e66f00;text-align:center}.ab-wrap{margin:0 0 0 -10px;padding:20px 0 0;text-rendering:optimizeLegibility}.ab-column{float:left;margin:0 0 0 10px;position:relative}.ab-column h3{margin:0;font-size:18px;font-weight:400;line-height:20px;color:#23282d}.ab-column h3.icon:before{font:400 30px/30px Dashicons;top:4px;right:20px;speak:none;width:1em;color:#8e959c;position:absolute;text-align:center;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ab-column.ab-arrow h3.icon:before{content:'\f536'}.ab-column.ab-join h3.icon:before{content:'\f108'}.ab-column.ab-diff h3.icon:before{content:'\f237'}.ab-column h6{clear:both;color:#555d66;margin:0 0 20px;font-weight:400;font-size:13px}.ab-column input[type=text],.ab-column input[type=number],.ab-column select{font-size:13px;text-align:center;background:#f8f8f9}.ab-column input[type=number]{padding:0}.ab-column select[multiple]{width:175px;min-height:60px}.ab-column input.ab-mini-field{width:40px}.ab-column .ab-medium-field{width:100%;max-width:285px}.ab-column input[type=text]+label,.ab-column select+label{color:#8e959c;margin:0 0 0 7px;display:inline-block;text-transform:uppercase}.ab-column>ul{padding:0 20px 0 0}.ab-column:last-of-type>ul{border:0}.ab-column>ul>li{width:330px;margin:0 0 36px;padding:10px 0 12px 12px;position:relative;background:#fff}.ab-column>ul>li a{text-decoration:underline}.ab-column>ul>li a:hover{border-color:inherit}.ab-column>ul>li label{cursor:default;display:inline-block;font-size:14px;max-width:286px;color:#23282d}.ab-column>ul>li label span{color:#555d66;display:block;font-size:13px;line-height:16px;margin-top:5px}.ab-column>ul>li:after,.ab-column>ul>li:before{width:0;content:'';position:absolute}.ab-column.ab-arrow>ul>li:after,.ab-column.ab-arrow>ul>li:before{left:157px;border-width:10px 10px 0;border-style:solid}.ab-column.ab-arrow>ul>li:before{bottom:-24px;border-color:#fff transparent}.ab-column.ab-arrow>ul>li:after{bottom:-22px;border-color:#f1f1f1 transparent}.ab-column.ab-join>ul>li:before{left:171px;bottom:-27px;height:18px;border-right:2px solid #fff}.ab-column.ab-diff>ul>li:before{left:162px;bottom:-19px;width:18px;height:0;border-bottom:2px solid #fff}.ab-column--submit-service{width:342px;margin-top:20px;padding-right:20px}.ab-column--submit-service p{padding:5px 0;margin:0;text-align:center;width:100%}.ab-column--submit-service p:first-of-type{border-top:1px solid #e0e5e9}.ab-column--submit-service p:last-of-type{border-bottom:1px solid #e0e5e9}.ab-column--submit-service .button{width:100%;margin:35px 0 10px}.ab-column>ul>li:last-of-type:after,.ab-column>ul>li:last-of-type:before{display:none}.ab-column>ul>li>ul{margin:10px 10px 0 26px;display:none}.ab-column>ul>li>ul li{padding:2px 0}.ab-column>ul>li>ul label{margin:0 0 0 7px}.ab-column>ul>li>input[type=checkbox]:checked~ul{display:block}
js/dashboard.js CHANGED
@@ -40,8 +40,8 @@
40
  is_label_visible = false,
41
  leave_timer,
42
  blanket = r.set();
43
- label.push(r.text(60, 12, "24× Spam").attr(txt));
44
- label.push(r.text(60, 27, "23.12.2013").attr(txt).attr({fill: color}));
45
  label.hide();
46
  var frame = r.popup(100, 100, label, "right").attr({fill: "#fff", stroke: "#444", "stroke-width": 1}).hide();
47
 
@@ -65,7 +65,7 @@
65
  var dot = r.circle(x, y, 4).attr({fill: "#fff", stroke: color, "stroke-width": 1});
66
  blanket.push(r.rect(leftgutter + X * i, 0, X, height - bottomgutter).attr({stroke: "none", fill: '#fff', opacity: .2}));
67
  var rect = blanket[blanket.length - 1];
68
- (function (x, y, data, lbl, dot) {
69
  var timer, i = 0;
70
  rect.hover(function () {
71
  clearTimeout(leave_timer);
@@ -73,6 +73,9 @@
73
  if (x + frame.getBBox().width > width) {
74
  side = "left";
75
  }
 
 
 
76
  var ppp = r.popup(x, y, label, side, 1),
77
  anim = Raphael.animation({
78
  path: ppp.path,
@@ -82,13 +85,8 @@
82
  ly = label[0].transform()[0][2] + ppp.dy;
83
  frame.show().stop().animate(anim);
84
 
85
- var date = new Date(lbl * 1000),
86
- day = ( date.getDate() < 10 ? '0' : '' ) + date.getDate(),
87
- month = ( date.getMonth() + 1 < 10 ? '0' : '' ) + (date.getMonth() + 1),
88
- year = date.getFullYear();
89
-
90
- label[0].attr({text: data + "× Spam"}).show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
91
- label[1].attr({text: ( day + '.' + month + '.' + year ) }).show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
92
  dot.attr("r", 6);
93
  is_label_visible = true;
94
  }, function () {
40
  is_label_visible = false,
41
  leave_timer,
42
  blanket = r.set();
43
+ label.push(r.text(60, 12, "").attr(txt));
44
+ label.push(r.text(60, 27, "").attr(txt).attr({fill: color}));
45
  label.hide();
46
  var frame = r.popup(100, 100, label, "right").attr({fill: "#fff", stroke: "#444", "stroke-width": 1}).hide();
47
 
65
  var dot = r.circle(x, y, 4).attr({fill: "#fff", stroke: color, "stroke-width": 1});
66
  blanket.push(r.rect(leftgutter + X * i, 0, X, height - bottomgutter).attr({stroke: "none", fill: '#fff', opacity: .2}));
67
  var rect = blanket[blanket.length - 1];
68
+ (function (x, y, data, date, dot) {
69
  var timer, i = 0;
70
  rect.hover(function () {
71
  clearTimeout(leave_timer);
73
  if (x + frame.getBBox().width > width) {
74
  side = "left";
75
  }
76
+ // set label content to determine correct dimensions
77
+ label[0].attr({text: date });
78
+ label[1].attr({text: data + "× Spam"});
79
  var ppp = r.popup(x, y, label, side, 1),
80
  anim = Raphael.animation({
81
  path: ppp.path,
85
  ly = label[0].transform()[0][2] + ppp.dy;
86
  frame.show().stop().animate(anim);
87
 
88
+ label[0].show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
89
+ label[1].show().stop().animateWith(frame, anim, {transform: ["t", lx, ly]}, 200 * is_label_visible);
 
 
 
 
 
90
  dot.attr("r", 6);
91
  is_label_visible = true;
92
  }, function () {
js/dashboard.min.js CHANGED
@@ -1,5 +1,5 @@
1
- (function(){var r=[],h=[];jQuery("#ab_chart_data tfoot th").each(function(){r.push(jQuery(this).text())});jQuery("#ab_chart_data tbody td").each(function(){h.push(jQuery(this).text())});var v=jQuery("#ab_chart").parent().width()+8,g=Raphael("ab_chart",v,140),a={font:'bold 12px "Open Sans", sans-serif',fill:"#32373c"},e=(v-0)/r.length,t=Math.max.apply(Math,h),w=96/t;g.text(16,16,t).attr({font:'normal 10px "Open Sans", sans-serif',fill:"#b4b9be"});var t=g.path().attr({stroke:"#0073aa","stroke-width":2,"stroke-linejoin":"round"}),
2
- B=g.path().attr({stroke:"none",opacity:.3,fill:"#0073aa"}),f=g.set(),x=0,y=0,q=!1,z,u=g.set();f.push(g.text(60,12,"24\u00d7 Spam").attr(a));f.push(g.text(60,27,"23.12.2013").attr(a).attr({fill:"#0073aa"}));f.hide();for(var n=g.popup(100,100,f,"right").attr({fill:"#fff",stroke:"#444","stroke-width":1}).hide(),k,p,a=0,A=r.length;a<A;a++){var c=Math.round(118-w*h[a]),b=Math.round(0+e*(a+.5));a||(k=["M",b,c,"C",b,c],p=["M",0+.5*e,118,"L",b,c,"C",b,c]);if(a&&a<A-1){var d=Math.round(118-w*h[a-1]),C=Math.round(0+
3
- e*(a-.5)),D=Math.round(118-w*h[a+1]),E=Math.round(0+e*(a+1.5)),d=getAnchors(C,d,b,c,E,D);k=k.concat([d.x1,d.y1,b,c,d.x2,d.y2]);p=p.concat([d.x1,d.y1,b,c,d.x2,d.y2])}d=g.circle(b,c,4).attr({fill:"#fff",stroke:"#0073aa","stroke-width":1});u.push(g.rect(0+e*a,0,e,118).attr({stroke:"none",fill:"#fff",opacity:.2}));var F=u[u.length-1];(function(a,b,c,d,h){F.hover(function(){clearTimeout(z);var e="right";a+n.getBBox().width>v&&(e="left");var l=g.popup(a,b,f,e,1),e=Raphael.animation({path:l.path,transform:["t",
4
- l.dx,l.dy]},200*q);x=f[0].transform()[0][1]+l.dx;y=f[0].transform()[0][2]+l.dy;n.show().stop().animate(e);var m=new Date(1E3*d),l=(10>m.getDate()?"0":"")+m.getDate(),k=(10>m.getMonth()+1?"0":"")+(m.getMonth()+1),m=m.getFullYear();f[0].attr({text:c+"\u00d7 Spam"}).show().stop().animateWith(n,e,{transform:["t",x,y]},200*q);f[1].attr({text:l+"."+k+"."+m}).show().stop().animateWith(n,e,{transform:["t",x,y]},200*q);h.attr("r",6);q=!0},function(){h.attr("r",4);z=setTimeout(function(){n.hide();f[0].hide();
5
- f[1].hide();q=!1},1)})})(b,c,h[a],r[a],d)}k=k.concat([b,c,b,c]);p=p.concat([b,c,b,c,"L",b,118,"z"]);t.attr({path:k});B.attr({path:p});n.toFront();f[0].toFront();f[1].toFront();u.toFront()})();
1
+ (function(){var labels=[],data=[];jQuery("#ab_chart_data tfoot th").each(function(){labels.push(jQuery(this).text())});jQuery("#ab_chart_data tbody td").each(function(){data.push(jQuery(this).text())});var width=jQuery('#ab_chart').parent().width()+8,height=140,leftgutter=0,bottomgutter=22,topgutter=22,color='#0073aa',r=Raphael("ab_chart",width,height),txt={font:'bold 12px "Open Sans", sans-serif',fill:"#32373c"},X=(width-leftgutter*2)/labels.length,max=Math.max.apply(Math,data),Y=(height-bottomgutter-topgutter)/max;r.text(16,16,max).attr({'font':'normal 10px "Open Sans", sans-serif',fill:"#b4b9be"});var path=r.path().attr({stroke:color,"stroke-width":2,"stroke-linejoin":"round"}),bgp=r.path().attr({stroke:"none",opacity:.3,fill:color}),label=r.set(),lx=0,ly=0,is_label_visible=!1,leave_timer,blanket=r.set();label.push(r.text(60,12,"").attr(txt));label.push(r.text(60,27,"").attr(txt).attr({fill:color}));label.hide();var frame=r.popup(100,100,label,"right").attr({fill:"#fff",stroke:"#444","stroke-width":1}).hide();var p,bgpp;for(var i=0,ii=labels.length;i<ii;i++){var y=Math.round(height-bottomgutter-Y*data[i]),x=Math.round(leftgutter+X*(i+.5));if(!i){p=["M",x,y,"C",x,y];bgpp=["M",leftgutter+X*.5,height-bottomgutter,"L",x,y,"C",x,y]}
2
+ if(i&&i<ii-1){var Y0=Math.round(height-bottomgutter-Y*data[i-1]),X0=Math.round(leftgutter+X*(i-.5)),Y2=Math.round(height-bottomgutter-Y*data[i+1]),X2=Math.round(leftgutter+X*(i+1.5));var a=getAnchors(X0,Y0,x,y,X2,Y2);p=p.concat([a.x1,a.y1,x,y,a.x2,a.y2]);bgpp=bgpp.concat([a.x1,a.y1,x,y,a.x2,a.y2])}
3
+ var dot=r.circle(x,y,4).attr({fill:"#fff",stroke:color,"stroke-width":1});blanket.push(r.rect(leftgutter+X*i,0,X,height-bottomgutter).attr({stroke:"none",fill:'#fff',opacity:.2}));var rect=blanket[blanket.length-1];(function(x,y,data,date,dot){var timer,i=0;rect.hover(function(){clearTimeout(leave_timer);var side="right";if(x+frame.getBBox().width>width){side="left"}
4
+ label[0].attr({text:date});label[1].attr({text:data+"× Spam"});var ppp=r.popup(x,y,label,side,1),anim=Raphael.animation({path:ppp.path,transform:["t",ppp.dx,ppp.dy]},200*is_label_visible);lx=label[0].transform()[0][1]+ppp.dx;ly=label[0].transform()[0][2]+ppp.dy;frame.show().stop().animate(anim);label[0].show().stop().animateWith(frame,anim,{transform:["t",lx,ly]},200*is_label_visible);label[1].show().stop().animateWith(frame,anim,{transform:["t",lx,ly]},200*is_label_visible);dot.attr("r",6);is_label_visible=!0},function(){dot.attr("r",4);leave_timer=setTimeout(function(){frame.hide();label[0].hide();label[1].hide();is_label_visible=!1},1)})})(x,y,data[i],labels[i],dot)}
5
+ p=p.concat([x,y,x,y]);bgpp=bgpp.concat([x,y,x,y,"L",x,height-bottomgutter,"z"]);path.attr({path:p});bgp.attr({path:bgpp});frame.toFront();label[0].toFront();label[1].toFront();blanket.toFront()})()
readme.txt CHANGED
@@ -3,8 +3,8 @@
3
  * Tags: anti-spam, antispam, block spam, comment, comments, comment spam, pingback, spam, spam filter, trackback, GDPR
4
  * Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TD4AMD2D8EMZW
5
  * Requires at least: 4.5
6
- * Tested up to: 4.9
7
- * Stable tag: 2.9.0
8
  * License: GPLv2 or later
9
  * License URI: https://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -33,6 +33,7 @@ Say Goodbye to comment spam on your WordPress blog or website. *Antispam Bee* bl
33
 
34
  ### Support ###
35
  * Community support via the [support forums on wordpress.org](https://wordpress.org/support/plugin/antispam-bee)
 
36
  * We don’t handle support via e-mail, Twitter, GitHub issues etc.
37
 
38
  ### Contribute ###
@@ -75,9 +76,12 @@ If the antispam plugin has passed some spam comments, these comments can be repo
75
 
76
  ### Antispam Bee with Varnish? ###
77
  If WordPress is operated with Apache + Varnish, the actual IP address of the visitors does not appear in WordPress. Accordingly the Antispam-Plugin lacks the base for the correct functionality. An adaptation in the Varnish configuration file /etc/varnish/default.vcl provides a remedy and forwards the original (not from Apache) IP address in the HTTP header X-Forwarded-For:
78
- `if (req.restarts == 0) {`
79
- `set req.http.X-Forwarded-For = client.ip;`
80
- `}`
 
 
 
81
 
82
  ### Are there some paid services or limitations? ###
83
  No, Antispam Bee is free forever, for both private and commercial projects. You can use it on as many sites as you want. There is no limitation to the number of sites you use the plugin on.
@@ -86,6 +90,14 @@ A complete documentation is available in the [GitHub repository Wiki](https://gi
86
 
87
  ## Changelog ##
88
 
 
 
 
 
 
 
 
 
89
  ### 2.9.0 ###
90
  * Introduction of coding standards.
91
  * Switch to franc language detection API for the language check.
@@ -96,7 +108,7 @@ A complete documentation is available in the [GitHub repository Wiki](https://gi
96
  * Use the regular expressions check also for trackbacks.
97
  * Add option to delete Antispam Bee related data when plugin gets deleted via the admin interface.
98
  * Save a hashed + salted IP for every comment
99
- * New check for incoming Trackbacks.
100
  * Introduction of behat tests.
101
  * Updates the used JavaScript library for the statistics widget.
102
  * Bugfix in the "Comment form used outside of posts" option.
3
  * Tags: anti-spam, antispam, block spam, comment, comments, comment spam, pingback, spam, spam filter, trackback, GDPR
4
  * Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TD4AMD2D8EMZW
5
  * Requires at least: 4.5
6
+ * Tested up to: 5.0
7
+ * Stable tag: 2.9.1
8
  * License: GPLv2 or later
9
  * License URI: https://www.gnu.org/licenses/gpl-2.0.html
10
 
33
 
34
  ### Support ###
35
  * Community support via the [support forums on wordpress.org](https://wordpress.org/support/plugin/antispam-bee)
36
+ * Read [the documentation](https://github.com/pluginkollektiv/antispam-bee/wiki/en-Documentation)
37
  * We don’t handle support via e-mail, Twitter, GitHub issues etc.
38
 
39
  ### Contribute ###
76
 
77
  ### Antispam Bee with Varnish? ###
78
  If WordPress is operated with Apache + Varnish, the actual IP address of the visitors does not appear in WordPress. Accordingly the Antispam-Plugin lacks the base for the correct functionality. An adaptation in the Varnish configuration file /etc/varnish/default.vcl provides a remedy and forwards the original (not from Apache) IP address in the HTTP header X-Forwarded-For:
79
+
80
+ ```
81
+ if (req.restarts == 0) {
82
+ set req.http.X-Forwarded-For = client.ip;
83
+ }
84
+ ```
85
 
86
  ### Are there some paid services or limitations? ###
87
  No, Antispam Bee is free forever, for both private and commercial projects. You can use it on as many sites as you want. There is no limitation to the number of sites you use the plugin on.
90
 
91
  ## Changelog ##
92
 
93
+ ### 2.9.1 ###
94
+ * Improved backend accessibility
95
+ * Prefilled comment textareas do now work with the honeypot
96
+ * AMP compatibility
97
+ * Improved dashboard tooltips
98
+ * Improvements for the language detection API
99
+ * Scalable IP look up for local spam database
100
+
101
  ### 2.9.0 ###
102
  * Introduction of coding standards.
103
  * Switch to franc language detection API for the language check.
108
  * Use the regular expressions check also for trackbacks.
109
  * Add option to delete Antispam Bee related data when plugin gets deleted via the admin interface.
110
  * Save a hashed + salted IP for every comment
111
+ * New check for incoming trackbacks.
112
  * Introduction of behat tests.
113
  * Updates the used JavaScript library for the statistics widget.
114
  * Bugfix in the "Comment form used outside of posts" option.