Вход Регистрация
Файл: IPBMafia.ru_IPB_3.4.6_Final_Rus _Nulled/board/upload/admin/sources/classes/like/composite.php
Строк: 1613
<?php
/**
 * @file        composite.php     Provides classes to manage the like system
 *~TERABYTE_DOC_READY~
 * $Copyright: (c) 2001 - 2011 Invision Power Services, Inc.$
 * $License: http://www.invisionpower.com/company/standards.php#license$
 * $Author: mmecham $
 * @since        11th October 2010
 * $LastChangedDate: 2013-08-14 06:45:49 -0400 (Wed, 14 Aug 2013) $
 * @version        v3.4.6
 * $Revision: 12334 $
 * 
 * <b>Example Usage:</b>
 * @code
 * $app  = 'gallery';
 * $area = 'images';
 * $like = classes_like::bootstrap( $app, $area );
 * print $like->isLiked( $relId );
 * $html    = $like->render( 'summary', $relId );
 * print $html;
 * 
 * RESULT: Matt, Joe, Bob and 5 others like this
 * @endcode
 */

if ( ! defined'IN_IPB' ) )
{
    print 
"<h1>Incorrect access</h1>You cannot access this file directly. If you have recently upgraded, make sure you upgraded all the relevant files.";
    exit();
}

/**
 *
 * @class        classes_like_registry
 * @brief        Quick registry class for common methods
 * @author        Matt
 */
class classes_like_registry
{
    
/**
     * App key
     *
     * @var        $app
     */
    
static protected $app    null;
    
    
/**
     * Area key
     *
     * @var        $area
     */
    
static protected $area    null;
    
    
/**
     * Total count
     *
     * @var        $count
     */
    
static protected $count    null;
    
    
/**
     * Cached bootstrap loaders
     *
     * @var        $bootstraps
     */
    
static protected $bootstraps = array();
    
    
/**
     * Get application key
     * 
     * @return    @e string
     */
    
static public function getApp()
    {
        return 
self::$app;
    }
    
    
/**
     * Set application key
     * 
     * @param    string        $app        Application key
     * @return    @e void
     */
    
static public function setApp$app )
    {
        
self::$app $app;
    }
    
    
/**
     * Get area key
     * 
     * @return    @e string
     */
    
static public function getArea()
    {
        return 
self::$area;
    }
    
    
/**
     * Set area key
     * 
     * @param    string        $area        Area key
     * @return    @e void
     */
    
static public function setArea$area )
    {
        
self::$area $area;
    }
    
    
/**
     * Get total count (saves on queries)
     * 
     * @return    @e integer
     */
    
static public function getTotalCount()
    {
        return 
self::$count;
    }
    
    
/**
      * Set total count (saves on queries)
     * 
     * @param    integer        $count        Likes count
     * @return    @e void
     */
    
static public function setTotalCount$count )
    {
        
self::$count $count;
    }
    
    
/**
     * Build key, either for cache table or full table
     *
     * @param    integer        $relId        Relationship ID
     * @param    integer        $memberId    Member ID
     * @return    @e binary    Binary key
     */
    
static public function getKey$relId$memberId=null )
    {
        
/* Check */
        
if ( empty( $relId ) AND empty( $memberId ) )
        {        
            
trigger_error"Relationship ID missing in " __CLASS__ '::' __FUNCTION__E_USER_WARNING );
        }
        
        if ( 
$memberId === null )
        {
            
/* For relative item only */
            
return md5self::$app ';' self::$area ';' $relId );
        }
        else if ( ! empty( 
$memberId ) )
        {
            if ( 
$relId === null )
            {
                
/* For member speciifc relative item */
                
return md5self::$app ';' self::$area ';' $memberId );
            }
            else
            {
                
/* For member speciifc relative item */
                
return md5self::$app ';' self::$area ';' $relId ';' $memberId );
            }
        }
        else
        {
            
trigger_error"Member ID missing in " __CLASS__ '::' __FUNCTION__E_USER_WARNING );
        }
    }
    
    
/**
     * Build key for SQL query
     *
     *
     * @param    integer        $relId            Relationship ID
     * @param    string        $memberField    Member Field or value
     * @return    @e binary    Binary key
     */
    
static public function getKeyForSql$relId$memberField=NULL )
    {        
        
/* Check */
        
if ( empty( $relId ) AND empty( $memberField ) )
        {        
            
//var_dump(IPSDebug::prettifyBackTrace(debug_backtrace()));exit;
            
trigger_error"Relationship ID missing in " __CLASS__ '::' __FUNCTION__E_USER_WARNING );
        }
        
        if ( 
$memberField === null )
        {
            
/* For relative item only */
            
return ipsRegistry::DB()->buildMd5StatementipsRegistry::DB()->buildConcat( array(
                array( 
self::$app'string' ),
                array( 
';''string' ),
                array( 
self::$area'string' ),
                array( 
';''string' ),
                array( 
$relId'string' ),
                ) ) );
        }
        else if ( ! empty( 
$memberField ) )
        {
            if ( 
$relId === null )
            {
                
/* For member speciifc relative item */
                
return ipsRegistry::DB()->buildMd5StatementipsRegistry::DB()->buildConcat( array(
                    array( 
self::$app'string' ),
                    array( 
';''string' ),
                    array( 
self::$area'string' ),
                    array( 
';''string' ),
                    array( 
$memberField ),
                    ) ) );
            }
            else
            {
                
/* For member speciifc relative item */
                
return ipsRegistry::DB()->buildMd5StatementipsRegistry::DB()->buildConcat( array(
                            array( 
self::$app'string' ),
                            array( 
';''string' ),
                            array( 
self::$area'string' ),
                            array( 
';''string' ),
                            array( 
$relId'string' ),
                            array( 
';''string' ),
                            array( 
$memberField ),
                            ) ) );
            }
        }
        else
        {
            
trigger_error"Member ID missing in " __CLASS__ '::' __FUNCTION__E_USER_WARNING );
        }
    }
}


/**
 *
 * @class        classes_like_meta
 * @brief        Simple static class for fetching meta data
 * @author        Matt
 */
class classes_like_meta
{
    
/**
     * Retrieve the content title
     *
     * @param    array        $like        Like data
     * @return    @e string    Title of content liked
     */
    
static public function getTitleFromId$like )
    {
        if ( empty( 
$like['like_app'] ) OR empty( $like['like_area'] ) OR empty( $like['like_rel_id'] ) )
        {
            
trigger_error"You must have like_app, like_rel_id and like_area set"E_USER_WARNING );
        }
        
        
$_bootstrap    classes_like::bootstrap$like['like_app'], $like['like_area'] );
        
        return 
$_bootstrap->getTitleFromId$like['like_rel_id'] );
    }

    
/**
     * Retrieve the content URL
     *
     * @param    array        $like        Like data
     * @return    @e string    URL of content liked
     */
    
static public function getUrlFromId$like )
    {
        if ( empty( 
$like['like_app'] ) OR empty( $like['like_area'] ) OR empty( $like['like_rel_id'] ) )
        {
            
trigger_error"You must have like_app, like_rel_id and like_area set"E_USER_WARNING );
        }
        
        
$_bootstrap    classes_like::bootstrap$like['like_app'], $like['like_area'] );
        
        return 
$_bootstrap->getUrlFromId$like['like_rel_id'] );
    }
    
    
/**
     * Fetch all meta data from various like rows
     * 
     * @param    array        $likes        Like rows
     * @return    @e array
     */
    
static public function get( array $likes )
    {
        
$apps    = array();
        
$process = array();
        
$lookUp  = array();
        
        foreach( 
$likes as $id => $like )
        {
            if ( empty( 
$like['like_app'] ) OR empty( $like['like_area'] ) OR empty( $like['like_rel_id'] ) )
            {
                continue;
            }
            
            
$apps$like['like_app'] . '-' $like['like_area'] ][ $id ] = $like;
            
$lookUp$like['like_app'] . '-' $like['like_area'] . '-' $like['like_rel_id'] ] = $like['like_id'];
        }
        
        
/* Process based on apps */
        
foreach( $apps as $app => $data )
        {
            list( 
$_app$_area ) = explode'-'$app );
            
            
$_bootstrap    classes_like::bootstrap$_app$_area );
            
            
$relIds = array();
            
            foreach( 
$data as $_k => $_v )
            {
                
$relIds[] = $_v['like_rel_id'];
            }
        
            
/* Bulk fetch */
            
