SQLの窓

2015年01月28日


JavaScript : setTimeout の第一引数の正しい書き方。

IE10、IE11 で第三引数以降を使って関数に値を引き渡せるので、第一引数は無名関数を書くか、定義済みの関数名を書くのが正解です。但し、過去の IE9 以前の対応が必要な場合は、グローバル変数等を使用すればいいと思います。文字列は使えますが、セキュリティ上の理由から避けたほうが良いとされています。

昔の IE のマニュアルにはこう書いていたのですが、

文字列。指定されたインターバルで実行されるコード。
(  eval() を使うのと同様の理由で非推奨です / Mozilla のドキュメント より )

最新のマニュアルではこう書かれています。
( このドキュメントでは、第3引数が正しくないようです / 正解は Mozilla のドキュメントを参照 )

指定した間隔が経過したときに実行されるコードを示す関数ポインタや文字列を指定します( Microsoft の英文には API なので、実際に function pointer とあります )


▼ 無名関数


現在では関数名を記述する場所で無名関数をその場に書く事が多いですが、もちろん文字列でも動作します。文字列の場合は、処理する内容をタイマーが発生する時間に内容を確定させておく事が重要です( 全て文字列なので、それがかならずそのまま実行される事を利用します )

具体的には、関数内に値を埋め込む事で時間差の問題を解決できます。

関数名では、IE11、Firefox、Google Chrome、Opera で第三引数(関数の引数の数ぶん第4、第5と指定できます)が利用できる事を確認しています。
( IE では IE10 以降で第3引数が使用できるようです )
<script type="text/javascript">

var param = "NO!"

function startTest(param) {

	// OK はこの時点で確定です
	// ※ 文字列に startTest の引数を埋め込んでいます
	setTimeout(
		'(function(str){alert(str)})("startTestの引数:'+param+'");',
		500);

	// 第三引数で、setTimeout 実行時点の値を引き渡します
	// 関数の引数を第三引数以降で渡します
	// ( IE9 以前は実装されていません )
	setTimeout(
		function(a,b){console.log(param);alert(a+" / "+b)},
		600,
		"setTimeoutの引数:" + param,
		"ここは第4引数です");

	setTimeout(
		refTest,
		700,
		param,
		"ここは第4引数です");

	// param 内は NO! になります
	// ※ グローバルの param を参照します
	setTimeout('alert("グローバル:"+param)',1000);

}

function refTest(a,b) {

	console.log(param);
	alert("定義済み関数の参照:"+a+" / "+b)

}

startTest("OK");

</script>



posted by lightbox at 2015-01-28 06:40 | JavaScript DOM | このブログの読者になる | 更新情報をチェックする

2015年01月23日


Google の Plus API を使って Google+ 投稿データを jQuery UI のアコーディオン(accordion)で表示する

アプリケーションを認証すると、以下のようになります。



一番上のブルーのメニューは、ログアウトや関連リンクをセットしてあります。下の黒いバーが jQuery UI のアコーディオンを使用して Google Plus の投稿データを表示しています。


▼ 実際のデモページです。
http://winofsql.jp/gapi/examples/page2.htm

page2.htm に IFRAME 内で接続用のリンクを用意しています。こうしておくと、どんなページにも簡単に接続リンクを追加できます。IFRAME 内は普通のリンクで、target="_top" になっています。

リンクには QueryString として初期ページと処理ページを渡して標準化しています。

page2.htm
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<iframe
	src="start.php?start=page2&action=main_action2"
	name="myframe"
	frameborder="0"
	scrolling="no"
	width="200"
	height="100"
></iframe>

</body>
</html>

▼ 接続リンクのあるページ
start.php
<?php
require_once("user_client.php");

$_SESSION['start'] = $_GET['start'];
$_SESSION['action'] = $_GET['action'];

$authUrl = $client->createAuthUrl();

?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
</head>
<body style='background-color:#e0e0e0;'>
<a href="<?= $authUrl ?>" target="_top">接続</a>
<br><br><br>
ここは IFREAME です
</body>
</html>


▼ Google Plus の投稿情報を jQuery UI のアコーディオンとして
main_action2.php
<?php
require_once("user_client.php");

/************************************************
  アクセストークンがある場合
  但し、期限が切れている場合は再度取得する必要
  があるので、認証用の URL を作成する
 ************************************************/
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
  $client->setAccessToken($_SESSION['access_token']);
  if ($client->isAccessTokenExpired()) {
    unset($_SESSION['access_token']);
    header("Location: {$_SESSION['start']}htm");
    exit();
  }
}
/************************************************
  アクセストークンが無い場合
  認証用の URL を作成する
 ************************************************/
