Seditio Source
Root |
./othercms/ips_4.3.4/applications/core/sources/Profanity/Profanity.php
<?php
/**
 * @brief        Profanity Model
 * @author        <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
 * @copyright    (c) Invision Power Services, Inc.
 * @license        https://www.invisioncommunity.com/legal/standards/
 * @package        Invision Community
 * @since        10 Nov 2016
 */

namespace IPS\core;

/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
   
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
    exit;
}

/**
 * Profanity Model
 */
class _Profanity extends \IPS\Patterns\ActiveRecord implements \JsonSerializable
{
   
/**
     * @brief    Database Table
     */
   
public static $databaseTable = 'core_profanity_filters';
   
   
/**
     * @brief    Database Prefix
     */
   
public static $databasePrefix = '';
   
   
/**
     * @brief    Multiton Store
     */
   
protected static $multitons;
       
   
/**
     * @brief    [ActiveRecord] ID Database Column
     */
   
public static $databaseColumnId = 'wid';
   
   
/**
     * @brief    [ActiveRecord] Database ID Fields
     */
   
protected static $databaseIdFields = array( 'type' );
   
   
/**
     * @brief    [ActiveRecord] Multiton Map
     */
   
protected static $multitonMap    = array();
   
   
/**
     * @brief    Action Type
     */
   
public static $actionTypes = array( 'swap', 'moderate' );
   
   
/**
     * Table
     *
     * @return    \IPS\Helpers\Table
     */
   
public static function table()
    {
       
$table = new \IPS\Helpers\Table\Db( 'core_profanity_filters', \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&tab=profanityFilters' ) );
       
$table->langPrefix = 'profanity_';
       
$table->mainColumn = 'type';
       
       
/* Columns we need */
       
$table->include = array( 'type', 'action', 'm_exact' );

       
/* Default sort options */
       
$table->sortBy = $table->sortBy ?: 'type';
       
$table->sortDirection = $table->sortDirection ?: 'asc';
       
       
/* Filters */
       
$table->filters = array(
           
'profanity_require_approval'    => "action='moderate'",
           
'profanity_replace_text'        => "action='swap'",
        );
       
       
/* Search */
       
$table->quickSearch = 'type';
       
       
/* Custom parsers */
       
$table->parsers = array(
           
'action'                => function( $val, $row )
            {
                if (
$val == 'swap' )
                {
                    return \
IPS\Member::loggedIn()->language()->addToStack( 'profanity_replace_with_x', FALSE, array( 'sprintf' => array( $row['swop'] ) ) );
                }
                else
                {
                    return \
IPS\Member::loggedIn()->language()->addToStack('profanity_filter_action_moderate');
                }
            },
           
'm_exact'                => function( $val, $row )
            {
                return (
$val ) ? \IPS\Member::loggedIn()->language()->addToStack('profanity_filter_exact') : \IPS\Member::loggedIn()->language()->addToStack('profanity_filter_loose');
            }
        );
       
       
/* Specify the root buttons */

       
$table->rootButtons['add'] = array(
           
'icon'        => 'plus',
           
'title'        => 'profanity_add',
           
'link'        => \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&do=profanity' ),
           
'data'        => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('profanity_add') )
        );

       
$table->rootButtons['download'] = array(
           
'icon'        => 'download',
           
'title'        => 'download',
           
'link'        => \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&do=downloadProfanity' ),
           
'data'        => array( 'confirm' => '', 'confirmMessage' => \IPS\Member::loggedIn()->language()->addToStack('profanity_download'), 'confirmIcon' => 'info', 'confirmButtons' => json_encode( array( 'ok' => \IPS\Member::loggedIn()->language()->addToStack('download'), 'cancel' => \IPS\Member::loggedIn()->language()->addToStack('cancel') ) ) )
        );

       
/* And the row buttons */
       
$table->rowButtons = function( $row )
        {
           
$return = array();

           
$return['edit'] = array(
               
'icon'        => 'pencil',
               
'title'        => 'edit',
               
'link'        => \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&do=profanity&id=' ) . $row['wid'],
               
'data'        => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('edit') )
            );

           
$return['delete'] = array(
               
'icon'        => 'times',
               
'title'        => 'delete',
               
'link'        => \IPS\Http\Url::internal( 'app=core&module=settings&controller=posting&do=deleteProfanityFilters&id=' ) . $row['wid'],
               
'data'        => array( 'delete' => '' ),
            );
               
            return
$return;
        };
       
        return
$table;
    }
   
   
/**
     * Form
     *
     * @param    \IPS\core\Profanity|NULL    $current    If we are editing, an \IPS\core\Profanity instance of the record
     * @return    \IPS\Helpers\Form
     */
   