$process$app ] = $_bootstrap->getMeta$relIds );
        }
        
        
/* Fold up array */
        
foreach( $process as $app => $data )
        {
            list( 
$_app$_area ) = explode'-'$app );
            
            foreach( 
$data as $relId => $_like )
            {
                if ( 
$relId )
                {
                    
$like_id $lookUp$_app '-' $_area '-' $relId ];
                    
                    if ( 
$like_id AND is_array$_like ) AND is_array$likes$like_id ] ) )
                    {
                        
$likes$like_id ] = array_merge$likes$like_id ], $_like );
                    }
                }
            }
        }
        
        return 
$likes;
    }
}


/**
 *
 * @class        classes_like
 * @brief        Factory class, loads composite and child class
 * @author        Matt
 */
class classes_like
{
    
/**
     * Applications objects
     *
     * @var $apps
     */
    
static protected $apps;
    
    
/**
     * Bootstrap function to load the required file/class and setup the initial variables
     *
     * @param    string        $app        Application key
     * @param    string        $area        Area
     * @return    @e object    Like object for the loaded key (app.area)
     */
    
static public function bootstrap$app=null$area=null )
    {
        if ( 
$app === null OR $area === null )
        {
            
trigger_error"App or area missing from classes_like"E_USER_WARNING );
        }
        
        
/* Pointless comment! */
        
if( $area != 'default' )
        {
            
$_file    IPSLib::getAppDir$app ) . '/extensions/like/' $area '.php';
            
$_class    'like_' $app '_' $area '_composite';
        }

        
$_key    md5$app $area );
        
        
/* Get from cache if already cached */
        
if( isset( self::$apps$_key ] ) )
        {
            return 
self::$apps$_key ];
        }
        
        
/* Otherwise create object and cache */
        
if ( ! is_file$_file ) )
        {
            
$_file IPSLib::getAppDir$app ) . '/extensions/like/default.php';
            
$_class 'like_' $app '_composite';
            
            if ( ! 
is_file$_file ) )
            {
                throw new 
Exception"No like class available for $app - $area);
            }
        }
                
        
$classToLoad IPSLib::loadLibrary$_file$_class$app );
        
        if ( 
class_exists$classToLoad ) )
        {
            
classes_like_registry::setApp$app );
            
classes_like_registry::setArea$area );
            
            
self::$apps$_key ] = new $classToLoad();
            
self::$apps$_key ]->init();
        }
        else
        {
            throw new 
Exception"No like class available for $app - $area);
        }
        
        return 
self::$apps$_key ];
    }
}


/**
 *
 * @class        classes_like_composite
 * @brief        Composite class, holds main functionality
 * @author        Matt
 */
abstract class classes_like_composite
{
    
/**
     * Registry Object Shortcuts
     *
     * @var        $registry
     * @var        $DB
     * @var        $settings
     * @var        $request
     * @var        $lang
     * @var        $member
     * @var        $memberData
     * @var        $cache
     * @var        $caches
     */
    
protected $registry;
    protected 
$DB;
    protected 
$settings;
    protected 
$request;
    protected 
$lang;
    protected 
$member;
    protected 
$memberData;
    protected 
$cache;
    protected 
$caches;    
    
    
/**
     * Cache object
     *
     * @var        $likeCache
     */
    
protected $likeCache null;
    
    
/**
     * Application key
     *
     * @var        $_app
     */
    
protected $_app;
    
    
/**
     * Area key
     *
     * @var        $_area
     */
    
protected $_area;
    
    
/**
     * Can bypass permission checks
     *
     * @var        $_area
     */
    
protected $_bypassPermissionChecks false;

    
    
/**
     * Init all. Yes, it is.
     *
     * @return    @e void
     */
    
public function init()
    {
        
$this->_app  classes_like_registry::getApp();
        
$this->_area classes_like_registry::getArea();
        
        if ( empty( 
$this->_app ) OR empty( $this->_area ) )
        {
            
trigger_error"Missing area or app variable in " __CLASS__ '::' __FUNCTION__E_USER_WARNING );
        }
        
        
/* Fetch cache class */
        
$this->likeCache classes_like_cache::getInstance();
        
        
/* Set a default cache expiration of 24 hours */
        
$this->likeCache->setExpiration86400 );
    }
    
    
/**
     * Toggle visibility
     * 
     * @param    mixed        $relId        Relationship ID or array ids
     * @param    boolean        $visible    Visible (true) or not (false)
     * @return    @e boolean
     */
    
public function toggleVisibility$relId$visible=true )
    {
        if ( empty( 
$relId ) )
        {
            
trigger_error"Data missing in " __CLASS__ '::' __FUNCTION__E_USER_WARNING  );
        }
        
        
/* Update any rows pointing to this rel id to be visible/invisible */
        
if( is_array($relId) )
        {
            if( 
count($relId) )
            {
                
$this->DB->update'core_like', array( 'like_visible' => $visible ), "like_app='{$this->_app}' AND like_area='{$this->_area}' AND like_rel_id IN (" implode','$relId ) . ')' );
            }
        }
        else
        {
            
$this->DB->update'core_like', array( 'like_visible' => $visible ), "like_app='{$this->_app}' AND like_area='{$this->_area}' AND like_rel_id=" $relId );
        }

        
/* Flag cache as stale */
        
$this->likeCache->isNowStale$relId );
        
        return 
true;
    }
    
    
/**
     * Add a like
     * 
     * @param    integer        $relId            Relationship ID
     * @param    integer        $memberID        Member ID of user being added
     * @param    array        $notifyOpts        Notification options
     * @param    integer        $isAnon            Anonymous flag
     * @return    @e boolean
     */
    
public function add$relId$memberId, array $notifyOpts$isAnon=)
    {
        if ( empty( 
$relId ) OR empty( $memberId ) )
        {
            
trigger_error"Data missing in " __CLASS__ '::' __FUNCTION__E_USER_WARNING  );
        }
        
        
/* first check to ensure we've not already like'd this item */
        
if ( $this->isLiked$relId$memberIdtrue ) )
        {
            
/* if any one cares to check, then we're all good */
            
return false;
        }
        
        
$memberData IPSMember::load$memberId );
        
        
/* Build */
        
$save = array(  'like_id'          => classes_like_registry::getKey$relId$memberId ),
                        
'like_lookup_id'   => classes_like_registry::getKey$relId ),
                        
'like_lookup_area' => classes_like_registry::getKeynull$memberId ),
                        
'like_app'         => $this->_app,
                        
'like_area'        => $this->_area,
                        
'like_rel_id'      => $relId,
                         
'like_member_id'   => $memberId,
                        
'like_added'       => IPS_UNIX_TIME_NOW,
                        
'like_is_anon'       => intval($isAnon),
                        
'like_notify_do'   => $notifyOpts['like_notify_do'],
                        
'like_notify_meta' => $notifyOpts['like_notify_meta'],
                        
'like_notify_freq' => ( $notifyOpts['like_notify_do'] ) ? $notifyOpts['like_notify_freq'] : '',
                        
'like_visible'       => 1,
                        
'like_notify_sent' => );
        
        
/* Do we have permission ? */
        
if ( ! $this->notificationCanSendarray_merge$save$memberData ) ) )
        {
            return 
false;
        }
        
        
$notifyOpts $this->_cleanNotifyOptions($notifyOpts);
        
        
/* Save to deebee */
        
$this->DB->insert'core_like'$save );
        
        
/* Flag cache as stale */
        
$this->likeCache->isNowStale$relId );
        
        return 
true;
    }
    
    
/**
     * Function to let plugins determine if a notification should not be sent.  Return false to send notification, or true to skip sending it.
     * 
     * @param    array    Notification data
     * @return    @e bool
     */
     
public function excludeNotification$row )
     {
         return 
false;
     }
    
    
/**
     * Send notifications to anyone subscribed to item
     *
     * @param    integer        $relId                Relationship ID
     * @param    array        $type                Types of notifications to send (Possible: immediate, offline, daily, weekly)
     * @param    array        $notificationOpts    Notification options [Keys: notification_key, notification_url, email_template, email_subject, email_lang_file, email_lang_app, build_message_array, from (optional), ignore_data( type => ids )]
     * @param    array        $sentToIds            Pass a value by reference to get the ID numbers of members that were sent a notification
     * @return    @e boolean
     * @see        allowedFrequencies()
     */
    
