mirror of
https://github.com/HDInnovations/UNIT3D-Community-Edition.git
synced 2026-04-23 11:39:19 -05:00
(Feature) Replace/Redact Language Censor System
This commit is contained in:
@@ -83,5 +83,6 @@ class Kernel extends HttpKernel
|
||||
'immune' => \App\Http\Middleware\CheckForImmunity::class,
|
||||
'check_ip' => \App\Http\Middleware\CheckIfAlreadyVoted::class,
|
||||
'language' => \App\Http\Middleware\SetLanguage::class,
|
||||
'censor' => \App\Http\Middleware\LanguageCensor::class,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* UNIT3D is open-sourced software licensed under the GNU General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D
|
||||
* @license https://choosealicense.com/licenses/gpl-3.0/ GNU General Public License v3.0
|
||||
* @author HDVinnie
|
||||
*/
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Config;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Class LanguageCensor
|
||||
*
|
||||
* A middleware that can replace or redact(blur) words on a page.
|
||||
*
|
||||
*/
|
||||
class LanguageCensor
|
||||
{
|
||||
/** @var An associative array of keys as words or patterns and values as the replacements */
|
||||
protected $replaceDict;
|
||||
|
||||
/** @var An array specifying the words and patterns that are to be redacted */
|
||||
protected $redactDict;
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $ability
|
||||
* @param string|null $boundModelName
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
|
||||
*/
|
||||
public function handle($request, Closure $next, $ability = null, $boundModelName = null)
|
||||
{
|
||||
$response = $next($request);
|
||||
|
||||
$this->prepareDictionary();
|
||||
|
||||
$content = $response->getContent();
|
||||
$content = $this->censorResponse($content);
|
||||
|
||||
$response->setContent($content);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the dictionaries, normalizes the content provided for censor
|
||||
* for replacing and redacting stuff on page
|
||||
*/
|
||||
private function prepareDictionary()
|
||||
{
|
||||
$replaceDict = Config::get('censor.replace', []);
|
||||
$redactDict = Config::get('censor.redact', []);
|
||||
|
||||
$replaceDictKeys = array_keys($replaceDict);
|
||||
$replaceDictValues = array_values($replaceDict);
|
||||
|
||||
$replaceDictKeys = $this->normalizeRegex($replaceDictKeys);
|
||||
|
||||
$this->replaceDict = array_combine($replaceDictKeys, $replaceDictValues);
|
||||
$this->redactDict = $this->normalizeRegex($redactDict);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the words containing wildcards to regex patterns
|
||||
*
|
||||
* @param $dictionary
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function normalizeRegex($dictionary)
|
||||
{
|
||||
foreach ($dictionary as &$pattern) {
|
||||
$pattern = str_replace('%', '(?:[^<\s]*)', $pattern);
|
||||
}
|
||||
|
||||
return $dictionary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Censor the request response.
|
||||
*
|
||||
* @param $source
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function censorResponse($source)
|
||||
{
|
||||
$replaceables = array_keys($this->replaceDict);
|
||||
$replaceables = array_merge($replaceables, $this->redactDict);
|
||||
|
||||
// Word boundary and word matching regex
|
||||
$replaceables = '\b' . implode('\b|\b', $replaceables) . '\b';
|
||||
$regex = '/>(?:[^<]*?(' . $replaceables . ')[^<]*?)</i';
|
||||
|
||||
// Make the keys lower case so that it is easy to lookup
|
||||
// the replacements
|
||||
$toReplace = array_change_key_case($this->replaceDict, CASE_LOWER);
|
||||
$toRedact = $this->redactDict;
|
||||
|
||||
// Find all the matches and keep redacting/replacing
|
||||
$source = preg_replace_callback($regex, function ($match) use ($toReplace, $toRedact) {
|
||||
|
||||
$temp = strtolower($match[1]);
|
||||
|
||||
// If we have to replace it
|
||||
if (isset($toReplace[$temp])) {
|
||||
return str_replace($match[1], $toReplace[$temp], $match[0]);
|
||||
} elseif ($regexKey = $this->getReplaceRegexKey($temp)) { // Get the key i.e. pattern of the replace dictionary
|
||||
return str_replace($match[1], $toReplace[$regexKey], $match[0]);
|
||||
} elseif ($this->_inArray($temp, $toRedact) || $this->getRedactRegexKey($temp)) { // If it matches a word or pattern to redact
|
||||
$wrapWith = "<span class='censor'>{$temp}</span>";
|
||||
|
||||
return str_replace($match[1], $wrapWith, $match[0]);
|
||||
} else {
|
||||
return $match[0];
|
||||
}
|
||||
|
||||
}, $source);
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a matched item, checks the replace dictionary for any satisfying pattern
|
||||
* and returns the matching pattern key item if any
|
||||
*
|
||||
* @param $matched
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function getReplaceRegexKey($matched)
|
||||
{
|
||||
foreach ($this->replaceDict as $pattern => $replaceWith) {
|
||||
if (preg_match('/' . $pattern . '/', $matched)) {
|
||||
return $pattern;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Case in-sensitive in_array
|
||||
*
|
||||
* @param $needle
|
||||
* @param $haystack
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function _inArray($needle, $haystack)
|
||||
{
|
||||
return in_array(strtolower($needle), array_map('strtolower', $haystack));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the matched item and check if it matches any of the patterns
|
||||
* available in redact dictionary
|
||||
*
|
||||
* @param $matched
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getRedactRegexKey($matched)
|
||||
{
|
||||
foreach ($this->redactDict as $pattern) {
|
||||
if (preg_match('/' . $pattern . '/', $matched)) {
|
||||
return $pattern;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ class Shoutbox extends Model
|
||||
{
|
||||
$code = new Decoda($message);
|
||||
$code->defaults();
|
||||
$code->removeHook('Censor');
|
||||
$code->removeHook('Clickable');
|
||||
$code->addHook(new ClickableHook());
|
||||
$code->setXhtml(false);
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* UNIT3D is open-sourced software licensed under the GNU General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D
|
||||
* @license https://choosealicense.com/licenses/gpl-3.0/ GNU General Public License v3.0
|
||||
* @author HDVinnie
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Replace list
|
||||
|--------------------------------------------------------------------------
|
||||
| An associative array with a list of words that you want to replace.
|
||||
| keys of the array will be the words that you want to replace and the
|
||||
| values will be the words with which the key words will be replaced e.g.
|
||||
|
|
||||
| 'replace' => [
|
||||
| 'seventh' => '7th',
|
||||
| 'monthly' => 'every month',
|
||||
| 'yearly' => 'every year',
|
||||
| 'weekly' => 'every week',
|
||||
| ],
|
||||
|
|
||||
| In this case "seventh" will be replaced with "7th" and so on.
|
||||
|
|
||||
*/
|
||||
'replace' => [
|
||||
//
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redact List
|
||||
|--------------------------------------------------------------------------
|
||||
| Specify the words that you want to completely redact. The words
|
||||
| specified in here will have a blur effect placed over them.
|
||||
|
|
||||
| 'redact' => [
|
||||
| 'bitch',
|
||||
| ],
|
||||
|
|
||||
| In this case "bitch" will have a censor class wrapped around it
|
||||
|
|
||||
*/
|
||||
'redact' => [
|
||||
'asshole',
|
||||
'bitch',
|
||||
'btch',
|
||||
'blowjob',
|
||||
'cock',
|
||||
'cawk',
|
||||
'clt',
|
||||
'clit',
|
||||
'clitoris',
|
||||
'cock',
|
||||
'cocksucker',
|
||||
'cum',
|
||||
'cunt',
|
||||
'cnt',
|
||||
'dildo',
|
||||
'dick',
|
||||
'douche',
|
||||
'fag',
|
||||
'faggot',
|
||||
'fcuk',
|
||||
'fuck',
|
||||
'fuk',
|
||||
'motherfucker',
|
||||
'nigga',
|
||||
'nigger',
|
||||
'nig',
|
||||
'penis',
|
||||
'pussy',
|
||||
'rimjaw',
|
||||
'scrotum',
|
||||
'shit',
|
||||
'sht',
|
||||
'slut',
|
||||
'twat',
|
||||
'whore',
|
||||
'whre',
|
||||
'vagina',
|
||||
'vag',
|
||||
'rape',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -22351,3 +22351,18 @@ div.header div.inner_content h1 {
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
}
|
||||
.censor {
|
||||
filter:blur(3px);
|
||||
}
|
||||
|
||||
.censor:hover{
|
||||
-webkit-animation: 0.25s linear forwards censor;
|
||||
-moz-animation: 0.25s linear forwards censor;
|
||||
-o-animation: 0.25s linear forwards censor;
|
||||
animation: 0.25s linear forwards censor;
|
||||
}
|
||||
|
||||
@-webkit-keyframes censor { from { filter:blur(3px); } to { filter:blur(0px); } }
|
||||
@-moz-keyframes censor { from { filter:blur(3px); } to { filter:blur(0px); } }
|
||||
@-o-keyframes censor { from { filter:blur(3px); } to { filter:blur(0px); } }
|
||||
@keyframes censor { from { filter:blur(3px); } to { filter:blur(0px); } }
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<link rel="stylesheet" href="{{ url('css/nav/hoe.css?v=05') }}">
|
||||
<link rel="stylesheet" href="{{ url('css/main/app.css?v=04') }}">
|
||||
<link rel="stylesheet" href="{{ url('css/main/custom.css?v=37') }}">
|
||||
<link rel="stylesheet" href="{{ url('css/main/custom.css?v=38') }}">
|
||||
@if(Auth::check()) @if(Auth::user()->style != 0)
|
||||
<link rel="stylesheet" href="{{ url('css/main/dark.css?v=02') }}">
|
||||
@endif @endif
|
||||
|
||||
+1
-1
@@ -256,7 +256,7 @@ Route::group(['middleware' => 'auth'], function () {
|
||||
| ShoutBox Routes Group (when authorized)
|
||||
|------------------------------------------
|
||||
*/
|
||||
Route::group(['prefix' => 'shoutbox', 'middleware' => 'auth'], function () {
|
||||
Route::group(['prefix' => 'shoutbox', 'middleware' => ['auth','censor']], function () {
|
||||
Route::get('/', 'HomeController@home')->name('shoutbox-home');
|
||||
Route::get('/messages/{after?}', 'ShoutboxController@fetch')->name('shoutbox-fetch');
|
||||
Route::post('/send', 'ShoutboxController@send')->name('shoutbox-send');
|
||||
|
||||
Reference in New Issue
Block a user