2013年4月16日火曜日

facebook のタブアプリの迷宮

今やすっかり人気の無くなってしまったFBの診断アプリだが、なぜか今更ながら作ってみることにした。FBタブアプリの作り方は多くのサンプルが流れているので、簡単なアプリならば何とかなるだろうと思っていた。

こんな書き出しから始まるのはそれ相応の迷宮に迷い込んだ顛末を記載するからだが、それにしても自分のダメさ加減を毎回さらけ出すわけだから、かなりの自虐好きなのだろう。さらに、このダメさ加減については、ネタ切れという物がないのもまた好都合なのである。

いろいろな方が書いているとおり、「開発者登録→サンプルアプリ配置→SSLサーバー構築→アプリ登録」と駒を進めていくと、開発者登録以外の全てのステージではまりました。はまった順番がこの順番だったわけではなく、サンプルアプリを動かしながら各ステージに原因を求めて延々とさまよったのである。

事の顛末は別にして、アプリって診断アプリとかではなくてグループ上での地図連動型共有データベースみたいな物だったら、小規模グルナビだったり、小規模地理研究グループだったり、色々と使いでありそうな気がするのだが・・・

アプリ登録の失敗

ことの始まりは、「いいね」ボタンの押下の有無の情報を取得するgetSignedRequest()の内容が空であることだった。原因は、FBのアプリ登録画面で「ページタブ」以外の「FBのアプリ」の項目が未入力であったことだった。また、App Domains の入力も「FBのアプリ」で利用しているサーバー名を記入しないとエラーコード 191 というエラーが発生した。いずれも、以下に示すFBの基本設定画面で入力チェックできるはずのものなのにと嘆くのは、責任転嫁好きな私の感想である。また、このgetSignedRequest() の取得元変数 $_REQUESTの中身は php.ini の variables_order、request_order の項目でフォーマット指定できることなど余計な事も学ばさせられた。



SSLサーバーの疑惑

節約の為におれおれSSLサーバーを仕立てたが、上記のgetSignedRequest()が取得できないのは偽SSLのせいなのか区別が出来なかったので、サクラレンタルサーバーを契約してみることにした。結論は正SSLサーバーでも解消しなかったのでおれおれSSLサーバーでも何の問題もなかったわけだが、逃げまどうバンビちゃんはこんなこともしてしまうのである。正規のSSLサーバーを追い求めて検索していると、OpenSSL というフリーの認証書の入手手順もあることを知ったが、独自ドメインと独自メールサーバーが必要なので諦めた次第である。ちなみに、サクラレンタルサーバーを契約したことは後悔していない。OSがFreeBSDで日本語環境の設定も良く解らない状況ながら、20Gもの領域を年5000円で取得できてすこぶる高速なのには気を良くしている。負け惜しみではない。

サンプルアプリの選択

英語が良く読めないことをよいことにFBの開発者ページからPHPのサンプルを適当にコピーして実験用に当てたのだが、タブページ用に利用すべきアプリかどうかの吟味が必要だった。問題は、タブアプリからユーザ認証を呼び出すと認証画面に遷移できずに真っ白な画面になって固まってしまうことである。これの解となる日本語の情報はこちらに見つけたが、難しくて良く解らなかった。だが、そもそもタブページのアプリを起動するユーザは、FB認証したユーザであると仮定してこの問題を無き者することににした。解答らしきページの作者も死にかけたらしいので、命第一である。

FBのアプリ登録が時々おかしくなる

さらに問題の切り分けを難しくしているのが、アプリを登録後に頻繁に修正をしていると、FBのアプリ登録がエラーを返してくるようになることである。一回削除して再度登録し直すか、新しいアプリとして追加登録すると何事もなかったように動き出す。安全な仕様ということで納得することにしよう。