public function sendNotifications$relId$type$notificationOpts=array(), &$sentToIds=array() )
    {
        
$sentToIds = array();
        
        if ( empty( 
$relId ) )
        {
            
trigger_error"Data missing in " __CLASS__ '::' __FUNCTION__E_USER_WARNING  );
        }
        
        if ( isset( 
$notificationOpts['notification_bypass_permission_checks'] ) && $notificationOpts['notification_bypass_permission_checks'] === true )
        {
            
$this->_bypassPermissionChecks true;
        }
        else
        {
            
/* Make sure this is reset (Not used currently) */
            
$this->_bypassPermissionChecks false;
        }
        
        
$type    is_array($type) ? $type : array( $type );
        
        
/* Use session expiration, unless it is longer than 1 hour, then limit to 1 hour */
        
$_limit    $this->settings['session_expiration'];
        
        if( 
$_limit 3600 )
        {
            
$_limit    3600;
        }
        
        
$expire IPS_UNIX_TIME_NOW $_limit;
                
        
/* Fetch data by relID */
        
$data $this->getDataByRelationshipId$relIdfalse, (array) $notificationOpts['ignore_data'], $type );
                
        
/* Remove ourselves from notifications... */
        
if ( $this->memberData['member_id'] && isset($data$this->memberData['member_id'] ]) && $notificationOpts['includeOwner'] !== true )
        {
            unset(
$data$this->memberData['member_id'] ]);
        }
        
        
/* If no users, forget it */
        
if ( is_null($data) )
        {
            return 
false;
        }
        
        
$classToLoad    IPSLib::loadLibraryIPS_ROOT_PATH '/sources/classes/member/notifications.php''notifications' );
        
$notifyLibrary    = new $classToLoad$this->registry );
        
        
$htmlMessage = array();
        
$textMessage = array();
        
        foreach( 
$data as $row )
        {
            if ( ! 
$row['like_notify_freq'] OR ! in_array$row['like_notify_freq'], $type ) )
            {
                continue;
            }
            
            
/* make sure user is offline */
            
if ( $row['like_notify_freq'] == 'offline' AND $row['last_activity'] > $expire )
            {
                continue;
            }
            
            
/* Custom content type exclusion rules (e.g. in forums only send one notification per visit for offline types) */
            
if( $this->excludeNotification$row ) )
            {
                continue;
            }
            
            
/* Can we actually send this one? */
            
if ( $this->notificationCanSend$row ) === false )
            {
                continue;
            }

            
/* Extra protection in case a ghost record is in the table for a member that doesn't exist
                @link    http://community.invisionpower.com/tracker/issue-36793-error-when-editing-some-marketplace-files */
            
if( !$row['like_member_id'] )
            {
                continue;
            }

            
/* Expensively process HTML/Text versions once per language.. <#NAME#> is converted to -members:members_display_name- for processing later */
            
if ( ! isset( $htmlMessage$row['language'] ] ) )
            {                
                
$buildMessage = array();
                    
                if ( 
is_array$notificationOpts['build_message_array'] ) and count$notificationOpts['build_message_array'] ) )
                {
                    foreach( 
$notificationOpts['build_message_array'] as $k => $v )
                    {
                        
$buildMessage$k ] = $v;
                    }
                }
                
                
$buildMessage['UNSUBCRIBE_URL']                            = '-member:unsubscribe_url-';
                
$notificationOpts['build_message_array']['UNSUBCRIBE_URL'] = '-member:unsubscribe_url-';
                
                
IPSText::getTextClass('email')->setPlainTextTemplateIPSText::getTextClass('email')->getTemplate$notificationOpts['email_template'], $row['language'], trim($notificationOpts['email_lang_file']), trim($notificationOpts['email_lang_app']) ) );
                
                
$textMessage$row['language'] ] = IPSText::getTextClass('email')->buildPlainTextContent$buildMessage );
                
$htmlMessage$row['language'] ] = IPSText::getTextClass('email')->buildHtmlContent$buildMessage );
            }
            
            
$thisTextMessage $textMessage$row['language'] ];
            
$thisHtmlMessage $htmlMessage$row['language'] ];
            
            
/* Add in unsubscribe link */
            
$unsubKey $row['like_app'] . ';' $row['like_area'] . ';' $row['like_rel_id'] . ';' $row['like_member_id'] . ';' $row['member_id'] . ';' $row['email'];
            
$unsubUrl $this->registry->output->buildSEOUrl'app=core&amp;module=global&amp;section=like&amp;do=unsubscribe&amp;key=' IPSText::base64_encode_urlSafe$unsubKey ), 'publicNoSession''unsubscribe''likeunsubscribe' );
            
            
/* Now process the dynamic replacements */
            
if ( is_array$notificationOpts['build_message_array'] ) and count$notificationOpts['build_message_array'] ) )
            {
                foreach( 
$notificationOpts['build_message_array'] as $k => $v )
                {
                    if ( 
preg_match'/-member:(.+?)-/'$v$_matches ) )
                    {
                        if ( 
$_matches[1] == 'unsubscribe_url' )
                        {
                            
$thisHtmlMessage str_replace$_matches[0], $unsubUrl$thisHtmlMessage );
                            
$thisTextMessage str_replace$_matches[0], $unsubUrl$thisTextMessage );
                        }
                        else
                        {
                            
$thisHtmlMessage str_replace$_matches[0], $row$_matches[1] ], $thisHtmlMessage );
                            
$thisTextMessage str_replace$_matches[0], $row$_matches[1] ], $thisTextMessage );
                        }
                    }
                }
            }
            
            
$sentToIds[] = $row['like_member_id'];
            
            
$notifyLibrary->setMember$row );
            
$notifyLibrary->setFrom$notificationOpts['from'] ? $notificationOpts['from'] : $this->memberData );
            
$notifyLibrary->setNotificationKey$notificationOpts['notification_key'] );
            
$notifyLibrary->setNotificationUrl$notificationOpts['notification_url'] );
            
$notifyLibrary->setNotificationTitle$notificationOpts['email_subject'] ? $notificationOpts['email_subject'] : IPSText::getTextClass('email')->subject );
            
$notifyLibrary->setNotificationText$thisTextMessage );
            
$notifyLibrary->setNotificationHtml$thisHtmlMessage );
            
            if ( ! empty( 
$notificationOpts['email_only_subject'] ) && $notificationOpts['email_only_subject'] != IPSText::getTextClass('email')->subject )
            {
                
$notifyLibrary->setEmailSubject$notificationOpts['email_only_subject'], $row['language'], trim($notificationOpts['email_lang_file']), trim($notificationOpts['email_lang_app']) );
            }
            try
            {
                
$notifyLibrary->sendNotification();
            }
            catch( 
Exception $e ){ }
            
            
//-----------------------------------------
            // Update sent timestamp
            //-----------------------------------------
            
            
$this->DB->update'core_like', array( 'like_notify_sent' => IPS_UNIX_TIME_NOW ), "like_id='" classes_like_registry::getKey$row['like_rel_id'], $row['like_member_id'] ) . "'" );
        }
        
        return ( 
is_array$sentToIds ) ) ? array_unique$sentToIds ) : true;
    }
    
    
/**
     * Send digest notifications to anyone subscribed to item
     *
     * @param    string        $type        Types of notifications to send (Possible: daily, weekly)
     * @param    integer        $sendMax    Null (use ipsRegistry::$setting or send INT only)
     * @return    @e boolean
     * @see        allowedFrequencies()
     */
    
public function sendDigestNotifications$type$sendMax=null )
    {    
        
/* Fetch data */
        
$blackhole time(); // mktime( 0, 0 ); - http://community.invisionpower.com/tracker/issue-35320-daily-digests-every-2-days
        // The below has been changed based on troubleshooting in a ticket, 10.17.2012.  With the code below not commented out, for a weekly digest we grab 15-7 days ago, rather than 8-0 days ago to send.
        
$time      $blackhole//( $type == 'daily' ) ? $blackhole - ( 86400 ) : $blackhole - ( 86400 * 7 );
        
$data      $this->getDataByAreaAndLastSentOlderThanDate$time, array( $type ), false$sendMax );

        if ( ! 
count$data ) )
        {
            return 
false;
        }
        
        
/* Check for outdated notifications and update them as required */
        