public static function form( \IPS\core\Profanity $current=NULL )
    {
       
$form = new \IPS\Helpers\Form;
       
        if ( !
$current )
        {
           
$form->addTab('add');
        }
       
       
$form->add( new \IPS\Helpers\Form\Text( 'profanity_type', ( $current ) ? $current->type : NULL, NULL, array() ) );
       
$form->add( new \IPS\Helpers\Form\Radio( 'profanity_action', ( $current ) ? $current->action : 'swap', FALSE, array(
           
'options'    => array(
               
'swap'        => 'profanity_filter_action_swap',
               
'moderate'    => 'profanity_filter_action_moderate'
           
),
           
'toggles'    => array(
               
'swap'        => array( 'profanity_swop' ),
               
'moderate'    => array()
            )
        ) ) );
       
$form->add( new \IPS\Helpers\Form\Text( 'profanity_swop', ( $current ) ? $current->swop : NULL, NULL, array(), NULL, NULL, NULL, 'profanity_swop' ) );
       
$form->add( new \IPS\Helpers\Form\Radio( 'profanity_m_exact', ( $current ) ? $current->m_exact : NULL, FALSE, array(
           
'options' => array(
               
'1' => 'profanity_filter_exact',
               
'0'    => 'profanity_filter_loose' )
        ),
NULL, NULL, NULL, 'profanity_m_exact' ) );
       
        if ( !
$current )
        {
           
$form->addTab('upload');
           
$form->add( new \IPS\Helpers\Form\Upload( 'profanity_upload', NULL, NULL, array( 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ) ) );
        }
       
        return
$form;
    }
   
   
/**
     * Create From Form
     *
     * @param    array                        $values        Array of values
     * @param    \IPS\core\Profanity|NULL    $current    If we are editing, an \IPS\core\Profanity instance of the record
     * @return    \IPS\core\Profanity
     */
   
public static function createFromForm( array $values, \IPS\core\Profanity $current=NULL )
    {
        if (
$current )
        {
           
$obj = $current;
        }
        else
        {
           
$obj = new static;
        }
       
        if (
array_key_exists( 'type', $values ) )
        {
           
$obj->type = $values['type'];
        }
       
        if (
array_key_exists( 'swop', $values ) )
        {
           
$obj->swop = $values['swop'];
        }
       
        if (
array_key_exists( 'm_exact', $values ) )
        {
           
$obj->m_exact = $values['m_exact'];
        }
       
        if (
array_key_exists( 'action', $values ) )
        {
            if (
in_array( $values['action'], static::$actionTypes ) )
            {
               
$obj->action = $values['action'];
            }
        }
       
       
$obj->save();
       
        unset( \
IPS\Data\Store::i()->profanityFilters );
       
        return
$obj;
    }
   
   
/**
     * Get Profanity
     *
     * @return    array
     */
   
public static function getProfanity()
    {
       
$return = array();
        if ( isset( \
IPS\Data\Store::i()->profanityFilters ) )
        {
            foreach( \
IPS\Data\Store::i()->profanityFilters AS $id => $row )
            {
               
$return[ $id ] = static::constructFromData( $row );
            }
           
            return
$return;
        }
       
       
$toCache    = array();

        foreach( \
IPS\Db::i()->select( '*', 'core_profanity_filters' ) AS $row )
        {
           
$toCache[ $row['wid'] ]    = $row;

           
$return[ $row['wid'] ] = static::constructFromData( $row );
        }
       
        \
IPS\Data\Store::i()->profanityFilters = $toCache;
       
        return
$return;
    }
   
   
/**
     * Check if the content should be hidden by profanity or url filters
     *
     * @param    string    $content    The content to check
     * @return    bool
     */
   
public static function hiddenByFilters( $content )
    {
       
$return = static::_checkProfanityFilters( $content );
       
        if (
$return == FALSE )
        {
           
$return = static::_checkUrlFilters( $content );
        }
       
        return
$return;
    }
   
   
/**
     * Check Profanity Filters
     *
     * @param    string    $content    The content to check
     * @return    bool
     */
   
protected static function _checkProfanityFilters( $content )
    {
       
$looseProfanity = array();
       
$exactProfanity = array();
        foreach( static::
getProfanity() AS $profanity )
        {
            if (
$profanity->action == 'moderate' )
            {
                if (
$profanity->m_exact )
                {
                   
$exactProfanity[] = $profanity->type;
                }
                else
                {
                   
$looseProfanity[] = $profanity->type;
                }
            }
        }
       
       
/* Loose is easy - if any of the words are present, then mod queue */
       
if ( count( $looseProfanity ) )
        {
            foreach(
$looseProfanity AS $word )
            {
                if (
mb_stristr( $content, $word ) )
                {
                    return
TRUE;
                    break;
                }
            }
        }
       
       
/* Still here? Check exact - this gets a bit more complicated. */
       
if ( count( $exactProfanity ) )
        {
           
$words = array();
            foreach(
$exactProfanity AS $word )
            {
               
$words[] = preg_quote( $word, '/' );
            }
           
           
$split = preg_split( '/((?=<^|\b)(?:' . implode( '|', $words ) . ')(?=\b|$))/iu', $content, null, PREG_SPLIT_DELIM_CAPTURE );
           
            if (
is_array( $split ) )
            {
                foreach(
$split AS $section )
                {
                    if (
in_array( $section, $exactProfanity ) )
                    {
                        return
TRUE;
                        break;
                    }
                }
            }
        }
       
       
/* Still here? All good */
       
return FALSE;
    }
   
   