else {
    header("Location: {$_SESSION['start']}.htm");
    exit();
}

/************************************************
  目的 API の処理
 ************************************************/
$service = new Google_Service_Plus($client);

// タスクリストの一覧
try {
	$obj_activities = $service->activities->listActivities("me", "public");
}
catch(Exception $ex) {
	header('Location: logout.php');
	exit();
}

// タスクリストの配列として取得
$activities = $obj_activities->getItems();
?>

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<link href="http://winofsql.jp/jquery/plugins/smartmenus/sm-core-css.css" rel="stylesheet" type="text/css" />
<link href="http://winofsql.jp/jquery/plugins/smartmenus/sm-blue.css" rel="stylesheet" type="text/css" />

<style type="text/css">
#main-menu {
	margin-top: -3px;
	position:relative;
	z-index:9999;
	width:400px;
}
pre {
	white-space: pre;
	white-space: pre-wrap;
	white-space: -pre-wrap;
	white-space: -o-pre-wrap;
	white-space: -moz-pre-wrap;
	white-space: -hp-pre-wrap;
	word-wrap: break-word;
	width: 700px;
}
</style>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="http://winofsql.jp/jquery/plugins/smartmenus/jquery.smartmenus.min.js"></script>

<link type="text/css" href="http://winofsql.jp/jquery/jqcss/black-tie/jquery-ui-1.10.1.custom.css" rel="stylesheet" />
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>

</head>
<body>

<ul id="main-menu" class="sm sm-vertical sm-blue sm-blue-vertical">
	<li><a href="#">処理</a>
		<ul>
			<li><a href="page1.htm">HOME</a></li>
			<li><a href="logout.php">ログアウト</a></li>
			<li><a href="https://security.google.com/settings/security/permissions" target="_blank">Google のアカウント権限</a></li>
			<li><a href="http://code.google.com/apis/console/" target="_blank">APIs Console</a></li>
		</ul>
	</li>
</ul>

<script>
$('#main-menu').smartmenus();
</script>


<div id="google-plus">
<?php

for( $i = 0; $i < count($activities); $i++ ) {


	$obj = $activities[$i]->getObject();


	$main_text = $obj->getContent();

	$attachments = $obj->getAttachments();

	$attachments_displayName = $attachments[0]->displayName;

	// アコーディオンタイトル
	print "<h3>投稿" . ($i+1) . " : " . mb_substr($main_text,0,20,"UTF-8") . " / " . mb_substr($attachments_displayName,0,20,"UTF-8") . "</h3>";

	// アコーディオンコンテンツ
	print "<pre>";

	print "投稿部分 : " . $main_text . "<br>";

	print "▼--- 添付部分----<br>";

	print $attachments[0]->displayName . "<br>";
	print $attachments[0]->content . "<br>";
	print $attachments[0]->url . "<br>";
	print $attachments[0]->image['url'] . "<br>";
	if ( $attachments[0]->image['url'] != "" ) {
		print "   <img src=\"" . $attachments[0]->image['url'] . "\"><br>";
		print "   " . $attachments[0]->image['type'] . "<br>";
		print "   " . $attachments[0]->image['width'] . "<br>";
		print "   " . $attachments[0]->image['height'] . "<br>";
	}
	print "<br>";

	print "</pre>";

}
?>
</div>

<script>
$('#google-plus').accordion({
		heightStyle: "content",
		header: "h3"
	});
</script>


</bodyy>
</html>


Google Plus の投稿データは多くの階層内にいろいろなクラスが使われており、仕様上メソッドで呼び出す内容も多い為、一つ一つ階層を print_r で表示して取り出し方を Goolge のクラスのソースと照らし合わせながら確認する必要があります。

APIs Explorer

APIs Explorer ページの右上で認証後、me と public をセットして内容を確認できます


▼ 使用する API によって、スコープを変えれるよう標準化しています
user_client.php( 共通 )
<?php
session_start();