$oldestPossDate 0;
        
        if ( 
$type == 'daily' )
        {
            
/* Grab 4 days to account for timezone differences */
            
$oldestPossDate $blackhole - ( 86400 );
        }
        else
        {
            
/* Grab 10 days to ensure we don't update rows ready to be sent */
            
$oldestPossDate $blackhole - ( 86400 10 );
        }
        
        if ( 
$oldestPossDate )
        {
            
$this->DB->update'core_like', array( 'like_notify_sent' => IPS_UNIX_TIME_NOW ),
                               
'like_app='' . classes_like_registry::getApp() . '' AND like_area='' . classes_like_registry::getArea() . '' AND like_notify_sent < ' $oldestPossDate " AND like_notify_freq='{$type}'" );
        }
        
        
$classToLoad   IPSLib::loadLibraryIPS_ROOT_PATH '/sources/classes/member/notifications.php''notifications' );
        
$notifyLibrary = new $classToLoad$this->registry );
        
        
/* Array structure will be $data[ $memberId ][ $likeId ] = array(); */
        
foreach( $data as $memberId => $_rows )
        {
            foreach( 
$_rows as $row )
            {
                if ( ! 
$row['like_notify_freq'] OR $row['like_notify_freq'] != $type )
                {
                    continue;
                }
                
                
/* Make sure we don't send huge digests if this is their first email since subscribing */
                
$row['like_notify_sent']    = $row['like_notify_sent'] ? $row['like_notify_sent'] : $row['like_added'];

                
/* Want to fetch the data from the plug in files? */
                
$notificationOpts $this->buildNotificationData$row$type );
                    
                if ( ! 
is_array$notificationOpts ) OR ! count$notificationOpts ) )
                {
                    
/* This will get hit if topic hasn't had a post since notification was last sent - to prevent the same row from getting picked up each time this runs
                        we need to update the last sent timestamp for the like */
                    
$this->DB->update'core_like', array( 'like_notify_sent' => IPS_UNIX_TIME_NOW ), 'like_id='' . classes_like_registry::getKey( $row['like_rel_id'], $row['like_member_id'] ) . ''' );

                    continue;
                }

                
$buildMessage    = array();
                
                if ( 
is_array$notificationOpts['build_message_array'] ) and count$notificationOpts['build_message_array'] ) )
                {
                    foreach( 
$notificationOpts['build_message_array'] as $k => $v )
                    {
                        if ( 
preg_match'/-member:(.+?)-/'$v$_matches ) )
                        {
                            
$v str_replace$_matches[0], $row[$_matches[1]], $v );
                        }
                        
                        
$buildMessage[$k] = $v;
                    }
                }

                
/* Add in unsubscribe link */
                
$key $row['like_app'] . ';' $row['like_area'] . ';' $row['like_rel_id'] . ';' $row['like_member_id'] . ';' $row['member_id'] . ';' $row['email'];
                
$buildMessage['UNSUBCRIBE_URL'] = $this->registry->output->buildSEOUrl'app=core&amp;module=global&amp;section=like&amp;do=unsubscribe&amp;key=' IPSText::base64_encode_urlSafe$key ), 'publicNoSession''unsubscribe''likeunsubscribe' );

                
IPSText::getTextClass('email')->setPlainTextTemplateIPSText::getTextClass('email')->getTemplate$notificationOpts['email_template'], $row['language'], trim($notificationOpts['email_lang_file']), trim($notificationOpts['email_lang_app']) ) );
                
IPSText::getTextClass('email')->buildPlainTextContent$buildMessage );
                
                
$notifyLibrary->setMember$row );
                
$notifyLibrary->setFrom$notificationOpts['from'] ? $notificationOpts['from'] : $this->memberData );
                
$notifyLibrary->setNotificationKey$notificationOpts['notification_key'] );
                
$notifyLibrary->setNotificationUrl$notificationOpts['notification_url'] );
                
$notifyLibrary->setNotificationTextIPSText::getTextClass('email')->getPlainTextContent() );
                
$notifyLibrary->setNotificationTitle$notificationOpts['email_subject'] ? $notificationOpts['email_subject'] : IPSText::getTextClass('email')->subject );
                
$notifyLibrary->setNotificationHtmlIPSText::getTextClass('email')->buildHtmlContent$buildMessage ) );
                
                try
                {
                    
$notifyLibrary->sendNotification();
                }
                catch( 
Exception $e ){ }
                                
                
/* Update sent timestamp */
                
$this->DB->update'core_like', array( 'like_notify_sent' => IPS_UNIX_TIME_NOW ), 'like_id='' . classes_like_registry::getKey( $row['like_rel_id'], $row['like_member_id'] ) . ''' );
            }
        }
        
        return 
true;
    }
    
    
/**
     * Removes all likes based on member ID
     * 
     * @param    integer        $memberId        Member ID
     * @return    @e boolean
     */
    
public function removeByMemberId$memberId )
    {
        if ( empty( 
$memberId ) )
        {
            
trigger_error"Data missing in " __CLASS__ '::' __FUNCTION__E_USER_WARNING  );
        }
        
        
$where "like_app='{$this->_app}' AND like_area='{$this->_area}' AND like_member_id=" intval($memberId);
        
        
/* Get rel ids */
        
$relIds    = array();
        
        
$this->DB->build( array( 'select' => 'like_rel_id''from' => 'core_like''where' => $where ) );
        
$this->DB->execute();
        
        while( 
$r $this->DB->fetch() )
        {
            
$relIds[]    = $r['like_rel_id'];
        }

        
/* Delete likes */
        
$this->DB->delete'core_like'$where );
                                    
        
/* Flag cache as stale */
        
$this->likeCache->isNowStale$relIds );
        
        return 
true;
    }
    
    
/**
     * Removes a like
     * 
     * @param    mixed        $relId        Relationship IDs (int|array)
     * @param    integer        $memberId    Optional - If supplied, it'll remove that member rel. or it will remove all
     * @return    @e boolean
     */
    
public function remove$relId$memberId=null )
    {
        if ( empty( 
$relId ) )
        {
            
trigger_error"Data missing in " __CLASS__ '::' __FUNCTION__E_USER_WARNING  );
        }
        
        
$where '';
        
        if ( 
is_numeric($relId) )
        {
            
$where "='" classes_like_registry::getKey$relId$memberId ) . "'";
        }
        elseif ( 
is_array($relId) )
        {
            
$relId IPSLib::cleanIntArray($relId);
            
$keys  = array();
            
            if ( 
count($relId) )
            {
                foreach( 
$relId as $id )
                {
                    
$keys[] = "'" classes_like_registry::getKey$id$memberId ) . "'";
                }
                
                
$where " IN (" implode","$keys ) . ")";
            }
        }
        
        
/* Nowhere to be found? */
        
if ( $where == '' )
        {
            return 
false;
        }
        
        
/* Still here? Delete then! */
        
if ( $memberId === null )
        {
            
$this->DB->delete'core_like'"like_lookup_id " $where );
        }
        else
        {
            
/* Not liked? Save us the bother, then */
            
if ( ! $this->isLiked($relId$memberId) )
            {
                
/* if any one cares to check, then we're all good */
                
return false;
            }
            
            
$this->DB->delete'core_like'"like_id " $where );
        }
                                                
        
/* Flag cache as stale */
        
$this->likeCache->isNowStale$relId );
        
        return 
true;
    }
    
    
/**
     * Updates a member's ID in the like rows
     *
     * @param    integer    Old Id
     * @param    integer    New Id
     */
    
public function updateMemberId$oldId$newId )
    {
        
$_likes    = array();
        
        
/* We might be merging */
        
$this->DB->build( array( 'select' => 'like_lookup_id''from' => 'core_like''where' => "like_member_id=" $newId ) );
        
$this->DB->execute();
            
        while( 
$r $this->DB->fetch() )
        {
            
$_likes[]    = $r['like_lookup_id'];
        }
        
/* not done */
        
if ( count$_likes ) )
        {
            
$this->DB->update'core_like'         , array( 'like_member_id' => $newId ), "like_member_id=" $oldId " AND like_lookup_id NOT IN('" implode"','"array_map'addslashes'$_likes ) ) . "')" );
            
$this->DB->delete'core_like_cache' "like_cache_id IN('" implode"','"array_map'addslashes'$_likes ) ) . "')" );
        }
        else
        {
            
$this->DB->update'core_like'         , array( 'like_member_id' => $newId ), "like_member_id=" $oldId );
        }
        
        
/* Now remove duplicates */
        
$dupes = array();
        
        
$this->DB->build( array( 'select' => 'like_id, count(*) as cnt',
                                 
'from'   => 'core_like',
                                 
'where'  => 'like_member_id=' $newId,
                                 
'group'  => 'like_app, like_area, like_member_id, like_rel_id',
                                 
'order'  => 'cnt desc' ) );
        
