* Invision Power Services * IP.Board v3.1.4 * Static Classes for IP.Board 3 * * These classes are not required as objects. We have grouped * together several singletons to prevent multiple file loads * Last Updated: $Date: 2010-11-16 11:26:43 -0500 (Tue, 16 Nov 2010) $ * * * @author $Author: mmecham $ * @copyright (c) 2001 - 2009 Invision Power Services, Inc. * @license http://www.invisionpower.com/community/board/license.html * @package IP.Board * @link http://www.invisionpower.com * @since 12th March 2002 * @version $Revision: 7195 $ * * @author Matt */ /** * This class for legacy reasons only - should be removed in 3.2 * Just stops skins breaking. */ class IPSSearchIndex { /** * Loads the correct search plugin based on search settings * * @param string [$force_index] Optional, force a particular index * @return object */ static public function getSearchPlugin( $force_index='' ) { return false; } /** * Determines if the application can be searched * * @param string $app Application key * @return bool */ static public function appIsSearchable( $app ) { /* INI */ return IPSLib::appIsInstalled( $app ); } /** * Returns the search display plugin for the specified app * * @param string $app Application Key * @return object */ static public function getSearchDisplayPlugin( $app ) { return false; } } class IPSAdCodeDefault { /** * Registry Object Shortcuts * * @var object */ public $registry; public $DB; public $settings; public $request; public $lang; public $member; public $memberData; public $cache; public $caches; /** * Ad code to overwrite the global header code * * @var string */ public $headerCode = ''; /** * Ad code to overwrite the global footer code * * @var string */ public $footerCode = ''; public function __construct( ipsRegistry $registry ) { /* Make object */ $this->registry = $registry; $this->DB = $this->registry->DB(); $this->settings =& $this->registry->fetchSettings(); $this->request =& $this->registry->fetchRequest(); $this->lang = $this->registry->getClass('class_localization'); $this->member = $this->registry->member(); $this->memberData =& $this->registry->member()->fetchMemberData(); $this->cache = $this->registry->cache(); $this->caches =& $this->registry->cache()->fetchCaches(); } /** * Basic functionality */ public function userCanViewAds() { return false; } } /** * Deletion Log Class */ class IPSDeleteLog { /** * Add entry to the delete log * * @param int Object ID * @param string Object Type * @param string Reason for addition * @param array Array of member data of user adding log entry */ public static function addEntry( $id, $type, $reason, $memberData ) { if ( $id AND $type AND is_array( $memberData ) AND $memberData['member_id'] ) { ipsRegistry::DB()->replace( 'core_soft_delete_log', array( 'sdl_obj_id' => $id, 'sdl_obj_key' => $type, 'sdl_obj_reason' => $reason, 'sdl_obj_member_id' => $memberData['member_id'], 'sdl_obj_date' => time(), 'sdl_locked' => 0 ), array( 'sdl_obj_id', 'sdl_obj_key' ) ); return TRUE; } return FALSE; } /** * Remove entres to the delete log * * @param array Object IDs * @param string Object Type * @param boolean Force deletion (used when deleting topics/posts/etc) */ public static function removeEntries( $ids, $type, $forceDelete=false ) { if ( is_array( $ids ) AND count( $ids ) AND $type ) { $ids = IPSLib::cleanIntArray( $ids ); /* if we're not a global mod, then lock these not remove unless we're deleting stuff */ if ( ! ipsRegistry::member()->getProperty('g_is_supmod') AND $forceDelete === false ) { ipsRegistry::DB()->update( 'core_soft_delete_log', array( 'sdl_locked' => 1 ), 'sdl_obj_id IN (' . implode( ',', $ids ) . ') AND sdl_obj_key=\'' . $type . '\'' ); } else { ipsRegistry::DB()->delete( 'core_soft_delete_log', 'sdl_obj_id IN (' . implode( ',', $ids ) . ') AND sdl_obj_key=\'' . $type . '\'' ); } return TRUE; } return FALSE; } /** * Fetch entries from the delete log * * @param array Object IDs * @param string Object Type * @param boolean Parse Member Data */ public static function fetchEntries( $ids, $type, $parseMember=true ) { $return = array(); if ( is_array( $ids ) AND count( $ids ) AND $type ) { $ids = IPSLib::cleanIntArray( $ids ); ipsRegistry::DB()->build( array( 'select' => 'l.*', 'from' => array( 'core_soft_delete_log' => 'l' ), 'where' => 'sdl_obj_id IN (' . implode( ',', $ids ) . ') AND sdl_obj_key=\'' . $type . '\'', 'add_join' => array( array( 'select' => 'm.*', 'from' => array( 'members' => 'm' ), 'where' => 'l.sdl_obj_member_id=m.member_id' ), array( 'select' => 'p.*', 'from' => array( 'profile_portal' => 'p' ), 'where' => 'l.sdl_obj_member_id=p.pp_member_id' ) ) ) ); $i = ipsRegistry::DB()->execute(); while( $row = ipsRegistry::DB()->fetch( $i ) ) { if ( $parseMember ) { $row['member'] = IPSMember::buildDisplayData( $row ); } $return[ $row['sdl_obj_id'] ] = $row; } return $return; } return array(); } } class IPSContentCache { /** * Keep track of what tables are linked to which key * * @var array */ private static $_tables = array( 'post' => 'content_cache_posts', 'sig' => 'content_cache_sigs' ); /** * Keep track of what settings are linked to which key * * @var array */ private static $_settings = array( 'post' => 'cc_posts', 'sig' => 'cc_sigs' ); /** * Check to see whether content caching is enabled * * @return boolean */ static public function isEnabled() { return ( ipsRegistry::$settings['cc_on'] AND ( ipsRegistry::$settings['cc_posts'] OR ipsRegistry::$settings['cc_sigs'] ) ) ? TRUE : FALSE; } /** * Check to see whether we have a valid type * * @param string Content Type (post/sig/etc) * @return boolean */ static public function isValidType( $type ) { return ( in_array( $type, array_keys( self::$_tables ) ) ) ? TRUE : FALSE; } /** * Fetch correct table name based on type * Assumes isValidType has been run * * @param string Content Type (post/sig/etc) * @return boolean */ static public function fetchTableName( $type ) { return self::$_tables[ $type ]; } /** * Fetch correct setting value based on type * Assumes isValidType has been run * * @param string Content Type (post/sig/etc) * @return boolean */ static public function fetchSettingValue( $type ) { return ipsRegistry::$settings[ self::$_settings[ $type ] ]; } /** * Monitor: See how many items are served from the cache versus ... not * * You need to add 'cc_monitor' => 1 into conf_global.php to enable this * * @param array Array of data like array( 'post' => array( 'cached' => x, 'raw' => x ) * @return void */ static public function updateMonitor( $array ) { /* Check.. */ if ( ! ipsRegistry::$settings['cc_monitor'] ) { return FALSE; } /* Enabled?? */ if ( ! self::isEnabled() ) { return FALSE; } /* Search engine? */ if ( ipsRegistry::member()->is_not_human === TRUE ) { return FALSE; } $savedData = ipsRegistry::cache()->getCache( 'ccMonitor' ); /* Ensure its valid */ if ( ! is_array( $savedData ) OR ! count( $savedData ) ) { $savedData = array( 'post' => array( 'cached' => 0, 'raw' => 0 ), 'sig' => array( 'cached' => 0, 'raw' => 0 ) ); } foreach( $array as $type => $data ) { if ( self::isValidType( $type ) ) { $savedData[ $type ]['cached'] += intval( $data['cached'] ); $savedData[ $type ]['raw'] += intval( $data['raw'] ); } } /* Write back */ ipsRegistry::cache()->setCache( 'ccMonitor', $savedData, array( 'array' => 1, 'donow' => 1 ) ); } /** * Add data to the cache * * @param int Content ID * @param string Content Type (post/sig/etc) * @param string Content * @param boolean Already had preDb/preDisplay run. It FALSE, assumed preDb has been run and no HTML will be parsed but smilies and bbcode will be * @return bool */ static public function update( $id, $type, $content, $parsed=TRUE ) { /* Enabled?? */ if ( ! self::isEnabled() ) { return FALSE; } /* Valid type?? */ if ( ! self::isValidType( $type ) ) { return FALSE; } /* Search engine? */ if ( ipsRegistry::member()->is_not_human === TRUE ) { return FALSE; } /* Init */ $parsingSection = 'topics'; if ( $content AND $parsed !== TRUE ) { /* What are we parsing? */ switch( $type ) { case 'post': $parsingSection = 'topics'; break; case 'sig': $parsingSection = 'signatures'; break; } /* Set up parser */ IPSText::getTextClass( 'bbcode' )->parse_smilies = 1; IPSText::getTextClass( 'bbcode' )->parse_html = 0; IPSText::getTextClass( 'bbcode' )->parse_nl2br = 1; IPSText::getTextClass( 'bbcode' )->parse_bbcode = 1; IPSText::getTextClass( 'bbcode' )->parsing_section = $parsingSection; IPSText::getTextClass( 'bbcode' )->parsing_mgroup = ipsRegistry::member()->getProperty( 'member_group_id' ); IPSText::getTextClass( 'bbcode' )->parsing_mgroup_others = ipsRegistry::member()->getProperty( 'mgroup_others' ); /* Format */ $content = IPSText::getTextClass( 'bbcode' )->preDisplayParse( IPSText::getTextClass( 'bbcode' )->preDbParse( $content ) ); } if ( $content ) { ipsRegistry::DB()->force_data_type = array( 'cache_content' => 'string' ); ipsRegistry::DB()->replace( self::fetchTableName( $type ), array( 'cache_content_id' => $id, 'cache_content' => $content, 'cache_updated' => time() ), array( 'cache_content_id' ) ); } else { /* No content, then drop it */ self::drop( $type, $id ); } return TRUE; } /** * Drop data from the cache * If no ID is passed, it'll drop all caches for the supplied 'type' * * @param string Content Type (post/sig/etc) * @param int/array [Content ID] * @return bool */ static public function drop( $type, $id=0 ) { if ( ! self::isEnabled() ) { return FALSE; } /* Valid type?? */ if ( ! self::isValidType( $type ) ) { return FALSE; } if ( $id ) { if ( is_array( $id ) ) { $id = implode( ',', $id ); } ipsRegistry::DB()->delete( self::fetchTableName( $type ), "cache_content_id IN (" . $id . ")" ); } else { ipsRegistry::DB()->delete( self::fetchTableName( $type ) ); } return TRUE; } /** * Remove all "type" data from the cache * * @param string [Content Type (post/sig/etc)] * @return int Number of rows affected */ static public function truncate( $type='' ) { if ( ! self::isEnabled() ) { return 0; } /* Valid type?? */ if ( $type AND ! self::isValidType( $type ) ) { return 0; } $affected = 0; if ( $type ) { $count = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as total', 'from' => self::fetchTableName( $type ) ) ); ipsRegistry::DB()->delete( self::fetchTableName( $type ) ); $affected = $count['total']; // ipsRegistry::DB()->getAffectedRows(); - With no where clause, mysql_affected_rows always returns 0 } else { foreach( self::$_tables as $type => $name ) { $count = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as total', 'from' => $name ) ); ipsRegistry::DB()->delete( $name ); $affected += $count['total']; // $affected += ipsRegistry::DB()->getAffectedRows(); - With no where clause, mysql_affected_rows always returns 0 } } return intval( $affected ); } /** * Count the number of cached items * * @param string [Content Type (post/sig/etc)] * @return int Combined number of items */ static public function count( $type='' ) { if ( ! self::isEnabled() ) { return FALSE; } /* Valid type?? */ if ( $type AND ! self::isValidType( $type ) ) { return FALSE; } $count = 0; if ( $type ) { $row = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as c', 'from' => self::fetchTableName( $type ) ) ); $count = intval( $row['c'] ); } else { foreach( self::$_tables as $type => $name ) { $row = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as c', 'from' => $name ) ); $count += intval( $row['c'] ); } } return intval( $count ); } /** * Prune items back to X days * * If no type is supplied, all types are pruned * * @param string [Content Type (post/sig/etc)] * @return int Number of rows affected */ static public function prune( $type='' ) { if ( ! self::isEnabled() ) { return FALSE; } /* Valid type?? */ if ( $type AND ! self::isValidType( $type ) ) { return FALSE; } $affected = 0; if ( $type ) { $time = time() - ( self::fetchSettingValue( $type ) * 86400 ); ipsRegistry::DB()->delete( self::fetchTableName( $type ), "cache_updated <" . $time ); $affected = ipsRegistry::DB()->getAffectedRows(); } else { foreach( self::$_tables as $type => $name ) { $time = time() - ( self::fetchSettingValue( $type ) * 86400 ); ipsRegistry::DB()->delete( self::fetchTableName( $type ), "cache_updated <" . $time ); $affected += ipsRegistry::DB()->getAffectedRows(); } } return intval( ipsRegistry::DB()->getAffectedRows() ); } /** * Fetch table join * * Cheap way of grabbing the join on the cache table so that your code * doesn't have to check for whether we're using the cache or not, etc * * @param string Content Type (post/sig/etc) * @param string Join field (eg 'p.pid') * @param string [Table alias - default 'cca'] * @param string [Table join type - default 'left'] * @param string [Custom select so that fields can be aliased, etc] * @return bool */ static public function join( $type, $joinField, $alias='cca', $joinType='left', $customSelect='' ) { if ( ! self::isEnabled() ) { return FALSE; } /* Valid type?? */ if ( ! self::isValidType( $type ) ) { return FALSE; } return array( 'select' => ( $customSelect ) ? $customSelect : $alias.'.*', 'from' => array( self::fetchTableName( $type ) => $alias ), 'where' => $alias . '.cache_content_id=' . $joinField, 'type' => $joinType ); } } /** * Experimental class for storing options as bitwise * * @author Matt */ class IPSBWOptions { /** * Convert a bit field into an array of options * * @param int Bitwise option * @param string Type of options to decipher (user / groups / etc) * @param string App * @return array * $options = IPSBWOptions::thawOptions( 18, 'user', 'forums' ); */ static public function thaw( $bitfield, $type, $app='global' ) { /* INIT */ $bitfield = intval($bitfield); $array = array(); /* Generate bitwise array */ $bitArray = self::_getBitWiseArray( $type, $app ); if ( ! $bitArray OR ! count( $bitArray ) ) { return array(); } /* Build options */ foreach( $bitArray as $key => $bitvalue ) { if ( $bitfield & intval( $bitvalue ) ) { $array[ $key ] = 1; } else { $array[ $key ] = 0; } } return $array; } /** * Build an SQL query bit * * @param string Field (field name as assigned by thaw) * @param string SQL field * @param string Type (members, groups, etc ) * @param string App (global, forums, etc) * @param string Type of SQL query (add/remove/has) * @return string Formatted SQL field */ static public function sql( $bitField, $sqlField, $type, $app='global', $sql='has' ) { /* Generate int sign */ switch( $sql ) { default: case 'has': $_sign = '&'; break; case 'remove': $_sign = '-'; break; case 'add': $_sign = '+'; break; } /* Generate bitwise array */ $bitArray = self::_getBitWiseArray( $type, $app ); /* Do it.. .*/ if ( in_array( $bitField, array_keys( $bitArray ) ) ) { return '( ' . $sqlField . ' ' . $_sign . ' ' . $bitArray[ $bitField ] . ' ) != 0'; } else { return FALSE; } } /** * Freeze options * Converts an array of options array( 'key' => 0 ... ) into an int for saving in a DB field * * @param array Array of key => values to save * @param string Type of options to save * @param string App * @return int */ static public function freeze( $toSave, $type, $app='global' ) { /* INIT */ $int = 0; /* Generate bitwise array */ $bitArray = self::_getBitWiseArray( $type, $app ); if ( ! $bitArray OR ! count( $bitArray ) ) { return 0; } foreach( $bitArray as $key => $value ) { if ( isset( $toSave[ $key ] ) ) { if ( $toSave[ $key ] == 1 ) { $int += $value; } } } return intval( $int ); } /** * Fetch and build the bitwise array * * @param string Array key to return * @return array */ static private function _getBitWiseArray( $type, $app ) { $bitArray = array(); $allOptions = ipsRegistry::fetchBitWiseOptions( $app ); if ( is_array( $allOptions ) ) { if ( isset( $allOptions[ $type ] ) AND is_array( $allOptions[ $type ] ) ) { $n = 1; foreach( $allOptions[ $type ] as $key ) { $bitArray[ $key ] = $n; $n *= 2; } } } return $bitArray; } } /** * Time Class * * Class for handling timestamps * */ class IPSTime { /** * Current timestamp * * @var integer */ private static $timestamp = IPS_UNIX_TIME_NOW; /** * Number of seconds in a minute * * @var integer */ private static $minute = 60; /** * Number of seconds in a hour * * @var integer */ private static $hour = 3600; /** * Number of seconds in a day * * @var integer */ private static $day = 86400; /** * Number of seconds in a week * * @var integer */ private static $week = 604800; /** * Number of seconds in a year * * @var integer */ private static $year = 220752000; /** * Months with 31 days * * @var array */ private static $months_31 = array( 01, 03, 05, 07, 08, 10, 12 ); /** * Months with 30 days * * @var array */ private static $months_30 = array( 04, 06, 09, 11 ); /** * time_class::dmy_format() * Generates a time stamp for the day/month/year * * @param integer [$ts] Timestamp to format, self::$timestamp used if none specified * @return void */ static public function dmy_format( $ts=0 ) { /* Set the timestamp */ $_ts = ( $ts ) ? $ts : self::$timestamp; /* Break it into dmy format */ $_ts = date( "m,d,Y", $_ts ); $_ts = explode( ",", $_ts ); /* Return the timestamp */ return mktime( 0, 0, 0, $_ts[0], $_ts[1], $_ts[2] ); } /** * time_class::time_ago() * Returns how long ago the specified time stamp was * * @param integer $ts Timestamp to format * @return void */ static public function time_ago( $ts ) { if( $ts == time() ) { return '--'; } if( $ts < 60 ) { $plural = ( $ts == 1 ) ? '' : 's'; return sprintf( "%0d", $ts ) . " second$plural"; } else if( $ts < self::$hour ) { $plural = ( sprintf("%0d", ( $ts / self::$minute ) ) == 1 ) ? '' : 's'; return sprintf("%0d", ( $ts / self::$minute ) ) . " minute$plural"; } else if( $ts < self::$day ) { $plural = ( sprintf("%0d", ( $ts / self::$hour ) ) == 1 ) ? '' : 's'; return sprintf("%0d", ( $ts / self::$hour ) ) . " hour$plural"; } else { $plural = ( sprintf("%0d", ( $ts / self::$day ) ) == 1 ) ? '' : 's'; return sprintf("%0d", ( $ts / self::$day ) ) . " day$plural"; } } /** * Set the timestamp * * @param int New timestamp * @return void */ static public function setTimestamp( $time ) { self::$timestamp = $time; } /** * Get the timestamp * * @return int Timestamp */ static public function getTimestamp() { return self::$timestamp; } /** * time_class::add_minutes() * Adds the specified number of minutes to the timestamp * * @param integer [$num] Number of minutes to add, 1 by default * @return void */ static public function add_minutes( $num=1 ) { self::$timestamp += self::$minute * $num; } /** * time_class::add_hours() * Adds the specified number of hours to the timestamp * * @param integer [$num] Number of hours to add, 1 by default * @return void */ static public function add_hours( $num=1 ) { self::$timestamp += self::$hours * $num; } /** * time_class::add_days() * Adds the specified number of days to the timestamp * * @param integer [$num] Number of days to add, 1 by default * @return void */ static public function add_days( $num=1 ) { self::$timestamp += self::$day * $num; } /** * time_class::add_weeks() * Adds the specified number of weeks to the timestamp * * @param integer [$num] Number of weeks to add, 1 by default * @return void */ static public function add_weeks( $num=1 ) { self::$timestamp += self::$week * $num; } /** * time_class::add_month() * Adds a single month to the current timestamp, takes into account leap years * * @return void */ static public function add_month() { $daysInMonth = date( 't', self::$timestamp ); self::$timestamp += self::add_days( intval( $daysInMonth ) ); } /** * time_class::add_months() * Adds the specified number of months to the timestamp * * @param integer $num Number of months to add, 1 by default * @return void */ static public function add_months( $num=1 ) { for( $i = 0; $i < $num; $i++ ) { self::add_month(); } } /** * time_class::add_years() * Adds the specified number of years to the timestamp * * @param integer $num Number of years to add, 1 by default * @return void */ static public function add_years( $num=1 ) { for( $i = 0; $i < $num; $i++ ) { self::add_months( 12 ); self::remove_days( 1 ); } } /** * time_class::remove_days() * Removes the specified number of days to the timestamp * * @param integer [$num] Number of days to remove, 1 by default * @return void */ static public function remove_days( $num=1 ) { self::$timestamp -= self::$day * $num; } /** * Convert unix timestamp into: (no leading zeros) * array( 'day' => x, 'month' => x, 'year' => x, 'hour' => x, 'minute' => x ); * Written into separate static public function to allow for timezone to be used easily * * @param integer [$unix] Timestamp * @return array Date parts */ static public function unixstamp_to_human( $unix=0 ) { $tmp = gmdate( 'j,n,Y,G,i', $unix ); list( $day, $month, $year, $hour, $min ) = explode( ',', $tmp ); return array( 'day' => $day, 'month' => $month, 'year' => $year, 'hour' => $hour, 'minute' => $min ); } /** * Convert unix timestamp into mmddyyyy * * @param integer [$unix] Timestamp * @param string [$sep] Separator * @return string mm/dd/yyyy */ static public function unixstamp_to_mmddyyyy( $unix=0, $sep='/' ) { if ( ! $unix ) { return ""; } $date = self::unixstamp_to_human( $unix ); return sprintf("%02d{$sep}%02d{$sep}%04d", $date['month'], $date['day'], $date['year'] ); } /** * Convert mmddyyyy into unix timestamp * * @param string [$date] Date * @param string [$sep] Separator * @param bool [$checkdate] Whether to validate date or not * @return integer Timestamp */ static public function mmddyyyy_to_unixstamp( $date='', $sep='/', $checkdate=true ) { if ( ! $date ) { return ""; } list( $month, $day, $year ) = explode( $sep, $date ); if ( $checkdate ) { if ( ! checkdate( $month, $day, $year ) ) { return ""; } } return self::human_to_unixstamp( $day, $month, $year, 0, 0 ); } /** * Wrapper for gmmktime (separated for timezone management) * * @param integer $day Day * @param integer $month Month * @param integer $year Year * @param integer $hour Hour * @param integer $minute Minute * @return integer Timestamp */ static public function human_to_unixstamp( $day, $month, $year, $hour, $minute ) { return gmmktime( intval($hour), intval($minute), 0, intval($month), intval($day), intval($year) ); } /** * My gmmktime() - PHP func seems buggy * * @param integer $hour Hour * @param integer $min Minute * @param integer $sec Second * @param integer $month Month * @param integer $day Day * @param integer $year Year * @return integer Timestamp * @since 2.0 */ static public function date_gmmktime( $hour=0, $min=0, $sec=0, $month=0, $day=0, $year=0 ) { // Calculate UTC time offset $offset = date( 'Z' ); // Generate server based timestamp $time = mktime( $hour, $min, $sec, $month, $day, $year ); // Calculate DST on / off $dst = intval( date( 'I', $time ) - date( 'I' ) ); return $offset + ($dst * 3600) + $time; } /** * Hand rolled GETDATE method * * getdate doesn't work apparently as it doesn't take into account * the offset, even when fed a GMT timestamp. * * @param integer Unix date * @return array 0, seconds, minutes, hours, mday, wday, mon, year, yday, weekday, month * @since 2.0 */ static public function date_getgmdate( $gmt_stamp ) { //$tmp = gmdate( 'j,n,Y,G,i,s,w,z,l,F,W,M', $gmt_stamp ); $format = '%e,%m,%Y,%H,%M,%S,%u,%j,%A,%B,%W,%b'; //----------------------------------------- // Some flags not available on Windows // @see http://www.php.net/manual/en/function.strftime.php#53340 //----------------------------------------- if( strpos( strtolower( PHP_OS ), 'win' ) === 0 ) { $mapping = array( '%e' => sprintf("%' 2d", date("j", $gmt_stamp)), '%u' => ($w = date("w", $gmt_stamp)) ? $w : 7, ); $format = str_replace( array_keys($mapping), array_values($mapping), $format ); } $tmp = gmstrftime( $format, $gmt_stamp ); list( $day, $month, $year, $hour, $min, $seconds, $wday, $yday, $weekday, $fmon, $week, $smon ) = explode( ',', $tmp ); return array( 0 => $gmt_stamp, "seconds" => $seconds, // Numeric representation of seconds 0 to 59 "minutes" => $min, // Numeric representation of minutes 0 to 59 "hours" => $hour, // Numeric representation of hours 0 to 23 "mday" => trim($day), // Numeric representation of the day of the month 1 to 31 "wday" => $wday, // Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday) "mon" => $month, // Numeric representation of a month 1 through 12 "year" => $year, // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 "yday" => $yday, // Numeric representation of the day of the year 0 through 365 "weekday" => $weekday, // A full textual representation of the day of the week Sunday through Saturday "month" => $fmon, // A full textual representation of a month, such as January or Mar "week" => $week, // Week of the year "smonth" => $smon, "smon" => $smon ); } } /** * IPSLib * * Dumping ground for functions that don't fit anywhere else * */ class IPSLib { /** * FURL Templates * * @param array */ static private $_furlTemplates = array(); /** * Search configs * * @param array */ static private $_searchConfigs = array(); /** * Log in methods * * @param array */ static private $_lims = array(); /** * Returns the class name to be instantiated, the class file will already be included * * @param string $filePath File location of the class * @param string $className Name of the class * @param string $app Application (defaults to 'core') * @return string Class Name */ static public function loadLibrary( $filePath, $className, $app='core' ) { /* Get the class */ require_once( $filePath ); /* Check for hooks */ $hooksCache = ipsRegistry::cache()->getCache('hooks'); if( isset( $hooksCache['libraryHooks'][ $app ][ $className ] ) AND is_array( $hooksCache['libraryHooks'][ $app ][ $className ] ) AND count( $hooksCache['libraryHooks'][ $app ][ $className ] ) ) { foreach( $hooksCache['libraryHooks'][ $app ][ $className ] as $classOverloader ) { /* Hooks: Do we have a hook that extends this class? */ if( file_exists( DOC_IPS_ROOT_PATH . 'hooks/' . $classOverloader['filename'] ) ) { /* Hooks: Do we have the hook file? */ require_once( DOC_IPS_ROOT_PATH . 'hooks/' . $classOverloader['filename'] ); if( class_exists( $classOverloader['className'] ) ) { /* Hooks: We have the hook file and the class exists - reset the classname to load */ $className = $classOverloader['className']; } } } } /* Return Class Name */ return $className; } /** * Returns the class name to be instantiated, the class file will already be included * * @param string $filePath File location of the class * @param string $className Name of the class * @return string Class Name */ static public function loadActionOverloader( $filePath, $className ) { /* Get the class */ require_once( $filePath ); /* Hooks: Are we overloading this class? */ $hooksCache = ipsRegistry::cache()->getCache('hooks'); if( isset( $hooksCache['commandHooks'][ $className ] ) AND is_array( $hooksCache['commandHooks'][ $className ] ) AND count( $hooksCache['commandHooks'][ $className ] ) ) { foreach( $hooksCache['commandHooks'][ $className ] as $classOverloader ) { if( file_exists( DOC_IPS_ROOT_PATH . 'hooks/' . $classOverloader['filename'] ) ) { /* Hooks: Do we have the hook file? */ require_once( DOC_IPS_ROOT_PATH . 'hooks/' . $classOverloader['filename'] ); if( class_exists( $classOverloader['className'] ) ) { /* Hooks: We have the hook file and the class exists - reset the classname to load */ $className = $classOverloader['className']; } } } } /* Return Class Name */ return $className; } /** * Checks if there are any data hooks to run * * @param array $dataArray Data to be passed into the hooks * @param string $hookLocation Location the data was sent from * @return void */ static public function doDataHooks( &$dataArray, $hookLocation ) { /* Loop through the cache */ $hooksCache = ipsRegistry::cache()->getCache( 'hooks' ); if( isset($hooksCache['dataHooks'][ $hookLocation ]) AND is_array( $hooksCache['dataHooks'][ $hookLocation ] ) AND count( $hooksCache['dataHooks'][ $hookLocation ] ) ) { foreach( $hooksCache['dataHooks'][ $hookLocation ] as $r ) { /* Check for hook file */ if( file_exists( DOC_IPS_ROOT_PATH . 'hooks/' . $r['filename'] ) ) { /* Check for hook class */ require_once( DOC_IPS_ROOT_PATH . 'hooks/' . $r['filename'] ); if( class_exists( $r['className'] ) ) { /* Create and run the hook */ $_hook = new $r['className']; $newArray = $_hook->handleData( $dataArray ); /* Make sure the array isn't wiped out */ if( is_array( $newArray ) && count( $newArray ) ) { $dataArray = $newArray; } } } } } } /** * Returns an array of data hook locations * * @return array */ static public function getDataHookLocations() { $_locations = array(); /* Loop all apps and get back our locations! */ foreach( ipsRegistry::$applications as $app_dir => $application ) { $dataHookLocations = array(); if( file_exists( IPSLib::getAppDir( $app_dir ) . '/extensions/dataHookLocations.php' ) ) { require_once( IPSLib::getAppDir( $app_dir ) . '/extensions/dataHookLocations.php' ); if ( count($dataHookLocations) ) { $_locations = array_merge( $_locations, $dataHookLocations ); } } } return $_locations; } /** * Checks to see if there is a template hook installed at the specified location * * * @param string $group * @param array $id * @return bool */ static public function locationHasHooks( $group, $ids ) { /* Return right away if we don't have an ids to check */ if( ! is_array( $ids ) || ! count( $ids ) ) { return false; } /* Reformat the cache on the first call, to save processing later */ static $formattedCache = array(); if( !isset($formattedCache[ $group ]) ) { $formattedCache[ $group ] = array(); $hookCache = ipsRegistry::cache()->getCache( 'hooks' ); if ( isset( $hookCache['templateHooks'][ $group ] ) AND is_array($hookCache['templateHooks'][ $group ]) AND count($hookCache['templateHooks'][ $group ]) ) { foreach( $hookCache['templateHooks'][ $group ] as $_hook ) { $formattedCache[ $group ][] = $_hook['id']; } } } /* Use formatted cache to check */ if( count( $formattedCache[ $group ] ) ) { foreach( $ids as $id ) { if( in_array( $id, $formattedCache[ $group ] ) ) { return true; } } } return false; } /** * Quickly determines if we've got VK enabled and set up * * @access public * @return boolean */ static public function vkontakte_enabled() { return ( ipsRegistry::$settings['vk_enabled'] AND ipsRegistry::$settings['vk_api_id'] AND ipsRegistry::$settings['vk_secret'] ) ? TRUE : FALSE; } /** * Central setlocale method so we can adjust as needed * * @param string Locale to set * @return void * @link http://community.invisionpower.com/tracker/issue-16386-language-locale-gives-error/ * @link http://community.invisionpower.com/tracker/issue-18424-change-lang-locale/ */ static public function setlocale( $locale='' ) { if( !$locale ) { return; } if ( stripos( $locale, 'tr_' ) !== FALSE ) { setlocale( LC_COLLATE, $locale ); setlocale( LC_MONETARY, $locale ); setlocale( LC_NUMERIC, $locale ); setlocale( LC_TIME, $locale ); setlocale( LC_MESSAGES, $locale ); } else { setlocale( LC_ALL, $locale ); } } /** * Quickly determines if we've got FB enabled and set up * * @return boolean */ static public function fbc_enabled() { return ( ipsRegistry::$settings['fbc_enable'] AND ipsRegistry::$settings['fbc_api_id'] AND ipsRegistry::$settings['fbc_secret'] ) ? TRUE : FALSE; } /** * Quickly determines if we've got twitter enabled and set up * * @return boolean */ static public function twitter_enabled() { return ( ipsRegistry::$settings['tc_enabled'] AND ipsRegistry::$settings['tc_token'] AND ipsRegistry::$settings['tc_secret'] ) ? TRUE : FALSE; } /** * Quickly determines if we've got other log in enabled and set up * * @return boolean */ static public function loginMethod_enabled( $method ) { if ( ! count( self::$_lims ) ) { if ( is_array( ipsRegistry::cache()->getCache('login_methods') ) ) { $cache = ipsRegistry::cache()->getCache('login_methods'); foreach( $cache as $lim ) { self::$_lims[ $lim['login_folder_name'] ] = $lim['login_folder_name']; } } } switch( $method ) { case 'vkontakte': return self::vkontakte_enabled(); break; case 'facebook': return self::fbc_enabled(); break; case 'twitter': return self::twitter_enabled(); break; default: return in_array( $method, self::$_lims ) ? true : false; break; } } /** * Unpack group bitwise options * * @param array * @param bool Do not warn on overwrite * @return array */ static public function unpackGroup( $group, $silence=false ) { /* Unpack bitwise fields */ $_tmp = IPSBWOptions::thaw( $group['g_bitoptions'], 'groups', 'global' ); if ( count( $_tmp ) ) { foreach( $_tmp as $k => $v ) { /* Trigger notice if we have DB field */ if ( $silence === false AND isset( $group[ $k ] ) ) { trigger_error( "Thawing bitwise options for GROUPS: Bitwise field '$k' has overwritten DB field '$k'", E_USER_WARNING ); } $group[ $k ] = $v; } } return $group; } /** * Little function to return the version number data * * Handy to use when dealing with IN_DEV, etc * Uses the constant where available * * @param string App ( Default 'core') * @return array array( 'long' => x, 'human' => x ) */ static public function fetchVersionNumber( $app='core' ) { if ( ! defined( IPB_VERSION ) OR ! defined( IPB_LONG_VERSION ) ) { require_once( IPS_ROOT_PATH . 'setup/sources/base/setup.php' ); $XMLVersions = IPSSetUp::fetchXmlAppVersions( $app ); $tmp = $XMLVersions; krsort( $tmp ); foreach( $tmp as $long => $human ) { $return = array( 'long' => $long, 'human' => $human ); break; } } else { $return = array( 'long' => IPB_LONG_VERSION, 'human' => IPB_VERSION ); } return $return; } /** * Cheeky little function to locate group table fields from other apps * * @return array Array of fields from different apps */ static function fetchNonDefaultGroupFields() { $fields = array(); foreach( array( 'gallery', 'blog', 'downloads', 'ccs', 'ipchat', 'nexus', 'subscriptions' ) as $app ) { $_file = IPSLib::getAppDir( $app ) . '/setup/versions/install/sql/' . $app . '_mysql_tables.php'; if ( file_exists( $_file ) ) { $TABLE = array(); require( $_file ); foreach( $TABLE as $t ) { if ( preg_match( '#^ALTER TABLE\s+?groups\s+?ADD\s+?(\S+?)\s#i', $t, $match ) ) { $fields[] = $match[1]; } } } } return $fields; } /** * Update settings * * @param array array('conf_key' => 'new value') * @return true/false * @author MarkWade */ static public function updateSettings($update=array()) { $fields = array_keys($update); ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'core_sys_conf_settings', 'where' => "conf_key IN ('" . implode( "','", $fields ) . "')" ) ); ipsRegistry::DB()->execute(); $db_fields = array(); while ( $r = ipsRegistry::DB()->fetch() ) { $db_fields[ $r['conf_key'] ] = $r; } if (empty($db_fields)) { return false; } foreach( $db_fields as $key => $data ) { $value = str_replace( "'", "'", IPSText::safeslashes($update[ $key ]) ); $value = $value == '' ? "{blank}" : $value; ipsRegistry::DB()->update( 'core_sys_conf_settings', array( 'conf_value' => $value ), 'conf_id=' . $data['conf_id'] ); } ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'core_sys_conf_settings', 'where' => 'conf_add_cache=1' ) ); $info = ipsRegistry::DB()->execute(); while ( $r = ipsRegistry::DB()->fetch($info) ) { $value = $r['conf_value'] != "" ? $r['conf_value'] : $r['conf_default']; if ( $value == '{blank}' ) { $value = ''; } $settings[ $r['conf_key'] ] = $value; } ipsRegistry::cache()->setCache( 'settings', $settings, array( 'array' => 1 ) ); return true; } /** * Retrieve the default language * * @return string Default language id (most likely a number) */ static public function getDefaultLanguage() { $cache = ipsRegistry::cache()->getCache('lang_data'); if( !count($cache) OR !is_array($cache) ) { ipsRegistry::getClass('class_localization')->rebuildLanguagesCache(); $cache = ipsRegistry::cache()->getCache('lang_data'); } $_default = 1; foreach( $cache as $_lang ) { if( $_lang['lang_default'] ) { $_default = $_lang['lang_id']; break; } } return $_default; } /** * Build furl templates from FURL extensions * * @return array */ static public function buildFurlTemplates() { /* INIT */ $apps = array(); $_SEOTEMPLATES = array(); static $_apps = array(); /* Done this already? */ if ( self::$_furlTemplates ) { return self::$_furlTemplates; } /** * Get app data and cache - 1 query is better than 1 per app */ ipsRegistry::DB()->build( array( 'select' => 'app_directory, app_public_title, app_enabled', 'from' => 'core_applications' ) ); ipsRegistry::DB()->execute(); while( $_r = ipsRegistry::DB()->fetch() ) { $_apps[ $_r['app_directory'] ] = $_r; } /* Because this is called before the cache is unpacked, we need to expensively grab all app dirs */ foreach( array( 'applications', 'applications_addon/ips', 'applications_addon/other' ) as $folder ) { try { foreach( new DirectoryIterator( IPS_ROOT_PATH . $folder ) as $file ) { if ( ! $file->isDot() AND $file->isDir() ) { $_name = $file->getFileName(); if ( substr( $_name, 0, 1 ) != '.' ) { /* Check if this app is enabled before including the templates.. */ $_check = $_apps[ $_name ]; if ( $_check['app_public_title'] && $_check['app_enabled'] ) { $apps[ $folder . '/' . $_name ] = $_name; } } } } } catch ( Exception $e ) {} } /* First, add in core stuffs */ ipsRegistry::_loadCoreVariables(); $templates = ipsRegistry::_fetchCoreVariables('templates'); if ( is_array( $templates ) ) { foreach( $templates as $key => $data ) { self::$_furlTemplates[ $key ] = $data; } } // FURL Pack select $furl_templates_filename = 'furlTemplates.php'; if ( IPB_USE_ONLY_ID_FURL ) { $furl_templates_filename = 'furlIdTemplates.php'; } /* Loop over the applications and build */ foreach( $apps as $path => $app_dir ) { if ( file_exists( IPS_ROOT_PATH . $path . '/extensions/' . $furl_templates_filename ) ) { $_SEOTEMPLATES = array(); require( IPS_ROOT_PATH . $path . '/extensions/' . $furl_templates_filename ); if ( is_array( $_SEOTEMPLATES ) && count( $_SEOTEMPLATES ) ) { foreach( $_SEOTEMPLATES as $key => $data ) { self::$_furlTemplates[ $key ] = $data; } } } } /* Return for anyone else */ return self::$_furlTemplates; } /** * Cache templates from FURL extensions * * @return boolean * @exceptions * CANNOT_WRITE Cannot write to cache file * NO_DATA_TO_WRITE No data to write */ static public function cacheFurlTemplates() { if ( ! count( self::$_furlTemplates ) ) { self::buildFurlTemplates(); } if ( count( self::$_furlTemplates ) ) { $_date = gmdate( 'r', time() ); $_var = var_export( self::$_furlTemplates, TRUE ); $data = << EOF; if ( ! @file_put_contents( DOC_IPS_ROOT_PATH . 'cache/furlCache.php', $data ) ) { throw new Exception( 'CANNOT_WRITE' ); } } else { throw new Exception( 'NO_DATA_TO_WRITE' ); } return TRUE; } /** * Rebuild the Sphinx conf * * @return string Null, or the new sphinx config */ static public function rebuildSphinxConfig() { //----------------------------------------- // Init some sphinx vars //----------------------------------------- $sphinxTemplate = ''; $sphinxCompiled = ''; //----------------------------------------- // Got the template file? //----------------------------------------- if( !file_exists( IPS_ROOT_PATH . '/extensions/sphinxTemplate.php' ) ) { return null; } require_once( IPS_ROOT_PATH . '/extensions/sphinxTemplate.php' ); //----------------------------------------- // Does the template exist? //----------------------------------------- if( !$sphinxTemplate ) { return null; } //----------------------------------------- // Replace out the SQL details //----------------------------------------- $sphinxTemplate = str_replace( "" , ipsRegistry::$settings['sql_host'] , $sphinxTemplate ); $sphinxTemplate = str_replace( "" , ipsRegistry::$settings['sql_user'] , $sphinxTemplate ); $sphinxTemplate = str_replace( "" , ipsRegistry::$settings['sql_pass'] , $sphinxTemplate ); $sphinxTemplate = str_replace( "", ipsRegistry::$settings['sql_database'], $sphinxTemplate ); $sphinxTemplate = str_replace( "" , ipsRegistry::$settings['sql_port'] ? ipsRegistry::$settings['sql_port'] : 3306, $sphinxTemplate ); //----------------------------------------- // Loop over the applications and build //----------------------------------------- foreach( ipsRegistry::$applications as $app_dir => $application ) { $appSphinxTemplate = ''; if( file_exists( IPSLib::getAppDir( $app_dir ) . '/extensions/sphinxTemplate.php' ) ) { require_once( IPSLib::getAppDir( $app_dir ) . '/extensions/sphinxTemplate.php' ); $sphinxCompiled .= $appSphinxTemplate; } } //----------------------------------------- // Replace DB prefix //----------------------------------------- $sphinxCompiled = str_replace( "" , ipsRegistry::$settings['sql_tbl_prefix'], $sphinxCompiled ); //----------------------------------------- // And replace out the content with the compilation //----------------------------------------- $sphinxTemplate = str_replace( "", $sphinxCompiled, $sphinxTemplate ); //----------------------------------------- // Replace out the /var/sphinx/ path //----------------------------------------- $sphinxTemplate = str_replace( "", rtrim( ipsRegistry::$settings['sphinx_base_path'], '/' ), $sphinxTemplate ); $sphinxTemplate = str_replace( "", ipsRegistry::$settings['search_sphinx_port'], $sphinxTemplate ); //----------------------------------------- // Wildcard support on? //----------------------------------------- if ( ipsRegistry::$settings['sphinx_wildcard'] ) { $sphinxTemplate = str_replace( '#infix_fields' , 'infix_fields', $sphinxTemplate ); $sphinxTemplate = str_replace( '#min_infix_len', 'min_infix_len', $sphinxTemplate ); $sphinxTemplate = str_replace( '#enable_star' , 'enable_star', $sphinxTemplate ); } //----------------------------------------- // Return the new content //----------------------------------------- return $sphinxTemplate; } /** * Recursively cleans keys and values and * inserts them into the input array * * @param mixed Input data * @param array Storage array for cleaned data * @param integer Current iteration * @return array Cleaned data */ static public function parseIncomingRecursively( &$data, $input=array(), $iteration = 0 ) { // Crafty hacker could send something like &foo[][][][][][]....to kill Apache process // We should never have an input array deeper than 20.. if ( $iteration >= 20 ) { return $input; } foreach( $data as $k => $v ) { if ( is_array( $v ) ) { $input[ $k ] = self::parseIncomingRecursively( $data[ $k ], array(), $iteration + 1 ); } else { $k = IPSText::parseCleanKey( $k ); $v = IPSText::parseCleanValue( $v, false ); $input[ $k ] = $v; } } return $input; } /** * Recursively cleans values after settings have been loaded. * Necessary for certain functions (such as whether to strip space chars or not) * * @param mixed Input data * @param integer Current iteration * @return array Cleaned data */ static public function postParseIncomingRecursively( $request, $iteration = 0 ) { // Crafty hacker could send something like &foo[][][][][][]....to kill Apache process // We should never have an input array deeper than 20.. if ( $iteration >= 20 OR !is_array($request) ) { return $request; } foreach( $request as $k => $v ) { if ( is_array( $v ) ) { $request[ $k ] = self::postParseIncomingRecursively( $v, ++$iteration ); } else { $v = IPSText::postParseCleanValue( $v ); $request[ $k ] = $v; } } return $request; } /** * Performs basic cleaning, Null characters, etc * * @param array Input data * @return array Cleaned data */ static public function cleanGlobals( &$data, $iteration = 0 ) { // Crafty hacker could send something like &foo[][][][][][]....to kill Apache process // We should never have an input array deeper than 10.. if ( $iteration >= 10 ) { return; } foreach( $data as $k => $v ) { if ( is_array( $v ) ) { self::cleanGlobals( $data[ $k ], ++$iteration ); } else { # Null byte characters $v = str_replace( chr('0') , '', $v ); $v = str_replace( "\0" , '', $v ); $v = str_replace( "\x00" , '', $v ); // @link http://community.invisionpower.com/tracker/issue-21188-post-processor-eating-characters/ //$v = str_replace( '%00' , '', $v ); # File traversal $v = str_replace( "../", "../", $v ); /* RTL override */ $v = str_replace( '‮', '', $v ); $data[ $k ] = $v; } } } /** * Determines if member is viewing images * If not, unparses smilies * * @param string Raw input text to parse * @return string Parsed text ready to be stored in database */ public static function memberViewImages( $text ) { //----------------------------------------- // Parse //----------------------------------------- if ( ! ipsRegistry::member()->getProperty('view_img') ) { //----------------------------------------- // Second regex needed for content caching //----------------------------------------- $text = self::unconvertSmilies( $text ); $text = preg_replace( '/]+?)class=[\'\"]bbc_img[\"\'](?:[^\/>]+?)\/>/', "\\1", $text ); return $text; } else { return $text; } } /** * Unconvert smilies * * @param string Raw text * @return string Converted text */ public static function unconvertSmilies( $txt ) { //----------------------------------------- // Unconvert smilies //----------------------------------------- $txt = str_replace( "<#EMO_DIR#>", "<#EMO_DIR>", $txt ); preg_match_all( "#(]+?)class=['\"]bbc_emoticon[\"'](?:[^>]+?)alt=['\"](.+?)[\"'](?:[^>]+?)?>)#is", $txt, $matches ); if( is_array($matches[1]) AND count($matches[1]) ) { foreach( $matches[1] as $index => $value ) { if ( count( ipsRegistry::cache()->getCache('emoticons') ) > 0 ) { foreach( ipsRegistry::cache()->getCache('emoticons') as $row ) { $_emoCode = str_replace( '<', '<', str_replace( '>', '>', $row['typed'] ) ); if( $matches[2][ $index ] == $_emoCode ) { /* We need to make sure emoticons are wrapped in spaces so they are parsed properly */ //$txt = str_replace( $value, ' ' . $_emoCode . ' ', $txt ); /* We are no longer matching opening/closing "space" so no need to add it */ $txt = str_replace( $value, $_emoCode, $txt ); continue 2; } } } } } $txt = str_replace( "<#EMO_DIR>", "<#EMO_DIR#>", $txt ); return $txt; } /** * Fetch emoticons as JSON for editors, etc * * @param string Directory for emos [optional] * @param bool Include emoticons not marked clickable * @return string JSON */ static public function fetchEmoticonsAsJson( $emoDir='', $nonClickable=false ) { $emoDir = ( $emoDir ) ? $emoDir : ipsRegistry::getClass('output')->skin['set_emo_dir']; $emoArray = array(); $emoString = ''; $smilie_id = 0; foreach( ipsRegistry::cache()->getCache('emoticons') as $elmo ) { if ( $elmo['emo_set'] != $emoDir ) { continue; } if ( ! $elmo['clickable'] AND !$nonClickable ) { continue; } $smilie_id++; //----------------------------------------- // Make single quotes as URL's with html entites in them // are parsed by the browser, so ' causes JS error :o //----------------------------------------- if ( strstr( $elmo['typed'], "'" ) ) { $in_delim = '"'; } else { $in_delim = "'"; } $emoArray[] = $in_delim . addslashes($elmo['typed']) . $in_delim . ' : "' . $smilie_id . ','.$elmo['image'].'"'; } //----------------------------------------- // Finish up smilies... //----------------------------------------- if ( count( $emoArray ) ) { $emoString = implode( ",\n", $emoArray ); } return $emoString; } /** * Fetch bbcode as JSON for editors, etc * * @return string JSON */ static public function fetchBbcodeAsJson() { $bbcodes = array(); $protectedBbcodes = array( 'right', 'left', 'center', 'b', 'i', 'u', 'url', 'img', 'quote', 'indent', 'list', 'strike', 'sub', 'sup', 'email', 'background', 'color', 'size', 'font', 'media' ); foreach( ipsRegistry::cache()->getCache('bbcode') as $bbcode ) { if( in_array( $bbcode['bbcode_tag'], $protectedBbcodes ) ) { continue; } if( $bbcode['bbcode_groups'] != 'all' ) { $pass = false; $groups = array_diff( explode( ',', $bbcode['bbcode_groups'] ), array('') ); $mygroups = array( ipsRegistry::member()->getProperty('member_group_id') ); $mygroups = array_diff( array_merge( $mygroups, explode( ',', IPSText::cleanPermString( ipsRegistry::member()->getProperty('mgroup_others') ) ) ), array('') ); foreach( $groups as $g_id ) { if( in_array( $g_id, $mygroups ) ) { $pass = true; break; } } if( !$pass ) { continue; } } $bbcodes[ $bbcode['bbcode_tag'] ] = array( 'id' => $bbcode['bbcode_id'], 'title' => $bbcode['bbcode_title'], 'desc' => $bbcode['bbcode_desc'], 'tag' => $bbcode['bbcode_tag'], 'useoption' => $bbcode['bbcode_useoption'], 'example' => $bbcode['bbcode_example'], 'switch_option' => $bbcode['bbcode_switch_option'], 'menu_option_text' => $bbcode['bbcode_menu_option_text'], 'menu_content_text' => $bbcode['bbcode_menu_content_text'], 'single_tag' => $bbcode['bbcode_single_tag'], 'optional_option' => $bbcode['bbcode_optional_option'], 'image' => $bbcode['bbcode_image'], ); } return IPSText::simpleJsonEncode($bbcodes); } /** * Create profile link * * @param string User's display name * @param integer User's DB ID * @param string SEO display name * @param string Classname * @return string Parsed a href link * @since 2.0 */ static public function makeProfileLink($name, $id="", $_seoName="", $className='') { $_seoName = ( $_seoName ) ? $_seoName : IPSText::makeSeoTitle( $name ); $_class = ( $className ) ? ' class="' . $className . '" ' : ''; if ($id > 0) { return "buildSEOUrl( 'showuser=' . $id, 'public', $_seoName, 'showuser' ) . "'" . $_class .">{$name}"; } else { return $name; } } /** * Runs the specified member sync module, takes a variable number of arguments. * * @param string $module The module to run, ex: onCreateAccount, onRegisterForm, etc * @param mixed ... Remaining params should match the module being called. ex: array of member data for onCreateAccount, * or an id and email for onEmailChange * @return void */ static public function runMemberSync( $module ) { /* ipsRegistry::$applications only contains apps with a public title #15785 */ $app_cache = ipsRegistry::cache()->getCache('app_cache'); /* Params */ $params = func_get_args(); array_shift( $params ); /* Loop through applications */ foreach( $app_cache as $app_dir => $app ) { /* Only if app enabled... */ if ( $app['app_enabled'] ) { /* Setup */ $_file = self::getAppDir( $app['app_directory'] ) . '/extensions/memberSync.php'; /* Check for the file */ if( file_exists( $_file ) ) { /* Get the file */ $_class = self::loadLibrary( $_file, $app['app_directory'] . 'MemberSync', $app['app_directory'] ); /* Check for the class */ if( class_exists( $_class ) ) { /* Create an object */ $_obj = new $_class(); /* Check for the module */ if( method_exists( $_obj, $module ) ) { /* Call it */ call_user_func_array( array( $_obj, $module ), $params ); IPSDebug::addLogMessage( $app_dir . '-' . $module, 'mem' ); } } } } } } /** * Pick the highest number from an array * Used in classItemMarking.. figured it might be useful elsewhere... * * @param array Array of numbers * @return integer Highest number in the array */ static public function fetchHighestNumber( $array ) { if ( is_array( $array ) ) { $_array = array(); foreach( $array as $number ) { $_array[] = intval( $number ); } sort( $_array ); return intval( array_pop( $_array ) ); } else { return 0; } } /** * Hand-rolled 'is_writable' function to overcome * annoyances with the PHP in built version * Based on user notes at php.net (is_writable function comments) * * @param string Path to check * @return boolean */ static public function isWritable( $path ) { if ( substr( $path, -1 ) == '/' ) { return self::isWritable( $path . uniqid( mt_rand() ) . '.tmp'); } else if ( is_dir( $path ) ) { return self::isWritable( $path.'/'.uniqid( mt_rand() ) . '.tmp'); } $e = file_exists( $path ); $f = @fopen( $path, 'a' ); if ( $f === FALSE ) { return FALSE; } fclose ($f ); if ( $e === FALSE ) { unlink($path); } return TRUE; } /** * Acts like PHPs next() but if the pointer is at the end of the array or it finds a false * value, then it rewinds the array and starts over * * @param array Reference to an array * @return mixed Next value in the array */ static public function next( &$array ) { if ( ! is_array( $array ) ) { return FALSE; } $next = next( $array ); if ( ! $next || $next === FALSE ) { reset( $array ); $next = next( $array ); } return $next; } /** * Function to naturally sort an array by keys * * @param array Array to sort * @return array Sorted array */ static public function knatsort( $array ) { $_a = array_keys( $array ); $_b = array(); natsort( $_a ); foreach( $_a as $__a ) { $_b[ $__a ] = $array[ $__a ]; } return $_b; } /** * Merges two arrays using a custom call back function (like array_merge using usort) * * $a = array( 'red' => 100, * 'green' => 200, * 'blue' => 300, * 'orange' => 600 ); * * $b = array( 'red' => 101, * 'green' => 199, * 'blue' => 305, * 'black' => 100 ); * * Merge both arrays, finding the highest value in each * print_r( array_umerge( $a, $b, create_function( '$a,$b', 'return $a < $b;' ) ) ); * Produces: * Array * ( * [red] => 101 * [green] => 200 * [blue] => 305 * [orange] => 600 * [black] => 100 * ) * * * @param array Array to merge * @param array Array to merge * @param string Callback function * @return array Merged array * @deprecated This will likely be removed in a future version */ static public function arrayUmerge( $array1, $array2, $callback ) { $return = array(); if ( ! is_array( $array1 ) AND ! is_array( $array2 ) ) { return FALSE; } foreach( $array1 as $_k1 => $_v1 ) { $return[ $_k1 ] = $_v1; /* Key exists in second array - so compare */ if ( isset( $array2[$_k1] ) ) { if ( call_user_func( $callback, $_v1, $array2[$_k1] ) ) { $return[ $_k1 ] = $array2[$_k1]; } } } /* Now check for keys in the second array that aren't in the first */ foreach( $array2 as $_k2 => $_v2 ) { if ( ! isset( $array1[ $_k2 ] ) ) { $return[ $_k2 ] = $_v2; } } return $return; } /** * Merges arrays like array_merge_recursive but replaces indentical keys * */ static public function arrayMergeRecursive() { $arrays = func_get_args(); $base = array_shift( $arrays ); if ( ! is_array($base) ) { $base = empty($base) ? array() : array($base); } foreach( $arrays as $append ) { if ( ! is_array( $append ) ) { $append = array( $append ); } foreach( $append as $key => $value) { if ( ! array_key_exists( $key, $base ) and ! is_numeric( $key ) ) { $base[$key] = $append[$key]; continue; } if ( is_array($value) or is_array( $base[$key] ) ) { $base[$key] = self::arrayMergeRecursive( $base[$key], $append[$key] ); } else if ( is_numeric( $key ) ) { if ( ! in_array( $value, $base ) ) { $base[] = $value; } } else { $base[$key] = $value; } } } return $base; } /** * Merge two arrays. Unlike array_merge, however, duplicate keys are ignored rather than overwritten. * * @param array * @param array * @return array */ static public function mergeArrays( $array1, $array2 ) { $returnArray = $array1; foreach( $array2 as $_key => $_value ) { if( !array_key_exists( $_key, $returnArray ) ) { $returnArray[ $_key ] = $_value; } } return $returnArray; } /** * arraySearchLoose * * @param string "Needle" * @param array Array of text to search * @return mixed Key of array, or false on failure */ static public function arraySearchLoose( $needle, $haystack ) { if( !is_array( $haystack ) OR !count($haystack) OR ! $needle ) { return false; } foreach( $haystack as $k => $v ) { if( $v AND stripos( $v, $needle ) !== false ) { return $k; } } return false; } /** * Check to see if any applications support 'like' system * * @return bool True if there are any applications that support the 'like' system, otherwise false */ static public function haveAppsWithLike() { foreach( ipsRegistry::$applications as $_app => $data ) { if( $data['_supports_likes'] ) { return true; } } return false; } /** * Get the application title. Uses lang file keys if present. * * @param string application * @return string Text to show for application title */ static public function getAppTitle( $app ) { if ( ! $app ) { return ''; } return isset( ipsRegistry::getClass('class_localization')->words[ $app . '_display_title' ] ) ? ipsRegistry::getClass('class_localization')->words[ $app . '_display_title' ] : ( IN_ACP ? ipsRegistry::$applications[ $app ]['app_title'] : ipsRegistry::$applications[ $app ]['app_public_title'] ); } /** * Generates the app [ -> module ] path. The module is optional, if module is not * specified then just the app dir is returned. If this is called from the ACP and module * is present, then it'll return modules_admin, otherwise modules_public * * @param string application * @param string module (optional) * @return mixed Directory to app or module (or false if error) */ static public function getAppDir( $app, $module='' ) { $location = ''; if ( ! $app OR !is_string($app) ) { return FALSE; } /* Ok, chicken and egg scenario. Applications has not been set up - most likely because we're using this function before the caches have been loaded and unpacked. So we guess based on folder names.... */ if ( ! is_array( ipsRegistry::$applications ) OR ! count( ipsRegistry::$applications ) OR ! isset( ipsRegistry::$applications[ $app ] ) ) { $location = self::extractAppLocationKey( $app ); } else { $location = ipsRegistry::$applications[ $app ]['app_location']; } $pathBit = IPS_ROOT_PATH . 'applications'; $modulesFolder = ( IPS_AREA != 'admin' ) ? 'modules_public' : 'modules_admin'; switch ( $location ) { default: case 'root': $pathBit .= '/' . $app; break; case 'ips': $pathBit .= '_addon/ips/' . $app; break; case 'other': $pathBit .= '_addon/other/' . $app; break; } if ( $module ) { return $pathBit . "/" . $modulesFolder . "/" . $module; } else { return $pathBit; } } /** * Extracts app_location from app key * * @param string File path * @return string root, ips, other */ static public function extractAppLocationKey( $app ) { /* Test core apps first... */ if ( is_dir( IPS_ROOT_PATH . 'applications/' . $app ) ) { $location = 'root'; } else if ( is_dir( IPS_ROOT_PATH . 'applications_addon/ips/' . $app ) ) { $location = 'ips'; } else { $location = 'other'; } return $location; } /** * Generates the app folder name, either "applications" or "applications_addon" * * @param string application * @return mixed Directory to app or module (or false if error) */ static public function getAppFolder( $app ) { if ( ! $app OR ! isset(ipsRegistry::$applications[ $app ]) ) { return FALSE; } switch ( ipsRegistry::$applications[ $app ]['app_location'] ) { default: case 'root': $pathBit = 'applications'; break; case 'ips': $pathBit = 'applications_addon/ips'; break; case 'other': $pathBit = 'applications_addon/other'; break; } return $pathBit; } /** * Determines if the application can be searched * * @param string $app Application key * @return bool */ static public function appIsSearchable( $app, $type='search' ) { /* Init */ $_ck = ''; /* map config */ switch( strtolower( $type ) ) { default: case 'search': $_ck = 'can_search'; break; case 'vnc': case 'newcontent': case 'viewnewcontent': $_ck = 'can_viewNewContent'; break; case 'active': case 'activecontent': $_ck = 'can_activeContent'; break; case 'usercontent': case 'users': case 'user': $_ck = 'can_userContent'; break; } /* got anything? */ if ( ! is_array( self::$_searchConfigs ) OR ! count( self::$_searchConfigs ) ) { foreach( ipsRegistry::$applications as $_app => $data ) { /* use the cache if we can */ if ( ! IN_DEV AND isset( ipsRegistry::$applications[$_app]['search'] ) AND is_array( ipsRegistry::$applications[$_app]['search'] ) AND count( ipsRegistry::$applications[$_app]['search'] ) ) { self::$_searchConfigs[ $_app ] = ipsRegistry::$applications[$_app]['search']; } else { $_file = IPSLib::getAppDir( $_app ) . '/extensions/search/config.php'; if ( IPSLib::appIsInstalled( $_app ) AND file_exists( $_file ) ) { $CONFIG = array(); require( $_file ); if ( is_array( $CONFIG ) AND count( $CONFIG ) ) { self::$_searchConfigs[ $_app ] = $CONFIG; unset( $CONFIG ); } } } } } /* return */ if ( isset( self::$_searchConfigs[ $app ] ) AND is_array( self::$_searchConfigs[ $app ] ) AND count( self::$_searchConfigs[ $app ] ) ) { return ( self::$_searchConfigs[ $app ][ $_ck ] ) ? true : false; } return false; } /** * Checks to see if the given application is currently installed and enabled * * @param string $app * @return bool */ static public function appIsInstalled( $app, $checkEnabled=true ) { if ( isset( ipsRegistry::$applications[$app] ) ) { if( $checkEnabled ) { if( ipsRegistry::$applications[$app]['app_enabled'] ) { return TRUE; } } else { return TRUE; } } return FALSE; } /** * Check to see if the givem module is currently installed and enabled * * @param string $module module_key * @param string [$app] app_key, current application by default * @return bool */ static public function moduleIsEnabled( $module, $app='' ) { $app = $app ? $app : ipsRegistry::$current_application; foreach( ipsRegistry::$modules[$app] as $_m ) { if ( $_m['sys_module_key'] == $module ) { return $_m['sys_module_visible'] == 1; } } return FALSE; } /** * Grab max post upload * * @return integer Max post size */ static public function getMaxPostSize() { $max_file_size = 16777216; $tmp = 0; $_post = @ini_get('post_max_size'); $_upload = @ini_get('upload_max_filesize'); if ( $_upload > $_post ) { $tmp = $_post; } else { $tmp = $_upload; } if ( $tmp ) { $max_file_size = $tmp; unset($tmp); preg_match( '#^(\d+)(\w+)$#', strtolower($max_file_size), $match ); if( $match[2] == 'g' ) { $max_file_size = intval( $max_file_size ) * 1024 * 1024 * 1024; } else if ( $match[2] == 'm' ) { $max_file_size = intval( $max_file_size ) * 1024 * 1024; } else if ( $match[2] == 'k' ) { $max_file_size = intval( $max_file_size ) * 1024; } else { $max_file_size = intval( $max_file_size ); } } return $max_file_size; } /** * Convert strlen to bytes * * @param integer string length (no chars) * @return integer Bytes * @since 2.0 */ static public function strlenToBytes( $strlen=0 ) { $dh = pow(10, 0); return round( $strlen / ( pow(1024, 0) / $dh ) ) / $dh; } /** * Takes a number of bytes and formats in k or MB as required * * @param string Size, in bytes * @param boolean TRUE = no language class avaiable (during start up, debug, etc) * @return string Size, in MB, KB or bytes, whichever is closest */ static public function sizeFormat($bytes="", $noLang=FALSE) { $retval = ""; if ( $noLang === FALSE ) { $lang['sf_gb'] = ipsRegistry::getClass('class_localization')->words['sf_gb'] ? ipsRegistry::getClass('class_localization')->words['sf_gb'] : 'gb'; $lang['sf_mb'] = ipsRegistry::getClass('class_localization')->words['sf_mb'] ? ipsRegistry::getClass('class_localization')->words['sf_mb'] : 'mb'; $lang['sf_k'] = ipsRegistry::getClass('class_localization')->words['sf_k'] ? ipsRegistry::getClass('class_localization')->words['sf_k'] : 'kb'; $lang['sf_bytes'] = ipsRegistry::getClass('class_localization')->words['sf_bytes'] ? ipsRegistry::getClass('class_localization')->words['sf_bytes'] : 'b'; } else { $lang['sf_gb'] = 'gb'; $lang['sf_mb'] = 'mb'; $lang['sf_k'] = 'kb'; $lang['sf_bytes'] = 'b'; } if ( $bytes >= 1073741824 ) { $retval = round($bytes / 1073741824 * 100 ) / 100 . $lang['sf_gb']; } else if ($bytes >= 1048576) { $retval = round($bytes / 1048576 * 100 ) / 100 . $lang['sf_mb']; } else if ($bytes >= 1024) { $retval = round($bytes / 1024 * 100 ) / 100 . $lang['sf_k']; } else { $retval = $bytes . $lang['sf_bytes']; } return $retval; } /** * Makes int based arrays safe * XSS Fix: Ticket: 24360 (Problem with cookies allowing SQL code in keys) * * @param array Array * @return array Array (Cleaned) * @since 2.1.4(A) */ static public function cleanIntArray( $array=array() ) { $return = array(); if ( is_array( $array ) and count( $array ) ) { foreach( $array as $k => $v ) { $return[ intval($k) ] = intval($v); } } return $return; } /** * Loads an interface. Abstracted incase we change location / method * of loading an interface * * @param string File name * @return void * @since 3.0.0 */ static public function loadInterface( $filename ) { //----------------------------------------- // Very simple, currently. //----------------------------------------- include_once( IPS_ROOT_PATH . 'sources/interfaces/' . $filename ); } /** * Create a random 15 character password * * @return string Password * @since 2.0 */ public static function makePassword() { $pass = ""; // Want it random you say, eh? // (enter evil laugh) $unique_id = uniqid( mt_rand(), TRUE ); $prefix = IPSMember::generatePasswordSalt(); $unique_id .= md5( $prefix ); usleep( mt_rand(15000,1000000) ); // Hmm, wonder how long we slept for $new_uniqueid = uniqid( mt_rand(), TRUE ); $final_rand = md5( $unique_id . $new_uniqueid ); for ($i = 0; $i < 15; $i++) { $pass .= $final_rand{ mt_rand(0, 31) }; } return $pass; } /** * Scale a remote image * * @param string URL * @param int Max width * @param int Max height * @return string width='#' height='#' string */ static public function getTemplateDimensions( $image, $width, $height ) { if( empty( $width ) AND empty( $height ) ) { return; } if( !$image ) { return; } //----------------------------------------- // Checking image dimensions via disk instead // of http is faster...can we try that..? //----------------------------------------- if( strpos( $image, ipsRegistry::$settings['board_url'] ) === 0 ) { $image = DOC_IPS_ROOT_PATH . str_replace( ipsRegistry::$settings['board_url'], '', $image ); } //----------------------------------------- // Dimensions // If set maxwidth and no maxheight, then we want the script to // reduce based on width only. And vice-versa. //----------------------------------------- $maxWidth = ( $width ) ? intval($width) : 1000000000; $maxHeight = ( $height ) ? intval($height) : 1000000000; //----------------------------------------- // Existing dims //----------------------------------------- $_dims = @getimagesize( $image ); if( !$_dims[0] ) { return; } $_newDims = IPSLib::scaleImage( array( 'cur_width' => $_dims[0], 'cur_height' => $_dims[1], 'max_width' => $maxWidth, 'max_height' => $maxHeight, ) ); //----------------------------------------- // Process the tag and return the data //----------------------------------------- return " width='{$_newDims['img_width']}' height='{$_newDims['img_height']}'"; } /** * Given current dimensions + max dimensions, return scaled image dimensions constrained to maximums * * @param array Current dimensions + max dimensions * @return array New image dimensions * @since 2.0 * @todo [Future] We may want to consider moving this to kernel classImage.php. Method exists there, protected, already. */ public static function scaleImage($arg) { // max_width, max_height, cur_width, cur_height $ret = array( 'img_width' => $arg['cur_width'], 'img_height' => $arg['cur_height'] ); if ( $arg['cur_width'] > $arg['max_width'] ) { $ret['img_width'] = $arg['max_width']; $ret['img_height'] = ceil( ( $arg['cur_height'] * ( ( $arg['max_width'] * 100 ) / $arg['cur_width'] ) ) / 100 ); $arg['cur_height'] = $ret['img_height']; $arg['cur_width'] = $ret['img_width']; } if ( $arg['cur_height'] > $arg['max_height'] ) { $ret['img_height'] = $arg['max_height']; $ret['img_width'] = ceil( ( $arg['cur_width'] * ( ( $arg['max_height'] * 100 ) / $arg['cur_height'] ) ) / 100 ); } return $ret; } /** * Format name based on group suffix/prefix * * @param string User's display name * @param integer User's group ID * @param string Optional prefix override (uses group setting if not provided) * @param string Optional suffix override (uses group setting if not provided) * @return string Formatted name * @since 2.2 */ public static function makeNameFormatted($name='', $group_id="", $prefix="", $suffix="") { if ( ipsRegistry::$settings['ipb_disable_group_psformat'] ) { return $name; } if ( ! $group_id ) { $group_id = 0; } $groupCache = ipsRegistry::cache()->getCache('group_cache'); if ( ! $prefix ) { if( $groupCache[ $group_id ]['prefix'] ) { $prefix = $groupCache[ $group_id ]['prefix']; } } if( ! $suffix ) { if( $groupCache[ $group_id ]['suffix'] ) { $suffix = $groupCache[ $group_id ]['suffix']; } } if ( ! $name ) { if( $groupCache[ $group_id ]['g_title'] ) { $name = $groupCache[ $group_id ]['g_title']; } } return $prefix.$name.$suffix; } /** * Retrieve all IP addresses a user (or multiple users) have used * * @param string Where clause for ip address * @param string Defaults to 'All', otherwise specify which tables to check (comma separated) * @return array Multi-dimensional array of found IP addresses in which sections */ static public function findIPAddresses( $ip_where, $tables_to_check='all' ) { //----------------------------------------- // INIT //----------------------------------------- $ip_addresses = array(); $tables = array( 'admin_logs' => array( 'member_id', 'ip_address', 'ctime' ), 'dnames_change' => array( 'dname_member_id', 'dname_ip_address', 'dname_date' ), 'email_logs' => array( 'from_member_id', 'from_ip_address', 'email_date' ), 'members' => array( 'member_id', 'ip_address', 'joined' ), 'message_posts' => array( 'msg_author_id', 'msg_ip_address', 'msg_date' ), 'moderator_logs' => array( 'member_id', 'ip_address', 'ctime' ), 'posts' => array( 'author_id', 'ip_address', 'post_date' ), 'profile_comments' => array( 'comment_by_member_id', 'comment_ip_address', 'comment_date' ), 'profile_ratings' => array( 'rating_by_member_id', 'rating_ip_address', '' ), //'sessions' => array( 'member_id', 'ip_address', 'running_time' ), 'topic_ratings' => array( 'rating_member_id', 'rating_ip_address', '' ), 'validating' => array( 'member_id', 'ip_address', 'entry_date' ), 'voters' => array( 'member_id', 'ip_address', 'vote_date' ), 'error_logs' => array( 'log_member', 'log_ip_address', 'log_date' ), ); //----------------------------------------- // Check apps // @see http://forums.invisionpower.com/tracker/issue-16966-members-download-manag/ //----------------------------------------- foreach( ipsRegistry::$applications as $app_dir => $data ) { if( file_exists( IPSLib::getAppDir( $app_dir ) . "/extensions/coreExtensions.php") ) { $classX = IPSLib::loadLibrary( IPSLib::getAppDir( $app_dir ) . "/extensions/coreExtensions.php", $app_dir . '_findIpAddress', $app_dir ); if( class_exists( $classX ) ) { $ipLookup = new $classX( ipsRegistry::instance() ); if( method_exists( $ipLookup, 'getTables' ) ) { $tables = array_merge( $tables, $ipLookup->getTables() ); } } } } //----------------------------------------- // Got tables? //----------------------------------------- $_tables = explode( ',', $tables_to_check ); if( !is_array($_tables) OR !count($_tables) ) { return array(); } //----------------------------------------- // Loop through them and grab the IPs //----------------------------------------- foreach( $tables as $tablename => $fields ) { if( $tables_to_check == 'all' OR in_array( $tablename, $_tables ) ) { $extra = ''; $ids = array(); if( $tablename == 'members' ) { if( $fields[2] ) { $extra = ',' . $fields[2] . ' as date'; } ipsRegistry::DB()->build( array( 'select' => $fields[1] . $extra . ', member_id', 'from' => $tablename, 'where' => $fields[1] . $ip_where, 'group' => 'member_id, ip_address, joined', 'order' => 'joined DESC', 'limit' => array( 250 ), ) ); } else { if( $fields[2] ) { $extra = ', c.' . $fields[2] . ' as date'; } $extra .= ', c.' . $fields[1] . ' as ip_address, c.' . $fields[0]; ipsRegistry::DB()->build( array( 'select' => 'c.' . $fields[1] . $extra, 'from' => array( $tablename => 'c' ), 'where' => 'c.' . $fields[1] . $ip_where, 'order' => $fields[2] ? 'c.' . $fields[2] . ' DESC' : 'c.' . $fields[0] . ' DESC', 'group' => 'c.' . $fields[0] . ', c.' . $fields[1], 'limit' => array( 250 ), 'add_join' => array( array( 'select' => 'm.member_id, m.members_display_name, m.email, m.posts, m.joined', 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id=c.' . $fields[0], 'type' => 'left', ) ) ) ); } ipsRegistry::DB()->execute(); $i = 0; while( $r = ipsRegistry::DB()->fetch() ) { if ( $r[ $fields[0] ] ) { $ids[] = $r[ $fields[0] ]; } if( $r[ $fields[1] ] ) { $rawData[ ++$i ] = $r; } } /* Get members */ $members = IPSMember::load( $ids, 'core' ); if ( is_array( $rawData ) and count ( $rawData ) ) { foreach( $rawData as $idx => $data ) { if ( $data[ $fields[0] ] && is_array( $members[ $data[ $fields[0] ] ] ) ) { $ip_addresses[ $tablename ][ $idx ] = array_merge( $data, $members[ $data[ $fields[0] ] ] ); } } } } } //----------------------------------------- // Here are your IPs kind sir. kthxbai //----------------------------------------- return $ip_addresses; } /** * Display a strip of share links * * @param string Document title (can be left blank and it will attempt to self-discover) * @param array Addition params: url, cssClass, group [string template group], bit [string template bit], skip [array of share_keys to skip] * @return string HAITHTEEEMEL */ static public function shareLinks( $title='', $params=array() ) { $url = ( isset( $params['url'] ) ) ? $params['url'] : ''; $cssClass = ( isset( $params['cssClass'] ) ) ? $params['cssClass'] : 'topic_share left'; $group = ( isset( $params['group'] ) ) ? $params['group'] : 'global'; $bit = ( isset( $params['bit'] ) ) ? $params['bit'] : 'shareLinks'; $skip = ( isset( $params['skip'] ) ) ? $params['skip'] : array(); /* Disabled? */ if ( ! ipsRegistry::$settings['sl_enable'] ) { return ''; } $canon = ipsRegistry::getClass('output')->fetchRootDocUrl(); $url = ( $url ) ? $url : ipsRegistry::$settings['this_url']; $canon = IPSText::base64_encode_urlSafe( ( $canon ) ? $canon : $url ); $title = IPSText::base64_encode_urlSafe( $title ); $url = IPSText::base64_encode_urlSafe( $url ); $cache = ipsRegistry::cache()->getCache('sharelinks'); if ( ! $cache OR ! is_array( $cache ) ) { ipsRegistry::cache()->rebuildCache('sharelinks', 'global' ); $cache = ipsRegistry::cache()->getCache('sharelinks'); } /* Check for required canonical urls or not */ foreach( $cache as $key => $data ) { if ( is_array( $skip ) AND in_array( $key, $skip ) ) { unset( $cache[ $key ] ); } else { $cache[ $key ]['_url'] = ( $data['share_canonical'] ) ? $canon : $url; } } return ipsRegistry::getClass('output')->getTemplate( $group )->$bit( $cache, $title, $canon, $cssClass ); } /** * Checks to see if the logged in user can recieve mobile notifications * * @param array $memberData Optional, logged in user will be used if this is not passed in * @return BOOL */ static public function canReceiveMobileNotifications( $memberData=array() ) { /* INIT */ $memberData = ( is_array( $memberData ) && count( $memberData ) ) ? $memberData : ipsRegistry::member()->fetchMemberData(); /* Check to see if notifications are enabled */ if( ! ipsRegistry::$settings['iphone_notifications_enabled'] ) { return false; } /* Check to see if the user has permission to get notifications */ if( ipsRegistry::$settings['iphone_notifications_groups'] ) { if( ipsMember::isInGroup( $memberData, explode( ',', ipsRegistry::$settings['iphone_notifications_groups'] ) ) ) { return true; } else { return false; } } return true; } /** * Quick function to see if a string is serialized * End up using something similar throughout the code * * @param string String to test * @return boolean */ static public function isSerialized( $string ) { if ( ! is_string( $string ) OR ! trim( $string ) ) { return false; } if ( preg_match( "#^(i|s|a|o|d):(.*)#si", $string ) !== false ) { return true; } return false; } } /** * IPSDebug * * Only useful when developing */ class IPSDebug { /** * Memory debug array * * @var array Memory debug info */ static public $memory_debug = array(); /** * Messages * * @var array Messages */ static private $_messages = array(); /** * Turn off constructor * * @return void */ private function __construct() {} /** * Start time * * @var integer Start time */ static private $_starttime; /** * Add message * * @param string * @return void */ static public function addMessage( $message ) { self::$_messages[] = $message; } /** * Send a FirePHP message * * @param string $method Method to call * @param string $vars Parameters to pass * @return void * @link http://www.firephp.org/HQ/ */ static public function fireBug( $method, $parameters=array() ) { if( IN_DEV ) { if( !class_exists( 'FB' ) ) { require_once( IPS_KERNEL_PATH . '/FirePHPCore/fb.php' ); } if( $method == 'registerExceptionHandler' ) { $firephp = FirePHP::getInstance(true); $firephp->registerExceptionHandler(); } if( $method == 'registerErrorHandler' ) { $firephp = FirePHP::getInstance(true); $firephp->registerErrorHandler(); } if( method_exists( 'FB', $method ) ) { $function = 'FB::' . $method; call_user_func_array( $function, $parameters ); } } } /** * Prettify a debug backtrace * * @param array Data from backtrace * @return string */ static public function prettifyBackTrace( $debug ) { $_dString = ''; if ( is_array( $debug ) and count( $debug ) ) { foreach( $debug as $idx => $data ) { /* Remove non-essential items */ if ( $data['class'] == 'dbMain' OR $data['class'] == 'ips_DBRegistry' OR $data['class'] == 'ipsRegistry' OR $data['class'] == 'ipsController' OR $data['class'] == 'ipsCommand' OR $data['class'] == 'db_driver_mysql' ) { continue; } $_dbString[ $idx ] = array( 'file' => $data['file'], 'line' => $data['line'], 'function' => $data['function'], 'class' => $data['class'] ); } } if ( count( $_dbString ) ) { $_error_string .= "\n .--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------."; $_error_string .= "\n | File | Function | Line No. |"; $_error_string .= "\n |----------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------|"; foreach( $_dbString as $i => $data ) { if ( defined('DOC_IPS_ROOT_PATH') ) { $data['file'] = str_replace( DOC_IPS_ROOT_PATH, '', $data['file'] ); } /* Reset */ $data['func'] = "[" . $data['class'] . '].' . $data['function']; /* Pad right */ $data['file'] = str_pad( $data['file'], 75 ); $data['func'] = str_pad( $data['func'], 78 ); $data['line'] = str_pad( $data['line'], 18 ); $_error_string .= "\n | " . $data['file'] . "| " . $data['func'] . '| ' . $data['line'] . '|'; $_error_string .= "\n '----------------------------------------------------------------------------+-------------------------------------------------------------------------------+-------------------'"; } } return $_error_string; } /** * Custom error handler * * @param integer Error number * @param string Error string * @param string Error file * @param string Error line number * @return void */ static public function errorHandler( $errno, $errstr, $errfile, $errline ) { /* Did we turn off errors with @? */ if ( ! error_reporting() ) { return; } /* Are we truly debugging? */ if ( IPS_ERROR_CAPTURE === FALSE ) { return; } $errfile = str_replace( @getcwd(), "", $errfile ); $log = false; $message = "> [$errno] $errstr\n> > Строка: $errline\n> > Файл: $errfile"; /* What do we have? */ switch ($errno) { case E_ERROR: $log = true; echo "Ошибка [$errno] $errstr (Строка: $errline в $errfile)
\n"; exit(1); break; case E_WARNING: $log = true; echo "Предупреждение [$errno] $errstr (Строка: $errline в $errfile)
\n"; break; case E_NOTICE: $log = true; break; default: return FALSE; //Do nothing break; } /* Logging? */ if ( $log ) { if ( IPS_ERROR_CAPTURE === TRUE ) { self::addLogMessage( $message, "phpNotices", false, true ); } else { foreach( explode( ',', IPS_ERROR_CAPTURE ) as $class ) { if ( preg_match( "#/" . preg_quote( $class, '#' ) . "\.#", $errfile ) ) { self::addLogMessage( $message, "phpNotices", false, true ); break; } } } } } /** * Add a message to the log file * Handy for __destruct stuff, etc * * @param string Message to add * @param string Which file to add it to * @param mixed False, or an array of vars to include in log * @param bool Force log even if IPS_LOG_ALL is off - handy for on-the-fly debugging * @param bool Unlink file before writing * @return void */ static public function addLogMessage( $message, $file='debugLog', $array=FALSE, $force=FALSE, $unlink=FALSE ) { /* Make sure IN_DEV is on to prevent logs filling up where people forget to turn it off... */ if ( ( defined( 'IPS_LOG_ALL' ) AND IPS_LOG_ALL === TRUE ) OR $force === TRUE ) { if ( $unlink === TRUE ) { @unlink( DOC_IPS_ROOT_PATH . 'cache/' . $file . '.cgi' ); } /* Array to dump? */ if ( is_array( $array ) ) { $message .= "\n" . var_export( $array, TRUE ); } $message = "\n" . str_repeat( '-', 80 ) . "\n> Time: " . time() . ' / ' . gmdate( 'r' ) . "\n> URL: " . $_SERVER['REQUEST_URI'] . "\n> " . $message; @file_put_contents( DOC_IPS_ROOT_PATH . 'cache/' . $file . '.cgi', $message, FILE_APPEND ); } } /** * Return messages * * @return array Stored messages */ static public function getMessages() { return self::$_messages; } /** * Displays a templating error * Only used when IN_DEV is on * * @param string Complete PHP error string * @param string Text evaluated by PHP * @return void */ static public function showTemplateError( $errorText, $evalCode ) { $output = array(); $count = 0; $openDiv = '
'; $lineNumber = 0; /* Convert text into lines */ $evalCode = preg_replace( "#\r#", "\n", $evalCode ); $lines = explode( "\n", $evalCode ); if ( count( $lines ) ) { foreach( $lines as $l ) { $count++; $output[ $count ] = htmlspecialchars($l); } } /* Anything we can deal with? */ if ( strstr( $errorText, "eval()'d code" ) ) { preg_match( "#eval\(\)'d code on line (\d+?)#", $errorText, $matches ); if ( $matches[1] ) { $lineNumber = $matches[1]; $output[ $lineNumber ] = "" . $output[ $lineNumber ] . ""; if ( $lineNumber > 20 ) { $_lineNumber = $lineNumber - 20; $output[ $_lineNumber ] = "" . $output[ $_lineNumber ]; } } } if ( count( $output ) ) { if ( $lineNumber ) { print "