header( "Content-Type: text/html; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

require_once realpath(dirname(__FILE__) . '/../autoload.php');

$client_id = '';
$client_secret = '';
$redirect_uri = 'http://winofsql.jp/gapi/examples/idtoken.php';

$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
if ( $_GET['start'] == 'page1' || $_SESSION['start'] == 'page1' ) {
	$client->addScope('https://www.googleapis.com/auth/tasks');
}
if ( $_GET['start'] == 'page2' || $_SESSION['start'] == 'page2' ) {
	$client->addScope('https://www.googleapis.com/auth/plus.login');
	$client->addScope('https://www.googleapis.com/auth/plus.me');
}

?>


▼ API から呼び出されるページ
idtoken.php
<?php
session_start();

require_once("user_client.php");

if (isset($_GET['code'])) {

  $client->authenticate($_GET['code']);
  $_SESSION['access_token'] = $client->getAccessToken();
  header("Location: {$_SESSION['action']}.php");

}
else {
  header("Location: {$_SESSION['start']}.htm");
}

?>

関連する記事

Google API の中でも単純な Task API を使って、アクセストークン取得のテンプレートを整備しました



タグ:google API
posted by lightbox at 2015-01-23 20:44 | Google | このブログの読者になる | 更新情報をチェックする

2015年01月21日


VB.net(C#) : Picasa より指定したアルバム内の画像の URL 一覧を取得する

▼ バッチビルドキット


▼ クライアントライブラリのダウンロード
google-gdata - .NET library for the Google Data API

google-gdata : Google の C# サンプル : PhotoBrowser

Google_Data_API_Setup_1.9.0.0.msi をダウンロードしてインストールすると、
Google_Data_API_Setup_2.2.0.0.msi で再検証しました

C:\Program Files\Google\Google Data API SDK\Redist\Google.GData.Client.dll
C:\Program Files\Google\Google Data API SDK\Redist\Google.GData.Photos.dll
C:\Program Files\Google\Google Data API SDK\Redist\Google.GData.Extensions.dll

となるはずなので、カレントディレクトリにコピーして、

vbc.exe /r:Google.GData.Client.dll,Google.GData.Photos.dll,Google.GData.Extensions.dll ソースコード.vb

でビルドして実行します
Imports Google.GData.Client
Imports Google.GData.Photos

Module Module1

	Sub Main()

		' --------------------------------------------------
		' 引数の処理( ユーザ パスワード アルバム名 )
		' --------------------------------------------------
		' SQL作成に必要なテーブル名を引数から取得する
		' 空白を指定したい場合は、"文字列 文字列" のように指定する
		Dim arguments As String() = Environment.GetCommandLineArgs()
		' 引数は3つのみ許可
		if arguments.Length <> 4 then
			Console.WriteLine("引数を指定して下さい")
			Return
		end if

		' --------------------------------------------------
		' 認証処理
		' --------------------------------------------------
		' Picasa サービスにアプリケーション名を設定
		' companyName-applicationName-versionID で適当に設定
		Dim picasaService As PicasaService = New PicasaService("mymymy-photos-v1")

		' 認証情報を設定
		picasaService.setUserCredentials(arguments(1), arguments(2))

		' トークン文字列
		Dim authToken As String = Nothing

		' トークンの取得
		Try
			authToken = picasaService.QueryClientLoginToken()
		Catch ex As Exception
			Console.WriteLine( ex.Message )
			Exit Sub
		End Try

		' トークンの表示( ここでは使用しません )
		Console.WriteLine( "▼ トークン" )
		Console.WriteLine( authToken )

		' --------------------------------------------------
		' アルバムの処理
		' --------------------------------------------------
		' アルパム一覧を取得する為の要求オブジェクトを作成
		Dim query As AlbumQuery  = New AlbumQuery()

		' ユーザ名より、呼び出し先を設定
		query.Uri = New Uri(PicasaQuery.CreatePicasaUri(arguments(1)))

		' 一覧の取得
		Dim picasaFeed As PicasaFeed = picasaService.Query(query)
		Dim targetEntry As PicasaEntry = Nothing

		Console.WriteLine( "▼ 一覧の表示" )
		' 一覧の表示
		if (Not picasaFeed is Nothing) and (picasaFeed.Entries.Count > 0) then
			For Each entry As PicasaEntry In picasaFeed.Entries
				Console.Write(entry.Title.Text + " : ")
				Console.WriteLine(entry.GetPhotoExtensionValue(GPhotoNameTable.NumPhotos))

				' 目的のアルバムオブジェクトを保存
				if arguments(3) = entry.Title.Text then
					targetEntry = entry
				end if
			Next
		end if 

		Console.WriteLine( "▼ アルバムの内容" )
		' 保存されたアルバムの内容を再確認
		if Not targetEntry is Nothing then
			Console.WriteLine("アルバムタイトル : " + targetEntry.Title.Text)

			' --------------------------------------------------
			' 写真の処理
			' --------------------------------------------------
			Dim photoQuery As PhotoQuery = New PhotoQuery(targetEntry.FeedUri)
	
			' 写真一覧の取得
			Dim photoFeed As PicasaFeed = picasaService.Query(photoQuery)
	
			' 写真一覧の表示
			If (Not photoFeed Is Nothing) And (photoFeed.Entries.Count > 0) Then
				For Each entryPhoto As PicasaEntry In photoFeed.Entries
					' 通常はアップロードしたファイル名
					Console.WriteLine(entryPhoto.Title.Text)
					' Picasa の HTML ページ
					Console.WriteLine(entryPhoto.AlternateUri.Content )
					' 画像の URL
					Console.WriteLine(entryPhoto.Media.Content.Url)
					Console.WriteLine()
				Next
			End If

		end if

	End Sub

End Module

Protocol Reference

実行サンプル
vb.net>image_urllist.exe user password 画像
▼ トークン
DQAAABABAACUVSy0cLeHFszdiXR52VqipNzkwhG--jNYFdZsJiE-2tNWslwWDZ01PDFdGVSGEmo-54Es
rnC4plJ5dqWnb8ttvGwHR6_Hv7FfNcTV2Yvl0hK0DjVcCDKv4AGtcL5SXKz58NUAYFQpUU_xYuKSiH3Y
nipVGeJsnpKgLbJGJvBc0I7qYJFlqIy4ydAnpxjtd5jJEe6lVJPb08Dgszkzi4C_DAT-5Y1tLRWkX4iG
9LrwV72TAO7bYtGkE59E0GL6V96DmkQyhGD8e6QW6PYbtGq2zV3T49PqWXYCMyHuyu4oQ7zEVrCka51M
eVc--4wdsNEzn5-ORFfAQ5dDOkv1H2TWJZoUNtdjfx7QhqDIsSPeSg
▼ 一覧の表示
自動バックアップ : 2
Google DATA : 1
画像 : 5
2013/08/31 : 2
2013-08-15 : 1
2013-08-09 : 1
2013/08/09 : 1
書籍 : 1
2013-08-05 : 2
スクラップブック写真 : 1
プロフィール写真 : 1
2013-08-15 : 1
▼ アルバムの内容
アルバムタイトル : 画像
Koala.jpg
https://picasaweb.google.com/1062795259696282242/aBylLD02#6106631643771454306
https://lh.googleusercontent.com/-NAgA591vjCo/VL8dIC0oQ2I/AAAAAAAAAj8/auHvt-kPf
9M/Koala.jpg

Chrysanthemum.jpg
https://picasaweb.google.com/1062795259696282242/aBylLD02#6106632684030319666
https://lh.googleusercontent.com/-B_9ML605-us/VL8eEmFq3DI/AAAAAAAAAkk/XEdyJhv_R
is/Chrysanthemum.jpg

Desert.jpg
https://picasaweb.google.com/1062795259696282242/aBylLD02#6106632684185326466
https://lh.googleusercontent.com/-35nqu23mW98/VL8eEmqoE4I/AAAAAAAAAkY/g90EGpZOT
DM/Desert.jpg

Hydrangeas.jpg
https://picasaweb.google.com/1062795249696282242/aBylLD02#6106632687178019842
https://lh.googleusercontent.com/-S6Ip9iBwt00/VL8eEx0I9AI/AAAAAAAAAkc/VYd-YUeEC
w4/Hydrangeas.jpg

Jellyfish.jpg
https://picasaweb.google.com/1062795259696282242/aBylLD02#6106632704441986402
https://lh.googleusercontent.com/-og4Rsof929w/VL8eFyIMLWI/AAAAAAAAAkg/1kZH2SSCR
9U/Jellyfish.jpg
▼ C# ( Code Translation for .NET (C#<->VB.NET) でVBを変換 )
using System;
using Google.GData.Client;
using Google.GData.Photos;
class Module1 {
    
    static void Main() {
        string[] arguments = Environment.GetCommandLineArgs();
        if ((arguments.Length != 4)) {
            return;
        }
        PicasaService picasaService = new PicasaService("mymymy-photos-v1");
        picasaService.setUserCredentials(arguments[1], arguments[2]);
        string authToken = null;
        try {
            authToken = picasaService.QueryClientLoginToken();
        }
        catch (Exception ex) {
            Console.WriteLine(ex.Message);
            return;
        }
        Console.WriteLine("▼ token");
        Console.WriteLine(authToken);
        AlbumQuery query = new AlbumQuery();
        query.Uri = new Uri(PicasaQuery.CreatePicasaUri(arguments[1]));
        PicasaFeed picasaFeed = picasaService.Query(query);
        PicasaEntry targetEntry = null;
        Console.WriteLine("▼ list");
        if ((!(picasaFeed == null) 
                    && (picasaFeed.Entries.Count > 0))) {
            foreach (PicasaEntry entry in picasaFeed.Entries) {
                Console.Write((entry.Title.Text + " : "));
                Console.WriteLine(entry.GetPhotoExtensionValue(GPhotoNameTable.NumPhotos));
                if ((arguments[3] == entry.Title.Text)) {
                    targetEntry = entry;
                }
            }
        }
        Console.WriteLine("▼ albam");
        if (!(targetEntry == null)) {
            Console.WriteLine(("title : " + targetEntry.Title.Text));
            PhotoQuery photoQuery = new PhotoQuery(targetEntry.FeedUri);
            PicasaFeed photoFeed = picasaService.Query(photoQuery);
            if ((!(photoFeed == null) 
                        && (photoFeed.Entries.Count > 0))) {
                foreach (PicasaEntry entryPhoto in photoFeed.Entries) {
                    Console.WriteLine(entryPhoto.Title.Text);
                    Console.WriteLine(entryPhoto.AlternateUri.Content);
                    Console.WriteLine(entryPhoto.Media.Content.Url);
                    Console.WriteLine();
                }
            }
        }
    }
}



タグ:google API
posted by lightbox at 2015-01-21 14:04 | VB.NET : テクニカル | このブログの読者になる | 更新情報をチェックする

2015年01月20日


Google の タスク API(ToDoリスト) を使ってタスクリストとタスクのタイトルを jQuery のプラグインでメニュー化する

アプリケーションを認証すると、以下のようになります。



一番上は、最初から設定済みのメニューで、ログアウトや関連リンクをセットしてあります。下の3つが実際にタスクリストに登録した内容をタイトルだけ設定しています。

Google のタスクリストの管理

Google にログインしていない状態で認証プロセスを実行すると、ログイン画面が表示されるのですが、このログイン画面でログインしてしまうと、ログインを保持してしまうので注意して下さい。これを避けるには、あらかじめ Google にログインしてから認証すると OK です。

▼ 認証プロセス中のログイン


▼ 通常のログイン(ログイン状態を保持する為のチェックボックスがあります )


▼ 認証画面


▼ 実際のデモページです。
http://winofsql.jp/gapi/examples/page1.htm

page1.htm に IFRAME 内で接続用のリンクを用意しています。こうしておくと、どんなページにも簡単に接続リンクを追加できます。IFRAME 内は普通のリンクで、target="_top" になっています。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<iframe
	src="start.php?start=page1&action=main_action"
	name="myframe"
	frameborder="0"
	scrolling="no"
	width="200"
	height="100"
></iframe>

</body>
</html>

▼ 接続リンクのあるページ
start.php
<?php
require_once("user_client.php");

$_SESSION['start'] = $_GET['start'];
$_SESSION['action'] = $_GET['action'];

$authUrl = $client->createAuthUrl();

?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
</head>
<body style='background-color:#e0e0e0;'>
<a href="<?= $authUrl ?>" target="_top">接続</a>
<br><br><br>
ここは IFREAME です
</body>
</html>


▼ タスクリストのメニュー作成
main_action.php
<?php
require_once("user_client.php");

/************************************************
  アクセストークンがある場合
  但し、期限が切れている場合は再度取得する必要
  があるので、認証用の URL を作成する
 ************************************************/
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
  $client->setAccessToken($_SESSION['access_token']);
  if ($client->isAccessTokenExpired()) {
    unset($_SESSION['access_token']);
    header("Location: {$_SESSION['start']}htm");
    exit();
  }
}
/************************************************
  アクセストークンが無い場合
  認証用の URL を作成するページへのリンクがある
  ページに移動する
 ************************************************/
else {
    header("Location: {$_SESSION['start']}htm");
    exit();
}

/************************************************
  目的 API の処理
 ************************************************/
$service = new Google_Service_Tasks($client);

// タスクリストの一覧
try {
	$obj_tasklists = $service->tasklists->listTasklists();
}
catch(Exception $ex) {
	// セッションの継続中に、アプリケーションの
	// 認証登録が削除された場合
	header('Location: logout.php');
	exit();
}

// タスクリストの配列として取得
$tasklists = $obj_tasklists->getItems();

for( $i = 0; $i < count($tasklists); $i++ ) {

	$obj_tasks = $service->tasks->listTasks($tasklists[$i]->id);
	// タスクリストの数にに同期したタスクの配列の配列を作成
	$tasks[] = $obj_tasks->getItems();

}

/************************************************
  目的の処理
 ************************************************/

?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<link href="http://winofsql.jp/jquery/plugins/smartmenus/sm-core-css.css" rel="stylesheet" type="text/css" />
<link href="http://winofsql.jp/jquery/plugins/smartmenus/sm-blue.css" rel="stylesheet" type="text/css" />

<style type="text/css">
#main-menu {
	margin-top: -3px;
	position:relative;
	z-index:9999;
	width:400px;
}
</style>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="http://winofsql.jp/jquery/plugins/smartmenus/jquery.smartmenus.min.js"></script>
</head>
<body>