$this->DB->execute();
        
        while( 
$row $this->DB->fetch() )
        {
            if ( 
$row['cnt'] < )
            {
                break;
            }
            else
            {
                
$dupes[] = $row['like_id'];
            }
        }
        
        
/* Do it? */
        
if ( count$dupes ) )
        {
            
$this->DB->delete'core_like' "like_id IN('" implode"','"array_map'addslashes'$dupes ) ) . "')" );
        }
        
        
/* Now re-update so that hashes are correct */
        
$update "like_id=MD5( CONCAT( like_app, ';', like_area, ';', like_rel_id, ';', like_member_id ) ), " .
                  
"like_lookup_area=MD5( CONCAT( like_app, ';', like_area, ';', like_member_id ) )";
        
        
$where  "like_member_id=" $newId;
        
        
$this->DB->update'core_like'$update$wherefalsetrue );
    }
    
    
/**
     * Return number of people who 'like' the item
     * 
     * @param    integer        $relId        Relationship ID
     * @return    @e integer
     */
    
public function getCount$relId )
    {
        
$cache $this->likeCache->get$relId );

        return 
$cache['count'];
    }
    
    
/**
     * Render a view
     * 
     * @param    string        $view        Type of View
     * @param    integer        $relId        Relationship ID
     * @param    array        $opts        Options
     * @param    integer        $memberId    Member ID
     * @return    @e string    Formatted HTML
     * @todo    If $cache['count'] is 0, we can probably skip the getDataByMemberIdAndRelationshipId() call as it will be empty
     */
    
