Seditio Source
Root |
./othercms/ips_4.3.4/applications/convert/sources/App.php
<?php

/**
 * @brief        Converter Applications Class
 * @author        <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
 * @copyright    (c) Invision Power Services, Inc.
 * @package        Invision Community
 * @subpackage    Converter
 * @since        21 Jan 2015
 */

namespace IPS\convert;

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

class
_App extends \IPS\Patterns\ActiveRecord
{
   
/**
     * @brief    [ActiveRecord] ID Database Column
     */
   
public static $databaseColumnId        = 'app_id';
   
   
/**
     * @brief    [ActiveRecord] Database table
     */
   
public static $databaseTable        = 'convert_apps';
   
   
/**
     * @brief    [ActiveRecord] Database ID Fields
     */
   
protected static $databaseIdFields    = array( 'name' );
   
   
/**
     * @brief    Array Storage of loaded ID links.
     */
   
protected $linkCache                = array();
   
   
/**
     * @brief    Flag to indicate the log is simply a notice, and that it is informational only. Useful for when data is missing, but can be covered via default values.
     */
   
const LOG_NOTICE                    = 1;
   
   
/**
     * @brief    Flag to indicate the log is a warning, and should be checked to see if conversion happened correctly. Useful for indicating something cannot be converted due to being orphaned or otherwise having missing data (ex. no parent topic).
     */
   
const LOG_WARNING                    = 2;
   
   
/**
     * @brief    Flag to indicate something went wrong, and the data did not convert correctly. Useful for indicating when something legitimately does not convert, but should.
     */
   
const LOG_ERROR                        = 3;
   
   
/**
     * Get converted apps
     *
     * @return    array
     */
   
public static function apps()
    {
        if ( !isset( \
IPS\Data\Store::i()->convert_apps ) )
        {
            try
            {
               
$rows = iterator_to_array( \IPS\Db::i()->select( '*', 'convert_apps', array(), 'app_id ASC' ) );
            }
            catch ( \
IPS\Db\Exception $e )
            {
                if (
$e->getCode() === 1146 )
                {
                   
$rows = iterator_to_array( \IPS\Db::i()->select( '*', 'conv_apps', array(), 'app_id ASC' ) );
                }
                else
                {
                    throw
$e;
                }
            }
           
            \
IPS\Data\Store::i()->convert_apps = $rows;
        }
       
        return new \
IPS\Patterns\ActiveRecordIterator( new \ArrayIterator( \IPS\Data\Store::i()->convert_apps ), 'IPS\convert\App' );
   
    }
   
   
/**
     * [ActiveRecord]    Save Record
     *
     * @return    void
     */
   
public function save()
    {
        if ( !
$this->app_id )
        {
           
$this->start_date = time();
           
parent::save();
           
            \
IPS\convert\Application::checkConvParent( $this->getSource()->getLibrary()->app );
           
            \
IPS\Db::i()->insert( 'convert_app_sessions', array(
               
'session_app_id'    => $this->app_id,
               
'session_app_data'    => json_encode( array( 'completed' => array(), 'working' => array(), 'more_info' => array() ) ),
            ) );
        }
       
       
$classname            = get_class( $this->getSource( TRUE, FALSE ) );
       
$this->login        = ( $classname::loginEnabled() === TRUE ) ? 1 : 0;
       
$this->db_driver    = 'mysql';  /* I was going to drop this, but it has the potential for expansion in the future, as all we do is select from the source */
       
$this->app_merge    = 1;
       
parent::save();
       
        unset( \
IPS\Data\Store::i()->convert_apps );
    }
   
   
/**
     * [ActiveRecord]    Delete Record
     *
     * @return    void
     */
   
public function delete()
    {
        foreach( array(
'convert_link', 'convert_link_pms', 'convert_link_topics', 'convert_link_posts' ) AS $table )
        {
            \
IPS\Db::i()->delete( $table, array( 'app=?', $this->app_id ) );
        }
       
        \
IPS\Db::i()->delete( 'convert_app_sessions', array( 'session_app_id=?', $this->app_id ) );
        \
IPS\Db::i()->delete( 'convert_logs', array( 'log_app=?', $this->app_id ) );
       
       
parent::delete();
       
        unset( \
IPS\Data\Store::i()->convert_apps );
    }

   
/**
     * @brief    Session data store
     */
   
protected $_sessionData = null;
   
   
/**
     * Save Session Data for this application
     *
     * @param    array    Session Data
     * @return    void
     */
   
public function set__session( $value )
    {
        \
IPS\Db::i()->update( 'convert_app_sessions', array( 'session_app_data' => json_encode( $value ) ), array( 'session_app_id=?', $this->app_id ) );

       
$this->_sessionData = $value;
    }
   
   
/**
     * Get Session Data for this application
     *
     * @return    array
     */
   
public function get__session()
    {
       
/* Use ready-cached session data */
       
if( $this->_sessionData !== null )
        {
            return
$this->_sessionData;
        }

        try
        {
           
$this->_sessionData = json_decode( \IPS\Db::i()->select( 'session_app_data', 'convert_app_sessions', array( 'session_app_id=?', $this->app_id ) )->first(), TRUE );
            return
$this->_sessionData;
        }
        catch( \
Exception $e )
        {
           
/* If it doesn't exist, create it in the database and return an empty array. */
           
\IPS\Db::i()->insert( 'convert_app_sessions', array(
               
'session_app_id'    => $this->app_id,
               
'session_app_data'    => json_encode( array( 'completed' => array(), 'working' => array(), 'more_info' => array() ) )
            ) );
            return array(
'completed' => array(), 'working' => array(), 'more_info' => array() );
        }
    }
   
   
/**
     * [Legacy] Get Software. Automatically adjusts if legacy software which has since had its application key changed.
     *
     * @return    string
     */
   
public function get_sw()
    {
        switch(
$this->_data['sw'] )
        {
            case
'board':
                return
'forums';
            break;
           
            case
'ccs':
                return
'cms';
            break;
           
            default:
                return
$this->_data['sw'];
            break;
        }
    }
   
   
/**
     * [Legacy] Automatically fix any legacy application keys that have changed.
     *
     * @return    void
     */
   
public function set_sw( $value )
    {
        switch(
$value )
        {
            case
'board':
               
$this->_data['sw'] = 'forums';
            break;
           
            case
'ccs':
               
$this->_data['sw'] = 'cms';
            break;
           
            default:
               
$this->_data['sw'] = $value;
            break;
        }
    }
   
   
/**
     * @brief    Parent Store
     */
   
protected $parentStore = NULL;
   
   
/**
     * Get parent application
     *
     * @return    \IPS\convert\App
     * @throws    \BadMethodCallException
     */
   
public function get__parent()
    {
        if (
is_null( $this->parentStore ) )
        {
            if ( !
$this->parent )
            {
                throw new \
BadMethodCallException;
            }
           
            try
            {
               
$this->parentStore = static::constructFromData( \IPS\Db::i()->select( '*', 'convert_apps', array( "app_id=?", $this->parent ) )->first() );
            }
            catch( \
UnderflowException $e )
            {
                throw new \
BadMethodCallException;
            }
            catch( \
OutOfRangeException $e )
            {
                throw new \
BadMethodCallException;
            }
        }
       
        return
$this->parentStore;
    }
   
   
/**
     * Retrieves an Invision Community ID from a Foreign ID.
     *
     * @param    mixed            $foreign_id        The Foreign ID
     * @param    string|array    $type            The type of item, or an array of types to check.
     * @param    boolean            $parent            If set to TRUE, then retrieves from the parent application if one is available.
     * @param    boolean            $mainTable        If set to TRUE, and the type is of either 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', or 'forums_posts', then the ID is retrieved from convert_link, rather than the other link tables.
     *
     * @return    integer    The Invision Community ID.
     * @throws    \OutOfRangeException
     */
   
public function getLink( $foreign_id, $type, $parent=FALSE, $mainTable=FALSE )
    {
        if ( !
is_array( $type ) )
        {
           
$type = array( $type );
        }
       
       
$key = md5( json_encode( $type ) );
       
        if ( isset(
$this->linkCache[ $key ][ $foreign_id ] ) )
        {
            return
$this->linkCache[ $key ][ $foreign_id ];
        }
       
       
$table = 'convert_link';
       
        if (
$mainTable === FALSE )
        {
            foreach(
$type AS $t )
            {
                if (
in_array( $t, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) )
                {
                    if (
in_array( $t, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
                    {
                       
$table = 'convert_link_pms';
                    }
                    else
                    {
                       
$tableType = str_replace( 'forums_', '', $t );
                       
$table = "convert_link_{$tableType}";
                    }
                }
            }
        }
       
        try
        {
           
$link = \IPS\Db::i()->select( 'ipb_id', $table, array( \IPS\Db::i()->in( 'type', $type ) . ' AND foreign_id=? AND app=?', (string) $foreign_id, ( $parent === TRUE AND $this->parent ) ? $this->parent : $this->app_id ), 'link_id DESC' )->first();
           
$this->linkCache[ $key ][ $foreign_id ] = $link;
            return
$this->linkCache[ $key ][ $foreign_id ];
        }
        catch( \
UnderflowException $e )
        {
           
/* If lookup failed, and we have a parent, try it anyway */
           
try
            {
               
$link = \IPS\Db::i()->select( 'ipb_id', $table, array( \IPS\Db::i()->in( 'type', $type ) . ' AND foreign_id=? AND app=?', (string) $foreign_id, $this->parent ), 'link_id DESC' )->first();
               
$this->linkCache[ $key ][ $foreign_id ] = $link;
                return
$this->linkCache[ $key ][ $foreign_id ];
            }
            catch( \
UnderflowException $e ) {}
           
           
/* Still here? Throw the exception */
           
throw new \OutOfRangeException( 'link_invalid' );
        }
    }
   
   
/**
     * @brief    Sibling Link Cache
     */
   
protected $siblingLinkCache = array();
   
   
/**
     * Retrieves an Invision Community iD from a Foriegn ID in a Sibling Application
     *
     * @param    mixed        $foreign_id        The Foreign ID.
     * @param    string        $type            The type of item.
     * @param    string        $sibling        The sibling software library.
     * @param    boolean        $mainTable        If set to TRUE, and the type is of either 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', or 'forums_posts', then the ID is retrieved from convert_link, rather than the other link tables.
     *
     * @return    inteer    The Invision Community ID.
     * @throws    \OutOfRangeException
     */
   
public function getSiblingLink( $foreign_id, $type, $sibling, $mainTable=FALSE )
    {
        if ( isset(
$this->siblingLinkCache[$type][$sibling][$foreign_id] ) )
        {
            return
$this->siblingLinkCache[$type][$sibling][$foreign_id];
        }
       
        try
        {
           
$sibling = static::constructFromData( \IPS\Db::i()->select( '*', 'convert_apps', array( "sw=? AND parent=?", $sibling, $this->parent ) )->first() );
        }
        catch( \
UnderflowException $e )
        {
            throw new \
OutOfRangeException( 'sibling_invalid' );
        }
       
        return
$sibling->getLink( $foreign_id, $type, FALSE, $mainTable );
    }
   
   
/**
     * Saves a foreign ID to Invision Community ID reference to the convert_link tables.
     *
     * @param    integer        $ips_id            The Invision Community ID
     * @param    mixed        $foreign_id        The Foreign ID
     * @param    string        $type            The type of item
     * @param    boolean        $duplicate        If TRUE, then this item is a duplicate and was merged into existing $ips_id
     * @param    boolean        $mainTable        If TRUE, then link will be stored in the main convert_link table even if $type is 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', or 'forums_posts'
     *
     * @return    void
     */
   
public function addLink( $ips_id, $foreign_id, $type, $duplicate=FALSE, $mainTable=FALSE )
    {
       
$table = 'convert_link';
       
        if (
in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) AND $mainTable === FALSE )
        {
            if (
in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
            {
               
$table = 'convert_link_pms';
            }
            else
            {
               
$tableType = str_replace( 'forums_', '', $type );
               
$table = "convert_link_{$tableType}";
            }
        }
       
        \
IPS\Db::i()->insert( $table, array(
           
'ipb_id'        => $ips_id,
           
'foreign_id'    => $foreign_id,
           
'type'            => $type,
           
'duplicate'        => ( $duplicate === TRUE ) ? 1 : 0,
           
'app'            => $this->app_id
       
) );
       
       
$this->linkCache[$type][$foreign_id] = $ips_id;
    }
   
   
/**
     * Checks to see if a link exists for an Invision Community ID
     *
     * @param    integer    $ips_id    The Invision Community ID.
     * @return    void
     * @throws    \OutOfRangeException
     */
   
public function checkLink( $ips_id, $type, $mainTable=FALSE )
    {
       
$table = 'convert_link';
       
        if (
in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) AND $mainTable === FALSE )
        {
            if (
in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
            {
               
$table = 'convert_link_pms';
            }
            else
            {
               
$tableType = str_replace( 'forums_', '', $type );
               
$table = "convert_link_{$tableType}";
            }
        }
       
        try
        {
           
$link = \IPS\Db::i()->select( '*', $table, array( "app=? AND ipb_id=? AND type=?", $this->app_id, $ips_id, $type ) )->first();
        }
        catch( \
UnderflowException $e )
        {
            throw new \
OutOfRangeException;
        }
    }
   
   
/**
     * Checks to see if this application also has a sibling of a specific type
     *
     * @param    string    $software    The application key to look for
     * @return    void
     * @throws    \OutOfRangeException
     */
   
public function checkForSibling( $software )
    {
        try
        {
            \
IPS\Db::i()->select( '*', 'convert_apps', array( "sw=? AND parent=?", $software, $this->parent ) )->first();
        }
        catch( \
UnderflowException $e )
        {
            throw new \
OutOfRangeException;
        }
    }

   
/**
     * Delete a stored foreignId <-> IPS4 ID relation
     *
     * @param    mixed        $foreignId            Foreign data ID
     * @param    string        $type                The type of item
     */
   
public function deleteLink( $foreignId, $type )
    {
       
/* Build cache key */
       
if ( !is_array( $type ) )
        {
           
$type = array( $type );
        }

       
$key = md5( json_encode( $type ) );

       
$table = 'convert_link';

        if (
in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) )
        {
            if (
in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
            {
               
$table = 'convert_link_pms';
            }
            else
            {
               
$tableType = str_replace( 'forums_', '', $type );
               
$table = "convert_link_{$tableType}";
            }
        }

        \
IPS\Db::i()->delete( $table, array( 'foreign_id=? AND type=? AND app=?', $foreignId, $type, $this->app_id ) );

       
/* Remove this from the link cache */
       
if( isset( $this->linkCache[ $key ][ $foreignId ] ) )
        {
            unset(
$this->linkCache[ $key ][ $foreignId ] );
        }
    }
   
   
/**
     * @brief    Sibling Cache
     */
   
protected $siblingCache = array();
   
   
/**
     * Construct an \IPS\convert\App object for a sibling application.
     *
     * @param    string    $software    The application key to look for
     * @return    \IPS\convert\APP
     * @throws    \OutOfRangeException
     */
   
public function getSibling( $software )
    {
        if ( !isset(
$this->siblingCache[$software] ) )
        {
            try
            {
               
$this->siblingCache[$software] = static::constructFromData( \IPS\Db::i()->select( '*', 'convert_apps', array( "sw=? AND parent=?", $software, $this->parent ) )->first() );
            }
            catch( \
Exception $e )
            {
                throw new \
OutOfRangeException;
            }
        }
       
        return
$this->siblingCache[$software];
    }
   
   
/**
     * Fetch the source application class file.
     *
     * @param    bool    Construct the object
     * @param    bool    Establish a database connection ($construct must be TRUE)
     * @return    \IPS\convert\Software|string
     * @throws    \InvalidArgumentException
     */
   
public function getSource( $construct=TRUE, $needDB=TRUE )
    {
       
/* Update software keys for communities converted to 3.x */
       
switch( $this->_data['sw'] )
        {
            case
'board':
            case
'ccs':
               
$this->sw = $this->sw;
               
$this->save();
            break;
        }

       
/* Change any app keys to newer versions */
       
switch( $this->data['app_key'] )
        {
            case
'photopost8':
               
$this->app_key = 'photopost';
               
$this->save();
            break;
        }

        if ( !
class_exists( 'IPS\\convert\\Software\\' . ucwords( $this->_data['sw'] ) . '\\' . ucwords( $this->_data['app_key'] ) ) )
        {
            throw new \
InvalidArgumentException( 'invalid_source' );
        }
       
       
$classname = 'IPS\\convert\\Software\\' . ucwords( $this->_data['sw'] ) . '\\' . ucwords( $this->_data['app_key'] );
       
        if (
$construct )
        {
            return new
$classname( $this, $needDB );
        }
        else
        {
            return
$classname;
        }
    }
   
   
/**
     * Log Something
     *
     * @param    string        $message    The message to log.
     * @param    string        $method        The current conversion method (convert_posts, convert_topics, etc.)
     * @param    integer        $severity    The severity level of the log. Default to LOG_NOTICE
     * @return    void
     * @throws \InvalidArgumentException
     */
   
public function log( $message, $method, $severity=1, $id=NULL )
    {
        if ( !
in_array( $severity, array( static::LOG_NOTICE, static::LOG_WARNING, static::LOG_ERROR ) ) )
        {
            throw new \
InvalidArgumentException( 'invalid_severity' );
        }
       
        \
IPS\Db::i()->insert( 'convert_logs', array(
           
'log_message'    => $message,
           
'log_app'        => $this->app_id,
           
'log_severity'    => $severity,
           
'log_method'    => $method,
           
'log_item_id'    => $id,
           
'log_time'        => time()
        ) );
    }
   
   
/**
     * Callback function to return all dependencies not yet converted.
     *
     * @param    string    Value from depency array
     * @return    boolean
     */
   
public function dependencies( $value )
    {
        if ( !
in_array( $value, $this->_session['completed'] ) )
        {
            return
TRUE;
        }
       
        return
FALSE;
    }
   
   
/**
     * Saves information about the current step to the session.
     *
     * @param    array    $values        Values from the form.
     * @return    void
     */
   
public function saveMoreInfo( $method, $values=array() )
    {
       
$sessionData = $this->_session;
       
        unset(
$values['reconfigure'], $values['empty_local_data'] );
       
       
$this->_session = array( 'working' => $sessionData['working'], 'completed' => $sessionData['completed'], 'more_info' => array_merge( $sessionData['more_info'], array( $method => $values ) ) );
    }
   
   
/**
     * Magic __isset() method
     *
     * @param    mixed $key key
     * @return    bool
     */
   
public function __isset( $key )
    {
        if (
method_exists( $this, 'get_' . $key ) )
        {
            return
TRUE;
        }
       
        if ( isset(
$this->data[$key] ) )
        {
            return
TRUE;
        }

        return
FALSE;
    }

   
/**
     * Set running flag
     *
     * @param    string        $method        Method name
     * @param     bool        $status        Flag status
     */
   
public function setRunningFlag( $method, $status )
    {
       
$running = isset( $this->_session['running'] ) ? $this->_session['running'] : array();

       
/* If setting running flag, set to a timestamp */
       
if( $status === TRUE )
        {
           
$status = time();
        }

       
$running[ $method ] = $status;

       
$this->_session = array_merge( $this->_session, array( 'running' => $running ) );
    }

   
/**
     * Get running flag
     *
     * @param    string        $method        Method name
     * @return    bool|int                False or a timestamp
     */
   
public function getRunningFlag( $method )
    {
        if( isset(
$this->_session['running'][ $method ] ) )
        {
            return
$this->_session['running'][ $method ];
        }

        return
FALSE;
    }
}