<ul id="main-menu" class="sm sm-vertical sm-blue sm-blue-vertical">
	<li><a href="#">処理</a>
		<ul>
			<li><a href="page1.htm">HOME</a></li>
			<li><a href="logout.php">ログアウト</a></li>
			<li><a href="https://security.google.com/settings/security/permissions" target="_blank">Google のアカウント権限</a></li>
			<li><a href="http://code.google.com/apis/console/" target="_blank">APIs Console</a></li>
			<li><a href="https://mail.google.com/tasks/canvas" target="_blank">Google のタスク管理ページ</a></li>
 
		</ul>
	</li>
</ul>

<div>
  <pre class="data" style='padding:20px;'>

<?php 
// 主処理

$obj = json_decode($_SESSION['access_token']);
print_r($obj);

print "期限 :" . ($obj->{'created'}+$obj->{'expires_in'}) . "\n";
print "現在 :" . time() . "\n";

print "\n";


?>
  </pre>
</div>

<script type="text/javascript">
// PHP の タスクリストを JavaScript のオブジェクトとして埋め込む
var taskLists = <?php print json_encode( $tasklists ) ?>;
// PHP の タスクの配列の配列を JavaScript のオブジェクトとして埋め込む
var tasks = <?php print json_encode( $tasks ) ?>;
var ul_unit;