public function render$view$relId$opts=array(), $memberId=null )
    {
        
/* In the future we could abstract this out to its own class 
         *  At this time we only have a few views so it would be overkill */
        
if ( $view == 'summary' )
        {
            
$data   = array( 'names' => array(), 'count' => 0'formatted' => '''iLike' => false'iLikeAnon' => false );
            
$cache  $this->likeCache->get$relId$memberId );
            
$max    0;
            
            
/* Fetch data for us? */
            
if ( $this->memberData['member_id'] && ( $cache['count'] + $cache['count_anon'] ) > )
            {
                
/* We can't use isLiked or it runs twice the query and adding a cache to the function doesn't work either */
                
$_me $this->getDataByMemberIdAndRelationshipId$relId$this->memberData['member_id'] );
                
                if ( 
is_array($_me) && count($_me) && $_me['like_id'] )
                {
                    
/* Now mix up the data */
                    
$data['names'][] = array( 'name' => $this->lang->words['fave_moi'], 'seo' => $this->memberData['members_seo_name'], 'id' => $this->memberData['member_id'] );
                    
                    
/* Flag as me */
                    
$data['iLike'] = true;
                    
                    
/* Anonymous? Let the template know */
                    
if ( $_me['like_is_anon'] )
                    {
                        
$data['iLikeAnon'] = true;
                    }
                }
            }
            
            
/* We had the total count set from cache.get - doesn't count anonymous members thought! */
            
if ( classes_like_registry::getTotalCount() )
            {
                if ( 
is_array$cache['members'] ) )
                {
                    
$i   1;
                    
$max 3;
                    
                    foreach( 
$cache['members'] as $mid => $mdata )
                    {
                        
$_last = ( $i == $cache['count'] || $i == $max ) ? 0;
                        
                        
/* Is this you? */
                        
if ( $mid == $this->memberData['member_id'] )
                        {
                            continue;
                        }
                        
                        
/* Push it on */
                        
$data['names'][] = array( 'name' => $mdata['n'], 'seo' => $mdata['s'], 'id' => $mid'last' => $_last );
                        
                        
$i++;
                        
                        if ( 
$i $max )
                        {
                            
/* Done thanks */
                            
break;
                        }
                    }
                }
            }
            
            
/* Finish off */
            
$data['totalCount']  = $cache['count'] + $cache['count_anon'];
            
$data['anonCount']   = $cache['count_anon'];
            
$data['othersCount'] = ( $cache['count'] > $max ) ? $cache['count'] - $max 0;
            
$data['app']         = $this->_app;
            
$data['area']         = $this->_area;
            
$data['formatted']   = $this->_formatNameString$data );
            
$data['vernacular']     = $this->getVernacular();
            
            if ( 
IPS_IS_AJAX )
            {
                
$_template    $this->templatePrefix() . 'likeSummaryContents';
                
                
/* Got an override template? */
                
if ( method_exists$this->registry->output->getTemplate$this->skin() ), $_template ) )
                {
                    return 
$this->registry->output->getTemplate$this->skin() )->$_template$data$relId$opts );
                }
                else
                {
                    
/* Fallback on default template */
                    
return $this->registry->output->getTemplate('global_other')->likeSummaryContents$data$relId$opts );
                }
            }
            else
            {
                
$_template    $this->templatePrefix() . 'likeSummary';
                                
                
/* Got an override template? */
                
if ( method_exists$this->registry->output->getTemplate$this->skin() ), $_template ) )
                {
                    return 
$this->registry->output->getTemplate$this->skin() )->$_template$data$relId$opts );
                }
                else
                {
                    
/* Fallback on default template */
                    
return $this->registry->output->getTemplate('global_other')->likeSummary$data$relId$opts );
                }
            }
        }
        else if ( 
$view == 'more' )
        {
            
/* We need some counts here because we need them! */
            
$cache                 $this->likeCache->get$relId$memberId );
            
$cache['totalCount'] = $cache['count'] + $cache['count_anon'];
            
$cache['anonCount']     = $cache['count_anon'];
            
            
/* Fetch members who have wanted to like this item */
            
$data        $this->getDataByRelationshipId$relId );
            
$_template    $this->templatePrefix() . 'likeMoreDialogue';
            
            
/* Sort out some numbers if we're following anonymously */
            
if ( $this->memberData['member_id'] && isset($data$this->memberData['member_id'] ]) && $data$this->memberData['member_id'] ]['like_is_anon'] )
            {
                
$cache['count_anon']--;
                
$cache['anonCount']--;
            }
            
            
/* Got an override template? */
            
if ( method_exists$this->registry->output->getTemplate$this->skin() ), $_template ) )
            {
                return 
$this->registry->output->getTemplate$this->skin() )->$_template$data$relId$cache );
            }
            else
            {
                
/* Fallback on default template */
                
return $this->registry->output->getTemplate('global_other')->likeMoreDialogue$data$relId$cache );
            }
        }
    }

    
/**
     * Fetch form data for set dialogue
     * 
     * @param    integer        $relId        Relationship ID
     * @return    @e array
     */
    
public function getDataForSetDialogue$relid )
    {
        
$return = array( 'frequencies' => $this->allowedFrequencies(),
                         
'notifyType'  => $this->getNotifyType(),
                         
'vernacular'  => $this->getVernacular()
                        );
        
        return 
$return;
    }
    
    
/**
     * Has this user made this item a like already dudes?
     * 
     * @param    mixed        $relId        Relationship IDs (int|array)
     * @param    integer        $memberId    Member ID
     * @param    boolean        $checkNotVisibleToo    return a match even if not visible to the user
     * @return    @e boolean
     */
    
public function isLiked$relId$memberId$checkNotVisibleToo=false )
    {
        
/* Grab the data */
        
return ( $this->getDataByMemberIdAndRelationshipId$relId$memberId$checkNotVisibleToo ) === null ) ? false true;
    }
    
    
/**
     * Get data based on a relationship ID and a member ID
     *
     * @param    mixed        $relId        Relationship IDs (int|array)
     * @param    integer        $memberId    Member ID
     * @param    boolean        $checkNotVisibleToo    return a match even if not visible to the user
     * @return    @e mixed    Array of likes data OR null
     */
    
public function getDataByMemberIdAndRelationshipId$relId$memberId$checkNotVisibleToo=false )
    {
        
$where    '';
        
$data    null;
        
        if ( 
is_numeric$relId ) )
        {
            
$where "like_id='" classes_like_registry::getKey$relId$memberId ) . "'";
        }
        else if ( 
is_array$relId ) )
        {
            
$relId IPSLib::cleanIntArray$relId );
            
$keys  = array();
            
            foreach( 
$relId as $id )
            {
                
$keys[] = "'" classes_like_registry::getKey$id$memberId ) . "'";
            }
            
            if ( ! 
count$keys ) )
            {
                return 
null;
            }
            
            
$where 'like_id IN (' implode','$keys ) . ')';
            
        }
        
        if ( 
$checkNotVisibleToo === false )
        {
            
$where .= ' AND like_visible=1';
        }
        
        
$this->DB->build( array( 'select' => '*',
                                    
'from'   => 'core_like',
                                 
'where'  => $where ) );
        
        
$o $this->DB->execute();
        
        while( 
$row $this->DB->fetch$o ) )
        {
            
$data$row['like_rel_id'] ] = $row;
        }
        
        
/* Just the one? */
        
if ( is_numeric($relId) && count($data) )
        {
            
$data array_shift($data);
        }
        
        return 
is_array($data) ? $data null;
    }
    
    
/**
     * Get data based on a member ID
     *
     * @param    integer        $memberId        Member ID
     * @param    integer        $limit            Max results
     * @return    @e mixed     Array of likes data OR null
     */
    
public function getDataByMemberIdAndArea$memberId$limit=500 )
    {
        
$data null;
        
        
$this->DB->build( array( 'select' => '*',
                                    
'from'   => 'core_like',
                                 
'where'  => 'like_lookup_area='' . classes_like_registry::getKey( null, $memberId ) . '' AND like_visible=1',
                                 
'order'  => 'like_added DESC',
                                 
'limit'  => array( 0$limit ) ) );
        
        
$o $this->DB->execute();
        
        while( 
$row $this->DB->fetch$o ) )
        {
            
$data$row['like_rel_id'] ] = $row;
        }    
        
        return ( 
is_array$data ) ) ? $data null;
    }
    
    
/**
     * Get data based on a relationship ID
     *
     * @param    integer        $relId        Relationship ID
     * @param    boolean        $skipAnon    Skip anonymous rows flag
     * @param    array
     * @param    array        Type of follow (immediate, none, offline, daily, etc)
     * @return    @e mixed    Array of likes data OR null
     */
    
public function getDataByRelationshipId$relId$skipAnon=true$ignoreData=array(), $followType=array() )
    {
        
/* Init */
        
$mids        = array();
        
$members    = array();
        
$rows       = array();
        
$followType    is_array$followType ) ? $followType : array( $followType );

        
/* Determine where clause */
        
$where        = array( "like_visible=1""like_is_anon" . ( $skipAnon '=0' ' IN(0,1)' ) );

        
/*if( $this->memberData['member_id'] )
        {
            $where[]    = "(like_lookup_id='" . classes_like_registry::getKey( $relId ) . "' OR like_id='" . classes_like_registry::getKey( $relId, $this->memberData['member_id'] ) . "')";
        }
        else
        {*/
            
$where[]    = "like_lookup_id='" classes_like_registry::getKey$relId ) . "'";
        
//}

        /* Add in follow type */
        
if ( count$followType ) )
        {
            
$where[]    = "like_notify_freq IN ('" implode"','"$followType ) ."')";
        }
        
        
/* Fetch data */
        
$this->DB->build( array( 'select' => '*',
                                    
'from'   => 'core_like',
                                 
'where'  => implode" AND "$where ),
                                 
'order'  => 'like_added DESC',
                                 
'limit'  => array( 0500 ) ) );
        
$o $this->DB->execute();
        
        while( 
$row $this->DB->fetch$o ) )
        {
            
$data$row['like_member_id'] ] = $row;
            
$mids$row['like_member_id'] ] = intval$row['like_member_id'] );
        }
        
        
/* Just the one? */
        
if ( count$mids ) )
        {
            
$members IPSMember::load$mids'all' );
        
            foreach( 
$members as $i => $d )
            {
                
$_m IPSMember::buildProfilePhoto$d );
                
$data$i ] = array_merge( (array) $_m, (array) $data$i ] );
            }
        }
        
        
/* Are we giving this bloke a good ignoring? */
        
if ( !empty( $ignoreData ) )
        {
            
$ignoreWhere = array();
            foreach ( 
$ignoreData as $type => $ids )
            {
                
$ignoreWhere[] = "( {$type}=1 AND " $this->DB->buildWherePermission$ids'ignore_ignore_id'FALSE ) . ')';
            }
            
            
$this->DB->build( array( 'select' => '*''from' => 'ignored_users''where' => implode' OR '$ignoreWhere ) ) );
            
$this->DB->execute();
            while ( 
$row $this->DB->fetch() )
            {
                if ( isset( 
$data$row['ignore_owner_id'] ] ) )
                {
                    unset( 
$data$row['ignore_owner_id'] ] );
                }
            }
            
        }
                
        return 
is_array($data) ? $data null;
    }
    
    
/**
     * Get data based on an area and last sent greater than date [unix timestampe]
     *
     * @param    integer        $date            Unix timestamp
     * @param    array        $types            Array of notification types (optional)
     * @param    array        $parseMembers    Parse extra data for each member and build display photo (false is default)
     * @param    integer        $sendMax    Null (use ipsRegistry::$setting or send INT only)
     * @return    @e mixed    Array of likes data OR null
     * @see        allowedFrequencies()     
     */
    
public function getDataByAreaAndLastSentOlderThanDate$date$types=array(), $parseMembers=false$sendMax=null )
    {
        
/* Init */
        
$mids     = array();
        
$members = array();
        
$rows    = array();
        
$joins   = array();
        
$where   = ( is_array$types ) ) ? ' AND l.like_notify_freq IN ('' . implode( "','", $types ) . '')' '';
        
$sendMax = ( $sendMax !== null )  ? $sendMax ipsRegistry::$settings['like_notifications_limit'];
        
        
/* figure out joins */
        
$joins[] = array( 'select' => 'm.*',
                          
'from'   => array( 'members' => 'm' ),
                          
'where'  => 'm.member_id=l.like_member_id',
                          
'type'   => 'left' );
        
        
$moreJoins $this->getDataJoins();
        
        if ( 
is_array$moreJoins ) AND count$moreJoins ) )
        {
            foreach( 
$moreJoins as $join )
            {
                
$joins[] = $join;
            }
        }
        
        
/* Prevent it going back EONS AND EONS */
        
$oldestPossDate 0;
        
        if ( 
in_array'weekly'$types ) )
        {
            
$oldestPossDate $date - ( 86400 );
        }
        else
        {
            
$oldestPossDate $date - ( 86400 );
        }
        
        if ( 
$oldestPossDate )
        {
            
$where .= ' AND ( CASE WHEN l.like_notify_sent > 0 THEN l.like_notify_sent ELSE l.like_added END ) < ' intval$oldestPossDate );
        }
        
        
/* Fetch data */    
        
$this->DB->build( array( 'select'   => 'l.*',
                                    
'from'     => array( 'core_like' => 'l' ),
                                 
'where'    => 'l.like_notify_do=1 AND l.like_app='' . classes_like_registry::getApp() . '' AND l.like_area='' . classes_like_registry::getArea() . '' AND l.like_visible=1 AND ( CASE WHEN l.like_notify_sent > 0 THEN l.like_notify_sent ELSE l.like_added END ) < ' intval$date ) . $where,
                                 
'order'    => 'l.like_notify_sent ASC',
                                 
'limit'    => array( 0$sendMax ),
                                 
'add_join' => $joins ) );
        
                        
        
$o $this->DB->execute();
        
        while( 
$row $this->DB->fetch$o ) )
        {
            
$row['like_member_id']            = intval($row['like_member_id']);
            
$mids$row['like_member_id'] ]    = $row['like_member_id'];
            
            
/* Need to apply secondary groups and grab g_perm_id if $parseMembers is false (default)
                @link http://community.invisionpower.com/tracker/issue-34691-digest-notifications-not-going-out/ */
            
$row['mgroup_others']    = ($row['mgroup_others'] != '') ? IPSText::cleanPermString($row['mgroup_others']) : '';
            
//by denchu
            
if (is_array($this->caches['group_cache'][ $row['member_group_id'] ])){
            
$row                    array_merge$row$this->caches['group_cache'][ $row['member_group_id'] ] );
            }
            
$row                    $this->registry->member()->setUpSecondaryGroups$row );
            
            if( 
$row['topic_last_post'] )
            {
                
$row['last_post']    = $row['topic_last_post'];
            }
            
            
/* @link http://community.invisionpower.com/tracker/issue-32204-dailyweekly-notifications */
            
$data$row['like_member_id'] ][ $row['like_id'] ]    = $row;
        }
                
        
/* Just the one? */
        
if ( $parseMembers && count($mids) )
        {
            
$members IPSMember::load$mids'all' );
        
            foreach( 
$members as $i => $d )
            {
                
$_m IPSMember::buildProfilePhoto$d );
                
                foreach( 
$data$i ] as $likeId => $likeData )
                {
                    
$data$i ][ $likeId ] = array_merge( (array) $_m, (array) $data$i ][ $likeId ] );
                }
            }
        }
        
        return 
is_array($data) ? $data null;
    }
    
    
/**
     * Clean the notification options to make sure they're all nice and fluffy
     * 
     * @param    array        $notifyOpts        Notification options
     * @return    @e array    Cleaned notification options array
     */
    
protected function _cleanNotifyOptions( array $notifyOpts )
    {
        if ( isset( 
$notifyOpts['like_notify_do'] ) )
        {
            
$notifyOpts['like_notify_do'] = intval$notifyOpts['like_notify_do'] );
        }
        else
        {
            
$notifyOpts['like_notify_do'] = 0;
        }
        
        if ( ! isset( 
$notifyOpts['like_notify_meta'] ) )
        {
            
$notifyOpts['like_notify_meta'] = '';
        }
        
        if ( isset( 
$notifyOpts['like_notify_freq'] ) )
        {
            
$notifyOpts['like_notify_freq'] = ( in_array$notifyOpts['like_notify_freq'], $this->allowedFrequencies() ) ) ? $notifyOpts['like_notify_freq'] : '';
        }
        else
        {
            
$notifyOpts['like_notify_freq'] = 0;
        }
        
        return 
$notifyOpts;
    }
    
    
/**
     * Return an array of acceptable frequencies
     * Possible: immediate, offline, daily, weekly
     * 
     * @return    @e array
     */
    
public function allowedFrequencies()
    {
        return array( 
'immediate''offline''daily''weekly' );
    }
    
    
/**
     * Return types of notification available for this item
     * 
     * @return    @e array    array( key, human readable )
     */
    
public function getNotifyType()
    {
        return array( 
'comments'ipsRegistry::getClass('class_localization')->words['gbl_comments_like'] );
    }
    
    
/**
     * Fetch the template group
     * 
     * @return    @e string
     */
    
public function skin()
    {
        return 
'global_other';
    }

    
/**
     * Fetch the template prefix.  This allows you to have two follow implementations in
     * one skin file (i.e. skin_calendars -> eventLikeMoreDialog() and skin_calendars -> calendarLikeMoreDialog())
     * 
     * @return    @e string
     */
    
public function templatePrefix()
    {
        return 
'';
    }
    
    
/**
     * Gets the vernacular (like or follow)
     *
     * @return    @e string
     */
    
public function getVernacular()
    {
        return 
'like';
    }
    
    
/**
     * Builds the notification data via the app class
     * 
     * @param    array        $data        like_ DB data and like owner member data
     * @param    string        $type        Types of notifications to send
     * @return    @e array    array( notification_key, notification_url, email_template, email_subject, build_message_array )
     * @see        allowedFrequencies()
     */
    
public function buildNotificationData$data$type )
    {
        return array();
    }
    
    
/**
     * Check notifications that are to be sent to make sure they're valid and that
     * 
     * @param    array        $metaData        like_ DB data and like owner member data
     * @return    @e boolean
     */
    
public function notificationCanSend$metaData )
    {
        return 
true;
    }
    
    
/**
     * Fetches joins for fetching data
     * 
     * @param    string        $field        DB field name (defaults to 'l.like_rel_id')
     * @return    @e array
     */
    
public function getDataJoins$field='l.like_rel_id' )
    {
        return array();
    }
    
    
/**
     * Return the title based on the passed id
     * 
     * @param    mixed        $relId        Relationship ID or array of IDs
     * @return    @e mixed    Title or array of titles
     */
    
public function getTitleFromId$relId )
    {
        
$meta $this->getMeta$relId, array( 'title' ) );
        
        if ( 
is_numeric$relId ) )
        {
            return 
$meta$relId ]['like.title'];
        }
        else
        {
            
$return = array();
            
            foreach( 
$meta as $id => $data )
            {
                
$return$id ] = $data['like.title'];
            }
            
            return 
$return;
        }
    }

    
/**
     * Return the URL based on the passed id
     * 
     * @param    mixed        $relId        Relationship ID or array of IDs
     * @return    @e mixed    URL or array of URLs
     */
    
public function getUrlFromId$relId )
    {
        
$meta $this->getMeta$relId, array( 'url' ) );
        
        if ( 
is_numeric$relId ) )
        {
            return 
$meta$relId ]['like.url'];
        }
        else
        {
            
$return = array();
            
            foreach( 
$meta as $id => $data )
            {
                
$return$id ] = $data['like.url'];
            }
            
            return 
$return;
        }
    }
    
    
/**
     * Formats the Bob, Bill, Joe and 2038 Others Hate You
     * 
     * @param    array        $data        Likes data
     * @return    @e string    Formatted names
     */
    
protected function _formatNameString( array $data )
    {
        
$langString  '';
        
$seeMoreLink 'app=core&amp;module=global&amp;section=like&amp;do=more';
        
        if ( ! 
is_array$data['names'] ) OR ! count$data['names'] ) )
        {
            return 
false;
        }
        
/* Format up the names */
        
$i      0;
        
$_names = array();
        
        foreach( 
$data['names'] as $name )
        {
            if ( 
$this->memberData['member_id'] AND $this->memberData['member_id'] == $name['id'] )
            {
                
$_names[$i] = $name['name'];
            }
            else
            {
                
$_names[$i] = IPSMember::makeProfileLink($name['name'], $name['id'], $name['seo'] );
            }
            
            
$i++;
        }

        
/* More than one? */
        
if ( $data['totalCount'] > )
        {
            
/* Joe and Matt love you */
            
if ( $data['totalCount'] == )
            {
                
$_n $_names[0] . ' ' $this->lang->words['fave_and'] . ' ' $_names[1];
                
                
$langString sprintf$this->lang->words['fave_formatted_many'], $_n );
            }
            
/* Joe, Matt and Mike love you more */
            
else if ( $data['totalCount'] == )
            {
                
$_n $_names[0] . ', ' $_names[1] . ' ' $this->lang->words['fave_and'] . ' ' $_names[2];
                
                
$langString sprintf$this->lang->words['fave_formatted_many'], $_n );
            }
            
/* Joe, Matt, Mike and 1 more love you */
            
else if ( $data['totalCount'] == )
            {
                
$_n $_names[0] . ', ' $_names[1] . ' ' $this->lang->words['fave_and'] . ' ' $_names[2];
                
                
$langString sprintf$this->lang->words['fave_formatted_one_more'], $_n$seeMoreLink );
            }
            
/* Joe, Matt, Mike and 5 more are indifferent to your redonkulous comments */
            
else
            {
                
$_n $_names[0] . ', ' $_names[1] . ', ' $_names[2];
                
                
$langString sprintf$this->lang->words['fave_formatted_more'], $_n$seeMoreLink$data['othersCount'] );
            }
        }
        else
        {
            
/* Just the one and it might be you! */    
            
if ( $data['names'][0]['id'] == $this->memberData['member_id'] )
            {
                
$langString $this->lang->words['fave_formatted_me'];
            }
            else
            {
                
$langString sprintf$this->lang->words['fave_formatted_one'], $_names[0] );
            }
        }

        return 
$langString;
    }
    
    
/**
     * Replace all likes on one item with another
     * Used when merging two items
     *
     * @param    int        $id1            The ID of the item being deleted
     * @param    int        $id2            The ID of the item being kept
     */
    
public function merge$id1$id2 )
    {
        
/* We need to tell the driver not to escape our md5 compilation statements */
        
$_fieldEncapsulate $this->DB->fieldEncapsulate;
        
$this->DB->fieldEncapsulate '';
        
$this->DB->manual_addslashes TRUE;
        
        
/* Run the query - this looks a bit nuts, but basically we're setting any records for
        likes on the old item to now be likes on the new, unelss the user has already liked
        the new, in which case do nothing and we'll delete the left-overs in a minute */
        
$this->DB->update(
            array( 
'core_like' => 'a' ),
            
$this->DB->compileUpdateString( array(
                
'a.like_id'            => classes_like_registry::getKeyForSql$id2'a.like_member_id' ),
                
'a.like_lookup_id'    => classes_like_registry::getKeyForSql$id2 ),
                
'a.like_lookup_area'=> classes_like_registry::getKeyForSqlnull'a.like_member_id' ),
                
'a.like_rel_id'        => intval$id2 ),
            ) ),
            
"a.like_app='{$this->_app}' AND a.like_area='{$this->_area}' AND a.like_rel_id={$id1} AND b.like_id " $this->DB->buildIsNull(),
            
false,
            
true,
            array( array(
                
'from'    => array( 'core_like' => 'b' ),
                
'where'    => "b.like_app='{$this->_app}' AND b.like_area='{$this->_area}' AND b.like_rel_id={$id2}",
                ) )
            );
            
        
/* Reset the DB driver */
        
$this->DB->manual_addslashes FALSE;
        
$this->DB->fieldEncapsulate $_fieldEncapsulate;
        
        
/* Remove any stragglers (would happen if user was subscribed to both topics */
        
$this->remove$id1 );
        
        
/* Reset the cache */
        
classes_like_cache::getInstance()->isNowStale$id1 );
        
classes_like_cache::getInstance()->isNowStale$id2 );
    }
}

/**
 *
 * @class        classes_like_cache
 * @brief        Favorites cache class
 * @author        Matt
 */
class classes_like_cache
{
    
/**
     * Registry Object Shortcuts
     *
     * @var        $registry
     * @var        $DB
     * @var        $settings
     * @var        $request
     * @var        $lang
     * @var        $member
     * @var        $memberData
     * @var        $cache
     * @var        $caches
     */
    
protected $registry;
    protected 
$DB;
    protected 
$settings;
    protected 
$request;
    protected 
$lang;
    protected 
$member;
    protected 
$memberData;
    protected 
$cache;
    protected 
$caches;    
    
    
/**
     * Cache expiration time in seconds
     *
     * @var        $_expire
     */
    
protected $_expire 0;
    
    
/**
     * Application key
     *
     * @var        $_app
     */
    
protected $_app;
    
    
/**
     * Area key
     *
     * @var        $_area
     */
    
protected $_area;
    
    
/**
     * Instance of an object
     *
     * @var        $instance
     */
    
private static $instance null;
    
    
/**
     * Singleton
     *
     * @return    @e object
     */
    
public static function getInstance()
    {
        if ( 
self::$instance === null )
        {
            
self::$instance = new self();
        }
        
        return 
self::$instance;
    }
    
    
/**
     * Constructor
     *
     * @return    @e void
     */
    
public function __construct()
    {
        
/* Fetch registry like a good boy */
        
$this->registry   =  ipsRegistry::instance();
        
$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();
        
        
$this->_app  classes_like_registry::getApp();
        
$this->_area classes_like_registry::getArea();
        
        if ( empty( 
$this->_app ) OR empty( $this->_area ) )
        {
            
trigger_error"Missing area or app variable in " __CLASS__ '::' __FUNCTION__E_USER_WARNING );
        }
    }
    
    
/**
     * Set the cache expiration
     *
     * @param    integer        $seconds        Time in seconds
     * @return    @e void
     */
    
public function setExpiration$seconds )
    {
        
$this->_expire intval$seconds );
    }
    
    
/**
     * Fetch an item from the cache
     * 
     * @param    integer        $relId        Relationship ID
     * @param    integer        $memberId     Member ID
     * @return    @e array
     */
    
public function get$relId$memberId=null )
    {
        
$cache = array();
        
        
/* Possible future expansion */
        
if ( $memberId === null )
        {
            
$cache $this->DB->buildAndFetch( array( 'select' => '*',
                                                      
'from'   => 'core_like_cache',
                                                      
'where'  => 'like_cache_id='' .  classes_like_registry::getKey( $relId ) . ''' ) );
            
            if ( !
is_array($cache) OR $cache['like_cache_expire'] <= IPS_UNIX_TIME_NOW )
            { 
                
/* We don't have a valid cache, so create one and return the data */
                
$cache $this->create$relId$memberId );
            }
            
            