Ошибка в шаблоне: " . $lineNumber . "

"; } else { print "

" . $errorText . "

"; } print $openDiv; foreach( $output as $number => $data ) { print "".$number."" . ' : ' . $data . "
"; } print "
"; exit(); } /* Still here? */ print "

" . $errorText . "

"; print htmlspecialchars( $evalCode ); exit(); } /** * Get current memory usage * * @return integer Current memory usage */ static public function getMemoryDebugFlag() { if ( IPS_MEMORY_START AND function_exists( 'memory_get_usage' ) ) { return memory_get_usage(); } } /** * Set a memory debug flag * * @param string Comment to set * @param integer Memory usage to compare against * @return int Memory used */ static public function setMemoryDebugFlag( $comment, $init_usage=0 ) { if ( IPS_MEMORY_START AND function_exists( 'memory_get_usage' ) ) { $_END = memory_get_usage(); $_USED = $_END - $init_usage; self::$memory_debug[] = array( $comment, $_USED ); return $_USED; } } /** * Start a timer * * @return void */ static public function startTimer() { $mtime = microtime (); $mtime = explode (' ', $mtime); $mtime = $mtime[1] + $mtime[0]; self::$_starttime = $mtime; } /** * Stop the timer * * @return integer Length of time */ static public function endTimer() { $mtime = microtime (); $mtime = explode (' ', $mtime); $mtime = $mtime[1] + $mtime[0]; $endtime = $mtime; $totaltime = round (($endtime - self::$_starttime), 5); return $totaltime; } /** * Start a timer (return value instead of storing locally) * * @return integer Time */ static public function startTimerInstance() { $mtime = microtime ( true ); $mtime = explode (' ', $mtime); $mtime = isset( $mtime[1] ) ? $mtime[1] + $mtime[0] : $mtime[0]; return $mtime; } /** * Stop the timer (compare against provided time instead of stored time) * * @param integer Start time * @return integer Length of time */ static public function endTimerInstance( $startTime=0 ) { $mtime = microtime ( true ); $mtime = explode (' ', $mtime); $mtime = isset( $mtime[1] ) ? $mtime[1] + $mtime[0] : $mtime[0]; $endtime = $mtime; $totaltime = round (($endtime - $startTime), 5); return $totaltime; } /** * Retrieve server load and update cache if appropriate * * @return string Server load */ static public function getServerLoad() { $load_limit = "--"; //----------------------------------------- // Check cache first... //----------------------------------------- $cache = ipsRegistry::instance()->cache()->getCache('systemvars'); if( $cache['loadlimit'] ) { $loadinfo = explode( "-", $cache['loadlimit'] ); if ( intval($loadinfo[1]) > (time() - 30) ) { //----------------------------------------- // Cache is less than 30 secs old, use it //----------------------------------------- $server_load_found = 1; $load_limit = $loadinfo[0]; } } //----------------------------------------- // No cache or it's old, check real time //----------------------------------------- if( !$server_load_found ) { # @ supressor stops warning in > 4.3.2 with open_basedir restrictions if ( @file_exists('/proc/loadavg') ) { if ( $fh = @fopen( '/proc/loadavg', 'r' ) ) { $data = @fread( $fh, 6 ); @fclose( $fh ); $load_avg = explode( " ", $data ); $load_limit = trim($load_avg[0]); } } else if( strpos( strtolower( PHP_OS ), 'win' ) === 0 ) { /*--------------------------------------------------------------- | typeperf is an exe program that is included with Win NT, | XP Pro, and 2K3 Server. It can be installed on 2K from the | 2K Resource kit. It will return the real time processor | Percentage, but will take 1 second processing time to do so. | This is why we shall cache it, and check only every 2 mins. | | Can also be obtained from COM, but it's extremely slow... ---------------------------------------------------------------*/ $serverstats = @shell_exec("typeperf \"Processor(_Total)\% Processor Time\" -sc 1"); if( $serverstats ) { $server_reply = explode( "\n", str_replace( "\r", "", $serverstats ) ); $serverstats = array_slice( $server_reply, 2, 1 ); $statline = explode( ",", str_replace( '"', '', $serverstats[0] ) ); $load_limit = round( $statline[1], 4 ); } } else { if ( $serverstats = @exec("uptime") ) { preg_match( "/(?:averages)?\: ([0-9\.]+)(,|)[\s]+([0-9\.]+)(,|)[\s]+([0-9\.]+)/", $serverstats, $load ); $load_limit = $load[1]; } } $cache['loadlimit'] = $load_limit . "-" . time(); if( $load_limit ) { ipsRegistry::instance()->cache()->setCache( 'systemvars', $cache, array( 'array' => 1 ) ); } } return $load_limit; } } /** * IPSCookie * * This deals with saving and writing cookies */ class IPSCookie { /** * Sensitive cookies * * @var array Sensitive cookies */ static public $sensitive_cookies = array(); /** * Handle cookies internally * so that when you SET one it is available to GET in the same process * * @var array */ static private $_cookiesSet = array(); /** * Set a cookie. * * Abstract layer allows us to do some checking, etc * * @param string Cookie name * @param string Cookie value * @param integer Is sticky flag * @param integer Number of days to expire cookie in * @return void * @since 2.0 */ static public function set( $name, $value="", $sticky=1, $expires_x_days=0 ) { //----------------------------------------- // Check //----------------------------------------- if ( isset( ipsRegistry::$settings['no_print_header'] ) AND ipsRegistry::$settings['no_print_header'] ) { return; } /* Update internal array */ self::$_cookiesSet[ $name ] = $value; //----------------------------------------- // Auto serialize arrays //----------------------------------------- if ( is_array( $value ) ) { $value = serialize( $value ); } //----------------------------------------- // Set vars //----------------------------------------- if ( $sticky == 1 ) { $expires = time() + 60*60*24*365; } else if ( $expires_x_days ) { $expires = time() + ( $expires_x_days * 86400 ); } else { $expires = FALSE; } //----------------------------------------- // Finish up... //----------------------------------------- ipsRegistry::$settings['cookie_domain'] = ipsRegistry::$settings['cookie_domain'] == "" ? "" : ipsRegistry::$settings['cookie_domain'] ; ipsRegistry::$settings['cookie_path'] = ipsRegistry::$settings['cookie_path'] == "" ? "/" : ipsRegistry::$settings['cookie_path'] ; //----------------------------------------- // Set the cookie //----------------------------------------- if ( in_array( $name, self::$sensitive_cookies ) ) { if ( PHP_VERSION < 5.2 ) { if ( ipsRegistry::$settings['cookie_domain'] ) { @setcookie( ipsRegistry::$settings['cookie_id'].$name, $value, $expires, ipsRegistry::$settings['cookie_path'], ipsRegistry::$settings['cookie_domain'] . '; HttpOnly' ); } else { @setcookie( ipsRegistry::$settings['cookie_id'].$name, $value, $expires, ipsRegistry::$settings['cookie_path'] ); } } else { @setcookie( ipsRegistry::$settings['cookie_id'].$name, $value, $expires, ipsRegistry::$settings['cookie_path'], ipsRegistry::$settings['cookie_domain'], NULL, TRUE ); } } else { @setcookie( ipsRegistry::$settings['cookie_id'].$name, $value, $expires, ipsRegistry::$settings['cookie_path'], ipsRegistry::$settings['cookie_domain']); } } /** * Get a cookie. * Abstract layer allows us to do some checking, etc * * @param string Cookie name * @return mixed * @since 2.0 */ static public function get($name) { /* Check internal data first */ if ( isset( self::$_cookiesSet[ $name ] ) ) { return self::$_cookiesSet[ $name ]; } else if ( isset( $_COOKIE[ipsRegistry::$settings['cookie_id'].$name] ) ) { $_value = $_COOKIE[ ipsRegistry::$settings['cookie_id'].$name ]; if ( substr( $_value, 0, 2 ) == 'a:' ) { return unserialize( stripslashes( urldecode( $_value ) ) ); } else { return IPSText::parseCleanValue( urldecode( $_value ) ); } } else { return FALSE; } } } /** * IPSText * * This deals with cleaning and parsing text items. */ class IPSText { /** * Class Convert Object * * @var object */ static private $classConvertCharset; /** * Default document character set * * @var string Character set */ static public $gb_char_set = 'UTF-8'; /** * Remove dodgy control characters? * * @var boolean Remove emulated spaces (e.g. alt+160) */ static public $strip_space_chr = true; /** * Classes * * @var array */ static private $_internalClasses = array(); /** * Ensure no one can create this as an object * * @return void */ private function __construct() {} /** * Simple JSON encode for when its not possible to convert data * into UTF-8 (for example polls that display the contents, etc) * This should only used for light lifting. * * @param array Simple array * @return object */ static public function simpleJsonEncode( $array ) { $final = array(); if ( is_array( $array ) ) { foreach( $array as $k => $v ) { $k = str_replace( '"', '\"', $k ); if ( is_array( $v ) ) { $v = self::simpleJsonEncode( $v ); } else { $v = str_replace( '"', '\"', $v ); $v = str_replace( "\n", '\n', str_replace( "\r", '', $v ) ); $v = '"' . $v . '"'; } $final[] = '"' . $k . '":' . $v . ''; } return '{' . implode( ",", $final ) . '}'; } } /** * Get helper classes * Used here to allow classes to be loaded and used as-and-when they're needed * * @param mixed Name of item requested * @return object */ static public function getTextClass( $name ) { if ( isset( self::$_internalClasses[ $name ] ) && is_object( self::$_internalClasses[ $name ] ) ) { return self::$_internalClasses[ $name ]; } else { switch( $name ) { default: case 'bbcode': $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . "sources/handlers/han_parse_bbcode.php", 'parseBbcode' ); $_class = new $classToLoad( ipsRegistry::instance() ); $_class->allow_update_caches = 1; $_class->bypass_badwords = ipsRegistry::instance()->member() ? intval( ipsRegistry::instance()->member()->getProperty('g_bypass_badwords') ) : 0; break; case 'editor': $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . "sources/handlers/han_editor.php", 'hanEditor' ); $_class = new $classToLoad( ipsRegistry::instance() ); $_class->init(); break; case 'email': $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . "sources/handlers/han_email.php", 'hanEmail' ); $_class = new $classToLoad( ipsRegistry::instance() ); $_class->init(); break; } if ( is_object( $_class ) ) { self::$_internalClasses[ $name ] = $_class; return self::$_internalClasses[ $name ]; } } } /** * Encode for saving data into the DB that will be exported to XML * * Mostly used to ensure that data designed for UTF-8 XML files is actually stored as UTF-8 from * 'flat' files that may not be saved as UTF-8. * Most likely this will have to be expanded to include different char sets eventually. * * @param string Data in * @return string Data out */ static public function encodeForXml( $string ) { if ( strtolower( IPS_DOC_CHAR_SET ) == 'utf-8' ) { $string = utf8_encode( $string ); } return $string; } /** * Convert russian mounth names to english * * @access public * @param string Date * @return string Converted date */ static public function monthNameRu2En( $text ) { return str_replace( array('Январь','Февраль','Март','Апрель','Май','Июнь,','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'), array('January','February','March','April','May','June','July','August','September','October','November','December'), $text ); } /** * SEO Clean up * * @param string Raw SEO title or text * @return string Cleaned up SEO title * @deprecated Will most likely be removed in a future version */ static public function seoClean( $text ) { $text = str_replace( " ", "-", $text ); /* Ensure we don't have /_/ anywhere in the URL */ $text = str_replace( "_", "-", $text ); $text = utf8_encode( $text ); return $text; } static public function transliterate( $text ) { return str_replace( array('а','б','в','г','д','е','ё','ж','з','и','й','к','л','м','н','о','п','р','с','т','у','ф','х','ц','ч','ш','щ','э','ю','я','ы','ь','ъ'), array('a','b','v','g','d','e','e','zh','z','i','i','k','l','m','n','o','p','r','s','t','u','f','h','c','ch','sh','sh','a','ju','ja','i','',''), $text); } static public function makeSeoTransliterate( $text ) { if ( IPB_USE_SEO_TRANSLIT ) { $text = urldecode($text); $text = self::transliterate( $text ); $text = preg_replace('#[^a-z0-9-]#', '', $text); $text = urlencode($text); } return $text; } /** * Make an SEO title for use in the URL * We parse them even if friendly urls are off so that the data is there when you do switch it on * * @param string Raw SEO title or text * @return string Cleaned up SEO title */ static public function makeSeoTitle( $text ) { if ( ! $text ) { return ''; } /* Strip all HTML tags first */ $text = strip_tags($text); /* Preserve %data */ $text = preg_replace('#%([a-fA-F0-9][a-fA-F0-9])#', '-xx-$1-xx-', $text); $text = str_replace( array( '%', '`' ), '', $text); $text = preg_replace('#-xx-([a-fA-F0-9][a-fA-F0-9])-xx-#', '%$1', $text); /* Convert accented chars */ $text = self::convertAccents($text); /* Convert it */ if ( self::isUTF8( $text ) ) { if ( function_exists('mb_strtolower') ) { $text = mb_strtolower($text, 'UTF-8'); } $text = self::utf8Encode( $text, 250 ); } /* Finish off */ $text = mb_strtolower($text); if ( strtolower( IPS_DOC_CHAR_SET ) == 'utf-8' ) { $text = preg_replace( '#&.+?;#' , '', $text ); $text = preg_replace( '#[^%a-z0-9 _-]#', '', $text ); } else { /* Remove &#xx; and &#xxx; but keep &#xxxx; */ $text = preg_replace( '/&#(\d){2,3};/', '', $text ); $text = preg_replace( '#[^%&\#;a-z0-9 _-]#', '', $text ); $text = str_replace( array( '"', '&'), '', $text ); } $text = str_replace( array( '`', ' ', '+', '.', '?', '_', '#' ), '-', $text ); $text = preg_replace( "#-{2,}#", '-', $text ); $text = trim($text, '-'); $text = self::makeSeoTransliterate( $text ); IPSDebug::addMessage( "makeSeoTitle ($text) called" ); return ( $text ) ? $text : '-'; } /** * Seems like UTF-8? * hmdker at gmail dot com {@link php.net/utf8_encode} * * @param string Raw text * @return boolean */ static public function isUTF8($str) { $c=0; $b=0; $bits=0; $len=strlen($str); for($i=0; $i<$len; $i++) { $c=ord($str[$i]); if($c > 128) { if(($c >= 254)) return false; elseif($c >= 252) $bits=6; elseif($c >= 248) $bits=5; elseif($c >= 240) $bits=4; elseif($c >= 224) $bits=3; elseif($c >= 192) $bits=2; else return false; if(($i+$bits) > $len) return false; while( $bits > 1 ) { $i++; $b = ord($str[$i]); if($b < 128 || $b > 191) return false; $bits--; } } } return true; } /** * Converts accented characters into their plain alphabetic counterparts * * @param string Raw text * @return string Cleaned text */ static public function convertAccents($string) { if ( ! preg_match('/[\x80-\xff]/', $string) ) { return $string; } if ( self::isUTF8( $string) ) { $_chr = array( /* Latin-1 Supplement */ chr(195).chr(128) => 'A', chr(195).chr(129) => 'A', chr(195).chr(130) => 'A', chr(195).chr(131) => 'A', chr(195).chr(132) => 'A', chr(195).chr(133) => 'A', chr(195).chr(135) => 'C', chr(195).chr(136) => 'E', chr(195).chr(137) => 'E', chr(195).chr(138) => 'E', chr(195).chr(139) => 'E', chr(195).chr(140) => 'I', chr(195).chr(141) => 'I', chr(195).chr(142) => 'I', chr(195).chr(143) => 'I', chr(195).chr(145) => 'N', chr(195).chr(146) => 'O', chr(195).chr(147) => 'O', chr(195).chr(148) => 'O', chr(195).chr(149) => 'O', chr(195).chr(150) => 'O', chr(195).chr(153) => 'U', chr(195).chr(154) => 'U', chr(195).chr(155) => 'U', chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y', chr(195).chr(159) => 's', chr(195).chr(160) => 'a', chr(195).chr(161) => 'a', chr(195).chr(162) => 'a', chr(195).chr(163) => 'a', chr(195).chr(164) => 'a', chr(195).chr(165) => 'a', chr(195).chr(167) => 'c', chr(195).chr(168) => 'e', chr(195).chr(169) => 'e', chr(195).chr(170) => 'e', chr(195).chr(171) => 'e', chr(195).chr(172) => 'i', chr(195).chr(173) => 'i', chr(195).chr(174) => 'i', chr(195).chr(175) => 'i', chr(195).chr(177) => 'n', chr(195).chr(178) => 'o', chr(195).chr(179) => 'o', chr(195).chr(180) => 'o', chr(195).chr(181) => 'o', chr(195).chr(182) => 'o', chr(195).chr(182) => 'o', chr(195).chr(185) => 'u', chr(195).chr(186) => 'u', chr(195).chr(187) => 'u', chr(195).chr(188) => 'u', chr(195).chr(189) => 'y', chr(195).chr(191) => 'y', /* Latin Extended-A */ chr(196).chr(128) => 'A', chr(196).chr(129) => 'a', chr(196).chr(130) => 'A', chr(196).chr(131) => 'a', chr(196).chr(132) => 'A', chr(196).chr(133) => 'a', chr(196).chr(134) => 'C', chr(196).chr(135) => 'c', chr(196).chr(136) => 'C', chr(196).chr(137) => 'c', chr(196).chr(138) => 'C', chr(196).chr(139) => 'c', chr(196).chr(140) => 'C', chr(196).chr(141) => 'c', chr(196).chr(142) => 'D', chr(196).chr(143) => 'd', chr(196).chr(144) => 'D', chr(196).chr(145) => 'd', chr(196).chr(146) => 'E', chr(196).chr(147) => 'e', chr(196).chr(148) => 'E', chr(196).chr(149) => 'e', chr(196).chr(150) => 'E', chr(196).chr(151) => 'e', chr(196).chr(152) => 'E', chr(196).chr(153) => 'e', chr(196).chr(154) => 'E', chr(196).chr(155) => 'e', chr(196).chr(156) => 'G', chr(196).chr(157) => 'g', chr(196).chr(158) => 'G', chr(196).chr(159) => 'g', chr(196).chr(160) => 'G', chr(196).chr(161) => 'g', chr(196).chr(162) => 'G', chr(196).chr(163) => 'g', chr(196).chr(164) => 'H', chr(196).chr(165) => 'h', chr(196).chr(166) => 'H', chr(196).chr(167) => 'h', chr(196).chr(168) => 'I', chr(196).chr(169) => 'i', chr(196).chr(170) => 'I', chr(196).chr(171) => 'i', chr(196).chr(172) => 'I', chr(196).chr(173) => 'i', chr(196).chr(174) => 'I', chr(196).chr(175) => 'i', chr(196).chr(176) => 'I', chr(196).chr(177) => 'i', chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij', chr(196).chr(180) => 'J', chr(196).chr(181) => 'j', chr(196).chr(182) => 'K', chr(196).chr(183) => 'k', chr(196).chr(184) => 'k', chr(196).chr(185) => 'L', chr(196).chr(186) => 'l', chr(196).chr(187) => 'L', chr(196).chr(188) => 'l', chr(196).chr(189) => 'L', chr(196).chr(190) => 'l', chr(196).chr(191) => 'L', chr(197).chr(128) => 'l', chr(197).chr(129) => 'L', chr(197).chr(130) => 'l', chr(197).chr(131) => 'N', chr(197).chr(132) => 'n', chr(197).chr(133) => 'N', chr(197).chr(134) => 'n', chr(197).chr(135) => 'N', chr(197).chr(136) => 'n', chr(197).chr(137) => 'N', chr(197).chr(138) => 'n', chr(197).chr(139) => 'N', chr(197).chr(140) => 'O', chr(197).chr(141) => 'o', chr(197).chr(142) => 'O', chr(197).chr(143) => 'o', chr(197).chr(144) => 'O', chr(197).chr(145) => 'o', chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe', chr(197).chr(148) => 'R',chr(197).chr(149) => 'r', chr(197).chr(150) => 'R',chr(197).chr(151) => 'r', chr(197).chr(152) => 'R',chr(197).chr(153) => 'r', chr(197).chr(154) => 'S',chr(197).chr(155) => 's', chr(197).chr(156) => 'S',chr(197).chr(157) => 's', chr(197).chr(158) => 'S',chr(197).chr(159) => 's', chr(197).chr(160) => 'S', chr(197).chr(161) => 's', chr(197).chr(162) => 'T', chr(197).chr(163) => 't', chr(197).chr(164) => 'T', chr(197).chr(165) => 't', chr(197).chr(166) => 'T', chr(197).chr(167) => 't', chr(197).chr(168) => 'U', chr(197).chr(169) => 'u', chr(197).chr(170) => 'U', chr(197).chr(171) => 'u', chr(197).chr(172) => 'U', chr(197).chr(173) => 'u', chr(197).chr(174) => 'U', chr(197).chr(175) => 'u', chr(197).chr(176) => 'U', chr(197).chr(177) => 'u', chr(197).chr(178) => 'U', chr(197).chr(179) => 'u', chr(197).chr(180) => 'W', chr(197).chr(181) => 'w', chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y', chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z', chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z', chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z', chr(197).chr(190) => 'z', chr(197).chr(191) => 's', /* Euro Sign */ chr(226).chr(130).chr(172) => 'E', /* GBP (Pound) Sign */ chr(194).chr(163) => '' ); $string = strtr($string, $_chr); } else { $_chr = array(); $_dblChars = array(); /* We assume ISO-8859-1 if not UTF-8 */ $_chr['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158) .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194) .chr(195).chr(199).chr(200).chr(201).chr(202) .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210) .chr(211).chr(212).chr(213).chr(217).chr(218) .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227) .chr(231).chr(232).chr(233).chr(234).chr(235) .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243) .chr(244).chr(245).chr(249).chr(250).chr(251) .chr(252).chr(253).chr(255).chr(191).chr(182).chr(179).chr(166) .chr(230).chr(198).chr(175).chr(172).chr(188) .chr(163).chr(161).chr(177); $_chr['out'] = "EfSZszYcYuAAAACEEEEIIIINOOOOUUUUYaaaaceeeeiiiinoooouuuuyyzslScCZZzLAa"; $string = strtr( $string, $_chr['in'], $_chr['out'] ); $_dblChars['in'] = array( chr(140), chr(156), chr(196), chr(197), chr(198), chr(208), chr(214), chr(216), chr(222), chr(223), chr(228), chr(229), chr(230), chr(240), chr(246), chr(248), chr(254)); $_dblChars['out'] = array('Oe', 'oe', 'Ae', 'Aa', 'Ae', 'DH', 'Oe', 'Oe', 'TH', 'ss', 'ae', 'aa', 'ae', 'dh', 'oe', 'oe', 'th'); $string = str_replace($_dblChars['in'], $_dblChars['out'], $string); } return $string; } /** * Manually utf8 encode to a specific length * Based on notes found at php.net * * @param string Raw text * @param int Length * @return string */ static public function utf8Encode( $string, $len=0 ) { $_unicode = ''; $_values = array(); $_nOctets = 1; $_unicodeLength = 0; $stringLength = strlen( $string ); for ( $i = 0 ; $i < $stringLength ; $i++ ) { $value = ord( $string[ $i ] ); if ( $value < 128 ) { if ( $len && ( $_unicodeLength >= $len ) ) { break; } $_unicode .= chr($value); $_unicodeLength++; } else { if ( count( $_values ) == 0 ) { $_nOctets = ( $value < 224 ) ? 2 : 3; } $_values[] = $value; if ( $len && ( $_unicodeLength + ($_nOctets * 3) ) > $len ) { break; } if ( count( $_values ) == $_nOctets ) { if ( $_nOctets == 3 ) { $_unicode .= '%' . dechex($_values[0]) . '%' . dechex($_values[1]) . '%' . dechex($_values[2]); $_unicodeLength += 9; } else { $_unicode .= '%' . dechex($_values[0]) . '%' . dechex($_values[1]); $_unicodeLength += 6; } $_values = array(); $_nOctets = 1; } } } return $_unicode; } /** * Converts UFT-8 into HTML entities (xxx;) for correct display in browsers * * @param string UTF8 Encoded string * @return string ..converted into HTML entities (similar to what a browser does with POST) */ public static function utf8ToEntities($string) { /* * @see http://en.wikipedia.org/wiki/UTF-8#Description * @link http://community.invisionpower.com/tracker/issue-23681-possible-addition/ */ # Four-byte chars $string = preg_replace( "/([\360-\364])([\200-\277])([\200-\277])([\200-\277])/e", "'&#' . ( ( ord('\\1') - 240 ) * 262144 + ( ord('\\2') - 128 ) * 4096 + ( ord('\\3') - 128 ) * 64 + ( ord('\\4') - 128 ) ) . ';'", $string ); /* Three byte chars */ $string = preg_replace( "/([\340-\357])([\200-\277])([\200-\277])/e", "'&#'.((ord('\\1')-224)*4096 + (ord('\\2')-128)*64 + (ord('\\3')-128)).';'", $string ); /* Two byte chars */ $string = preg_replace("/([\300-\337])([\200-\277])/e", "'&#'.((ord('\\1')-192)*64+(ord('\\2')-128)).';'", $string); return $string; } /** * Returns an MD5 hash of content which has whitespace stripped. * This is used in some classes to determine if content has changed without * whitespace changes triggering it. * * @param string Incoming text * @return string MD5 hash of whitespace stripped content */ public static function contentToMd5( $t ) { return md5( trim( preg_replace( "#[\s\t\n\r]#", "", $t ) ) ); } /** * Replace Recursively * * @param string Text to search in * @param string Opening text to search for. (Example: ) * @param mixed Call back function that handles the replacement. If using a class, pass array( $classname, $function ) THIS MUST BE A STATIC FUNCTION * @return string Replaced text * * # We want to replace all instances of with * $text = IPSText::replaceRecursively( $text, "", array( 'myClass', 'replaceIt' ) ); * class myClass { * static function replaceIt( $text, $openText, $closeText ) * { * # $text contains the matched text between the tags, eg: "http://www.domain.com" * # $openText contains the searched for opening, eg: * # Remove http... * $text = str_replace( 'http://', '', $text ) * # Remove quotes * $text = str_replace( array( '"', "'" ), '', $text ); * return '"javascript:goLoad(\'' . $text . '\')"'; * } * } * */ public static function replaceRecursively( $text, $textOpen, $textClose, $callBackFunction ) { //---------------------------------------- // INIT //---------------------------------------- # Tag specifics $foundOpenText_pointer = 0; $foundCloseText_pointer = 0; $foundOpenTextRecurse_pointer = 0; //---------------------------------------- // Keep the server busy for a while //---------------------------------------- while ( 1 == 1 ) { # Reset pointer $startOfTextAfterOpenText_pointer = 0; # See if we have any 'textOpen' at all $foundOpenText_pointer = strpos( $text, $textOpen, $foundCloseText_pointer ); # No? if ( $foundOpenText_pointer === FALSE ) { break; } # Do we have any close text? $foundCloseText_pointer = strpos( $text, $textClose, $foundOpenText_pointer ); # No? if ( $foundCloseText_pointer === FALSE ) { return $text; } # Reset pointer for text between the open and close text $startOfTextAfterOpenText_pointer = $foundOpenText_pointer + strlen( $textOpen ); # Check recursively $foundOpenTextRecurse_pointer = $startOfTextAfterOpenText_pointer; while ( 1 == 1 ) { # Got any open text again? $foundOpenTextRecurse_pointer = strpos( $text, $textOpen, $foundOpenTextRecurse_pointer ); # No? if ( $foundOpenTextRecurse_pointer === FALSE OR $foundOpenTextRecurse_pointer >= $foundCloseText_pointer ) { break; } # Yes! Reset recursive pointer $foundCloseTextRecurse_pointer = $foundCloseText_pointer + strlen( $textClose ); # Yes! Reset close normal pointer to next close tag FROM the last found close point $foundCloseText_pointer = strpos( $text, $textClose, $foundCloseTextRecurse_pointer ); # Make sure we have a closing text if ( $foundCloseText_pointer === FALSE ) { return $text; } $foundOpenTextRecurse_pointer += strlen( $textOpen ); } # This is the text between the open text and close text $foundText = substr( $text, $startOfTextAfterOpenText_pointer, $foundCloseText_pointer - $startOfTextAfterOpenText_pointer ); # Recurse if ( strpos( $foundText, $textOpen ) !== FALSE ) { $foundText = IPSText::replaceRecursively( $foundText, $textOpen, $textClose, $callBackFunction ); } # Run the call back... $_newText = call_user_func( $callBackFunction, $foundText, $textOpen, $textClose ); # Run the replacement $text = substr_replace( $text, $_newText, $foundOpenText_pointer, ( $foundCloseText_pointer - $foundOpenText_pointer ) + strlen( $textClose ) ); # Reset pointer $foundCloseText_pointer = $foundOpenText_pointer + strlen($_newText); } return $text; } /** * Reset Text Classes * * @param string Classname to search for * @return boolean True if successful, false if not */ static public function resetTextClass( $name ) { if ( ! is_object( self::$_internalClasses[ $name ] ) ) { return false; } switch( $name ) { default: case 'bbcode': self::$_internalClasses[ $name ]->allow_cache_updates = 1; self::$_internalClasses[ $name ]->bypass_badwords = intval( ipsRegistry::instance()->member()->getProperty('g_bypass_badwords') ); self::$_internalClasses[ $name ]->parse_smilies = 1; self::$_internalClasses[ $name ]->parse_nl2br = 1; self::$_internalClasses[ $name ]->parse_html = 0; self::$_internalClasses[ $name ]->parse_bbcode = 1; self::$_internalClasses[ $name ]->parsing_section = 'post'; self::$_internalClasses[ $name ]->error = ''; self::$_internalClasses[ $name ]->parsing_mgroup = ''; self::$_internalClasses[ $name ]->parsing_mgroup_others = ''; break; case 'editor': self::$_internalClasses[ $name ]->error = ''; break; } return true; } /** * Clean _GET _POST key * * @param string Key name * @return string Cleaned key name * @since 2.1 */ static public function parseCleanKey($key) { if ( $key == "" ) { return ""; } $key = htmlspecialchars( urldecode($key) ); $key = str_replace( ".." , "" , $key ); $key = preg_replace( "/\_\_(.+?)\_\_/" , "" , $key ); $key = preg_replace( "/^([\w\.\-\_]+)$/", "$1", $key ); return $key; } /** * Clean _GET _POST value * * @param string Input * @param bool Also run postParseCleanValue * @return string Cleaned Input * @since 2.1 */ static public function parseCleanValue( $val, $postParse=true ) { if ( $val == "" ) { return ""; } $val = str_replace( " ", " ", IPSText::stripslashes($val) ); # Convert all carriage return combos $val = str_replace( array( "\r\n", "\n\r", "\r" ), "\n", $val ); $val = str_replace( "&" , "&" , $val ); $val = str_replace( "" , "-->" , $val ); $val = str_ireplace( "" , ">" , $val ); $val = str_replace( "<" , "<" , $val ); $val = str_replace( '"' , """ , $val ); $val = str_replace( "\n" , "
" , $val ); // Convert literal newlines $val = str_replace( "$" , "$" , $val ); $val = str_replace( "!" , "!" , $val ); $val = str_replace( "'" , "'" , $val ); // IMPORTANT: It helps to increase sql query safety. if ( IPS_ALLOW_UNICODE ) { $val = preg_replace("/&#([0-9]+);/s", "&#\\1;", $val ); //----------------------------------------- // Try and fix up HTML entities with missing ; //----------------------------------------- $val = preg_replace( "/&#(\d+?)([^\d;])/i", "&#\\1;\\2", $val ); } //----------------------------------------- // Shortcut to auto run other cleaning //----------------------------------------- if( $postParse ) { $val = IPSText::postParseCleanValue( $val ); } return $val; } /** * Clean _GET _POST value after settings loaded * * @param string Input * @return string Cleaned Input * @since 2.1 */ static public function postParseCleanValue($val) { if ( $val == "" ) { return ""; } /* This looks wrong but it's correct. During FURL set up in registry this function is called before settings are loaded * and we want to strip hidden chars in this instance, so.. */ if ( ! isset( ipsRegistry::$settings['strip_space_chr'] ) OR ipsRegistry::$settings['strip_space_chr'] ) { $val = IPSText::removeControlCharacters( $val ); } return $val; } /** * Check email address to see if it seems valid * * @param string Email address * @return boolean * @since 2.0 */ static public function checkEmailAddress( $email = "" ) { $email = trim($email); $email = str_replace( " ", "", $email ); //----------------------------------------- // Check for more than 1 @ symbol //----------------------------------------- if ( substr_count( $email, '@' ) > 1 ) { return FALSE; } if ( preg_match( "#[\;\#\n\r\*\'\"<>&\%\!\(\)\{\}\[\]\?\\/\s]#", $email ) ) { return FALSE; } else if ( preg_match( "/^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,4})(\]?)$/", $email) ) { return TRUE; } else { return FALSE; } } /** * Function to trim text around a word or phrase * * @param string $haystack Text * @param string $needle Phrase * @return string */ static public function truncateTextAroundPhrase( $haystack, $needle ) { /* Base on words */ $haystack = explode( " ", $haystack ); if( count( $haystack ) > 21 ) { $_term_at = IPSLib::arraySearchLoose( $needle, $haystack ); if( $_term_at - 11 > 0 ) { $begin = array_splice( $haystack, 0, $_term_at - 11 ); /* The term position will have changed now */ $_term_at = IPSLib::arraySearchLoose( $needle, $haystack ); } if( $_term_at + 11 < count( $haystack ) ) { $end = array_splice( $haystack, $_term_at + 11, count( $haystack ) ); } } else { $begin = array(); $end = array(); } $haystack = implode( " ", $haystack ); if( is_array( $begin ) && count( $begin ) ) { $haystack = '...' . $haystack; } if( is_array( $end ) && count( $end ) ) { $haystack = $haystack . '...'; } return $haystack; } /** * Replaces text with highlighted blocks * * @param string Incoming Content * @param string HL attribute * @return string Formatted text * @since 2.2.0 */ static public function searchHighlight( $text, $highlight ) { //----------------------------------------- // INIT //----------------------------------------- $highlight = self::parseCleanValue( urldecode( $highlight ) ); $loosematch = 1;//strstr( $highlight, '*' ) ? 1 : 0; $keywords = str_replace( '*', '', str_replace( "+", " ", str_replace( "++", "+", str_replace( '-', '', trim($highlight) ) ) ) ); $keywords = str_replace( '"', '', str_replace( '\\', '\', str_replace( '&quot;', '', $keywords ) ) ); $word_array = array(); $endmatch = "(.)?"; $beginmatch = "(.)?"; //----------------------------------------- // Get rid of links first... //----------------------------------------- $_storedUrls = array(); preg_match_all( "/
]*?)>/is", $text, $_urls ); for ( $i = 0; $i < count($_urls[0]); $i++ ) { $_bleh = md5( uniqid( microtime(), true ) ); $text = str_replace( $_urls[0][$i], "--URL::{$_bleh}-- ", $text ); $_storedUrls[ $_bleh ] = $_urls[0][$i]; } //----------------------------------------- // Go! //----------------------------------------- if ( $keywords ) { if ( preg_match("/,(and|or),/i", $keywords) ) { while ( preg_match("/\s+(and|or)\s+/i", $keywords, $match) ) { $word_array = explode( " ".$match[1]." ", $keywords ); $keywords = str_replace( $match[0], '', $keywords ); } } else if ( strstr( $keywords, ' ' ) ) { $word_array = explode( ' ', str_replace( ' ', ' ', $keywords ) ); } else { $word_array[] = $keywords; } if ( ! $loosematch ) { $beginmatch = "(^|\s|\>|;|\])"; $endmatch = "(\s|,|\.|!|stripAllTags( $text ) ); foreach ( $word_array as $keywords ) { /* We don't want to highlight small words, they're usually noise and it can produce memory errors with single chars being highlighted * Correction: We don't want to highlight them unless user used double quotes in search term */ if( strpos( $highlight, '&quot;' ) === false AND strlen( $keywords ) < ipsRegistry::$settings['min_search_word'] ) { continue; } /* Make sure we're not trying to process an empty keyword */ if( ! $keywords ) { continue; } preg_match_all( "/{$beginmatch}(".preg_quote($keywords, '/')."){$endmatch}/is", $textForMatch, $matches ); for ( $i = 0; $i < count($matches[0]); $i++ ) { $text = str_ireplace( $matches[0][$i], $matches[1][$i] . "" . $matches[2][$i] . "" . $matches[3][$i], $text ); } } } } //----------------------------------------- // Fix links //----------------------------------------- if( count($_storedUrls) ) { foreach( $_storedUrls as $k => $v ) { $text = str_replace( "--URL::{$k}-- ", $v, $text ); } } return $text; } /** * Check a URL to make sure it's not all hacky * * @param string Input String * @return boolean * @since 2.1.0 */ static public function xssCheckUrl( $url ) { // This causes problems if people submit bbcode with urlencoded items that are valid // e.g.: http://www.google.com/search?q=site%3Aipb3preview.ipslink.com+-%22Viewing+Profile%22 // %22 gets changed into " and then this fails, even though this is a valid url // $url = trim( urldecode( $url ) ); $url = trim( $url ); /* Test for http://%XX */ if ( stristr( $url, 'http://%' ) ) { return FALSE; } /* Test for http://&XX */ if ( stristr( $url, 'http://&' ) ) { return FALSE; } if ( ! preg_match( "#^(http|https|news|ftp)://(?:[^<>\"]+|[a-z0-9/\._\- !&\#;,%\+\?:=]+)$#iU", $url ) ) { return FALSE; } return TRUE; } /** * Strip URLs from stuff * * @param string Input string * @return string Output string */ static public function stripUrls( $txt ) { /* Start off by attempting to strip */ $txt = preg_replace( "##i", "", $txt ); /* Now grab any non linked items */ $txt = preg_replace( "#(http|https|news|ftp)://(?:[^<>\[\"\s]+|[a-z0-9/\._\-!&\#;,%\+\?:=]+)#i", "", $txt ); return $txt; } /** * Returns a cleaned MD5 hash * * @param string Input String * @return string Parsed string * @since 2.1 */ static public function md5Clean( $text ) { return preg_replace( "/[^a-zA-Z0-9]/", "" , substr( $text, 0, 32 ) ); } /** * Convert unicode entities * * @param string Input to convert (in the form of %u00E9 (example)) * @param bool Force to utf-8 (useful if you want to run convertCharsets() on it after) * @return string UTF-8 (or html entity) encoded content */ static public function convertUnicode( $t, $forceUtf8=false ) { $t = rawurldecode( $t ); /* Need this function? */ if ( ! strstr( $t, '%u' ) ) { return $t; } if ( strtolower(IPS_DOC_CHAR_SET) == 'utf-8' || $forceUtf8 ) { return preg_replace_callback( '#%u([0-9A-F]{1,4})#i', array( self, '_convertHexToUtf8' ), utf8_encode($t) ); } else { return preg_replace_callback( '#%u([0-9A-F]{1,4})#i', create_function( '$matches', "return '&#' . hexdec(\$matches[1]) . ';';" ), $t ); } } /** * Convert decimal character code to utf-8 * * @param integer Character code * @return string Character */ static private function _convertToUtf8( $int=0 ) { $return = ''; if ( $int < 0 ) { return chr(0); } else if ( $int <= 0x007f ) { $return .= chr($int); } else if ( $int <= 0x07ff ) { $return .= chr(0xc0 | ($int >> 6)); $return .= chr(0x80 | ($int & 0x003f)); } else if ( $int <= 0xffff ) { $return .= chr(0xe0 | ($int >> 12)); $return .= chr(0x80 | (($int >> 6) & 0x003f)); $return .= chr(0x80 | ($int & 0x003f)); } else if ( $int <= 0x10ffff ) { $return .= chr(0xf0 | ($int >> 18)); $return .= chr(0x80 | (($int >> 12) & 0x3f)); $return .= chr(0x80 | (($int >> 6) & 0x3f)); $return .= chr(0x80 | ($int & 0x3f)); } else { return chr(0); } return $return; } /** * Wrapper for dec_char_ref_to_utf8 * * @param array Hex character code * @return string Character */ static private function _convertHexToUtf8( $matches ) { return self::_convertToUtf8( hexdec( $matches[1] ) ); } /** * Convert a string between charsets * * @param string Input String * @param string Current char set * @param string Destination char set * @return string Parsed string * @since 2.1.0 * @todo [Future] If an error is set in classConvertCharset, show it or log it somehow */ static public function convertCharsets( $text, $original_cset, $destination_cset="UTF-8" ) { $original_cset = strtolower($original_cset); $destination_cset = strtolower( $destination_cset ); $t = $text; //----------------------------------------- // Not the same? //----------------------------------------- if ( $destination_cset == $original_cset ) { return $t; } if ( ! is_object( self::$classConvertCharset ) ) { require_once( IPS_KERNEL_PATH.'/classConvertCharset.php' ); self::$classConvertCharset = new classConvertCharset(); if ( ipsRegistry::$settings['charset_conv_method'] == 'mb' AND function_exists( 'mb_convert_encoding' ) ) { self::$classConvertCharset->method = 'mb'; } else if ( ipsRegistry::$settings['charset_conv_method'] == 'iconv' AND function_exists( 'iconv' ) ) { self::$classConvertCharset->method = 'iconv'; } else if ( ipsRegistry::$settings['charset_conv_method'] == 'recode' AND function_exists( 'recode_string' ) ) { self::$classConvertCharset->method = 'recode'; } else { self::$classConvertCharset->method = 'internal'; } } $text = self::$classConvertCharset->convertEncoding( $text, $original_cset, $destination_cset ); return $text ? $text : $t; } /** * Truncate a HTML string without breaking HTML entites * * @param string Input String * @param integer Desired min. length * @return string Parsed string * @since 2.0 */ static public function truncate($text, $limit=30) { $orig = $text; $text = str_replace( '&' , '&', $text ); $text = str_replace( '"', '"', $text ); $text = str_replace( '>', '>', $text ); $text = str_replace( '<', '<', $text ); $string_length = self::mbstrlen( $text ); if ( $string_length > $limit) { $text = self::mbsubstr( $text, 0, $limit - 3 ) . '...'; } else { $text = preg_replace( "/&(#{0,}([a-zA-Z0-9]+?)?)?$/", '', $text ); } // Let's just use the original string if the truncated one is longer or same length return ( self::mbstrlen( $text ) >= $string_length ) ? $orig : $text; } /** * MB strtolower * * @param string Input String * @return string Parsed string */ static public function mbstrtolower( $text ) { if ( function_exists('mb_list_encodings') AND function_exists('mb_strtolower') ) { $valid_encodings = array(); $valid_encodings = mb_list_encodings(); if ( count($valid_encodings) ) { if ( in_array( strtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) ) { $text = mb_strtolower( $text, strtoupper(IPS_DOC_CHAR_SET) ); } } else { $text = strtolower( $text ); } } else { $text = strtolower( $text ); } return $text; } /** * Substr support for this without mb_substr * * @param string Input String * @param integer Desired min. length * @return string Parsed string * @since 2.0 */ static public function mbsubstr( $text, $start=0, $limit=30 ) { $text = str_replace( '&' , '&', $text ); $text = str_replace( '"', '"', $text ); $text = str_replace( '>', '>', $text ); $text = str_replace( '<', '<', $text ); //----------------------------------------- // Got multibyte? //----------------------------------------- if( function_exists('mb_list_encodings') ) { $valid_encodings = array(); $valid_encodings = mb_list_encodings(); if( count($valid_encodings) ) { if( in_array( strtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) ) { $text = mb_substr( $text, $start, $limit, strtoupper(IPS_DOC_CHAR_SET) ); $text = preg_replace( "/&(#{0,}([a-zA-Z0-9]+?)?)?$/", '', $text ); return $text; } } } //----------------------------------------- // Handrolled method //----------------------------------------- $string_length = self::mbstrlen( $text ); //----------------------------------------- // Negative start? //----------------------------------------- if( $start < 0 ) { $start = $string_length + $start; } //----------------------------------------- // Do it! //----------------------------------------- if ( $string_length > $limit ) { if( strtoupper(IPS_DOC_CHAR_SET) == 'UTF-8' ) { // Multi-byte support //$text = preg_replace('#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,0}'. // '((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){'.intval($start).','.intval($limit).'}).*#s', // '$1',$text); /** * @link http://www.php.net/manual/en/function.substr.php#55107 */ preg_match_all( "/./su", $text, $ar ); $text = implode( "", array_slice( $ar[0], $start, $limit ) ); } else { $text = substr( $text, $start, $limit ); } $text = preg_replace( "/&(#{0,}([a-zA-Z0-9]+?)?)?$/", '', $text ); } else { $text = preg_replace( "/&(#{0,}([a-zA-Z0-9]+?)?)?$/", '', $text ); } return $text; } /** * mb_stripos - uses mb functions if available * * @param string Input String * @param integer Desired min. length * @return string Parsed string * @since 2.0 */ static public function mbstripos( $text, $string, $start=0 ) { // Do we have multi-byte functions? if( function_exists('mb_list_encodings') AND function_exists('mb_stripos') ) { $valid_encodings = array(); $valid_encodings = mb_list_encodings(); if( count($valid_encodings) ) { if( in_array( strtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) ) { return @mb_stripos( $text, $string, $start, strtoupper(IPS_DOC_CHAR_SET) ); } } } // No? Fallback to normal stripos return stripos( $text, $string, $start ); } /** * Clean a string to remove all non alphanumeric characters * * @param string Input String * @param string Additional tags * @return string Parsed string * @since 2.1 */ static public function alphanumericalClean( $text, $additional_tags="" ) { if ( $additional_tags ) { $additional_tags = preg_quote( $additional_tags, "/" ); } return preg_replace( "/[^a-zA-Z0-9\-\_" . $additional_tags . "]/", "" , $text ); } /** * Get the true length of a multi-byte character string * * @param string Input String * @return integer String length * @since 2.1 */ static public function mbstrlen( $t ) { if( function_exists( 'mb_list_encodings' ) ) { $encodings = mb_list_encodings(); if( in_array( strtoupper(IPS_DOC_CHAR_SET), array_map( 'strtoupper', $encodings ) ) ) { return mb_strlen( $t, IPS_DOC_CHAR_SET ); } } return strlen( preg_replace("/&#([0-9]+);/", "-", self::stripslashes( $t ) ) ); } /** * Convert text for use in a textarea * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function textToForm( $t="" ) { // Use forward look up to only convert & not { //$t = preg_replace("/&(?!#[0-9]+;)/s", '&', $t ); $t = str_replace( "&" , "&" , $t ); $t = str_replace( "<" , "<" , $t ); $t = str_replace( ">" , ">" , $t ); $t = str_replace( '"' , """ , $t ); $t = str_replace( "'" , ''' , $t ); if ( IN_ACP ) { $t = str_replace( "\\", "\" , $t ); } return $t; // A nice cup of? } /** * Cleaned form data back to text * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function formToText($t="") { $t = str_replace( "&" , "&", $t ); $t = str_replace( "<" , "<", $t ); $t = str_replace( ">" , ">", $t ); $t = str_replace( """ , '"', $t ); $t = str_replace( "'" , "'", $t ); $t = str_replace( "../" , "../", $t ); if ( IN_ACP ) { //$t = str_replace( '\\' , '\\\\', $t ); $t = str_replace( '\' ,'\\', $t ); } return $t; } /** * Attempt to make slashes safe for us in DB (not really needed now?) * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function safeslashes($t="") { return str_replace( '\\', "\\\\", self::stripslashes($t) ); } /** * Remove slashes if magic_quotes enabled * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function stripslashes($t) { if ( IPS_MAGIC_QUOTES ) { $t = stripslashes($t); } $t = preg_replace( "/\\\(?!&#|\?#)/", "\", $t ); return $t; } /** * Strip the attachment tag from data * * @param string Incoming text * @return string Text with any attach tags removed * @todo [Future] Move this to bbcode library? */ static public function stripAttachTag( $text ) { return preg_replace( "#\[attachment=(\d+?)\:(?:[^\]]+?)\]#is", '', $text ); } /** * Convert text for use in a textarea * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function raw2form($t="") { $t = str_replace( '$', "$", $t); if ( IPS_MAGIC_QUOTES ) { $t = stripslashes($t); } $t = preg_replace( "/\\\(?!&#|\?#)/", "\", $t ); //--------------------------------------- // Make sure macros aren't converted //--------------------------------------- $t = preg_replace( "/<{(.+?)}>/", "<{\\1}>", $t ); return $t; } /** * Converts slashes into HTML entities * * @param string Input String * @return string Parsed string * @since 2.0 * @deprecated * @see IPSText::stripslashes */ static public function makeSlashesSafe($t) { return IPSText::stripslashes( $t ); } /** * htmlspecialchars including entities * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function htmlspecialchars($t="") { // Use forward look up to only convert & not { $t = preg_replace("/&(?!#[0-9]+;)/s", '&', $t ); $t = str_replace( "<", "<" , $t ); $t = str_replace( ">", ">" , $t ); $t = str_replace( '"', """, $t ); $t = str_replace( "'", ''', $t ); return $t; // A nice cup of? } /** * unhtmlspecialchars including multi-byte characters * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function UNhtmlspecialchars($t="") { $t = str_replace( "&" , "&", $t ); $t = str_replace( "<" , "<", $t ); $t = str_replace( ">" , ">", $t ); $t = str_replace( """, '"', $t ); $t = str_replace( "'", "'", $t ); $t = str_replace( "'" , "'", $t ); $t = str_replace( "!" , "!", $t ); $t = str_replace( "$", '$', $t ); return $t; } /** * Remove leading comma from comma delim string * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function trimLeadingComma($t) { return ltrim( $t, ',' ); } /** * Remove trailing comma from comma delim string * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function trimTrailingComma($t) { return rtrim( $t, ',' ); } /** * Remove dupe commas from comma delim string * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function cleanComma($t) { return preg_replace( "/,{2,}/", ",", $t ); } /** * Clean perm string (wrapper for comma cleaners) * * @param string Input String * @return string Parsed string * @since 2.0 */ static public function cleanPermString($t) { $t = self::cleanComma($t); $t = self::trimLeadingComma($t); $t = self::trimTrailingComma($t); return $t; } /** * Convert HTML line break tags to \n * * @param string Input text * @return string Parsed text * @since 2.0 */ static public function br2nl($t="") { //print nl2br(htmlspecialchars($t)).'
--------------------------------
'; $t = str_replace( array( "\r", "\n" ), '', $t ); $t = str_replace( array( "
", "
" ), "\n", $t ); //$t = preg_replace( "#(?:\n|\r)?
(?:\n|\r)?#", "\n", $t ); //$t = preg_replace( "#(?:\n|\r)?
(?:\n|\r)?#" , "\n", $t ); //print nl2br(htmlspecialchars($t)).'
--------------------------------
'; return $t; } /** * Removes control characters (hidden spaces) * * @param string Input String * @return intger String length * @since 2.1 */ static public function removeControlCharacters( $t ) { /* This looks wrong but it's correct. During FURL set up in registry this function is called before settings are loaded * and we want to strip hidden chars in this instance, so.. */ if ( ! isset( ipsRegistry::$settings['strip_space_chr'] ) OR ipsRegistry::$settings['strip_space_chr'] ) { /** * @see http://en.wikipedia.org/wiki/Space_(punctuation) * @see http://www.ascii.cl/htmlcodes.htm */ //$t = str_replace( chr(160), ' ', $t ); $t = str_replace( chr(173), ' ', $t ); //$t = str_replace( chr(240), ' ', $t ); -> latin small letter eth //$t = str_replace( chr(0xA0), "", $t ); //Remove sneaky spaces Same as chr 160 //$t = str_replace( chr(0x11f9), "", $t); //Remove sneaky spaces //$t = str_replace( chr(0x2004), "", $t ); //Remove sneaky spaces //$t = str_replace( chr(0x2005), "", $t ); //Remove sneaky spaces //$t = str_replace( chr(0x2006), "", $t ); //Remove sneaky spaces //$t = str_replace( chr(0x2009), "", $t ); //Remove sneaky spaces //$t = str_replace( chr(0x200A), "", $t ); //Remove sneaky spaces //$t = str_replace( chr(0x200B), "", $t ); //Remove sneaky spaces //$t = str_replace( chr(0x200C), "", $t ); //Remove sneaky spaces //$t = str_replace( chr(0x200D), " ", $t ); //Remove sneaky spaces //$t = str_replace( chr(0x202F), " ", $t ); //Remove sneaky spaces //$t = str_replace( chr(0x205F), " ", $t ); //Remove sneaky spaces //$t = str_replace( chr(0xFEFF), "", $t ); //Remove sneaky spaces } return $t; } /** * Base64 encode for URLs * * @param string Data * @return string Data */ static public function base64_encode_urlSafe( $data ) { return strtr( base64_encode( $data ), '+/=', '-_,' );; } /** * Base64 decode for URLs * * @param string Data * @return string Data */ static public function base64_decode_urlSafe( $data ) { return base64_decode( strtr( $data, '-_,', '+/=' ) ); } } /** * IPSMember * * This deals with member data and some member functions */ class IPSMember { /** * Custom fields class * * @var object */ static private $custom_fields_class; /** * Member cache * * @var array */ static private $memberCache = array(); /** * Ignore cache * * @var boolean */ static private $ignoreCache = FALSE; /** * Debug data * * @var array */ static public $debugData = array(); /** * memberFunctions object reference * * @var object */ static private $_memberFunctions; /** * Parsed signatures to save resources * * @var array */ static private $_parsedSignatures = array(); /** * Parsed custom fields to save resources * * @var array */ static private $_parsedCustomFields = array(); /** * Parsed custom fields to save resources * * @var array */ static private $_parsedCustomGroups = array(); /** * Ban filters cache * * @var array */ static public $_banFiltersCache = NULL; /** * Member data array * * @var array */ static public $data = array( 'member_id' => 0, 'name' => "", 'members_display_name' => "", 'member_group_id' => 0, 'member_forum_markers' => array() ); /** * Remapped table array used in load and save * * @var array */ static private $remap = array( 'core' => 'members', 'extendedProfile' => 'profile_portal', 'customFields' => 'pfields_content', 'itemMarkingStorage' => 'core_item_markers_storage' ); /** * Create new member * Very basic functionality at this point. * * @param array Fields to save in the following format: array( 'members' => array( 'email' => 'test@test.com', * 'joined' => time() ), * 'extendedProfile' => array( 'signature' => 'My signature' ) ); * Tables: members, pfields_content, profile_portal. * You can also use the aliases: 'core [members]', 'extendedProfile [profile_portal]', and 'customFields [pfields_content]' * @param bool Flag to attempt to auto create a name if the desired is taken * @param bool Bypass custom field saving (if using the sso session integration this is required as member object isn't ready yet) * @param bool Whether or not to recache the stats so as to update the board's last member data * @return array Final member Data including member_id * * EXCEPTION CODES * CUSTOM_FIELDS_EMPTY - Custom fields were not populated * CUSTOM_FIELDS_INVALID - Custom fields were invalid * CUSTOM_FIELDS_TOOBIG - Custom fields too big */ static public function create( $tables=array(), $autoCreateName=FALSE, $bypassCfields=FALSE, $doStatsRecache=TRUE ) { //----------------------------------------- // INIT //----------------------------------------- $finalTables = array(); $password = ''; $plainPassword = ''; $bitWiseFields = ipsRegistry::fetchBitWiseOptions( 'global' ); //----------------------------------------- // Remap tables if required //----------------------------------------- foreach( $tables as $table => $data ) { $_name = ( isset( self::$remap[ $table ] ) ) ? self::$remap[ $table ] : $table; if ( $_name == 'members' ) { /* Magic password field */ $password = ( isset( $data['password'] ) ) ? trim( $data['password'] ) : IPSLib::makePassword(); $plainPassword = $password; $md_5_password = md5( $password ); unset( $data['password'] ); } $finalTables[ $_name ] = $data; } //----------------------------------------- // Custom profile field stuff //----------------------------------------- if( !$bypassCfields ) { $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/customfields/profileFields.php', 'customProfileFields' ); $fields = new $classToLoad(); if ( is_array( $finalTables['pfields_content'] ) AND count( $finalTables['pfields_content'] ) ) { $fields->member_data = $finalTables['pfields_content']; } $_cfieldMode = 'normal'; $fields->initData( 'edit' ); $fields->parseToSave( $finalTables['pfields_content'], 'register' ); /* Check */ if( count( $fields->error_fields['empty'] ) ) { //throw new Exception( 'CUSTOM_FIELDS_EMPTY' ); } if( count( $fields->error_fields['invalid'] ) ) { //throw new Exception( 'CUSTOM_FIELDS_INVALID' ); } if( count( $fields->error_fields['toobig'] ) ) { //throw new Exception( 'CUSTOM_FIELDS_TOOBIG' ); } } //----------------------------------------- // Make sure the account doesn't exist //----------------------------------------- if( $finalTables['members']['email'] ) { $existing = IPSMember::load( $finalTables['members']['email'], 'all' ); if( $existing['member_id'] ) { $existing['full'] = true; $existing['timenow'] = time(); return $existing; } } //----------------------------------------- // Fix up usernames and display names //----------------------------------------- /* Ensure we have a display name */ if( $autoCreateName AND $finalTables['members']['members_display_name'] !== FALSE ) { $finalTables['members']['members_display_name'] = ( $finalTables['members']['members_display_name'] ) ? $finalTables['members']['members_display_name'] : $finalTables['members']['name']; } //----------------------------------------- // Remove some basic HTML tags //----------------------------------------- if( $finalTables['members']['members_display_name'] ) { $finalTables['members']['members_display_name'] = str_replace( array( '<', '>', '"' ), '', $finalTables['members']['members_display_name'] ); } if( $finalTables['members']['name'] ) { $finalTables['members']['name'] = str_replace( array( '<', '>', '"' ), '', $finalTables['members']['name'] ); } //----------------------------------------- // Make sure the names are unique //----------------------------------------- /* Can specify display name of FALSE to force no entry to force partial member */ if( $finalTables['members']['members_display_name'] !== FALSE ) { try { if( IPSMember::getFunction()->checkNameExists( $finalTables['members']['members_display_name'], array(), 'members_display_name', true ) === true ) { if ( $autoCreateName === TRUE ) { /* Now, make sure we have a unique display name */ $max = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'MAX(member_id) as max', 'from' => 'members', 'where' => "members_l_display_name LIKE '" . ipsRegistry::DB()->addSlashes( mb_strtolower( $finalTables['members']['members_display_name'] ) ) . "%'" ) ); if ( $max['max'] ) { $_num = $max['max'] + 1; $finalTables['members']['members_display_name'] = $finalTables['members']['members_display_name'] . '_' . $_num; } } else { $finalTables['members']['members_display_name'] = ''; } } } catch( Exception $e ) {} } if( $finalTables['members']['name'] ) { try { if( IPSMember::getFunction()->checkNameExists( $finalTables['members']['name'], array(), 'name', true ) === true ) { if ( $autoCreateName === TRUE ) { /* Now, make sure we have a unique username */ $max = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'MAX(member_id) as max', 'from' => 'members', 'where' => "members_l_username LIKE '" . ipsRegistry::DB()->addSlashes( mb_strtolower( $finalTables['members']['name'] ) ) . "%'" ) ); if ( $max['max'] ) { $_num = $max['max'] + 1; $finalTables['members']['name'] = $finalTables['members']['name'] . '_' . $_num; } } else { $finalTables['members']['name'] = ''; } } } catch( Exception $e ) {} } //----------------------------------------- // Clean up characters //----------------------------------------- if( $finalTables['members']['name'] ) { $userName = IPSMember::getFunction()->cleanAndCheckName( $finalTables['members']['name'], array(), 'name' ); if( $userName['errors'] ) { $finalTables['members']['name'] = $finalTables['members']['email']; } else { $finalTables['members']['name'] = $userName['username']; } } if( $finalTables['members']['members_display_name'] ) { $displayName = IPSMember::getFunction()->cleanAndCheckName( $finalTables['members']['members_display_name'] ); if( $displayName['errors'] ) { $finalTables['members']['members_display_name'] = ''; } else { $finalTables['members']['members_display_name'] = $displayName['members_display_name']; } } //----------------------------------------- // Populate member table(s) //----------------------------------------- $finalTables['members']['members_l_username'] = isset($finalTables['members']['name']) ? mb_strtolower($finalTables['members']['name']) : ''; $finalTables['members']['joined'] = $finalTables['members']['joined'] ? $finalTables['members']['joined'] : time(); $finalTables['members']['email'] = $finalTables['members']['email'] ? $finalTables['members']['email'] : $finalTables['members']['name'] . '@' . $finalTables['members']['joined']; $finalTables['members']['member_group_id'] = $finalTables['members']['member_group_id'] ? $finalTables['members']['member_group_id'] : ipsRegistry::$settings['member_group']; $finalTables['members']['ip_address'] = $finalTables['members']['ip_address'] ? $finalTables['members']['ip_address'] : ipsRegistry::member()->ip_address; $finalTables['members']['members_created_remote'] = intval( $finalTables['members']['members_created_remote'] ); $finalTables['members']['member_login_key'] = IPSMember::generateAutoLoginKey(); $finalTables['members']['member_login_key_expire'] = ( ipsRegistry::$settings['login_key_expire'] ) ? ( time() + ( intval( ipsRegistry::$settings['login_key_expire'] ) * 86400 ) ) : 0; $finalTables['members']['view_sigs'] = 1; $finalTables['members']['view_img'] = 1; $finalTables['members']['view_avs'] = 1; $finalTables['members']['bday_day'] = intval( $finalTables['members']['bday_day'] ); $finalTables['members']['bday_month'] = intval( $finalTables['members']['bday_month'] ); $finalTables['members']['bday_year'] = intval( $finalTables['members']['bday_year'] ); $finalTables['members']['restrict_post'] = intval( $finalTables['members']['restrict_post'] ); $finalTables['members']['msg_count_total'] = 0; $finalTables['members']['msg_count_new'] = 0; $finalTables['members']['msg_show_notification'] = 1; $finalTables['members']['coppa_user'] = 0; $finalTables['members']['auto_track'] = intval( $finalTables['members']['auto_track'] ); $finalTables['members']['last_visit'] = $finalTables['members']['last_visit'] ? $finalTables['members']['last_visit'] : time(); $finalTables['members']['last_activity'] = $finalTables['members']['last_activity'] ? $finalTables['members']['last_activity'] : time(); $finalTables['members']['language'] = IPSLib::getDefaultLanguage(); $finalTables['members']['members_editor_choice'] = ipsRegistry::$settings['ips_default_editor']; $finalTables['members']['members_pass_salt'] = IPSMember::generatePasswordSalt(5); $finalTables['members']['members_pass_hash'] = IPSMember::generateCompiledPasshash( $finalTables['members']['members_pass_salt'], $md_5_password ); $finalTables['members']['members_display_name'] = isset($finalTables['members']['members_display_name']) ? $finalTables['members']['members_display_name'] : ''; $finalTables['members']['members_l_display_name'] = isset($finalTables['members']['members_display_name']) ? mb_strtolower($finalTables['members']['members_display_name']) : ''; $finalTables['members']['fb_uid'] = isset($finalTables['members']['fb_uid']) ? $finalTables['members']['fb_uid'] : 0; $finalTables['members']['fb_emailhash'] = isset($finalTables['members']['fb_emailhash']) ? strtolower($finalTables['members']['fb_emailhash']) : ''; $finalTables['members']['members_seo_name'] = IPSText::makeSeoTitle( $finalTables['members']['members_display_name'] ); $finalTables['members']['bw_is_spammer'] = intval( $finalTables['members']['bw_is_spammer'] ); //----------------------------------------- // Insert: MEMBERS //----------------------------------------- ipsRegistry::DB()->force_data_type = array( 'name' => 'string', 'members_l_username' => 'string', 'members_display_name' => 'string', 'members_l_display_name' => 'string', 'members_seo_name' => 'string', 'email' => 'string' ); /* Bitwise options */ if ( is_array( $bitWiseFields['members'] ) ) { $_freeze = array(); foreach( $bitWiseFields['members'] as $field ) { if ( isset( $finalTables['members'][ $field ] ) ) { /* Add to freezeable array */ $_freeze[ $field ] = $finalTables['members'][ $field ]; /* Remove it from the fields to save to DB */ unset( $finalTables['members'][ $field ] ); } } if ( count( $_freeze ) ) { $finalTables['members']['members_bitoptions'] = IPSBWOptions::freeze( $_freeze, 'members', 'global' ); } } ipsRegistry::DB()->insert( 'members', $finalTables['members'] ); //----------------------------------------- // Get the member id //----------------------------------------- $finalTables['members']['member_id'] = ipsRegistry::DB()->getInsertId(); //----------------------------------------- // Insert: PROFILE PORTAL //----------------------------------------- $finalTables['profile_portal']['pp_member_id'] = $finalTables['members']['member_id']; $finalTables['profile_portal']['pp_setting_count_friends'] = 1; $finalTables['profile_portal']['pp_setting_count_comments'] = 1; $finalTables['profile_portal']['pp_customization'] = serialize( array() ); foreach( array( 'pp_last_visitors', 'pp_about_me', 'notes', 'signature', 'fb_photo', 'fb_photo_thumb', 'pconversation_filters', 'avatar_location', 'avatar_type', 'avatar_size' ) as $f ) { $finalTables['profile_portal'][ $f ] = ( $finalTables['profile_portal'][ $f ] ) ? $finalTables['profile_portal'][ $f ] : ''; } ipsRegistry::DB()->insert( 'profile_portal', $finalTables['profile_portal'] ); //----------------------------------------- // Insert into the custom profile fields DB //----------------------------------------- if( !$bypassCfields ) { /* Check the website url field */ $website_field = $fields->getFieldIDByKey( 'website' ); if( $website_field && $fields->out_fields[ 'field_' . $website_field ] ) { if( ! stristr( $fields->out_fields[ 'field_' . $website_field ], 'http://' ) ) { $fields->out_fields[ 'field_' . $website_field ] = 'http://' . $fields->out_fields[ 'field_' . $website_field ]; } } $fields->out_fields['member_id'] = $finalTables['members']['member_id']; ipsRegistry::DB()->delete( 'pfields_content', 'member_id=' . $finalTables['members']['member_id'] ); ipsRegistry::DB()->insert( 'pfields_content', $fields->out_fields ); } else { ipsRegistry::DB()->delete( 'pfields_content', 'member_id=' . $finalTables['members']['member_id'] ); ipsRegistry::DB()->insert( 'pfields_content', array( 'member_id' => $finalTables['members']['member_id'] ) ); } //----------------------------------------- // Insert into partial ID table //----------------------------------------- $full_account = false; if( $finalTables['members']['members_display_name'] AND $finalTables['members']['name'] AND $finalTables['members']['email'] AND $finalTables['members']['email'] != $finalTables['members']['name'] . '@' . $finalTables['members']['joined'] ) { $full_account = true; } if ( ! $full_account ) { ipsRegistry::DB()->insert( 'members_partial', array( 'partial_member_id' => $finalTables['members']['member_id'], 'partial_date' => $finalTables['members']['joined'], 'partial_email_ok' => ( $finalTables['members']['email'] == $finalTables['members']['name'] . '@' . $finalTables['members']['joined'] ) ? 0 : 1, ) ); } /* Add plain password and run sync */ $finalTables['members']['plainPassword'] = $plainPassword; IPSLib::runMemberSync( 'onCreateAccount', $finalTables['members'] ); /* Remove plain password */ unset( $finalTables['members']['plainPassword'] ); //----------------------------------------- // Recache our stats (Ticket 627608) //----------------------------------------- if ( $doStatsRecache == TRUE ) { ipsRegistry::cache()->rebuildCache( 'stats', 'global' ); } return array_merge( $finalTables['members'], $finalTables['profile_portal'], !$bypassCfields ? $fields->out_fields : array(), array( 'timenow' => $finalTables['members']['joined'], 'full' => $full_account ) ); } /** * Save member * * @param int Member key: Either Array, ID or email address. If it's an array, it must be in the format: * array( 'core' => array( 'field' => 'member_id', 'value' => 1 ) ) - useful for passing custom fields through * @param array Fields to save in the following format: array( 'members' => array( 'email' => 'test@test.com', * 'joined' => time() ), * 'extendedProfile' => array( 'signature' => 'My signature' ) ); * Tables: members, pfields_content, profile_portal. * You can also use the aliases: 'core [members]', 'extendedProfile [profile_portal]', and 'customFields [pfields_content]' * @return boolean True if the save was successful * * Exception Error Codes: * NO_DATA : No data to save * NO_VALID_KEY : No valid key to save * NO_AUTO_LOAD : Could not autoload the member as she does not exist * INCORRECT_TABLE : Table one is attempting to save to does not exist * NO_MEMBER_GROUP_ID: Member group ID is in the array but blank */ static public function save( $member_key, $save=array() ) { $member_id = 0; $member_email = ''; $member_field = ''; $_updated = 0; $bitWiseFields = ipsRegistry::fetchBitWiseOptions( 'global' ); $member_k_array = array( 'members' => array(), 'pfields_content' => array(), 'profile_portal' => array() ); $_tables = array_keys( $save ); $_MEMBERKEY = 'member_id'; $_MEMBERVALUE = $member_key; //----------------------------------------- // Test... //----------------------------------------- if ( ! is_array( $save ) OR ! count( $save ) ) { throw new Exception( 'NO_DATA' ); } //----------------------------------------- // ID or email? //----------------------------------------- if ( ! is_array( $member_key ) ) { if ( strstr( $member_key, '@' ) ) { $_MEMBERKEY = 'email'; $member_k_array['members'] = array( 'field' => 'email', 'value' => "'" . ipsRegistry::instance()->DB()->addSlashes( mb_strtolower( $member_key ) ) . "'" ); //----------------------------------------- // Check to see if we've got more than the core // table to save on. //----------------------------------------- $_got_more_than_core = FALSE; foreach( $_tables as $table ) { if ( isset( self::$remap[ $table ] ) ) { $table = self::$remap[ $table ]; } if ( $table != 'members' ) { $_got_more_than_core = TRUE; break; } } if ( $_got_more_than_core === TRUE ) { /* Get the ID */ $_memberTmp = self::load( $member_key, 'core' ); if ( $_memberTmp['member_id'] ) { $member_k_array['pfields_content'] = array( 'field' => 'member_id' , 'value' => $_memberTmp['member_id'] ); $member_k_array['profile_portal'] = array( 'field' => 'pp_member_id', 'value' => $_memberTmp['member_id'] ); } else { throw new Exception( "NO_AUTO_LOAD" ); } } } else { $member_k_array['members'] = array( 'field' => 'member_id' , 'value' => intval( $member_key ) ); $member_k_array['pfields_content'] = array( 'field' => 'member_id' , 'value' => intval( $member_key ) ); $member_k_array['profile_portal'] = array( 'field' => 'pp_member_id' , 'value' => intval( $member_key ) ); self::_updateCache( $member_key, $save ); } } else { $_member_k_array = $member_k_array; foreach( $member_key as $table => $data ) { if ( isset( self::$remap[ $table ] ) ) { $table = self::$remap[ $table ]; } if ( ! in_array( $table, array_keys( $_member_k_array ) ) ) { throw new Exception( 'INCORRECT_TABLE' ); } $member_k_array[ $table ] = $data; } } //----------------------------------------- // Test... //----------------------------------------- if ( ! is_array( $member_k_array ) OR ! count( $member_k_array ) ) { throw new Exception( 'NO_DATA' ); } //----------------------------------------- // Now save... //----------------------------------------- foreach( $save as $table => $data ) { if ( isset( self::$remap[ $table ] ) ) { $table = self::$remap[ $table ]; } if ( $table == 'profile_portal' ) { $data[ $member_k_array[ $table ]['field'] ] = $member_k_array[ $table ]['value']; //----------------------------------------- // Does row exist? //----------------------------------------- $check = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'pp_member_id', 'from' => 'profile_portal', 'where' => 'pp_member_id=' . $data['pp_member_id'] ) ); if( !$check['pp_member_id'] ) { ipsRegistry::DB()->insert( $table, $data ); } else { ipsRegistry::DB()->update( $table, $data, 'pp_member_id=' . $data['pp_member_id'] ); } } else if ( $table == 'pfields_content' ) { $data[ $member_k_array[ $table ]['field'] ] = $member_k_array[ $table ]['value']; //----------------------------------------- // Does row exist? //----------------------------------------- $check = ipsRegistry::DB()->buildAndFetch( array( 'select' => 'member_id', 'from' => 'pfields_content', 'where' => 'member_id=' . $data['member_id'] ) ); foreach( $data as $_k => $_v ) { ipsRegistry::DB()->force_data_type[ $_k ] = 'string'; } if( !$check['member_id'] ) { ipsRegistry::DB()->insert( $table, $data ); } else { ipsRegistry::DB()->update( $table, $data, 'member_id=' . $data['member_id'] ); } } else { if ( $table == 'members' ) { /* Make sure we have a value for member_group_id if passed */ if ( isset( $data['member_group_id'] ) AND ! $data['member_group_id'] ) { throw new Exception( "NO_MEMBER_GROUP_ID" ); } /* Some stuff that can end up here */ unset( $data['_canBeIgnored'] ); /* Bitwise options */ if ( is_array( $bitWiseFields['members'] ) ) { $_freeze = array(); foreach( $bitWiseFields['members'] as $field ) { if ( isset( $data[ $field ] ) ) { /* Add to freezeable array */ $_freeze[ $field ] = $data[ $field ]; /* Remove it from the fields to save to DB */ unset( $data[ $field ] ); } } if ( count( $_freeze ) ) { $data['members_bitoptions'] = IPSBWOptions::freeze( $_freeze, 'members', 'global' ); } } ipsRegistry::DB()->force_data_type = array( 'name' => 'string', 'title' => 'string', 'members_l_username' => 'string', 'members_display_name' => 'string', 'members_l_display_name' => 'string', 'members_seo_name' => 'string', 'msg_count_total' => 'int', 'msg_count_new' => 'int', 'members_bitoptions' => 'int', ); } ipsRegistry::DB()->update( $table, $data, $member_k_array[ $table ]['field'] . '=' . $member_k_array[ $table ]['value'] ); } $_updated += ipsRegistry::instance()->DB()->getAffectedRows(); } //----------------------------------------- // If member login key is updated during // session creation, this causes fatal error //----------------------------------------- if( is_object( ipsRegistry::member() ) ) { $save[ $_MEMBERKEY ] = $_MEMBERVALUE; IPSLib::runMemberSync( 'onProfileUpdate', $save ); } return ( $_updated > 0 ) ? TRUE : FALSE; } /** * Load member * * @param string Member key: Either ID or email address OR array of IDs when $key_type is either ID or not set OR a list of $key_type strings (email address, name, etc) * @param string Extra tables to load(all, none or comma delisted tables) Tables: members, pfields_content, profile_portal, groups, sessions, core_item_markers_storage. * You can also use the aliases: 'extendedProfile', 'customFields' and 'itemMarkingStorage' * @param string Key type. Leave it blank to auto-detect or specify "id", "email", "username", "displayname". * @return array Array containing member data * * # Single member * $member = IPSMember::load( 1, 'extendedProfile,groups' ); * $member = IPSMember::load( 'matt@email.com', 'all' ); * $member = IPSMember::load( 'MattM', 'all', 'displayname' ); // Can also use 'username', 'email' or 'id' * # Multiple members * $members = IPSMember::load( array( 1, 2, 10 ), 'all' ); * $members = IPSMember::load( array( 'MattM, 'JoeD', 'DaveP' ), 'all', 'displayname' ); * */ static public function load( $member_key, $extra_tables='all', $key_type='' ) { //----------------------------------------- // INIT //----------------------------------------- $member_value = 0; $members = array(); $multiple_ids = array(); $member_field = ''; $joins = array(); $tables = array( 'pfields_content' => 0, 'profile_portal' => 0, 'core_item_markers_storage' => 0, 'groups' => 0, 'sessions' => 0 ); $remap = array( 'extendedProfile' => 'profile_portal', 'customFields' => 'pfields_content', 'itemMarkingStorage' => 'core_item_markers_storage' ); //----------------------------------------- // ID or email? //----------------------------------------- if ( ! $key_type ) { if ( is_array( $member_key ) ) { $multiple_ids = array_map( 'intval', $member_key ); // Bug #20908 $member_field = 'member_id'; } else { if ( strstr( $member_key, '@' ) ) { $member_value = "'" . ipsRegistry::DB()->addSlashes( mb_strtolower( $member_key ) ) . "'"; $member_field = 'email'; } else { $member_value = intval( $member_key ); $member_field = 'member_id'; } } } else { switch( $key_type ) { default: case 'id': if ( is_array( $member_key ) ) { $multiple_ids = $member_key; } else { $member_value = intval( $member_key ); } $member_field = 'member_id'; break; case 'fb_uid': if ( is_array( $member_key ) ) { $multiple_ids = $member_key; } else { $member_value = is_numeric( $member_key ) ? $member_key : 0; } $member_field = 'fb_uid'; if ( $member_value == 0 ) { return array(); } break; case 'twitter_id': if ( is_array( $member_key ) ) { $multiple_ids = $member_key; } else { $member_value = is_numeric( $member_key ) ? $member_key : 0; } $member_field = 'twitter_id'; if ( $member_value == 0 ) { return array(); } break; case 'vk_uid': if ( is_array( $member_key ) ) { $multiple_ids = $member_key; } else { $member_value = is_numeric( $member_key ) ? $member_key : 0; } $member_field = 'vk_uid'; if ( $member_value == 0 ) { return array(); } break; case 'email': if ( is_array( $member_key ) ) { array_walk( $member_key, create_function( '&$v,$k', '$v="\'".ipsRegistry::DB()->addSlashes( mb_strtolower( $v ) ) . "\'";' ) ); $multiple_ids = $member_key; } else { $member_value = "'" . ipsRegistry::DB()->addSlashes( mb_strtolower( $member_key ) ) . "'"; } $member_field = 'email'; break; case 'username': if ( is_array( $member_key ) ) { array_walk( $member_key, create_function( '&$v,$k', '$v="\'".ipsRegistry::DB()->addSlashes( mb_strtolower( $v ) ) . "\'";' ) ); $multiple_ids = $member_key; } else { $member_value = "'" . ipsRegistry::DB()->addSlashes( mb_strtolower( $member_key ) ) . "'"; } $member_field = 'members_l_username'; break; case 'displayname': if ( is_array( $member_key ) ) { array_walk( $member_key, create_function( '&$v,$k', '$v="\'".ipsRegistry::DB()->addSlashes( mb_strtolower( $v ) ) . "\'";' ) ); $multiple_ids = $member_key; } else { $member_value = "'" . ipsRegistry::DB()->addSlashes( mb_strtolower( $member_key ) ) . "'"; } $member_field = 'members_l_display_name'; break; } } //----------------------------------------- // Protected against member_id=0 //----------------------------------------- if( !count($multiple_ids) OR !is_array($multiple_ids) ) { if( $member_field == 'member_id' AND !$member_value ) { return array(); } } //----------------------------------------- // Sort out joins... //----------------------------------------- if ( $extra_tables == 'all' ) { foreach( $tables as $_table => $_val ) { /* Let's not load sessions unless specifically requested */ if ( $_table == 'sessions' ) { continue; } /* Same deal with item marking */ if ( $_table == 'core_item_markers_storage' ) { continue; } $tables[ $_table ] = 1; } } else if ( $extra_tables ) { $_tables = explode( ",", $extra_tables ); foreach( $_tables as $_t ) { $_t = trim( $_t ); if ( isset( $tables[ $_t ] ) ) { $tables[ $_t ] = 1; } else if ( isset( self::$remap[ $_t ] ) ) { if ( strstr( $tables[ self::$remap[ $_t ] ], ',' ) ) { $__tables = explode( ',', $tables[ self::$remap[ $_t ] ] ); foreach( $__tables as $__t ) { $tables[ $__t ] = 1; } } else { $tables[ self::$remap[ $_t ] ] = 1; } } } } //----------------------------------------- // Grab used tables //----------------------------------------- $_usedTables = array(); foreach( $tables as $_name => $_use ) { if ( $_use ) { $_usedTables[] = $_name; } } //----------------------------------------- // Check the cache first... //----------------------------------------- if ( $member_field == 'member_id' AND $member_value ) { $member = self::_fetchFromCache( $member_value, $_usedTables ); if ( $member !== FALSE ) { return $member; } } else if( count($multiple_ids) AND is_array($multiple_ids) ) { $_totalUsers = count($multiple_ids); $_gotFromCache = 0; $_fromCache = array(); foreach( $multiple_ids as $_memberValue ) { $member = self::_fetchFromCache( $_memberValue, $_usedTables ); if ( $member !== FALSE ) { $_fromCache[ $member['member_id'] ] = $member; $_gotFromCache++; } } //----------------------------------------- // Did we find all the members in cache? //----------------------------------------- if( $_gotFromCache == $_totalUsers ) { return $_fromCache; } } self::$ignoreCache = FALSE; //----------------------------------------- // Fix up joins... //----------------------------------------- if ( $tables['pfields_content'] ) { $joins[] = array( 'select' => 'p.*', 'from' => array( 'pfields_content' => 'p' ), 'where' => 'p.member_id=m.member_id', 'type' => 'left' ); } if ( $tables['profile_portal'] ) { $joins[] = array( 'select' => 'pp.*', 'from' => array( 'profile_portal' => 'pp' ), 'where' => 'pp.pp_member_id=m.member_id', 'type' => 'left' ); } if ( $tables['groups'] ) { $joins[] = array( 'select' => 'g.*', 'from' => array( 'groups' => 'g' ), 'where' => 'g.g_id=m.member_group_id', 'type' => 'left' ); } if ( $tables['sessions'] ) { $joins[] = array( 'select' => 's.*', 'from' => array( 'sessions' => 's' ), 'where' => 's.member_id=m.member_id', 'type' => 'left' ); } if ( $tables['core_item_markers_storage'] ) { $joins[] = array( 'select' => 'im.*', 'from' => array( 'core_item_markers_storage' => 'im' ), 'where' => 'im.item_member_id=m.member_id', 'type' => 'left' ); } if ( IPSContentCache::isEnabled() ) { if ( IPSContentCache::fetchSettingValue( 'sig' ) ) { $joins[] = IPSContentCache::join( 'sig' , 'm.member_id', 'ccb', 'left', 'ccb.cache_content' ); } } //----------------------------------------- // Do eeet //----------------------------------------- if ( count( $joins ) ) { ipsRegistry::DB()->build( array( 'select' => 'm.*, m.member_id as my_member_id', 'from' => array( 'members' => 'm' ), 'where' => ( is_array( $multiple_ids ) AND count( $multiple_ids ) ) ? 'm.'. $member_field . ' IN (' . implode( ',', $multiple_ids ) . ')' : 'm.'. $member_field . '=' . $member_value, 'add_join' => $joins ) ); } else { ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'members', 'where' => ( is_array( $multiple_ids ) AND count( $multiple_ids ) ) ? $member_field . ' IN (' . implode( ',', $multiple_ids ) . ')' : $member_field . '=' . $member_value ) ); } //----------------------------------------- // Execute //----------------------------------------- ipsRegistry::DB()->execute(); while( $mem = ipsRegistry::DB()->fetch() ) { if ( isset( $mem['my_member_id'] ) ) { $mem['member_id'] = $mem['my_member_id']; } $mem['full'] = true; if( !$mem['email'] OR !$mem['members_display_name'] OR $mem['email'] == $mem['name'] . '@' . $mem['joined'] ) { $mem['full'] = false; $mem['timenow'] = $mem['joined']; } /* Clean secondary groups */ $mem['mgroup_others'] = ($mem['mgroup_others'] != '') ? IPSText::cleanPermString($mem['mgroup_others']) : ''; //----------------------------------------- // Be sure we properly apply secondary permissions //----------------------------------------- if ( $tables['groups'] AND is_object( ipsRegistry::member() ) ) { $mem = ipsRegistry::member()->setUpSecondaryGroups( $mem ); /* Unpack groups */ $mem = IPSLib::unpackGroup( $mem, TRUE ); } //----------------------------------------- // Unblockable //----------------------------------------- $mem['_canBeIgnored'] = self::isIgnorable( $mem['member_group_id'], $mem['mgroup_others'] ); /* Bitwise Options */ $mem = self::buildBitWiseOptions( $mem ); /* Add to array */ $members[ $mem['member_id'] ] = $mem; //----------------------------------------- // Add to cache //----------------------------------------- self::_addToCache( $mem, $_usedTables ); } //----------------------------------------- // Return just a single if we only sent one id //----------------------------------------- return ( is_array( $multiple_ids ) AND count( $multiple_ids ) ) ? $members : array_shift( $members ); } /** * Delete member(s) * * @param mixed [Integer] member ID or [Array] array of member ids * @param boolean Check if request is from an admin * @return boolean Action completed successfully */ static public function remove( $id, $check_admin=true ) { //----------------------------------------- // INIT //----------------------------------------- $tmp_mids = array(); $emails = array(); //----------------------------------------- // Sort out thingie //----------------------------------------- if ( is_array( $id ) ) { $id = IPSLib::cleanIntArray( $id ); $mids = ' IN (' . implode( ",", $id ) . ')'; } else { $mids = ' = ' . intval($id); } //----------------------------------------- // Get accounts and check IDS //----------------------------------------- ipsRegistry::DB()->build( array( 'select' => 'm.member_id, m.name, m.member_group_id, m.email', 'from' => array( 'members' => 'm' ), 'where' => 'm.member_id' . $mids, 'add_join' => array( array( 'select' => 'g.g_access_cp', 'from' => array( 'groups' => 'g' ), 'where' => 'g.g_id=m.member_group_id', 'type' => 'left', ) ) ) ); ipsRegistry::DB()->execute(); while ( $r = ipsRegistry::DB()->fetch() ) { //----------------------------------------- // Non root admin attempting to edit root admin? //----------------------------------------- if( $check_admin ) { if ( !ipsRegistry::member()->getProperty('g_access_cp') ) { if ( $r['g_access_cp'] ) { continue; } } } $tmp_mids[] = $r['member_id']; $emails[] = $r['email']; self::_removeFromCache( $r['member_id'] ); } if ( ! count( $tmp_mids ) ) { return false; } $mids = ' IN (' . implode( ",", $tmp_mids ) . ')'; //----------------------------------------- // Get avatars / photo //----------------------------------------- $delete_files = array(); ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'profile_portal', 'where' => 'pp_member_id' . $mids ) ); ipsRegistry::DB()->execute(); while( $r = ipsRegistry::DB()->fetch() ) { if ( $r['pp_main_photo'] ) { $delete_files[] = $r['pp_main_photo']; } if ( $r['pp_thumb_photo'] ) { $delete_files[] = $r['pp_thumb_photo']; } if ( $r['avatar_type'] == 'upload' and $r['avatar_location'] ) { $delete_files[] = $r['avatar_location']; } $_customizations = unserialize($r['pp_customization']); if( $_customizations['type'] == 'upload' AND $_customizations['bg_url'] ) { $delete_files[] = $_customizations['bg_url']; } } //----------------------------------------- // Take care of forum stuff //----------------------------------------- ipsRegistry::DB()->update( 'posts' , array( 'author_id' => 0 ), "author_id" . $mids ); ipsRegistry::DB()->update( 'topics' , array( 'starter_id' => 0 ), "starter_id" . $mids ); ipsRegistry::DB()->update( 'announcements' , array( 'announce_member_id' => 0 ), "announce_member_id" . $mids ); ipsRegistry::DB()->update( 'attachments' , array( 'attach_member_id' => 0 ), "attach_member_id" . $mids ); ipsRegistry::DB()->update( 'polls' , array( 'starter_id' => 0 ), "starter_id" . $mids ); //ipsRegistry::DB()->update( 'topic_ratings' , array( 'rating_member_id' => 0 ), "rating_member_id" . $mids ); ipsRegistry::DB()->update( 'voters' , array( 'member_id' => 0 ), "member_id" . $mids ); ipsRegistry::DB()->update( 'forums' , array( 'last_poster_name' => '' ), "last_poster_id" . $mids ); ipsRegistry::DB()->update( 'forums' , array( 'seo_last_name' => '' ), "last_poster_id" . $mids ); ipsRegistry::DB()->update( 'forums' , array( 'last_poster_id' => 0 ), "last_poster_id" . $mids ); ipsRegistry::DB()->delete( 'core_share_links_log' , "log_member_id" . $mids ); ipsRegistry::DB()->delete( 'core_soft_delete_log' , "sdl_obj_member_id" . $mids ); ipsRegistry::DB()->update( 'rss_import' , array( 'rss_import_mid' => 0 ), "rss_import_mid" . $mids ); ipsRegistry::DB()->update( 'tags_index' , array( 'member_id' => 0 ), "member_id" . $mids ); //----------------------------------------- // Likes - also invalidates likes cache // @todo - Matt - Move this into like class //----------------------------------------- $_likes = array(); ipsRegistry::DB()->build( array( 'select' => 'like_lookup_id', 'from' => 'core_like', 'where' => "like_member_id" . $mids ) ); ipsRegistry::DB()->execute(); while( $r = ipsRegistry::DB()->fetch() ) { $_likes[] = $r['like_lookup_id']; } if( count($_likes) ) { ipsRegistry::DB()->delete( 'core_like' , "like_member_id" . $mids ); ipsRegistry::DB()->delete( 'core_like_cache' , "like_cache_id IN('" . implode( "','", array_map( 'addslashes', $_likes ) ) . "')" ); } //----------------------------------------- // Clean up profile stuff //----------------------------------------- ipsRegistry::DB()->update( 'profile_comments' , array( 'comment_by_member_id' => 0 ), "comment_by_member_id" . $mids ); ipsRegistry::DB()->update( 'profile_ratings' , array( 'rating_by_member_id' => 0 ), "rating_by_member_id" . $mids ); ipsRegistry::DB()->delete( 'profile_comments' , "comment_for_member_id" . $mids ); ipsRegistry::DB()->delete( 'profile_ratings' , "rating_for_member_id" . $mids ); ipsRegistry::DB()->delete( 'profile_portal' , "pp_member_id" . $mids ); ipsRegistry::DB()->delete( 'profile_portal_views' , "views_member_id" . $mids ); ipsRegistry::DB()->delete( 'profile_friends' , "friends_member_id" . $mids ); ipsRegistry::DB()->delete( 'profile_friends' , "friends_friend_id" . $mids ); ipsRegistry::DB()->delete( 'profile_friends_flood' , "friends_member_id" . $mids . " OR friends_friend_id" . $mids ); ipsRegistry::DB()->delete( 'dnames_change' , "dname_member_id" . $mids ); ipsRegistry::DB()->delete( 'mobile_notifications' , "member_id" . $mids ); //----------------------------------------- // Delete member... //----------------------------------------- ipsRegistry::DB()->delete( 'pfields_content' , "member_id" . $mids ); ipsRegistry::DB()->delete( 'members_partial' , "partial_member_id" . $mids ); ipsRegistry::DB()->delete( 'moderators' , "member_id" . $mids ); ipsRegistry::DB()->delete( 'sessions' , "member_id" . $mids ); ipsRegistry::DB()->delete( 'search_sessions' , "session_member_id" . $mids ); ipsRegistry::DB()->delete( 'upgrade_sessions' , "session_member_id" . $mids ); ipsRegistry::DB()->delete( 'warn_logs' , "wlog_mid" . $mids ); ipsRegistry::DB()->update( 'warn_logs' , array( 'wlog_addedby' => 0 ), "wlog_addedby" . $mids ); ipsRegistry::DB()->delete( 'member_status_actions' , "action_member_id" . $mids ); ipsRegistry::DB()->delete( 'member_status_actions' , "action_status_owner" . $mids ); ipsRegistry::DB()->delete( 'member_status_replies' , "reply_member_id" . $mids ); ipsRegistry::DB()->delete( 'member_status_updates' , "status_member_id" . $mids ); //----------------------------------------- // Update admin stuff and logs //----------------------------------------- ipsRegistry::DB()->delete( 'admin_permission_rows' , "row_id_type='member' AND row_id" . $mids ); ipsRegistry::DB()->delete( 'core_sys_cp_sessions' , 'session_member_id' . $mids ); ipsRegistry::DB()->delete( 'core_sys_login' , 'sys_login_id' . $mids ); ipsRegistry::DB()->update( 'upgrade_history' , array( 'upgrade_mid' => 0 ), "upgrade_mid" . $mids ); ipsRegistry::DB()->update( 'admin_logs' , array( 'member_id' => 0 ), "member_id" . $mids ); ipsRegistry::DB()->update( 'email_logs' , array( 'from_member_id' => 0 ), "from_member_id" . $mids ); ipsRegistry::DB()->update( 'email_logs' , array( 'to_member_id' => 0 ), "to_member_id" . $mids ); ipsRegistry::DB()->update( 'error_logs' , array( 'log_member' => 0 ), "log_member" . $mids ); ipsRegistry::DB()->update( 'moderator_logs' , array( 'member_id' => 0, 'member_name' => '' ), "member_id" . $mids ); //----------------------------------------- // Fix up member messages... //----------------------------------------- ipsRegistry::DB()->delete( 'message_topic_user_map' , 'map_user_id' . $mids ); ipsRegistry::DB()->update( 'message_posts' , array( 'msg_author_id' => 0 ), 'msg_author_id' . $mids ); ipsRegistry::DB()->update( 'message_topics' , array( 'mt_starter_id' => 0 ), 'mt_starter_id' . $mids ); ipsRegistry::DB()->delete( 'ignored_users' , "ignore_owner_id" . $mids . " or ignore_ignore_id" . $mids ); ipsRegistry::DB()->delete( 'inline_notifications' , "notify_to_id" . $mids ); ipsRegistry::DB()->update( 'inline_notifications' , array( 'notify_from_id' => 0 ), 'notify_from_id' . $mids ); //----------------------------------------- // Delete subs, views, markers, etc. //----------------------------------------- ipsRegistry::DB()->delete( 'tracker' , "member_id" . $mids ); ipsRegistry::DB()->delete( 'forum_tracker' , "member_id" . $mids ); ipsRegistry::DB()->delete( 'core_item_markers' , "item_member_id" . $mids ); ipsRegistry::DB()->delete( 'core_item_markers_storage', "item_member_id" . $mids ); ipsRegistry::DB()->update( 'rc_comments' , array( 'comment_by' => 0 ), "comment_by" . $mids ); ipsRegistry::DB()->delete( 'rc_modpref' , "mem_id" . $mids ); ipsRegistry::DB()->update( 'rc_reports' , array( 'report_by' => 0 ), "report_by" . $mids ); ipsRegistry::DB()->update( 'rc_reports_index' , array( 'updated_by' => 0 ), "updated_by" . $mids ); ipsRegistry::DB()->delete( 'rc_reports_index' , "seotemplate='showuser' AND exdat1" . $mids ); ipsRegistry::DB()->delete( 'reputation_cache' , "type='member' AND type_id" . $mids ); ipsRegistry::DB()->delete( 'reputation_index' , "member_id" . $mids ); //----------------------------------------- // Delete from validating.. //----------------------------------------- ipsRegistry::DB()->delete( 'validating' , "member_id" . $mids ); ipsRegistry::DB()->delete( 'members' , "member_id" . $mids ); /* Delete from profile cache */ if ( count( $tmp_mids ) ) { require_once( IPSLib::getAppDir('members') . '/sources/friends.php' ); $friends = new profileFriendsLib( ipsRegistry::instance() ); foreach( $tmp_mids as $_mid ) { $friends->recacheFriends( array( 'member_id' => $_mid ) ); } } //----------------------------------------- // Delete avatars / photos //----------------------------------------- if ( count($delete_files) ) { foreach( $delete_files as $file ) { @unlink( ipsRegistry::$settings['upload_dir'] . "/" . $file ); } } //----------------------------------------- // Member Sync //----------------------------------------- IPSLib::runMemberSync( 'onDelete', $mids ); /* Remove from cache */ IPSContentCache::drop( 'sig', $tmp_mids ); //----------------------------------------- // Get current stats... //----------------------------------------- ipsRegistry::cache()->rebuildCache( 'stats', 'global' ); ipsRegistry::cache()->rebuildCache( 'moderators', 'global' ); ipsRegistry::cache()->rebuildCache( 'birthdays', 'calendar' ); ipsRegistry::cache()->rebuildCache( 'announcements', 'forums' ); } /** * Set up moderator, populate moderator functions * * @param array Array of member data * @return array Array of member data populated with moderator details */ static public function setUpModerator( $member ) { $other_mgroups = array(); if ( $member['member_group_id'] != ipsRegistry::$settings['guest_group'] ) { //----------------------------------------- // Sprinkle on some moderator stuff... //----------------------------------------- if ( $member['g_is_supmod'] == 1 ) { $member['is_mod'] = 1; } else if ( is_array(ipsRegistry::cache()->getCache('moderators')) AND count(ipsRegistry::cache()->getCache('moderators')) ) { $other_mgroups = array(); if ( IPSText::cleanPermString( $member['mgroup_others'] ) ) { $other_mgroups = explode( ",", IPSText::cleanPermString( $member['mgroup_others'] ) ); } } if( is_array(ipsRegistry::cache()->getCache('moderators')) AND count(ipsRegistry::cache()->getCache('moderators')) ) { $_mod_forums = isset( $member['forumsModeratorData'] ) && is_array( $member['forumsModeratorData'] ) ? $member['forumsModeratorData'] : array(); foreach( ipsRegistry::cache()->getCache('moderators') as $r ) { $modForumIds = explode( ',', IPSText::cleanPermString( $r['forum_id'] ) ); if ( $r['member_id'] == $member['member_id'] ) { foreach( $modForumIds as $modForumId ) { $_mod_forums[ $modForumId ] = $r; } $member['is_mod'] = 1; } else if( $r['group_id'] == $member['member_group_id'] ) { // Individual mods override group mod settings // If array is set, don't override it foreach( $modForumIds as $modForumId ) { if( !is_array($_mod_forums[ $modForumId ]) OR !count($_mod_forums[ $modForumId ]) ) { $_mod_forums[ $modForumId ] = $r; } } $member['is_mod'] = 1; } else if( count( $other_mgroups ) AND in_array( $r['group_id'], $other_mgroups ) ) { // Individual mods override group mod settings // If array is set, don't override it if( !is_array($_mod_forums[ $r['forum_id'] ]) OR !count($_mod_forums[ $r['forum_id'] ]) ) { $_mod_forums[ $r['forum_id'] ] = $r; } $member['is_mod'] = 1; } } $member['forumsModeratorData'] = $_mod_forums; } } return $member; } /** * Fetches SEO name, updating the table if required * * @param array Member data * @return string SEO Name */ static public function fetchSeoName( $memberData ) { if ( ! is_array( $memberData ) OR ! $memberData['member_id'] ) { return; } if ( isset( $memberData['members_seo_name'] ) and ( $memberData['members_seo_name'] ) ) { return $memberData['members_seo_name']; } else if ( isset( $memberData['members_display_name'] ) and ( $memberData['members_display_name'] ) ) { $_seoName = IPSText::makeSeoTitle( $memberData['members_display_name'] ); ipsRegistry::DB()->update( 'members', array( 'members_seo_name' => $_seoName ), 'member_id=' . $memberData['member_id'] ); return $_seoName; } else { return '-'; } } /** * Fetches Ignore user data * * @param array Member data * @return array Array of ignored users */ static public function fetchIgnoredUsers( $memberData ) { /* INIT */ $ignore_users = array(); if ( $memberData['member_id'] ) { /* < 3.0.0 used comma delisted string. 3.0.0+ uses serialized array */ if ( strstr( $memberData['ignored_users'], 'a:' ) ) { $data = unserialize( $memberData['ignored_users'] ); return ( is_array( $data ) ) ? $data : array(); } else { if ( $memberData['ignored_users'] ) { $_data = explode( ",", $memberData['ignored_users'] ); foreach( $_data as $id ) { if ( $id ) { $ignore_users[ $id ] = array( 'ignore_ignore_id' => $id, 'ignore_messages' => 0, 'ignore_topics' => 1 ); } } } /* Now fetch them from the DB */ ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'ignored_users', 'where' => "ignore_owner_id=" . $memberData['member_id'] ) ); ipsRegistry::DB()->execute(); while( $r = ipsRegistry::DB()->fetch() ) { $ignore_users[ $r['ignore_ignore_id'] ] = array( 'ignore_ignore_id' => $r['ignore_ignore_id'], 'ignore_messages' => $r['ignore_messages'], 'ignore_topics' => $r['ignore_topics'] ); } /* Update.... */ self::save( $memberData['member_id'], array( 'core' => array( 'ignored_users' => serialize( $ignore_users ) ) ) ); } } return $ignore_users; } /** * Updates member.ignored_users * * @param mixed Member ID or Member data * @return array Array of ignored users */ static public function rebuildIgnoredUsersCache( $member ) { /* INIT */ $ignore_users = array(); $memberData = ( ! is_array( $member ) ) ? self::load( $member, 'all' ) : $member; /* Continue */ if ( $memberData['member_id'] ) { /* Fetch from DB */ ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'ignored_users', 'where' => "ignore_owner_id=" . $memberData['member_id'] ) ); ipsRegistry::DB()->execute(); while( $r = ipsRegistry::DB()->fetch() ) { $ignore_users[ $r['ignore_ignore_id'] ] = array( 'ignore_ignore_id' => $r['ignore_ignore_id'], 'ignore_messages' => $r['ignore_messages'], 'ignore_topics' => $r['ignore_topics'] ); } /* Update.... */ self::save( $memberData['member_id'], array( 'core' => array( 'ignored_users' => serialize( $ignore_users ) ) ) ); } } /** * Retrieve the member's location * * @author Brandon Farber * @param array Member information (including session info!) * @return array Member info with session info parsed * @since IPB 3.0 */ static public function getLocation( $member ) { $member['online_extra'] = ""; //----------------------------------------- // Grab 'where' info //----------------------------------------- if( $member['current_appcomponent'] AND IPSLib::appIsInstalled( $member['current_appcomponent'] ) ) { $member['current_appcomponent'] = IPSText::alphanumericalClean($member['current_appcomponent']); $filename = IPSLib::getAppDir( $member['current_appcomponent'] ) . '/extensions/coreExtensions.php'; if ( file_exists( $filename ) ) { $toload = IPSLib::loadLibrary( $filename, 'publicSessions__' . $member['current_appcomponent'], $member['current_appcomponent'] ); if( class_exists( $toload ) ) { $loader = new $toload; if( method_exists( $loader, 'parseOnlineEntries' ) ) { $tmp = $loader->parseOnlineEntries( array( $member['id'] => $member ) ); // Yes, this is really id - it's session id, not member id if( isset( $tmp[ $member['id'] ] ) && is_array( $tmp[ $member['id'] ] ) && count( $tmp[ $member['id'] ] ) ) { if ( isset( $tmp[ $member['id'] ]['_whereLinkSeo']) ) { $member['online_extra'] = "{$tmp[ $member['id'] ]['where_line']} " . IPSText::truncate( $tmp[ $member['id'] ]['where_line_more'], 35 ) . ""; } /* @link http://community.invisionpower.com/tracker/issue-20598-where-link-not-taken-into-account-on-profile-page-if-no-where-line-more-specified/ */ else if ( isset($tmp[ $member['id'] ]['where_link']) AND $tmp[ $member['id'] ]['where_line_more'] ) { $member['online_extra'] = "{$tmp[ $member['id'] ]['where_line']} " . IPSText::truncate( $tmp[ $member['id'] ]['where_line_more'], 35 ) . ""; } else if ( isset($tmp[ $member['id'] ]['where_link']) ) { $member['online_extra'] = "" . IPSText::truncate( $tmp[ $member['id'] ]['where_line'], 35 ) . ""; } else { $member['online_extra'] = $tmp[ $member['id'] ]['where_line']; } } } } } } if ( ! $member['online_extra'] ) { $member['online_extra'] = $member['id'] ? ipsRegistry::getClass('class_localization')->words['board_index'] : ipsRegistry::getClass('class_localization')->words['not_online']; } return $member; } /** * Determine if two members are friends * * @author Brandon Farber * @param integer Member ID to check for * @param integer Member ID to check against (defaults to current member id) * @return boolean Whether they are friends or not * @since IPB 3.0 */ static public function checkFriendStatus( $memberId, $checkAgainst=0 ) { /** * If no member id, obviously not friends */ if( !$memberId ) { return false; } /** * Get member data */ $memberData = array(); if( !$checkAgainst ) { $memberData = ipsRegistry::instance()->member()->getProperty('_cache'); } else { $member = self::load( $checkAgainst, 'extendedProfile' ); $memberData = $member['_cache']; } /** * Do we have a friends cache array? */ if( !$memberData['friends'] OR !is_array($memberData['friends']) OR !count($memberData['friends']) ) { return false; } /** * If there is, then check it.. */ return in_array( $memberId, array_keys( $memberData['friends'] ) ); } /** * Determine if a member is ignoring another member * * @author Brandon Farber * @param integer Member ID to check for * @param integer Member ID to check against (defaults to current member id) * @param string Type of ignoring to check [messages|topics]. Omit to check any type. * @return boolean Whether the member id to check for is being ignored by the member id to check against * @since IPB 3.0 */ static public function checkIgnoredStatus( $memberId, $checkAgainst=0, $type=false ) { /** * If no member id, obviously not ignored */ if( !$memberId ) { return false; } /** * Get member data */ $memberData = array(); if( !$checkAgainst ) { /** * Ignored users loaded at runtime and stored in an array...loop */ foreach( ipsRegistry::instance()->member()->ignored_users as $ignoredUser ) { /** * We found the user? */ if( $ignoredUser['ignore_ignore_id'] ) { /** * If not specifying a type, then just return */ if( !$type ) { return true; } /** * Otherwise verify we are ignoring that type */ else if( $ignoredUser[ 'ignore_' . $type ] ) { return true; } } } } else { /** * See if checkAgainst is ignoring memberId */ $checkAgainst = intval($checkAgainst); $ignoredUser = ipsRegistry::instance()->member()->DB()->buildAndFetch( array( 'select' => '*', 'from' => 'ignored_users', 'where' => 'ignore_owner_id=' . $checkAgainst . ' AND ignore_ignore_id=' . $memberId ) ); /** * No? */ if( !$ignoredUser['ignore_id'] ) { return false; } /** * He is? */ else { /** * If not specifying a type, then just return */ if( !$type ) { return true; } /** * Otherwise verify we are ignoring that type */ else if( $ignoredUser[ 'ignore_' . $type ] ) { return true; } } } /** * If we're here (which we shouldn't be) just return false */ return false; } /** * Retrieve all IP addresses a user (or multiple users) have used * * @param mixed [Integer] member ID or [Array] array of member ids * @param string Defaults to 'All', otherwise specify which tables to check (comma separated) * @return array Multi-dimensional array of found IP addresses in which sections */ static public function findIPAddresses( $id, $tables_to_check='all' ) { //----------------------------------------- // INIT //----------------------------------------- $ip_addresses = array(); $tables = array( 'admin_logs' => array( 'member_id', 'ip_address', 'ctime' ), 'dnames_change' => array( 'dname_member_id', 'dname_ip_address', 'dname_date' ), 'email_logs' => array( 'from_member_id', 'from_ip_address', 'email_date' ), 'members' => array( 'member_id', 'ip_address', 'joined' ), 'message_posts' => array( 'msg_author_id', 'msg_ip_address', 'msg_date' ), 'moderator_logs' => array( 'member_id', 'ip_address', 'ctime' ), 'posts' => array( 'author_id', 'ip_address', 'post_date' ), 'profile_comments' => array( 'comment_by_member_id', 'comment_ip_address', 'comment_date' ), 'profile_ratings' => array( 'rating_by_member_id', 'rating_ip_address', '' ), 'sessions' => array( 'member_id', 'ip_address', 'running_time' ), 'topic_ratings' => array( 'rating_member_id', 'rating_ip_address', '' ), 'validating' => array( 'member_id', 'ip_address', 'entry_date' ), 'voters' => array( 'member_id', 'ip_address', 'vote_date' ), 'error_logs' => array( 'log_member', 'log_ip_address', 'log_date' ), ); //----------------------------------------- // Check apps // @see http://forums.invisionpower.com/tracker/issue-16966-members-download-manag/ //----------------------------------------- foreach( ipsRegistry::$applications as $appDir => $data ) { if( file_exists( IPSLib::getAppDir( $appDir ) . "/extensions/coreExtensions.php") ) { $classX = IPSLib::loadLibrary( IPSLib::getAppDir( $appDir ) . "/extensions/coreExtensions.php", $appDir . '_findIpAddress', $appDir ); if( class_exists( $classX . '_findIpAddress' ) ) { $ipLookup = new $classX( ipsRegistry::instance() ); if( method_exists( $ipLookup, 'getTables' ) ) { $tables = array_merge( $tables, $ipLookup->getTables() ); } } } } //----------------------------------------- // Sort out thingie //----------------------------------------- if ( is_array( $id ) ) { $id = IPSLib::cleanIntArray( $id ); $mids = ' IN (' . implode( ",", $id ) . ')'; } else { $mids = ' = ' . intval($id); } //----------------------------------------- // Got tables? //----------------------------------------- $_tables = explode( ',', $tables_to_check ); if( !is_array($_tables) OR !count($_tables) ) { return array(); } //----------------------------------------- // Loop through them and grab the IPs //----------------------------------------- foreach( $tables as $tablename => $fields ) { if( $tables_to_check == 'all' OR in_array( $tablename, $_tables ) ) { $extra = ''; if( $fields[2] ) { $extra = ', ' . $fields[2] . ' as date'; } ipsRegistry::DB()->build( array( 'select' => $fields[1] . $extra, 'from' => $tablename, 'where' => $fields[0] . $mids ) ); ipsRegistry::DB()->execute(); while( $r = ipsRegistry::DB()->fetch() ) { if( $r[ $fields[1] ] ) { $r['date'] = $r['date'] > $ip_addresses[ $r[ $fields[1] ] ][1] ? $r['date'] : ( $ip_addresses[ $r[ $fields[1] ] ][1] ? $ip_addresses[ $r[ $fields[1] ] ][1] : 0 ); $ip_addresses[ $r[ $fields[1] ] ] = array( intval($ip_addresses[ $r[ $fields[1] ] ][0]) + 1, $r['date'] ); } } } } //----------------------------------------- // Here are your IPs kind sir. kthxbai //----------------------------------------- return $ip_addresses; } /** * Get / set member's ban info * * @param array Ban info (unit, timespan, date_end, date_start) * @return mixed * @since 2.0 */ static public function processBanEntry( $bline ) { if ( is_array( $bline ) ) { // Set ( 'timespan' 'unit' ) $factor = $bline['unit'] == 'd' ? 86400 : 3600; $date_end = time() + ( $bline['timespan'] * $factor ); return time() . ':' . $date_end . ':' . $bline['timespan'] . ':' . $bline['unit']; } else { $arr = array(); list( $arr['date_start'], $arr['date_end'], $arr['timespan'], $arr['unit'] ) = explode( ":", $bline ); return $arr; } } /** * Unpacks a member's cache. * Left as a function for any other processing * * @param string Serialized cache array * @return array Unpacked array */ static public function unpackMemberCache( $cache_serialized_array="" ) { return unserialize( $cache_serialized_array ); } /** * Packs up member's cache * * Takes an existing array and updates member's DB row * This will overwrite any existing entries by the same * key and create new entries for non-existing rows * * @param integer Member ID * @param array New array * @param array Current Array (optional) * @return boolean */ static public function packMemberCache( $member_id, $new_cache_array, $current_cache_array='' ) { //----------------------------------------- // INIT //----------------------------------------- $member_id = intval( $member_id ); //----------------------------------------- // Got a member ID? //----------------------------------------- if ( ! $member_id ) { return FALSE; } //----------------------------------------- // Got anything to update? //----------------------------------------- if ( ! is_array( $new_cache_array ) ) { return FALSE; } //----------------------------------------- // Got a current cache? //----------------------------------------- if ( ! is_array( $current_cache_array ) ) { $member = ipsRegistry::DB()->buildAndFetch( array( 'select' => "members_cache", 'from' => 'members', 'where' => 'member_id='.$member_id ) ); $member['members_cache'] = $member['members_cache'] ? $member['members_cache'] : array(); $current_cache_array = @unserialize( $member['members_cache'] ); } //----------------------------------------- // Overwrite... //----------------------------------------- foreach( $new_cache_array as $k => $v ) { $current_cache_array[ $k ] = $v; } //----------------------------------------- // Update... //----------------------------------------- ipsRegistry::DB()->update( 'members', array( 'members_cache' => serialize( $current_cache_array ) ), 'member_id='.$member_id ); //----------------------------------------- // Set member array right... //----------------------------------------- if ( self::$data['member_id'] == $member_id ) { self::$data['_cache'] = $current_cache_array; self::$data['members_cache'] = serialize( $current_cache_array ); } } /** * Check forum permissions * * @param string Permission type * @param int Forum ID to check against * @return boolean * @since 2.0 */ static public function checkPermissions( $perm="", $forumID=0 ) { /* Bit of a hack here, ugly */ if ( ipsRegistry::isClassLoaded('class_forums') !== TRUE ) { $classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( 'forums' ) . "/sources/classes/forums/class_forums.php", 'class_forums' ); ipsRegistry::setClass( 'class_forums', new $classToLoad( ipsRegistry::instance() ) ); ipsRegistry::getClass('class_forums')->strip_invisible = 1; ipsRegistry::getClass('class_forums')->forumsInit(); } return ipsRegistry::getClass( 'permissions' )->check( $perm, ipsRegistry::getClass('class_forums')->forum_by_id[ $forumID ] ); } /** * Check forum permissions * * @param string Comma delim. of group IDs (2,4,5,6) * @return string Comma delim of PERM MASK ids * @since 2.1.1 * @deprecated Will be removed in a future version */ static public function createPermsFromGroup( $in_group_ids ) { $out = ""; $cache = ipsRegistry::cache()->getCache('group_cache'); if ( $in_group_ids == '*' ) { foreach( $cache as $data ) { if ( ! $data['g_id'] ) { continue; } //----------------------------------------- // Got a perm mask? //----------------------------------------- if ( $data['g_perm_id'] ) { $out .= ',' . $data['g_perm_id']; } } } else if ( $in_group_ids ) { $groups_id = explode( ',', $in_group_ids ); if ( count( $groups_id ) ) { foreach( $groups_id as $pid ) { if ( ! $cache[ $pid ]['g_id'] ) { continue; } //----------------------------------------- // Got a perm mask? //----------------------------------------- if ( $cache[ $pid ]['g_perm_id'] ) { $out .= ',' . $cache[ $pid ]['g_perm_id']; } } } } //----------------------------------------- // Tidy perms_id //----------------------------------------- $out = IPSText::cleanPermString( $out ); return $out; } /** * Set up defaults for a guest user * * @param string Guest name * @return array Guest record * @since 2.0 */ static public function setUpGuest( $name="" ) { $cache = ipsRegistry::cache()->getCache('group_cache'); $name = $name ? $name : ( ipsRegistry::isClassLoaded( 'class_localization' ) ? ipsRegistry::getClass('class_localization')->words['global_guestname'] : 'Guest' ); $array = array( 'name' => $name, 'members_display_name' => $name, '_members_display_name' => $name, 'members_seo_name' => IPSText::makeSeoTitle( $name ), 'member_id' => 0, 'password' => '', 'email' => '', 'title' => '', 'posts' => 0, 'member_group_id' => ipsRegistry::$settings['guest_group'], 'view_sigs' => ipsRegistry::$settings['guests_sig'], 'view_img' => ipsRegistry::$settings['guests_img'], 'view_avs' => ipsRegistry::$settings['guests_ava'], 'member_forum_markers' => array(), 'avatar' => '', 'member_posts' => '', 'g_title' => $cache[ ipsRegistry::$settings['guest_group'] ]['g_title'], 'member_rank_img' => '', 'member_joined' => '', 'member_number' => '', 'members_auto_dst' => 0, 'has_blog' => 0, 'has_gallery' => 0, 'is_mod' => 0, 'last_visit' => time(), 'login_anonymous' => '', 'mgroup_others' => '', 'org_perm_id' => '', '_cache' => array( 'qr_open' => 0 ), 'auto_track' => 0, 'ignored_users' => NULL, 'members_editor_choice' => 'std', '_cache' => array( 'friends' => array() ), '_group_formatted' => IPSLib::makeNameFormatted( $cache[ ipsRegistry::$settings['guest_group'] ]['g_title'], ipsRegistry::$settings['guest_group'] ), ); /* Add in the group image, if we have one */ $member['member_rank_img'] = ''; $member['member_rank_img_i'] = ''; if ( $cache[ $array['member_group_id'] ]['g_icon'] ) { $_img = $cache[ $array['member_group_id'] ]['g_icon']; if ( substr( $_img, 0, 4 ) != 'http' ) { $_img = ipsRegistry::$settings['_original_base_url'] . '/' . ltrim( $_img, '/' ); } $array['member_rank_img_i'] = 'img'; $array['member_rank_img'] = $_img; } return is_array( $cache[ ipsRegistry::$settings['guest_group'] ] ) ? array_merge( $array, $cache[ ipsRegistry::$settings['guest_group'] ] ) : $array; } /** * Parse a member's profile photo * * @param mixed Either array of member data, or member ID to self load * @return array Member's photo details */ static public function buildProfilePhoto( $member ) { //----------------------------------------- // Load the member? //----------------------------------------- if ( ! is_array( $member ) AND ( $member == intval( $member ) ) AND $member > 0 ) { $member = self::load( $member, 'extendedProfile' ); } else if ( $member == 0 ) { $member = array(); } //----------------------------------------- // Facebook? //----------------------------------------- if ( ! empty( $member['fb_photo'] ) AND ipsRegistry::member()->getProperty('g_mem_info') ) { $member['_has_photo'] = 1; /* Main... */ $member['pp_main_photo'] = $member['fb_photo']; $member['pp_main_width'] = '*'; $member['pp_main_height'] = '*'; /* Thumb */ $member['pp_thumb_photo'] = $member['fb_photo_thumb']; $member['pp_thumb_width'] = 50; $member['pp_thumb_height'] = 50; /* Mini */ $member['pp_mini_photo'] = $member['fb_photo_thumb']; $member['pp_mini_width'] = 25; $member['pp_mini_height'] = 25; } else if ( ! empty( $member['tc_photo'] ) AND ipsRegistry::member()->getProperty('g_mem_info') ) { $member['_has_photo'] = 1; /* Main... */ $member['pp_main_photo'] = $member['tc_photo']; $member['pp_main_width'] = '*'; $member['pp_main_height'] = '*'; /* Thumb */ $member['pp_thumb_photo'] = $member['tc_photo']; $member['pp_thumb_width'] = 50; $member['pp_thumb_height'] = 50; /* Mini */ $member['pp_mini_photo'] = $member['tc_photo']; $member['pp_mini_width'] = 25; $member['pp_mini_height'] = 25; } else { //----------------------------------------- // Main photo //----------------------------------------- if ( empty( $member['pp_main_photo'] ) OR ! ipsRegistry::member()->getProperty('g_mem_info') ) { $member['pp_main_photo'] = ipsRegistry::$settings['img_url'] . '/profile/default_large.png'; $member['pp_main_width'] = 150; $member['pp_main_height'] = 150; $member['_has_photo'] = 0; } else { $member['pp_main_photo'] = ipsRegistry::$settings['upload_url'] . '/' . $member['pp_main_photo']; $member['_has_photo'] = 1; } //----------------------------------------- // Thumbie //----------------------------------------- if ( empty( $member['pp_thumb_photo'] ) OR $member['pp_thumb_photo'] == 'profile/' ) { if( $member['_has_photo'] ) { $member['pp_thumb_photo'] = $member['pp_main_photo']; } else { $member['pp_thumb_photo'] = ipsRegistry::$settings['img_url'] . '/profile/default_thumb.png'; } $member['pp_thumb_width'] = 50; $member['pp_thumb_height'] = 50; } else { if( $member['_has_photo'] ) { $member['pp_thumb_photo'] = ipsRegistry::$settings['upload_url'] . '/' . $member['pp_thumb_photo']; } else { $member['pp_thumb_photo'] = ipsRegistry::$settings['img_url'] . '/profile/default_thumb.png'; } } //----------------------------------------- // Try not to distort the image //----------------------------------------- if ( !ipsRegistry::member()->getProperty('g_mem_info') ) { $member['pp_thumb_width'] = 50; $member['pp_thumb_height'] = 50; } //----------------------------------------- // Mini //----------------------------------------- $_data = IPSLib::scaleImage( array( 'max_height' => 25, 'max_width' => 25, 'cur_width' => $member['pp_thumb_width'], 'cur_height' => $member['pp_thumb_height'] ) ); $member['pp_mini_photo'] = $member['pp_thumb_photo']; $member['pp_mini_width'] = $_data['img_width']; $member['pp_mini_height'] = $_data['img_height']; } return $member; } /** * Parse a member for display * * @param mixed Either array of member data, or member ID to self load * @param array Array of flags to parse: 'signature', 'customFields', 'avatar', 'warn' * @return array Parsed member data */ static public function buildDisplayData( $member, $_parseFlags=array() ) { $_NOW = IPSDebug::getMemoryDebugFlag(); //----------------------------------------- // Figure out parse flags //----------------------------------------- $parseFlags = array( 'signature' => isset( $_parseFlags['signature'] ) ? $_parseFlags['signature'] : 0, 'customFields' => isset( $_parseFlags['customFields'] ) ? $_parseFlags['customFields'] : 0, 'reputation' => isset( $_parseFlags['reputation'] ) ? $_parseFlags['reputation'] : 1, 'avatar' => isset( $_parseFlags['avatar'] ) ? $_parseFlags['avatar'] : 1, 'warn' => isset( $_parseFlags['warn'] ) ? $_parseFlags['warn'] : 1, 'cfSkinGroup' => isset( $_parseFlags['cfSkinGroup'] ) ? $_parseFlags['cfSkinGroup'] : '', 'cfGetGroupData' => isset( $_parseFlags['cfGetGroupData'] ) ? $_parseFlags['cfGetGroupData'] : '', 'cfLocation' => isset( $_parseFlags['cfLocation'] ) ? $_parseFlags['cfLocation'] : '', 'checkFormat' => isset( $_parseFlags['checkFormat'] ) ? $_parseFlags['checkFormat'] : 0, 'spamStatus' => isset( $_parseFlags['spamStatus'] ) ? $_parseFlags['spamStatus'] : 0 ); if ( isset( $_parseFlags['__all__'] ) ) { foreach( $parseFlags as $k => $v ) { if( in_array( $k, array( 'cfSkinGroup', 'cfGetGroupData' ) ) ) { continue; } $parseFlags[ $k ] = 1; } $parseFlags['spamStatus'] = ( isset( $parseFlags['spamStatus'] ) AND ( $parseFlags['spamStatus'] ) ) ? 1 : 0; } //----------------------------------------- // Load the member? //----------------------------------------- if ( ! is_array( $member ) AND ( $member == intval( $member ) AND $member > 0 ) ) { $member = self::load( $member, 'all' ); } //----------------------------------------- // Caching //----------------------------------------- static $buildMembers = array(); $_key = $member['member_id']; $_arr = serialize( $member ); foreach( $parseFlags as $_flag => $_value ) { $_key .= $_flag . $_value; } $_key = md5($_key.$_arr); if( array_key_exists( $_key, $buildMembers ) ) { IPSDebug::setMemoryDebugFlag( "IPSMember::buildDisplayData: ".$member['member_id']. " - CACHED", $_NOW ); return $buildMembers[ $_key ]; } //----------------------------------------- // Basics //----------------------------------------- if ( ! $member['member_group_id'] ) { $member['member_group_id'] = ipsRegistry::$settings['guest_group']; } /* Unpack bitwise if required */ if ( ! isset( $member['bw_is_spammer'] ) ) { $member = self::buildBitWiseOptions( $member ); } //----------------------------------------- // INIT //----------------------------------------- $rank_cache = ipsRegistry::cache()->getCache( 'ranks' ); $group_cache = ipsRegistry::cache()->getCache( 'group_cache' ); $group_name = IPSLib::makeNameFormatted( $group_cache[ $member['member_group_id'] ]['g_title'], $member['member_group_id'] ); $pips = 0; $topic_id = intval( isset( ipsRegistry::$request[ 't' ] ) ? ipsRegistry::$request[ 't' ] : 0 ); $forum_id = intval( isset( ipsRegistry::$request[ 'f' ] ) ? ipsRegistry::$request[ 'f' ] : 0 ); //----------------------------------------- // SEO Name //----------------------------------------- $member['members_seo_name'] = self::fetchSeoName( $member ); //----------------------------------------- // Avatar //----------------------------------------- if ( $parseFlags['avatar'] ) { $member['avatar'] = self::buildAvatar( $member ); } $member['_group_formatted'] = $group_name; //----------------------------------------- // Ranks //----------------------------------------- if ( is_array( $rank_cache ) AND count( $rank_cache ) ) { foreach( $rank_cache as $k => $v) { if ($member['posts'] >= $v['POSTS']) { if( ! isset( $member['title'] ) || $member['title'] === '' || is_null($member['title']) ) { $member['title'] = $v['TITLE']; } $pips = $v['PIPS']; break; } } } //----------------------------------------- // Group image //----------------------------------------- $member['member_rank_img'] = ''; $member['member_rank_img_i'] = ''; if ( $group_cache[ $member['member_group_id'] ]['g_icon'] ) { $_img = $group_cache[ $member['member_group_id'] ]['g_icon']; if ( substr( $_img, 0, 4 ) != 'http' ) { $_img = ipsRegistry::$settings['_original_base_url'] . '/' . ltrim( $_img, '/' ); } $member['member_rank_img_i'] = 'img'; $member['member_rank_img'] = $_img; } else if ( $pips ) { if ( is_numeric( $pips ) ) { for ($i = 1; $i <= $pips; ++$i) { $member['member_rank_img_i'] = 'pips'; $member['member_rank_img'] .= ipsRegistry::getClass('output')->getReplacement('pip_pip'); } } else { $member['member_rank_img_i'] = 'img'; $member['member_rank_img'] = ipsRegistry::$settings['public_dir'] . 'style_extra/team_icons/' . $pips; } } //----------------------------------------- // Moderator data //----------------------------------------- if( ( $parseFlags['spamStatus'] OR $parseFlags['warn'] ) AND $member['member_id'] ) { /* Possible forums class isn't init at this point */ if ( ! ipsRegistry::isClassLoaded('class_forums' ) ) { try { $viewingMember = IPSMember::setUpModerator( ipsRegistry::member()->fetchMemberData() ); ipsRegistry::member()->setProperty('forumsModeratorData', $viewingMember['forumsModeratorData'] ); } catch( Exception $error ) { IPS_exception_error( $error ); } } $moderator = ipsRegistry::member()->getProperty('forumsModeratorData'); } $forum_id = isset(ipsRegistry::$request['f']) ? intval( ipsRegistry::$request['f'] ) : 0; //----------------------------------------- // Spammer status //----------------------------------------- if ( $parseFlags['spamStatus'] AND $member['member_id'] AND ipsRegistry::member()->getProperty('member_id') ) { /* Defaults */ $member['spamStatus'] = NULL; $member['spamImage'] = NULL; if ( isset( $moderator[ $forum_id ]['bw_flag_spammers'] ) AND ( $moderator[ $forum_id ]['bw_flag_spammers'] ) OR ipsRegistry::member()->getProperty('g_is_supmod') == 1 ) { if ( !ipsRegistry::$settings['warn_on'] OR ( ! strstr( ','.ipsRegistry::$settings['warn_protected'].',', ','.$member['member_group_id'].',' ) ) ) { if ( $member['bw_is_spammer'] ) { $member['spamStatus'] = TRUE; } else { $member['spamStatus'] = FALSE; } } } } //----------------------------------------- // Warny porny? //----------------------------------------- if ( $parseFlags['warn'] AND $member['member_id'] ) { $member['warn_percent'] = NULL; $member['can_edit_warn'] = false; $member['warn_img'] = NULL; if ( ipsRegistry::$settings['warn_on'] and ( ! strstr( ','.ipsRegistry::$settings['warn_protected'].',', ','.$member['member_group_id'].',' ) ) ) { /* Warnings */ if ( ( isset($moderator[ $forum_id ]['allow_warn']) AND $moderator[ $forum_id ]['allow_warn'] ) OR ( ipsRegistry::member()->getProperty('g_is_supmod') == 1 ) OR ( ipsRegistry::$settings['warn_show_own'] and ( ipsRegistry::member()->getProperty('member_id') == $member['member_id'] ) ) ) { // Work out which image to show. if ( $member['warn_level'] <= ipsRegistry::$settings['warn_min'] ) { $member['warn_img'] = '{parse replacement="warn_0"}'; $member['warn_percent'] = 0; } else if ( $member['warn_level'] >= ipsRegistry::$settings['warn_max'] ) { $member['warn_img'] = '{parse replacement="warn_5"}'; $member['warn_percent'] = 100; } else { $member['warn_percent'] = $member['warn_level'] ? sprintf( "%.0f", ( ($member['warn_level'] / ipsRegistry::$settings['warn_max']) * 100) ) : 0; if ( $member['warn_percent'] > 100 ) { $member['warn_percent'] = 100; } if ( $member['warn_percent'] >= 81 ) { $member['warn_img'] = '{parse replacement="warn_5"}'; } else if ( $member['warn_percent'] >= 61 ) { $member['warn_img'] = '{parse replacement="warn_4"}'; } else if ( $member['warn_percent'] >= 41 ) { $member['warn_img'] = '{parse replacement="warn_3"}'; } else if ( $member['warn_percent'] >= 21 ) { $member['warn_img'] = '{parse replacement="warn_2"}'; } else if ( $member['warn_percent'] >= 1 ) { $member['warn_img'] = '{parse replacement="warn_1"}'; } else { $member['warn_img'] = '{parse replacement="warn_0"}'; } } if ( $member['warn_percent'] < 1 ) { $member['warn_percent'] = 0; } /* Bug 14770 - Change so you can't warn yourself */ if ( (( isset($moderator[ $forum_id ]['allow_warn']) AND $moderator[ $forum_id ]['allow_warn'] ) or ipsRegistry::member()->getProperty('g_is_supmod') == 1) AND $member['member_id'] != ipsRegistry::member()->getProperty('member_id') ) { $member['can_edit_warn'] = true; } } } } //----------------------------------------- // Profile fields stuff //----------------------------------------- $member['custom_fields'] = ""; if( $parseFlags['customFields'] == 1 AND $member['member_id'] ) { if ( isset( self::$_parsedCustomFields[ $member['member_id'] ] ) ) { $member['custom_fields'] = self::$_parsedCustomFields[ $member['member_id'] ]; if ( $parseFlags['cfGetGroupData'] AND isset( self::$_parsedCustomGroups[ $member['member_id'] ] ) AND is_array( self::$_parsedCustomGroups[ $member['member_id'] ] ) ) { $member['custom_field_groups'] = self::$_parsedCustomGroups[ $member['member_id'] ]; } else if( $parseFlags['cfGetGroupData'] ) { $member['custom_field_groups'] = self::$custom_fields_class->fetchGroupTitles(); self::$_parsedCustomGroups[ $member['member_id'] ] = $member['custom_field_groups']; } } else { if ( !is_object( self::$custom_fields_class ) ) { $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/customfields/profileFields.php', 'customProfileFields' ); self::$custom_fields_class = new $classToLoad(); } if ( self::$custom_fields_class ) { self::$custom_fields_class->member_data = $member; self::$custom_fields_class->skinGroup = $parseFlags['cfSkinGroup']; self::$custom_fields_class->initData(); self::$custom_fields_class->parseToView( $parseFlags['checkFormat'], $parseFlags['cfLocation'] ); $member['custom_fields'] = self::$custom_fields_class->out_fields; self::$_parsedCustomFields[ $member['member_id'] ] = $member['custom_fields']; if ( $parseFlags['cfGetGroupData'] ) { $member['custom_field_groups'] = self::$custom_fields_class->fetchGroupTitles(); self::$_parsedCustomGroups[ $member['member_id'] ] = $member['custom_field_groups']; } } } } //----------------------------------------- // Profile photo //----------------------------------------- $member = self::buildProfilePhoto( $member ); //----------------------------------------- // Signature bbcode //----------------------------------------- if ( isset( $member['signature'] ) AND $member['signature'] AND $parseFlags['signature'] ) { if( isset(self::$_parsedSignatures[ $member['member_id'] ]) ) { $member['signature'] = self::$_parsedSignatures[ $member['member_id'] ]; } else { if ( $member['cache_content'] ) { $member['signature'] = '' . $member['cache_content']; } else { IPSText::getTextClass('bbcode')->parse_bbcode = ipsRegistry::$settings['sig_allow_ibc']; IPSText::getTextClass('bbcode')->parse_smilies = 0; IPSText::getTextClass('bbcode')->parse_html = ipsRegistry::$settings['sig_allow_html']; IPSText::getTextClass('bbcode')->parse_nl2br = 1; IPSText::getTextClass('bbcode')->parsing_section = 'signatures'; IPSText::getTextClass( 'bbcode' )->parsing_mgroup = $member['member_group_id']; IPSText::getTextClass( 'bbcode' )->parsing_mgroup_others = $member['mgroup_others']; /* Work around */ $_tmp = ipsRegistry::member()->getProperty( 'view_img' ); ipsRegistry::member()->setProperty( 'view_img', 1 ); $member['signature'] = IPSText::getTextClass('bbcode')->preDisplayParse( $member['signature'] ); ipsRegistry::member()->setProperty( 'view_img', $_tmp ); IPSContentCache::update( $member['member_id'], 'sig', $member['signature'] ); } self::$_parsedSignatures[ $member['member_id'] ] = $member['signature']; } } //----------------------------------------- // If current session, reset last_activity //----------------------------------------- if( ! empty( $member['running_time'] ) ) { $member['last_activity'] = $member['running_time'] > $member['last_activity'] ? $member['running_time'] : $member['last_activity']; } //----------------------------------------- // Online? //----------------------------------------- $time_limit = time() - ipsRegistry::$settings['au_cutoff'] * 60; $member['_online'] = 0; list( $be_anon, $loggedin ) = explode( '&', isset($member['login_anonymous']) ? $member['login_anonymous'] : '0&0' ); if( ipsRegistry::$settings['disable_anonymous'] ) { $be_anon = 0; } $bypass_anon = 0; if ( ipsRegistry::member()->getProperty('g_access_cp') AND !ipsRegistry::$settings['disable_admin_anon'] ) { $bypass_anon = 1; } if ( ( $member['last_visit'] > $time_limit OR $member['last_activity'] > $time_limit ) AND ( $be_anon != 1 OR $bypass_anon == 1 ) AND $loggedin == 1 ) { $member['_online'] = 1; } //----------------------------------------- // Last Active //----------------------------------------- $member['_last_active'] = ipsRegistry::getClass('class_localization')->getDate( $member['last_activity'], 'SHORT' ); if( $be_anon == 1 ) { // Member last logged in anonymous if( !ipsRegistry::member()->getProperty('g_access_cp') OR ipsRegistry::$settings['disable_admin_anon'] ) { $member['_last_active'] = ipsRegistry::getClass('class_localization')->words['private']; } } //----------------------------------------- // Rating //----------------------------------------- $member['_pp_rating_real'] = intval( $member['pp_rating_real'] ); //----------------------------------------- // Long display names //----------------------------------------- $member['members_display_name_short'] = IPSText::truncate( $member['members_display_name'], 16 ); //----------------------------------------- // Reputation //----------------------------------------- $member['pp_reputation_points'] = $member['pp_reputation_points'] ? $member['pp_reputation_points'] : 0; if( $parseFlags['reputation'] AND $member['member_id'] ) { if( ! ipsRegistry::isClassLoaded( 'repCache' ) ) { $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/class_reputation_cache.php', 'classReputationCache' ); ipsRegistry::setClass( 'repCache', new $classToLoad() ); } $member['author_reputation'] = ipsRegistry::getClass( 'repCache' )->getReputation( $member['pp_reputation_points'] ); } //----------------------------------------- // Other stuff not worthy of individual comments //----------------------------------------- $member['members_profile_views'] = isset($member['members_profile_views']) ? $member['members_profile_views'] : 0; /* BG customization */ if ( $member['pp_customization'] AND ( isset($member['gbw_allow_customization']) AND $member['gbw_allow_customization'] ) AND ! $member['bw_disable_customization'] ) { $member['customization'] = unserialize( $member['pp_customization'] ); if ( is_array( $member['customization'] ) ) { /* Figure out BG URL */ if ( $member['customization']['type'] == 'url' AND $member['customization']['bg_url'] AND $member['gbw_allow_url_bgimage'] ) { $member['customization']['_bgUrl'] = $member['customization']['bg_url']; } else if ( $member['customization']['type'] == 'upload' AND $member['customization']['bg_url'] AND $member['gbw_allow_upload_bgimage'] ) { $member['customization']['_bgUrl'] = ipsRegistry::$settings['upload_url'] . '/' . $member['customization']['bg_url']; } else if ( $member['customization']['bg_color'] ) { $member['customization']['type'] = 'bgColor'; } } } IPSDebug::setMemoryDebugFlag( "IPSMember::buildDisplayData: ".$member['member_id']. " - Completed", $_NOW ); $buildMembers[ $_key ] = $member; return $member; } /** * Build member's bitwise field * * @param mixed Either an array of member data or a member ID * @return array */ static public function buildBitWiseOptions( $member ) { //----------------------------------------- // Load the member? //----------------------------------------- if ( ! is_array( $member ) AND ( $member == intval( $member ) ) ) { $member = self::load( $member, 'core,extendedProfile' ); } /* Unpack bitwise fields */ $_tmp = IPSBWOptions::thaw( isset($member['members_bitoptions']) ? $member['members_bitoptions'] : 0, 'members', 'global' ); if ( count( $_tmp ) ) { foreach( $_tmp as $k => $v ) { /* Trigger notice if we have DB field */ if ( isset( $member[ $k ] ) ) { trigger_error( "Thawing bitwise options for MEMBERS: Bitwise field '$k' has overwritten DB field '$k'", E_USER_WARNING ); } $member[ $k ] = $v; } } return $member; } /** * Returns user's avatar * * @param mixed Either an array of member data or a member ID * @param bool Whether to avoid caching * @param bool Whether to show avatar even if view_avs is off for member * @return string HTML * @since 2.0 */ static public function buildAvatar( $member, $no_cache=0, $overRide=0 ) { //----------------------------------------- // No avatar? //----------------------------------------- if ( ! $overRide AND ipsRegistry::member()->getProperty('view_avs') == 0 ) { return ""; } if( ! ipsRegistry::$settings['avatars_on'] ) { return ""; } //----------------------------------------- // Load the member? //----------------------------------------- if ( ! is_array( $member ) AND ( $member == intval( $member ) ) ) { $member = self::load( $member, 'core,extendedProfile' ); } //----------------------------------------- // Defaults... //----------------------------------------- $davatar_dims = explode( "x", strtolower(ipsRegistry::$settings['avatar_dims']) ); $default_a_dims = explode( "x", strtolower(ipsRegistry::$settings['avatar_def']) ); $this_dims = explode( "x", strtolower($member['avatar_size']) ); if (!isset($this_dims[0])) $this_dims[0] = $davatar_dims[0]; if (!isset($this_dims[1])) $this_dims[1] = $davatar_dims[1]; if (!$this_dims[0]) $this_dims[0] = $davatar_dims[0]; if (!$this_dims[1]) $this_dims[1] = $davatar_dims[1]; /* Gravatar defaults to 80px, so only set size if max dims are lower than 80 to prevent upscaling */ $lowestSize = $davatar_dims[0] < $davatar_dims[1] ? $davatar_dims[0] : $davatar_dims[1]; if( $lowestSize >= 80 ) { $lowestSize = ''; } //----------------------------------------- // Legacy: noavatar //----------------------------------------- if( $member['avatar_location'] == 'noavatar' ) { $member['avatar_location'] = ''; } //----------------------------------------- // LEGACY: Determine type //----------------------------------------- if ( ! $member['avatar_type'] ) { if ( preg_match( '/^http:\/\//', $member['avatar_location'] ) ) { $member['avatar_type'] = 'url'; } else if ( strstr( $member['avatar_location'], "upload:" ) or ( strstr( $member['avatar_location'], 'av-' ) ) ) { $member['avatar_type'] = 'upload'; $member['avatar_location'] = str_replace( 'upload:', '', $member['avatar_location'] ); } else { $member['avatar_type'] = 'local'; } } //----------------------------------------- // No cache? //----------------------------------------- if ( $no_cache && $member['avatar_type'] != 'gravatar' ) { $member['avatar_location'] .= '?_time=' . time(); } //----------------------------------------- // URL avatar? //----------------------------------------- if ( $member['avatar_type'] == 'url' ) { //----------------------------------------- // Hide if in ACP for security (CSRF/XSS) //----------------------------------------- if( IN_ACP ) { return "
" . ipsRegistry::getClass('class_localization')->words['m_remoteavatar_link'] . ""; } if ( substr( $member['avatar_location'], -4 ) == ".swf" ) { if( ipsRegistry::$settings['disable_flash'] ) { return ''; } return " "; } else { return ""; } } /* Gravatar */ else if( $member['avatar_type'] == 'gravatar' && ipsRegistry::$settings['allow_gravatars'] ) { $av_hash = md5( $member['avatar_location'] ); $s = $lowestSize ? "s={$lowestSize}" : ''; return ""; } /* Facebook or twitter */ else if( $member['avatar_type'] == 'facebook' OR $member['avatar_type'] == 'twitter' OR $member['avatar_type'] == 'vk' ) { return ""; } //----------------------------------------- // Not a URL? Is it an uploaded avatar? //----------------------------------------- else if ( (ipsRegistry::$settings['avup_size_max'] > 1) and ( $member['avatar_type'] == 'upload' ) ) { $member['avatar_location'] = str_replace( 'upload:', '', $member['avatar_location'] ); if ( substr( $member['avatar_location'], -4 ) == ".swf" ) { if( ipsRegistry::$settings['disable_flash'] ) { return ''; } return " "; } else { $url = ipsRegistry::$settings['upload_url'] . "/{$member['avatar_location']}"; return ""; } } //----------------------------------------- // No, it's not a URL or an upload, must // be a normal avatar then //----------------------------------------- else if ($member['avatar_location'] != "") { //----------------------------------------- // Do we have an avatar still ? //----------------------------------------- $url = ipsRegistry::$settings['avatars_url'] . "/{$member['avatar_location']}"; return ""; } else if( ipsRegistry::$settings['allow_gravatars'] ) { /* Try a gravatar, if all else fails */ $av_hash = md5( $member['email'] ); $s = $lowestSize ? "&s={$lowestSize}" : ''; $blank_av = urlencode(ipsRegistry::$settings['avatars_url'] . '/blank_avatar.gif' ); return ""; } } /** * Checks for a DB row that matches $email * * @param string Email address * @return boolean Record exists */ static public function checkByEmail( $email ) { $test = self::load( $email, '' ); if ( $test['member_id'] ) { return true; } else { return false; } } /** * Updates member's DB row password * * @param string Key: either member_id or email * @param string MD5-once hash of new password * @return boolean Update successful */ static public function updatePassword( $member_key, $new_md5_pass ) { if ( ! $member_key or ! $new_md5_pass ) { return false; } /* Load member */ $member = self::load( $member_key ); $new_pass = md5( md5( $member['members_pass_salt'] ) . $new_md5_pass ); self::save( $member_key, array( 'core' => array( 'members_pass_hash' => $new_pass ) ) ); return true; } /** * Check supplied password with database * * @param string Key: either member_id or email * @param string MD5 of entered password * @return boolean Password is correct */ static public function authenticateMember( $member_key, $md5_once_password ) { /* Load member */ $member = self::load( $member_key ); if ( ! $member['member_id'] ) { return FALSE; } if ( $member['members_pass_hash'] == self::generateCompiledPasshash( $member['members_pass_salt'], $md5_once_password ) ) { return true; } else { return false; } } /** * Generates a compiled passhash. * Returns a new MD5 hash of the supplied salt and MD5 hash of the password * * @param string User's salt (5 random chars) * @param string User's MD5 hash of their password * @return string MD5 hash of compiled salted password */ static public function generateCompiledPasshash( $salt, $md5_once_password ) { return md5( md5( $salt ) . $md5_once_password ); } /** * Generates a password salt. * Returns n length string of any char except backslash * * @param integer Length of desired salt, 5 by default * @return string n character random string */ static public function generatePasswordSalt($len=5) { $salt = ''; for ( $i = 0; $i < $len; $i++ ) { $num = mt_rand(33, 126); if ( $num == '92' ) { $num = 93; } $salt .= chr( $num ); } return $salt; } /** * Generates a log in key * * @param integer Length of desired random chars to MD5 * @return string MD5 hash of random characters */ static public function generateAutoLoginKey( $len=60 ) { $pass = self::generatePasswordSalt( $len ); return md5($pass); } /** * Check to see if a member is in a group or not * * @param mixed Either INT (member_id) OR Array of member data [MUST at least include member_group_id and mgroup_others] * @param mixed Either INT (group ID) or array of group IDs * @param boolean TRUE (default, check secondary groups also), FALSE (check primary only) * @return boolean TRUE (is in group) - FALSE (not in group) */ static public function isInGroup( $member, $group, $checkSecondary=true ) { $memberData = ( is_array( $member ) ) ? $member : self::load( $member, 'core' ); $group = ( is_array( $group ) ) ? $group : array( $group ); $others = explode( ',', $memberData['mgroup_others'] ); if ( ! $memberData['member_group_id'] OR ! count( $group ) ) { return FALSE; } /* Loop */ foreach( $group as $gid ) { if ( $gid == $memberData['member_group_id'] ) { return true; } if ( $checkSecondary AND is_array( $others ) AND count( $others ) ) { if ( in_array( $gid, $others ) ) { return true; } } } return false; } /** * Check to see if a member is banned (or not) * * @param string Type of ban check (ip/ipAddress, name, email) * @param string String to check * @return boolean TRUE (banned) - FALSE (not banned) */ static public function isBanned( $type, $string ) { /* Try and be helpful */ switch ( strtolower( $type ) ) { case 'ip': $type = 'ipAddress'; break; case 'emailaddress': $type = 'email'; break; case 'username': case 'displayname': $type = 'name'; break; } if ( $type == 'ipAddress' ) { $banCache = ipsRegistry::cache()->getCache('banfilters'); } else { if ( ! is_array( self::$_banFiltersCache ) ) { self::$_banFiltersCache = array(); /* Load Ban Filters */ ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'banfilters' ) ); ipsRegistry::DB()->execute(); while( $r = ipsRegistry::DB()->fetch() ) { self::$_banFiltersCache[ $r['ban_type'] ][] = $r['ban_content']; } } $banCache = self::$_banFiltersCache[ $type ]; } if ( is_array( $banCache ) and count( $banCache ) ) { foreach( $banCache as $entry ) { $ip = str_replace( '\*', '.*', preg_quote( trim($entry), "/") ); if ( $ip AND preg_match( "/^$ip$/", $string ) ) { return TRUE; } } } return FALSE; } /** * Check to see if a member is ignorable or not * * @param int Member's primary group ID * @param string Comma delisted list of 'other' member groups * @param string Type: 'post' or 'pm' * @return boolean True (member is ignorable) or False (member can not be ignored) */ static public function isIgnorable( $member_group_id, $mgroup_others, $type='post' ) { if( ! isset( ipsRegistry::$settings['_unblockableArray'] ) OR ! is_array( ipsRegistry::$settings['_unblockableArray'] ) ) { ipsRegistry::$settings['_unblockableArray'] = ipsRegistry::$settings['cannot_ignore_groups'] ? explode( ",", IPSText::cleanPermString( ipsRegistry::$settings['cannot_ignore_groups'] ) ) : array(); } $myGroups = array( $member_group_id ); if ( $mgroup_others ) { $myGroups = array_merge( $myGroups, explode( ",", IPSText::cleanPermString( $mgroup_others ) ) ); } /* Check PMs first */ if ( $type == 'pm' ) { $unblockable = explode( ",", ipsRegistry::$settings['unblockable_pm_groups'] ); /* Override with groups */ if ( is_array( $unblockable ) AND count( $unblockable ) ) { if ( in_array( $member_group_id, $unblockable ) ) { return FALSE; } } } foreach( $myGroups as $member_group ) { if ( in_array( $member_group, ipsRegistry::$settings['_unblockableArray'] ) ) { return FALSE; } } return TRUE; } /** * Easy peasy way to grab a function from member/memberFunctions.php * without having to bother setting it up each time. * * @return object memberFunctions object * @author MattMecham */ static public function getFunction() { if ( ! is_object( self::$_memberFunctions ) ) { $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/member/memberFunctions.php', 'memberFunctions' ); self::$_memberFunctions = new $classToLoad( ipsRegistry::instance() ); } return self::$_memberFunctions; } /** * Set the cache to ignore * Works for one LOAD only! It's reset again for the next load * * @return void */ static public function ignoreCache() { self::$ignoreCache = TRUE; } /** * Adds a member to the cache * * @param array Member Data * @param array Tables queried * @return void */ static private function _addToCache( $memberData, $tables ) { if ( ! $memberData['member_id'] OR ! is_array( $tables ) ) { return FALSE; } $_tables = self::__buildTableHash( $tables ); self::$memberCache[ $memberData['member_id'] ][ $_tables ] = $memberData; //print round( memory_get_usage() / 1048576 * 100 ) / 100 . ' mb'; self::$debugData[] = "ADDED: Member ID: " . $memberData['member_id'] . " with tables " . implode( ",", $tables ). ' key ('.$_tables.')'; } /** * Removes a member from the cache * * @param int Member ID * @return void */ static private function _removeFromCache( $memberID ) { if ( is_array( self::$memberCache[ $memberID ] ) ) { unset( self::$memberCache[ $memberID ] ); self::$debugData[] = "REMOVED: Member ID: " . $memberID; } } /** * Removes a member from the cache * * @param int Member ID to look for * @param array Tables required * @return mixed Array of data if a match is found, or FALSE if not. */ static private function _fetchFromCache( $memberID, $tables ) { if ( self::$ignoreCache === TRUE ) { return FALSE; } if ( ! $memberID OR ! is_array( $tables ) ) { return FALSE; } $_tables = self::__buildTableHash( $tables ); if ( isset( self::$memberCache[ $memberID ][ $_tables ] ) && is_array( self::$memberCache[ $memberID ][ $_tables ] ) ) { self::$debugData[] = "FETCHED: Member ID: " . $memberID. ' key ('.$_tables.')'; return self::$memberCache[ $memberID ][ $_tables ]; } else { return FALSE; } } /** * Updates a member from the cache * * @param int Member ID to update * @param array Array of data to update(eg: array( 'core' => 'member_login_key' => 'xxxxx' ) ) * @return mixed Array of data if a match is found, or FALSE if not. */ static private function _updateCache( $memberID, $data ) { if ( ! $memberID OR ! is_array( $data ) ) { return FALSE; } if ( is_array( self::$memberCache[ $memberID ] ) ) { foreach( self::$memberCache[ $memberID ] as $tableData => $memberData ) { foreach( $data as $table => $newData ) { foreach( $newData as $k => $v ) { self::$memberCache[ $memberID ][ $tableData ][ $k ] = $v; } } } self::$debugData[] = "Updated: Member ID: " . $memberID; return TRUE; } else { return FALSE; } } /** * Build table key. * Takes an array of tables and returns an MD5 comparison hash * * @param array Array of tables * @return string MD5 hash */ static private function __buildTableHash( $tables ) { sort( $tables ); return md5( implode( ',', $tables ) ); } /** * Sends a query to the IPS Spam Service * * @param string $email Email address to check/report * @param string [$ip] IP Address to check report, ipsRegistry::member()->ip_address will be used if the address is not specified * @param string [$type] Either register or markspam, register is default * @return string */ }