最後に汚いソースだが簡単な診断アプリのソースを記念に貼り付けて終わりとしよう。
  1. 出題定義ファイル
    %cat request.txt
    000:000 ヘビイチゴ
    000:001 ドクイチゴ
    000:002 木イチゴ
    000:IMG ./1.jpg
    001:000 200円
    001:001 400円
    001:002 600円
    001:IMG ./2.jpg
    002:000 ① ヨークシャーテリア
    002:001 ②ゴールデンリトリバー
    002:002 ③フレンチブルドック
    002:IMG ./3.jpg
    END:END 
    
  2. 解凍定義ファイル
    %cat judge.txt
    /000:000/001:000/002:000 パターン1
    /000:000/001:000/002:001 パターン2
    /000:000/001:000/002:002 パターン3
    /000:000/001:001/002:000 パターン4
    /000:000/001:001/002:001 パターン5
    /000:000/001:001/002:002 パターン6
    /000:000/001:002/002:000 パターン7
    /000:000/001:002/002:001 パターン8
    /000:000/001:002/002:002 パターン9
    /000:001/001:000/002:000 パターン10
    /000:001/001:000/002:001 パターン11
    /000:001/001:000/002:002 パターン12
    /000:001/001:001/002:000 パターン13
    /000:001/001:001/002:001 パターン14
    /000:001/001:001/002:002 パターン15
    /000:001/001:002/002:000 パターン16
    /000:001/001:002/002:001 パターン17
    /000:001/001:002/002:002 パターン18
    /000:002/001:000/002:000 パターン19
    /000:002/001:000/002:001 パターン20
    /000:002/001:000/002:002 パターン21
    /000:002/001:001/002:000 パターン22
    /000:002/001:001/002:001 パターン23
    /000:002/001:001/002:002 パターン24
    /000:002/001:002/002:000 パターン25
    /000:002/001:002/002:001 パターン26
    /000:002/001:002/002:002 パターン27
    
  3. ソースコード1
    <?php
      // Remember to copy files from the SDK's src/ directory to a
      // directory in your application on the server, such as php-sdk/
      require_once('facebook.php');
    
      $config = array(
        'appId' => '*******',
        'secret' => '************************',
      );
    
      $facebook = new Facebook($config);
      $user_id = $facebook->getUser();
    
    ?>
    <html>
      <head></head>
      <body>
    
      <?php
        // We have a user ID, so probably a logged in user.
        // If not, we'll get an exception, which we handle below.
        try {
            // 「いいね」の判定
          $signed_request = $facebook->getSignedRequest();
          $like_status = $signed_request['page']['liked'];
          if ($like_status) {
              $par = array(
                'canvas' => 1,
                'fbconnect' => 0,
                'scope' => 'publish_stream,read_friendlists',
                'redirect_uri' => 'http://apps.facebook.com/test-sample-b');
              $fb_login_url = $facebook->getLoginUrl($par);
    
            $user_profile = $facebook->api('/me','GET');
            echo "Name: " . $user_profile['name'];
              include_once('sample.php');
            }
            else {
              echo "<center>";
              echo "いいね押してね";
              echo "</center>";
            }
        } catch(FacebookApiException $e) {
          echo "<center>";
          echo "フェイスブックでログインしてね";
          echo "</center>";
          // If the user is logged out, you can have a 
          // user ID even though the access token is invalid.
          // In this case, we'll get an exception, so we'll
          // just ask the user to login again here.
          $login_url = $facebook->getLoginUrl(); 
    //     echo 'Please <a href="' . $login_url . '">login.</a>';
          error_log($e->getType());
          error_log($e->getMessage());
        }   
      ?>
    
      </body>
    </html>
    
  4. ソースコード2
    <?php
    function endproc($history) {
      // 質問票を連想配列 $LIST に格納
      $lines = file("./judge.txt", FILE_IGNORE_NEW_LINES);
        printf('<p> %s </p>' ,$history);
      foreach ($lines as $line) {
        chop($line);
        list($key,$val) = split("[\t]",$line);
        if ($key === $history) {
            printf('<p> %s </p>' ,$val);
            printf("\n");
            break;
        }
      }
    }
    
    function body1($history) {
      // 選択肢が未選択の場合
      $self   = htmlentities($_SERVER["PHP_SELF"]);
    
      // 質問票を連想配列 $LIST に格納
      $lines = file("./request.txt", FILE_IGNORE_NEW_LINES);
      foreach ($lines as $line) {
        chop($line);
        list($key,$val) = split("[\t]",$line);
        $LIST[$key] = $val;
      }
    
      // 質問項目抽出キーの作成
      list($srh,$dummy) = split("[:]",basename($history));
      if ($srh === "") {
        $srh = "000";
      }
      else {
        $srh = sprintf("%03d",$srh+1);
      }
    
      // 質問画像の貼り付け
      printf('<div>');
      $image = "";
      foreach ($LIST as $key => $val) {
        if(preg_match("/^$srh:IMG/",$key)) {
          $image = sprintf('  <img src="%s" alt="" />',$val);
        }
      }
      if ($image === "") {
        endproc($history);
      }
      else {
        printf("%s\n",$image);
      }
      printf("\n");
      printf("</div>\n");
        
      // リクエストヘッダを作成(form開始)
      if ($image !== "") {
        printf('<div class="sample-down">');
        printf("\n");
        printf('  <form action="'.$self.'" method="GET">');
        printf("\n");
        printf("  <fieldset class=sample-form>\n");
      
        foreach ($LIST as $key => $val) {
          if(preg_match("/^$srh:[0-9]+/",$key)) {
            printf('    <label><input type="radio" ');
            printf('name="ans[]" value="%s"/>%s</label>',$key,$val);
            printf("\n");
          }
        }
    
        printf("  </fieldset>\n");
        printf('  <input type="submit" value="Go!" />');
        printf("\n");
    
        // 画面遷移で共通に利用する変数 
        printf('  <input type="hidden" name="history" value="%s"/>',$history);
        printf("\n");
      }
    
      // リクエストヘッダを作成(form終了)
      printf('</form>');
      printf("</div>\n");
    }
    
    printf("<html>\n");
    printf("  <head>\n");
    printf('  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">');
    printf("\n");
    printf('  <link rel="stylesheet" type="text/css" href="style.css">');
    printf("\n");
    printf("  <title>Buliding a Form</title>\n");
    printf("  </head>\n");
    printf("<body>\n");
    
    printf('<div class="main_style">');
    printf("\n");
    
    if (is_array($_GET["ans"])) {
      // 選択値の取得
      $choice = $_GET["ans"];
    
      // 選択肢が選ばれている場合
      $history = $_GET["history"];
      $history .= ("/" .$choice[0]);
      body1($history,$choice);
    }
    else {
      // 選択肢が選ばれていない場合
      if (isset($_GET["history"])) {
        $history = $_GET["history"];
        body1($history,"");
      }
      else {
        body1($history,"");
      }
    }
    printf("</div>\n");
    printf("</body>\n");
    printf("</html>");
    
  5. 動作サンプル

0 件のコメント:

コメントを投稿