$tmp unserialize($cache['like_cache_data']);
            
$cache['members']    = $tmp['members'];
            
$cache['count']        = intval($tmp['count']);
            
$cache['count_anon']= intval($tmp['count_anon']);
            
            
/* Set total count for use elsewhere */
            
classes_like_registry::setTotalCount$cache['count'] );
        }
        
        return 
$cache;
    }
    
    
/**
     * Creates/updates the relationship ID's cache
     * 
     * @param    integer        $relId        Relationship ID
     * @param    integer        $memberId     Member ID
     * @return    @e array    Array of stored data
     */
    
public function create$relId$memberId=null )
    {
        
/* Init vars */
        
$store   = array();
        
$items   = array();
        
$members = array();
        
$mids     = array();
        
$data    = array( 'members' => null'count' => );
        
        
/* Possible future expansion */
        
if ( $memberId === null )
        {
            
/* Count all public first */
            
$cou $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as nt',
                                                     
'from'   => 'core_like',
                                                     
'where'  => 'like_lookup_id='' . classes_like_registry::getKey( $relId ) . '' AND like_is_anon=0 AND like_visible=1' ) );
            
            
$data['count'] = intval$cou['nt'] );
            
            
/* Count all anonymous second */
            
$ano $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as nt',
                                                     
'from'   => 'core_like',
                                                     