for( i = 0; i < taskLists.length; i++ ) {
	// main-menu に最上位メニューとしてタスクリストを追加
	$( "<li id='"+ taskLists[i].id +"'><a href='#'>" + taskLists[i].title + "</a></li>" ).appendTo( $("#main-menu") )
	if ( tasks[i].length != 0 ) {
		// サブメニューのユニットを追加
		ul_unit = $("<ul></ul>").appendTo( $("#"+ taskLists[i].id) );
		// サブメニューの項目としてタスクを全て追加
		for( j = 0; j < tasks[i].length; j++ ) {
			if ( tasks[i][j].title != "" ) {
				$( "<li><a href='#'>" + tasks[i][j].title + "</a></li>" ).appendTo( $(ul_unit) );
			}
		}
	}
}

$('#main-menu').smartmenus({});

</script>


</body>
</html>

jQuery のプラグインである SmartMenus を使用しています。SmartMenus のデモページはこちらになります。

▼ user_client.php( 共通 )
<?php
session_start();

header( "Content-Type: text/html; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

require_once realpath(dirname(__FILE__) . '/../autoload.php');

$client_id = '';
$client_secret = '';
$redirect_uri = 'http://winofsql.jp/gapi/examples/idtoken.php';

$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
if ( $_GET['start'] == 'page1' || $_SESSION['start'] == 'page1' ) {
	$client->addScope('https://www.googleapis.com/auth/tasks');
}
if ( $_GET['start'] == 'page2' || $_SESSION['start'] == 'page2' ) {
	$client->addScope('https://www.googleapis.com/auth/plus.login');
	$client->addScope('https://www.googleapis.com/auth/plus.me');
}

?>


▼ API から呼び出されるページ( idtoken.php )
<?php
session_start();

require_once("user_client.php");

if (isset($_GET['code'])) {

  $client->authenticate($_GET['code']);
  $_SESSION['access_token'] = $client->getAccessToken();
  header("Location: {$_SESSION['action']}.php");

}
else {
  header("Location: {$_SESSION['start']}.htm");
}

?>



タグ:API
posted by lightbox at 2015-01-20 20:36 | Google | このブログの読者になる | 更新情報をチェックする

2015年01月15日


Google API の中でも単純な Task API を使って、アクセストークン取得のテンプレートを整備しました

google-api-php-client
Client ID と Client secret と Redirect URIs が必要です ( APIs Console )

今までサンプルコードをそのまま使って来ましたが、よくよく読むと少し胡散臭いので整備しました。

どの API も一様に同じようになっていますが、その中でも Task API は単純で解りやすく、実際のオンラインページと比較しやすいのでおすすめです。

※ アクセストークンの期限を内部でチェックしている方式で目で見れるようにしました
<?php
/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
include_once "templates/base.php";
session_start();

header( "Content-Type: text/html; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

require_once realpath(dirname(__FILE__) . '/../autoload.php');

$client_id = '';
$client_secret = '';
$redirect_uri = 'http://localhost/gapi/examples/idtoken.php';

$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope('https://www.googleapis.com/auth/tasks');

/************************************************
  ログアウト処理
  アクセストークンをクリアして最初の状態に戻す
 ************************************************/
if (isset($_REQUEST['logout'])) {
  unset($_SESSION['access_token']);
}

/************************************************
  API からのコールバック
  つまり、アクセストークンが取得されるところ
  取得後、環境をリセットする為に自分自身を呼ぶ
 ************************************************/
if (isset($_GET['code'])) {
  $client->authenticate($_GET['code']);
  $_SESSION['access_token'] = $client->getAccessToken();
  $redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
  header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
  exit();
}

/************************************************
  アクセストークンがある場合
  但し、期限が切れている場合は再度取得する必要
  があるので、認証用の URL を作成する
 ************************************************/
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
  $client->setAccessToken($_SESSION['access_token']);
  if ($client->isAccessTokenExpired()) {
    unset($_SESSION['access_token']);
    $authUrl = $client->createAuthUrl();
  }
}
/************************************************
  アクセストークンが無い場合
  認証用の URL を作成する
 ************************************************/
else {
  $authUrl = $client->createAuthUrl();
}

/************************************************
  目的の処理
 ************************************************/

echo pageHeader("目的の処理");
?>
<div class="box" style='width:80%'>
  <div class="request">
<?php
// 接続用リンク
if (isset($authUrl)) {
  echo "<a class='login' href='" . $authUrl . "'>Connect Me!</a>";
} 
// ログアウト用リンク
else {
  echo "<a class='logout' href='?logout'>Logout</a>";
}

if ($client->getAccessToken() && isset($_SESSION['access_token']) && $_SESSION['access_token'] ) {
?>
  </div>

  <pre class="data">
<?php 
// 主処理

$obj = json_decode($_SESSION['access_token']);
print_r($obj);

print "期限 :" . ($obj->{'created'}+$obj->{'expires_in'}) . "\n";
print "現在 :" . time() . "\n";

print "\n";

$service = new Google_Service_Tasks($client);

// タスクリストの一覧
$obj_tasklists = $service->tasklists->listTasklists();

// タスクリストの配列として取得
$tasklists = $obj_tasklists->getItems();

print_r($tasklists);


?>
  </pre>
</div>
<?php
}

?>

環境をリセットする為と言うのは、コールバック時にはアクセストークンが URL に付加されており、そのままのページではトークンの内容が解ってしまうからです。( $_GET['code'] は URL から取得されるものです )

関連する記事


posted by lightbox at 2015-01-15 15:23 | Google | このブログの読者になる | 更新情報をチェックする

2015年01月14日


GitHub の google-api-php-client( PHP ) を使って、Gmail でメールを送る( 添付ファイル付き )

google-api-php-client
Client ID と Client secret と Redirect URIs が必要です ( APIs Console )

単純なテキスト本文の送信との違いは、メール送信時の仕様通りにバイナリデータ部分とテキスト本文をそれぞれパートに分けてデータを作成しているところです。

単純なテキスト本文の送信では、subject を JIS に変換して送っていましたが、結局 Google の API 側で UTF-8 に変更されていました。なので、ここでは最初から UTF-8 を使用しています。テキスト本文の部分には改行は必要無く、API 側で作成してくれていました。しかし、バイナリ部分の改行はこちらで作成する(chunk_split)必要がありました。

添付データはテスト結果が解りやすい画像を固定で使用しています。アクセストークンが取得てきた場合、フォームを表示するようにして、送り先メールアドレスと件名と本文を入力して送信した場合にメールを送るようにしています。

送信先のテストとしては、Yahoo!メール と、Hotmail と、Nifty のメールで正しく送信されている事を確認しました。
<?php
/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
include_once "templates/base.php";
session_start();

header( "Content-Type: text/html; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

require_once realpath(dirname(__FILE__) . '/../autoload.php');

/************************************************
  $redirect_uri はこのスクリプトの URL
 ************************************************/
$client_id = '';
$client_secret = '';
$redirect_uri = 'http://localhost/gapi/examples/gmail-send.php';

/************************************************
  クライアントの作成
 ************************************************/
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);

/************************************************
  サービスの作成
 ************************************************/
// https://developers.google.com/gmail/api/auth/scopes
// https://developers.google.com/gmail/api/v1/reference/users/messages/send
$client->addScope("https://mail.google.com/");
$service = new Google_Service_Gmail($client);

/************************************************
  アクセストークンの取得プロセス
 ************************************************/
if (isset($_REQUEST['logout'])) {
  unset($_SESSION['access_token']);
}

if (isset($_GET['code'])) {
  $client->authenticate($_GET['code']);
  $_SESSION['access_token'] = $client->getAccessToken();
  $redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
  header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
}

if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
  $client->setAccessToken($_SESSION['access_token']);
  if ($client->isAccessTokenExpired()) {
    unset($_SESSION['access_token']);
  }
} else {
  $authUrl = $client->createAuthUrl();
}