/**
     * Check URL Filters
     *
     * @param    string    $content    The content to check
     * @return    bool
     */
   
protected static function _checkUrlFilters( $content )
    {
       
/* If we are allowing ANY URL's and not doing anything, then do that */
       
if ( \IPS\Settings::i()->ipb_url_filter_option == 'none' AND \IPS\Settings::i()->url_filter_any_action == 'allow' )
        {
            return
FALSE;
        }
       
       
/* If we are using a black or white list, but not moderating, do that. */
       
if ( in_array( \IPS\Settings::i()->ipb_url_filter_option, array( 'black', 'white' ) ) AND \IPS\Settings::i()->url_filter_action == 'block' )
        {
            return
FALSE;
        }
       
       
$urls = ( \IPS\Settings::i()->ipb_url_filter_option == 'black' ) ? explode( ',', \IPS\Settings::i()->ipb_url_blacklist ) : explode( ',', \IPS\Settings::i()->ipb_url_whitelist );
       
        if ( \
IPS\Settings::i()->ipb_url_filter_option == 'white' )
        {
           
$urls[] = "http://" . parse_url( \IPS\Settings::i()->base_url, PHP_URL_HOST ) . "/*";
           
$urls[] = "https://" . parse_url( \IPS\Settings::i()->base_url, PHP_URL_HOST ) . "/*";
        }
       
        if (
$urls )
        {
            try
            {
               
/* Load the content so we can look for URL's */
               
$dom = new \IPS\Xml\DOMDocument;
               
$dom->loadHTML( $content );
               
               
/* Gather up all URL's */
               
$selector = new \DOMXPath($dom);
               
$tags = $selector->query('//img | //a | //iframe');
               
$good = NULL;
                foreach(
$tags AS $tag )
                {
                    if ( (
$tag->hasAttribute( 'href' ) and !$tag->hasAttribute( 'data-mentionid' ) ) OR ( $tag->hasAttribute( 'src' ) AND !$tag->hasAttribute( 'data-emoticon' ) ) )
                    {
                        if ( \
IPS\Settings::i()->ipb_url_filter_option == 'none' )
                        {
                            return
TRUE;
                        }

                       
$urlToCheck = $tag->hasAttribute( 'href' ) ? $tag->getAttribute( 'href' ) : $tag->getAttribute( 'src' );
                       
                        foreach(
$urls AS $url )
                        {
                           
/* Make sure we're not doing something weird like storing a blank URL */
                           
if ( $url )
                            {
                               
$url = preg_quote( $url, '/' );
                               
$url = str_replace( '\*', "(.*?)", $url );
                               
                                if ( \
IPS\Settings::i()->ipb_url_filter_option == 'black' )
                                {
                                    if (
preg_match( '/' . $url . '/i', $urlToCheck ) )
                                    {
                                        return
TRUE;
                                    }
                                }
                                else if ( \
IPS\Settings::i()->ipb_url_filter_option == 'white' )
                                {
                                   
/* @note http:// is hard-coded here as we're simply validating that the URL is on the same domain, so the protocol doesn't matter for the base_url replacement */
                                   
if ( !preg_match( '/' . $url . '/i', str_replace( '<___base_url___>', 'http://' . parse_url( \IPS\Settings::i()->base_url, PHP_URL_HOST ), $urlToCheck ), $matches ) )
                                    {
                                       
$good = FALSE;
                                    }
                                    else
                                    {
                                       
$good = TRUE;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
               
                if (
$good === FALSE AND \IPS\Settings::i()->ipb_url_filter_option == 'white' )
                {
                    return
TRUE;
                }
            }
            catch( \
Exception $e ) { }
        }
       
        return
FALSE;
    }

   
/**
     * [ActiveRecord] Delete Record
     *
     * @return    void
     */
   
public function delete()
    {
       
parent::delete();

        unset( \
IPS\Data\Store::i()->profanityFilters );
    }
   
   
/**
     * JSON Serialize
     *
     * @return    array
     */
   
public function jsonSerialize()
    {
        return array(
           
'wid'        => $this->wid,
           
'type'        => $this->type,
           
'swop'        => $this->swop,
           
'm_exact'    => $this->m_exact,
           
'action'    => $this->action
       
);
    }
}