'where'  => 'like_lookup_id='' . classes_like_registry::getKey( $relId ) . '' AND like_is_anon!=0 AND like_visible=1' ) );
            
            
$data['count_anon'] = intval$ano['nt'] );
            
            
/* Fetch all items for this app:key:$relId */
            
$this->DB->build( array( 'select' => '*',
                                     
'from'   => 'core_like',
                                     
'where'  => 'like_lookup_id='' . classes_like_registry::getKey( $relId ) . '' AND like_is_anon=0 AND like_visible=1',
                                     
'order'  => 'like_added DESC',
                                     
'limit'  => array( 0)  ) );
                                     
            
$o $this->DB->execute();
            
            while( 
$row $this->DB->fetch($o) )
            {
                
$items$row['like_rel_id'] ]   = $row;
                
$mids$row['like_member_id'] ] = $row['like_member_id'];
            }
                        
            
/* Load members */
            
$members IPSMember::load$mids'core' );
            
            
/* We don't need all the member's infos */
            
foreach( $members as $mid => $mdata )
            {
                
$data['members'][ $mid ] = array( 'n' => $mdata['members_display_name'], 's' => $mdata['members_seo_name'] );
            }
        
            
/* Build save array */
            
$store = array( 'like_cache_id'     => classes_like_registry::getKey$relId ),
                            
'like_cache_app'    => $this->_app,
                            
'like_cache_area'   => $this->_area,
                            
'like_cache_rel_id' => $relId,
                            
'like_cache_data'   => serialize$data ),
                            
'like_cache_expire' => IPS_UNIX_TIME_NOW $this->_expire );
            
            
/* Update the cache */
            
$this->DB->replace'core_like_cache'$store, array( 'like_cache_id' ) );
        }
        else
        {
            
/* We know there's a previous cache row, so delete it */
            
$this->DB->delete'core_like_cache''like_cache_id='' . classes_like_registry::getKey( $relId ) . ''' );
        }
        
        return 
$store;
    }
    
    
/**
     * Deletes a cache
     * 
     * @param    mixed        $relId        Relationship ID or array of IDs
     * @param    integer        $memberId     Member ID
     * @return    @e void
     */
    
public function delete$relId$memberId=null )
    {
        
/* Possible future expansion */
        
if ( $memberId === null )
        {
            
$where '';
            
            if ( 
is_numeric($relId) )
            {
                
$where "='" classes_like_registry::getKey$relId ) . "'";
            }
            elseif ( 
is_array($relId) )
            {
                
$relId IPSLib::cleanIntArray($relId);
                
$keys  = array();
                
                foreach( 
$relId as $id )
                {
                    
$keys[] = "'" classes_like_registry::getKey$id ) . "'";
                }
                
                if ( ! 
count($keys) )
                {
                    return 
null;
                }
                
                
$where " IN (" implode","$keys ) . ")";
            }
            
            
$this->DB->delete'core_like_cache''like_cache_id ' $where );
        }
    }
    
    
/**
     * Flags a cache as stale. We choose to delete, but the cache class should make the call
     * not the application
     * 
     * @param    mixed        $relId        Relationship ID or array of IDs
     * @param    integer        $memberId     Member ID
     * @return    @e void
     */
    
public function isNowStale$relId$memberId null )
    {
        return 
$this->delete$relId$memberId );
    }
}
Онлайн: 1
Реклама