/************************************************
  アクセストークンが取得できた時の処理
 ************************************************/
if ($client->getAccessToken() && $_POST['send'] != '' && $_POST['body'] != '') {

  // メール送信
  $subject = "Subject: =?UTF-8?B?" . base64_encode($_POST['subject']) . "?=";
  $to = "To: {$_POST['to']}";
  $body = $_POST['body'];

  // 添付用データの準備
  $uniqid = uniqid();
  $mime = "image/jpeg";
  $fname = "test.jpg";
  $attachment_fname = "日本語のファイル名.jpg";
  $attachment_fname = "=?UTF-8?B?" . base64_encode($attachment_fname) . "?=";

  // ファイルアップロードと同様の使用でメールデータを作成
  $sendtype .= "Content-Type: multipart/mixed; boundary=\"{$uniqid}\"";

  // テキスト本文
  $body  = "--{$uniqid}\n";
  $body .= "Content-Type: text/plain; charset=\"utf-8\"\n";
  $body .= "\n";
  $body .= $_POST['body'] . "\n";
  // バイナリ添付データ
  $body .= "--{$uniqid}\n";
  $body .= "Content-Type: {$mime}; name=\"{$attachment_fname}\"\n";
  $body .= "Content-Transfer-Encoding: base64\n";
  $body .= "Content-Disposition: attachment; filename=\"{$attachment_fname}\"\n";
  $body .= "\n";

  $path = "./$fname";
  $data = file_get_contents($path);
  $encode = base64_encode($data);

  $body .= chunk_split($encode);
  $body .= "\n--{$uniqid}--\n";

  // 全体データの構築
  $send_data = "$subject\n$to\n$sendtype\n\n$body";

  // Base64URL
  $send_data = rtrim(strtr(base64_encode($send_data), '+/', '-_'), '=');
  $msg = new Google_Service_Gmail_Message();
  $msg->setRaw($send_data);
  $result = $service->users_messages->send("me", $msg);

}

echo pageHeader("Gmail メール送信");
if ($client->getAccessToken()) {
?>
<div style='width:300px;margin:auto'>
<form method="post" >
<input type="text" name="to"><br>
<input type="text" name="subject"><br>
<textarea name="body" style='height:100px;'></textarea><br>
<input type="submit" name="send" value="送信">
</form>
</div>
<?php
}
?>
<div class="box">
  <div class="request">
<?php 
if (isset($authUrl)) {
  echo "<a class='login' href='" . $authUrl . "'>Connect Me!</a>";
}
?>
  </div>

  <pre class="shortened" style='width:100%;height:500px;'>
<?php 
if (isset($result) && $result) {
  print_r($result);
}
?>
  </pre>
</div>


関連する記事


posted by lightbox at 2015-01-14 15:34 | Google | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり