<?php
/**
 * simpleblog_posttweet
 *
 * 指定日分のTweetを取得し、ブログ記事を投稿する
 *
 * @package   simpleblog
 * @author    Risoluto Developers
 * @license   http://opensource.org/licenses/bsd-license.php new BSD license
 * @copyright (C) 2008-2011 Risoluto Developers / All Rights Reserved.
 */

  //------------------------------------------------------//
  // 動作環境チェック
  //------------------------------------------------------//
  // PHPバージョンが5.0.0以前であれば強制終了
  if ( version_compare( PHP_VERSION, '5.0.0', '<' ) )
  {
    exit( 'Risoluto requires PHP 5.0.0 or later...' );
  } // end of if

  // php.iniの設定値が動作上支障を及ぼす可能性がある場合強制終了
  if (
          ini_get( 'magic_quotes_gpc' )
       or ini_get( 'magic_quotes_runtime' )
       or ini_get( 'magic_quotes_sybase' )
       or ini_get( 'register_globals' )
       or ini_get( 'register_long_arrays' )
       or ini_get( 'safe_mode' )
     )
  {
    exit( 'magic_quotes_gpc/magic_quotes_runtime/magic_quotes_sybase/register_globals/register_long_arrays/safe_mode are MUST be off.' );
  } // end of if

  //------------------------------------------------------//
  // 定数定義
  //------------------------------------------------------//
  define( 'RISOLUTODIR',       dirname( dirname( dirname ( __FILE__ ) ) ) );
  define( 'RISOLUTO_SYSROOT',  RISOLUTODIR . '/risoluto/'                 );
  define( 'RISOLUTO_BATCH',    RISOLUTODIR . '/risoluto/batch/'           );
  define( 'RISOLUTO_CACHE',    RISOLUTODIR . '/risoluto/cache/'           );
  define( 'RISOLUTO_CONF',     RISOLUTODIR . '/risoluto/conf/'            );
  define( 'RISOLUTO_EXTLIB',   RISOLUTODIR . '/risoluto/extlib/'          );
  define( 'RISOLUTO_FUNC',     RISOLUTODIR . '/risoluto/func/'            );
  define( 'RISOLUTO_LOGS',     RISOLUTODIR . '/risoluto/logs/'            );
  define( 'RISOLUTO_SESS',     RISOLUTODIR . '/risoluto/sess/'            );
  define( 'RISOLUTO_UPLOAD',   RISOLUTODIR . '/risoluto/upload/'          );
  define( 'RISOLUTO_USERLAND', RISOLUTODIR . '/risoluto/userland/'        );

  //------------------------------------------------------//
  // インクルードパスの変更
  //------------------------------------------------------//
  //------------------------------------------------------//
  set_include_path(                 RISOLUTO_EXTLIB . 'PEAR' 
                 . PATH_SEPARATOR . RISOLUTO_EXTLIB . 'Smarty/libs'
                 . PATH_SEPARATOR . get_include_path() );

  //------------------------------------------------------//
  // クラスファイルの読み込み
  //------------------------------------------------------//
  /**
   * コンフィグ操作クラス
   */
  require_once( RISOLUTO_FUNC . 'risoluto_conf.php' );

  /**
   * ログ出力クラス
   */
  require_once( RISOLUTO_FUNC . 'risoluto_log.php' );

  /**
   * セッションクラス
   */
  require_once( RISOLUTO_FUNC . 'risoluto_session.php' );

  /**
   * ユーティリティファンクションクラス
   */
  require_once( RISOLUTO_FUNC . 'risoluto_util.php' );

  /**
   * データベース操作クラス
   */
  require_once( RISOLUTO_FUNC . 'risoluto_db.php' );

  /**
   * Smartyクラス
   */
  require_once( 'Smarty.class.php' );

  class simpleblog_posttweet
  {
    //------------------------------------------------------//
    // クラス変数定義
    //------------------------------------------------------//
    /**
     * クラスインスタンスを保持する変数
     * @access private
     * @var    object
     */
    private static $obj_instance;
    /**
     * コンフィグクラスインスタンスを保持する変数
     * @access private
     * @var    object
     */
    private $obj_conf;
    /**
     * コンフィグクラスインスタンスを保持する変数
     * @access private
     * @var    object
     */
    private $obj_simpleblogconf;
    /**
     * ログクラスインスタンスを保持する変数
     * @access private
     * @var    object
     */
    private $obj_logs;
    /**
     * セッションクラスインスタンスを保持する変数
     * @access private
     * @var    object
     */
    private $obj_sess;
    /**
     * ユーティリティクラスインスタンスを保持する変数
     * @access private
     * @var    object
     */
    private $obj_util;
    /**
     * データベース操作クラスインスタンスを保持する変数
     * @access private
     * @var    object
     */
    private $obj_db;
    /**
     * Smartyインスタンス格納用変数
     * @access private
     * @var    object
     */
    private $smarty;

    //------------------------------------------------------//
    // クラスメソッド定義
    //------------------------------------------------------//
    /**
     * コンストラクタメソッド
     *
     * コントローラのコンストラクタメソッド
     *
     * @param     void なし
     * @return    void なし
     */
    private function __construct()
    {
    } // end of function:__construct()

    /**
     * クローンメソッド
     *
     * コントローラのクローンメソッド
     *
     * @param     void なし
     * @return    void なし
     */
    public function __clone()
    {
    } // end of function:__clone()

    /**
     * シングルトンメソッド
     *
     * コントローラのインスタンスをシングルトンパターンで生成する
     *
     * @param     void なし
     * @return    object インスタンス
     */
    public static function singleton()
    {
        if ( ! isset( self::$obj_instance ) )
        {
            $tmp_myself = __CLASS__;
            self::$obj_instance = new $tmp_myself;
        } // end of if

        return self::$obj_instance;
    }

    /**
     * run()
     *
     * コントローラメインロジック
     *
     * @param     void なし
     * @return    void なし
     */
    public function run()
    {

      //------------------------------------------------------//
      // 必要なオブジェクトを生成する
      //------------------------------------------------------//
      // コンフィグ
      $this->obj_conf = new RisolutoConf();
      $this->obj_conf->parse( RISOLUTO_CONF . 'risoluto.ini' );
      // コンフィグ(simpleblog)
      $this->obj_simpleblogconf = new RisolutoConf();
      $this->obj_simpleblogconf->parse( RISOLUTO_CONF . 'simpleblog.ini' );
      // ログ
      $this->obj_logs = RisolutoLog::singleton();
      // セッション
      $this->obj_sess = RisolutoSession::singleton();
      // ユーティリティ
      $this->obj_util = RisolutoUtils::singleton();
      // データベース
      $this->obj_db   = new RisoluteDb();

      //------------------------------------------------------//
      // 初期化処理
      //------------------------------------------------------//
      // バッチ内処理関連
      $tmp_tweets     = array();
      $tmp_targetday  = date( 'Y-m-d', strtotime( $this->obj_simpleblogconf->get( 'simpleblog_posttweet', 'targetday' ) ) );
      $tmp_stopday    = date( 'Y-m-d', strtotime( $this->obj_simpleblogconf->get( 'simpleblog_posttweet', 'stopday'   ) ) );
      $tmp_fetchnext  = true;
      $tmp_getpage    = 0;
      $tmp_screenname = $this->obj_simpleblogconf->get( 'simpleblog_posttweet', 'screen_name' );
      $tmp_retweets   = $this->obj_simpleblogconf->get( 'simpleblog_posttweet', 'retweets'    );
      $output_title   = str_replace( '[DATE]', $tmp_targetday, $this->obj_simpleblogconf->get( 'simpleblog_posttweet', 'title' ) );
      $output_body    = null;

      // Smarty関連定義
      $this->smarty                = new Smarty;
      $this->smarty->template_dir  = RISOLUTO_BATCH;
      $this->smarty->config_dir    = RISOLUTO_BATCH;
      $this->smarty->compile_dir   = RISOLUTO_CACHE;
      $this->smarty->cache_dir     = RISOLUTO_CACHE;
      $this->smarty->caching       = false;
      $this->smarty->debugging     = false;
      $this->smarty->force_compile = true;
      $this->smarty->compile_check = true;

      //------------------------------------------------------//
      // 前日分のTweetを収集する
      //------------------------------------------------------//
      //--- 対象日より過去のTweetを検出するまでループ
      do
      {
        // Tweet情報を取得する
        $tmp_getitems = json_decode( $this->getTweets( $tmp_screenname, $tmp_getpage++, $tmp_retweets ), true );

        // 取得したTweetをひとつづつ分解して対象日のTweetだけ取得
        foreach( $tmp_getitems as $dat )
        {
          // Tweet中の時刻情報を整形して判定
          $tmp_currentday = date( 'Y-m-d', strtotime( $dat[ 'created_at' ] ) );

          if ( $tmp_targetday == $tmp_currentday )
          {
            // リツイートなら一部の情報をリツイート元ユーザの情報にする
            if ( isset( $dat[ 'retweeted_status' ] ) )
            {
              $tmp_tweets[ $dat[ 'id' ] ] = array(
                                                   'date'          => date( 'Y-m-d H:i:s', strtotime( $dat[ 'created_at' ] ) )
                                                 , 'tweet'         => $this->obj_util->autoUrlLink( $dat[ 'text' ] )
                                                 , 'profile_image' => $dat[ 'retweeted_status' ][ 'user' ][ 'profile_image_url' ]
                                                 , 'screen_name'   => $dat[ 'retweeted_status' ][ 'user' ][ 'screen_name' ]
                                                 , 'url'           => $dat[ 'retweeted_status' ][ 'user' ][ 'url' ]
                                                 );
            } // end of if
            // 普通のTweetならそのまま出力
            else
            {
              $tmp_tweets[ $dat[ 'id' ] ] = array(
                                                   'date'          => date( 'Y-m-d H:i:s', strtotime( $dat[ 'created_at' ] ) )
                                                 , 'tweet'         => $this->screennameLink( $this->obj_util->autoUrlLink( $dat[ 'text' ] ), $dat[ 'in_reply_to_screen_name' ] )
                                                 , 'profile_image' => $dat[ 'user' ][ 'profile_image_url' ]
                                                 , 'screen_name'   => $dat[ 'user' ][ 'screen_name' ]
                                                 , 'url'           => $dat[ 'user' ][ 'url' ]
                                                 );
            } // end of else

          } // end of if
          // Tweet中の時刻情報が対象日より過去なら処理を停止する
          else
          if ( $tmp_stopday >= $tmp_currentday )
          {
            // フラグを立ててHTMLを生成する
            $tmp_fetchnext = false;

            $this->smarty->assign( 'tweets', $tmp_tweets );
            $output_body = $this->smarty->fetch( 'simpleblog_posttweet.tpl' );
            break;
          } // end of else if
        } // end of foreach

      } while( $tmp_fetchnext ); // end of do - while



      // もし登録するTweetが無ければこの時点で終了する
      if ( empty( $output_body ) )
      {
        exit;
      } // end of if



      //------------------------------------------------------------------------
      // 現在の最新IDを取得
      //------------------------------------------------------------------------

      // データベースに接続する
      if ( $this->obj_db->dbConnect( $this->obj_conf->get( 'DBS', 'DEFAULT_DSN' ) ) )
      {
        // トランザクションを開始
        $this->obj_db->dbBeginTransaction();

        // SQL文を組み立てる
        $sql =<<<End_Of_SQL

              SELECT MAX( `blog`.`blog_id` ) + 1 as latest_id
                FROM `simpleblog_t_blog` blog
                 FOR UPDATE;

End_Of_SQL;

        // パラメタも用意する
        $param = array(
                      );

        // SQLの実行に失敗した場合は、例外を投げる
        $tmp_result = $this->obj_db->dbGetRow( $sql, $param );
        if ( PEAR::isError( $tmp_result ) )
        {
          // 接続をクローズする
          $this->obj_db->dbDisConnect();
          throw new Exception( 'Latest blog_id Get Failure' );
          return false;
        } // end of if
        // 入力された値と、取得した値を使ってデータをインサートする
        else
        {

          // 取得したIDが上限値を上回っていた場合、例外を投げる
          if ( $tmp_result[ 'latest_id' ] > $this->obj_conf->get( 'LIMITS', 'id_upper' ) )
          {
            // 接続をクローズする
            $this->obj_db->dbDisConnect();
            throw new Exception( 'id was exceeded upper limit' );
             return false;
          }// end of if

          // 取得したIDが空なら「1」をセットする
          if ( empty( $tmp_result[ 'latest_id' ] ) )
          {
            $tmp_result[ 'latest_id' ] = 1;
          } // end of if

          // トランザクションを開始
          $this->obj_db->dbBeginTransaction();

          // SQL文を組み立てる
          $sql =<<<End_Of_SQL

              INSERT INTO `simpleblog_t_blog`
              (
                `ctime`
              , `cuser`
              , `mtime`
              , `muser`
              , `user_id`
              , `blog_id`
              , `blog_title`
              , `blog_body`
              , `open_datetime`
              , `end_datetime`
              , `post_datetime`
              ) VALUES (
                  now()
                , 'simpleblog_posttweet'
                , now()
                , 'simpleblog_posttweet'
                , '0'
                , ?
                , ?
                , ?
                , now()
                , '0000-00-00 00:00:00'
                , now()
              );

End_Of_SQL;

          // パラメタも用意する
          $param = array(
                          $tmp_result [ 'latest_id' ]
                        , $output_title
                        , $output_body
                        );

          // Insertを実行
          $tmp_insert_result = $this->obj_db->dbExecSQL( $sql, $param );
          if ( PEAR::isError( $tmp_insert_result ) )
          {
            // エラーの場合はロールバックして接続を閉じ、例外を投げる
            $this->obj_db->dbRollback();
            $this->obj_db->dbDisConnect();
            throw new Exception( 'Insert Failure' );
            return false;
          } // end of if
          else
          {
            // 正常に実行できた場合はコミットして接続を閉じる
            $this->obj_db->dbCommit();
            $this->obj_db->dbDisConnect();
          } // end of else

        } // end of else

      } // end of if
      // データベースに接続できなかった場合は、例外を投げる
      else
      {
        throw new Exception( 'DB Connection Failure' );
        return false;
      } // end of else

    } // end of function::run()

    private function getTweets( $screen_name, $page = 1, $retweets = 'true' )
    {
      // 標準出力を抑制するため、/dev/nullなファイルポインタをオープン
      $tweetsfp       = fopen( '/dev/null', 'w' ); 

      // API呼び出し用にcURLをオープン
      $tweetscurl     = curl_init( 'https://api.twitter.com/1/statuses/user_timeline.json?screen_name=' . $screen_name . '&page=' . $page . '&include_rts=' . $retweets );

      // cURLのオプション設定（GETメソッドでアクセス、標準出力へのアウトプット抑止、成功時結果を取得）
      curl_setopt( $tweetscurl, CURLOPT_HTTPGET,        true      );
      curl_setopt( $tweetscurl, CURLOPT_FILE,           $tweetsfp );
      curl_setopt( $tweetscurl, CURLOPT_RETURNTRANSFER, true      );

      // cURLの実行（＝APIの呼び出し）
      $tweetsdat      = curl_exec( $tweetscurl );

      // cURLとファイルポインタをクローズ
      curl_close( $tweetscurl );
      fclose( $tweetsfp );

      return $tweetsdat;

    } // end of function::getTweets();

    /**
     * Tweetユーザ自動リンク設定メソッド
     *
     * 引数で指定された文字列中のscreen_name部分をリンクに変換する
     *
     * @param     string    $target      対象となる文字列
     * @param     boolean   $screen_name スクリーンネーム
     * @return    string    変換後の文字列
     */
    private function screennameLink( $target, $screen_name )
    {
      // 一度、一時変数へ格納する
      $tmp_target     = trim( $target      );
      $tmp_screenname = trim( $screen_name );

      // screen_nameが空っぽなら$targetをそのまま返す
      if ( empty( $tmp_screenname ) )
      {
        return $tmp_target;
      } // end of if

      // screen_nameを置換する
      $tmp_replace_text = '<a href="http://twitter.com/\\0" onclick="window.open(\'http://twitter.com/\\0\');return false;">\\0</a>';
      $tmp_target = preg_replace( "/$tmp_screenname/i", $tmp_replace_text, $tmp_target );

      return $tmp_target;
    }  // end of function:screennameLink()

  } // end of class:simpleblog_posttweet

  //------------------------------------------------------//
  // 実行
  //------------------------------------------------------//
  $tmp_this = simpleblog_posttweet::singleton();
  $tmp_this->run();

?>
