SQLの窓

2013年08月31日


PHP に GET で SQL の select 構文(UTF8 で urlencode)を与えて JSON 文字列を返す簡単な処理

eval を使って、動的に配列を作成する文字列を作って(データを埋め込む)、さらに eval で実際の配列を作成してから json_encode します。

データベースは、Windows の MySQL で、PHP も Windows なので、PDO の ODBC を使って処理しています。ですから、MySQL は utf-8 ですが、クライアントアプリは SHIFT_JIS を前提に環境作成しているので、ちょっと変則な文字列変換が行われています。

ただ、そこは本筋とは関係無いので差し替えて考えて下さい。

json_encode が、utf-8 前提なので、『?q=select構文』の日本語部分は utf-8 で urlencode が前提です。

テーブルの列名が日本語の場合は、自動的に item_連番 という形式に列名を変更して JSON に渡します。もし、意図的にその文字列を定義したい場合は、select 構文の列名に英数字(_と-は可)別名を与えて下さい。
<?php
header( "Content-Type: application/json; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

mb_language( "ja" );
mb_internal_encoding("utf-8");

// *********************************************************
// extension=php_pdo_odbc.dll ( php.ini )
// *********************************************************
$conf = array(
	"dns_driver" => "{MySQL ODBC 5.2w Driver}",
	"dns_server" => "localhost",
	"dns_db" => "lightbox",
	"dns_user" => "root",
	"dns_pass" => "パスワード",
	"dns_charset" => "cp932"
);

// **********************************************************
// 接続
// **********************************************************
try {
	$cn1 = new PDO(
		"odbc:Driver={$conf['dns_driver']};Server={$conf['dns_server']};" .
		"Database={$conf['dns_db']};Uid={$conf['dns_user']};Pwd={$conf['dns_pass']};".
		"charset={$conf['dns_charset']}");
}
catch ( Exception $ex ) {
	print '{"error": "接続できませんでした: ' . $ex->getMessage() . '"}';
	exit();
}

//print "<pre>";
//print "接続されました\n";
//print_r($conf);

//print_r($cn1);

// **********************************************************
// SQL
// **********************************************************
$query = mb_convert_encoding( $_GET["q"], "sjis", "utf8" );

// **********************************************************
// 結果セット
// **********************************************************
$rs = $cn1->query( $query );
if ( $rs === false ) {
	$er = $cn1->errorInfo();
	print '{ "error" : "' . $er[2] . '" }';
	exit();
}

// フィールド数
$field_cnt = $rs->columnCount();
// 文字列バッファ
$buffer = "";
$update_cnt = 0;
while( $result = $rs->fetch(PDO::FETCH_ASSOC) ) {
	// ループの最初で列名一覧を作成する
	if ( $buffer == "" ) {
		// 配列に列名を取得
		$field_names = array_keys($result);
		$entry = build_field( $field_names );
		for( $i = 0; $i < $field_cnt; $i++ ) {
			if ( $buffer != "" ) {
				$buffer .= ",";
			}
			$buffer .= $field_names[$i];
		}
	}

	// ***************************************************************
	// 最終 eval に埋め込む為の変数セット( フィールド個々の内容 )
	// $myfield(n) という変数に実際の値をセットしておいて、
	// $myfield(n) という変数を最終文字列に埋め込んで eval する
	// ***************************************************************
	for( $i = 0; $i < $field_cnt; $i++ ) {
		$wk = "$" . "myfield" . ($i+1) . ' = "' . mb_convert_encoding( $result[$field_names[$i]], "utf8", "sjis" ) . '";';
		eval($wk);
	}

	// JSON 文字列に変換する為の配列を作成する文字列
	$x = '$my_json["items"][] = array( ' . $entry  .  ');';
	// eval で $my_json["items"] を作成
	eval($x);
}

// JSON 文字列化
$my_json_string =json_encode($my_json);
print $my_json_string;


// *********************************************************
// フィールド文字列( 連想配列部分作成 )
// 列名が日本語の場合は、item_(n) に変更する
// 指定したい場合は select の 列名記述を as で作成する
// *********************************************************
function build_field( $fields ) {

	$buffer = "";

	$field_cnt = count( $fields );

	for( $i = 0; $i < $field_cnt; $i++ ) {
		if (preg_match("/[^0-9a-zA-Z_\-]+/", $fields[$i])) {
			$field_name = "item_" . ($i+1);
		}
		else {
			$field_name = $fields[$i];
		}
		if ( $buffer != "" ) {
			$buffer .= ",";
		}
		$buffer .= '"' . $field_name . '" => "' . "$" . "myfield" . ($i+1) .  '"';
	}

	return $buffer;

}
?>

関連する記事

PHP : PDO : SQLExpress(SQLServer)の読み込みと同時更新
VB.net 2008 : System.Data.Odbc 接続テスト( MySQL 5.1 )


▼ 『?q=select+%2A+from+%E7%A4%BE%E5%93%A1%E3%83%9E%E3%82%B9%E3%82%BF
+limit+4』で出力したもの 
{"items":[{"item_1":"0001","item_2":"\u6d66\u5ca1 \u53cb\u4e5f","item_3":"\u30a6\u30e9\u30aa\u30ab \u30c8\u30e2\u30e4","item_4":"0003","item_5":"0","item_6":"2013-05-04 00:00:00","item_7":"2013-07-20 00:00:00","item_8":"270000","item_9":"9000","item_10":""},{"item_1":"0002","item_2":"\u5c71\u6751 \u6d0b\u4ee3","item_3":"\u30e4\u30de\u30e0\u30e9 \u30d2\u30ed\u30e8","item_4":"0003","item_5":"1","item_6":"2013-02-06 00:00:00","item_7":"2013-05-10 00:00:00","item_8":"300000","item_9":"","item_10":""},{"item_1":"0003","item_2":"\u591a\u5ca1 \u51ac\u884c","item_3":"\u30bf\u30aa\u30ab \u30d5\u30e6\u30e6\u30ad","item_4":"0002","item_5":"0","item_6":"2013-04-05 00:00:00","item_7":"2013-07-06 00:00:00","item_8":"250000","item_9":"","item_10":""},{"item_1":"0004","item_2":"\u9ad8\u7530 \u51ac\u7f8e","item_3":"\u30bf\u30ab\u30bf \u30d5\u30e6\u30df","item_4":"0003","item_5":"1","item_6":"2013-02-02 00:00:00","item_7":"2013-05-27 00:00:00","item_8":"250000","item_9":"","item_10":""}]}

▼ 以下は整形したもの
{
    "items": [
        {
            "item_1": "0001",
            "item_2": "浦岡 友也",
            "item_3": "ウラオカ トモヤ",
            "item_4": "0003",
            "item_5": "0",
            "item_6": "2013-05-04 00:00:00",
            "item_7": "2013-07-20 00:00:00",
            "item_8": "270000",
            "item_9": "9000",
            "item_10": ""
        },
        {
            "item_1": "0002",
            "item_2": "山村 洋代",
            "item_3": "ヤマムラ ヒロヨ",
            "item_4": "0003",
            "item_5": "1",
            "item_6": "2013-02-06 00:00:00",
            "item_7": "2013-05-10 00:00:00",
            "item_8": "300000",
            "item_9": "",
            "item_10": ""
        },
        {
            "item_1": "0003",
            "item_2": "多岡 冬行",
            "item_3": "タオカ フユユキ",
            "item_4": "0002",
            "item_5": "0",
            "item_6": "2013-04-05 00:00:00",
            "item_7": "2013-07-06 00:00:00",
            "item_8": "250000",
            "item_9": "",
            "item_10": ""
        },
        {
            "item_1": "0004",
            "item_2": "高田 冬美",
            "item_3": "タカタ フユミ",
            "item_4": "0003",
            "item_5": "1",
            "item_6": "2013-02-02 00:00:00",
            "item_7": "2013-05-27 00:00:00",
            "item_8": "250000",
            "item_9": "",
            "item_10": ""
        }
    ]
}



posted by lightbox at 2013-08-31 22:55 | PHP + データベース | このブログの読者になる | 更新情報をチェックする

Android : Google Spreadsheets API version 3.0でGoogleスプレッドシートを参照

SkyDrive へ移動



Google のライブラリのダウンロード

gdata-java-client

かなり多くのライブラリが中に入っていますが、実際使うのは 5つです。

1) gdata-client-1.0.jar
2) gdata-core-1.0.jar
3) gdata-spreadsheet-3.0.jar
4) guava-11.0.2.jar
5) jsr305.jar

1〜3 は、lib ディレクトリ内で、4〜5 は、deps 内です。Google 的には、gdata を使うのは spreadsheet のみのようで、他の機能( 例えばカレンダー ) は他の API を使うようになっています。また、認証も本来は OAuth を推奨しているようですが、機能の説明に重点を置くために、簡単にユーザーとパスワードのログインで処理しています。


package com.example.posttest;

import java.net.URL;
import java.util.List;

import com.google.gdata.client.spreadsheet.*;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.*;

import android.os.AsyncTask;
import android.widget.EditText;

public class Android_Spreadsheet {

	// AsyncTask のインラインで参照する為の final
	private final String _userid;
	private final String _password;

	// **************************************************************
	// コンストラクタ
	// **************************************************************
	public Android_Spreadsheet(String _userid, String _password) {
		this._userid = _userid;
		this._password = _password;
	}

	// **************************************************************
	// AsyncTask の onPostExecute から外部イベントとして呼び出す為のインターフェイス
	// **************************************************************
	public interface SendMailed {
		public void onTweetResult(List<CellEntry> result);
   	}
	
	// **************************************************************
	// 通信処理
	// **************************************************************
	public void GetCells(String FileName, String SheetName,final SendMailed OnTweetResult) {
		
		new AsyncTask<String, Void, List<CellEntry>>() {

			// **************************************************************
			// 非同期処理
			// **************************************************************
			@Override
			protected List<CellEntry> doInBackground(String... params) {
				
				List<CellEntry> cells = null;

				String result_string = "";
				try {
					
					// *************************************************
					// ClientLogin
					// *************************************************
				    String USERNAME = _userid;
				    String PASSWORD = _password;

				    SpreadsheetService service = new SpreadsheetService("MySpreadsheetIntegration-v1");
				    service.setUserCredentials(USERNAME, PASSWORD);
				    
					// *************************************************
				    // バージョン3
					// *************************************************
				    SpreadsheetService sss = new SpreadsheetService("MySpreadsheetIntegration");
				    sss.setProtocolVersion(SpreadsheetService.Versions.V3);
				    
					// *************************************************
				    // 一覧を取得
					// *************************************************
				    URL SPREADSHEET_FEED_URL = new URL(
				        "https://spreadsheets.google.com/feeds/spreadsheets/private/full");
				    SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
				    // 一覧用のリスト
				    List<SpreadsheetEntry> spreadsheets = feed.getEntries();
				    
				    // スプレッドシート一覧より目的のインデックスを取得
				    int target = 0;
				    for (SpreadsheetEntry Spreadsheet : spreadsheets) {

				    	System.out.println( Spreadsheet.getTitle().getPlainText() );
				    	
				    	if ( Spreadsheet.getTitle().getPlainText().equals(params[0]) ) {
				    		break; 
				    	}
				    	
				    	target++;
				    	
				    }				    
				    

				    SpreadsheetEntry spreadsheet = spreadsheets.get(target);
				    WorksheetFeed worksheetFeed = service.getFeed(
					        spreadsheet.getWorksheetFeedUrl(), WorksheetFeed.class);
				    List<WorksheetEntry> worksheets = worksheetFeed.getEntries();

				    target = 0;
				    for (WorksheetEntry worksheet : worksheets) {

				    	System.out.println( worksheet.getTitle().getPlainText() );
				    	
				    	if ( worksheet.getTitle().getPlainText().equals(params[1]) ) {
				    		break; 
				    	}
				    	
				    	target++;
				    	
				    }				    
				    
				    WorksheetEntry worksheet = worksheets.get(target);

				    URL cellFeedUrl = worksheet.getCellFeedUrl();
				    CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
				    cells = cellFeed.getEntries();
					
				}
				catch( Exception e ) {
					System.out.println(e.getMessage());
				}
				
				return cells;
			}

			// **************************************************************
			// 非同期処理終了後の処理( 画面へのアクセスが可能 )
			// **************************************************************
			@Override
			protected void onPostExecute(List<CellEntry> result) {
				// 引数のインターフェイス内のメソッドを呼び出す
				OnTweetResult.onTweetResult(result);
			}
			
		}.execute(FileName,SheetName);
	
	}

}

スプレッドシートの一覧や、シートの一覧等、それぞれで通信処理をするので、Android では AsyncTask を用いて全て一括で処理を行って、最後の一覧のみを終了イベントとして処理するようにしています。

package com.example.posttest;

import java.net.URL;
import java.util.List;

import com.example.posttest.Android_Spreadsheet;
import com.google.gdata.data.spreadsheet.CellEntry;
import com.google.gdata.data.spreadsheet.CellFeed;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetFeed;

import android.os.Bundle;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;

public class MainActivity extends Activity {

	private Android_Spreadsheet as = null;
	private ArrayAdapter<String> adapter = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		as = new Android_Spreadsheet(
			"", // アカウント
			"" // パスワード
			);
		
		adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1);
		
	}

	public void postAction(View view) {
		
	    
	    new MessageBox(this,"確認","送信しますか?") {

			@Override
			void onYesClick(DialogInterface dialog, int which) {

				System.out.println("onYesClick");
				
				as.GetCells( "In Googleドライブ", "シート2" ,new Android_Spreadsheet.SendMailed() {
					
						@Override
						public void onTweetResult(List<CellEntry> result) {
							
							ListView listView = (ListView)MainActivity.this.findViewById(R.id.listView1);
							listView.setAdapter(adapter);
							
						    for (CellEntry cell : result) {
						    	
						    	adapter.add( cell.getCell().getValue().toString() );
						    	// System.out.println( cell.getCell().getValue().toString() );
						    	
						    }				    
						    
						}
				});
				
			}
			
		}.show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		
		// 選択されたメニュー項目の ID
		int menuId = item.getItemId();
		
		switch(menuId){
		case R.id.action_settings:
			System.out.println("action_settings が選択されました");
			break;
		case R.id.menu_action1:
			System.out.println("menu_action1  が選択されました");
			
			Intent intent = new Intent();
			intent.putExtra("data", "メッセージを別のページに引き渡します");
			intent.setClassName("com.example.posttest", "com.example.posttest.NextPage");
			startActivity(intent);		
			
			break;
		}

		return true;
	}		
	
}




posted by lightbox at 2013-08-31 01:10 | Android | このブログの読者になる | 更新情報をチェックする

2013年08月29日


C#(.NET) : Google Spreadsheets API version 3.0でGoogleスプレッドシートを参照

Google Spreadsheet は、Google ドライブに作成する事ができる Excel のような仕様の WEB アプリケーションです。WEB 上でデータを保存しておいて、インターネット経由で参照や更新が可能なのが Google Spreadsheets API です。( 他の API では、JavaScript より参照できるものもあます )

Google Spreadsheets API version 3.0

.NET ライブラリのダウンロード
( その他全てへのリンク )



ダウンロードしたライブラリは、直接参照する必要があります。本来 XML データでやりとりするものをライブラリ化しているので、あまり便利なものにはなっていませんが、『スプレッドシート』『ワークシート』を順々に一覧より取得するようにはなっていますので、後から解りやすい仕様ではあります。

ここでは、Dictionary を使って保存する手順を行っていますが、使いやすくするには最初に全てのワークシートまでを Dictionary に保存しておいて、名前で取得できるクラスを作成するといいと思います。

▼ で使った『In Googleドライブ』を公開したもの ( 取引先名は VLOOKUP を シート2から行っています )
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Google.GData.Client;
using Google.GData.Spreadsheets;
using System.Diagnostics;

namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {

        public SpreadsheetsService service = null;
        public Dictionary<string, SpreadsheetEntry> db_Spread = new Dictionary<string, SpreadsheetEntry>();
        public Dictionary<string, WorksheetEntry> table_Spread = null;

        public Form1()
        {
            InitializeComponent();
        }

        // ********************************************************
        // ログインデータを設定
        // ********************************************************
        private void button1_Click(object sender, EventArgs e)
        {
            service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
            service.setUserCredentials(this.textBox1.Text, this.textBox2.Text);

            Debug.WriteLine(service.ServiceIdentifier.ToString());
            
        }

        // ********************************************************
        // Google Spreadsheet 一覧をディクショナリに保存
        // ********************************************************
        private void button2_Click(object sender, EventArgs e)
        {
            this.listBox1.Items.Clear();

            SpreadsheetQuery query = new SpreadsheetQuery();

            try
            {
                SpreadsheetFeed feed = service.Query(query);
                foreach (SpreadsheetEntry entry in feed.Entries)
                {
                    Debug.WriteLine(entry.Title.Text);
                    this.listBox1.Items.Add(entry.Title.Text);
                    db_Spread.Add(entry.Title.Text, entry);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        // ********************************************************
        // ListBox の対象行をダブルクリックしてディクショナリにシートを保存
        // ********************************************************
        private void listBox1_DoubleClick(object sender, EventArgs e)
        {
            string target = this.listBox1.SelectedItem.ToString();
            Debug.WriteLine(target);

            try
            {
                SpreadsheetEntry target_Spread = db_Spread[target];

                table_Spread = new Dictionary<string, WorksheetEntry>();
                WorksheetFeed wsFeed = target_Spread.Worksheets;
                foreach (WorksheetEntry entry in wsFeed.Entries)
                {
                    table_Spread.Add(entry.Title.Text, entry);
                    Debug.WriteLine(entry.Title.Text);
                }

                // 先頭シートからデータを取得
                CellQuery cellQuery = new CellQuery(((WorksheetEntry)wsFeed.Entries[0]).CellFeedLink);
                CellFeed cellFeed = service.Query(cellQuery);

                uint nRow = 1;
                string strResult = "";
                foreach (CellEntry cell in cellFeed.Entries)
                {
                    if (nRow == cell.Row)
                    {
                        if (strResult == "")
                        {
                            strResult += cell.Value;
                        }
                        else
                        {
                            strResult += "," + cell.Value;
                        }
                    }
                    else
                    {
                        strResult += "\r\n" + cell.Value;
                    }
                    Debug.WriteLine(cell.Value);

                    nRow = cell.Row;
                }

                this.textBox3.Text = strResult;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }
    }
}




posted by lightbox at 2013-08-29 01:18 | API | このブログの読者になる | 更新情報をチェックする

2013年08月28日


PHP で GET statuses/user_timeline を使って何か作る為のテンプレート

GET statuses/user_timeline



基本的には、API 制限があるので( 300/app )たいした事はできませんが、個人的に楽しむぶんには十分使えると思います。投稿に関しては weetDeck 等の公式クライアントを使ったほうがいいと思いますが、PHP で投稿する方法はこちら

使用方法は、アプリを作って『Consumer key、Consumer secret、Access token、Access token secret』を手に入れてテキストエリアに1行づつ入力し、5行目にユーザの表示名( ログイン用の文字列 ) を入力します
<?php
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" );

$result = '';
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
	$token = explode("\r\n", $_POST['text']);
	$result = twitter_user_timeline(
		$token[0],
		$token[1],
		$token[2],
		$token[3],
		$token[4]
	);
}

?>
<!doctype html>
<html lang="ja">
<head>
<title>SkyDrive Word</title>
<meta charset="utf-8">
<style>
body {
	margin: 0px;
	padding:10px 0 30px 30px;
}
.entry {
	width: 600px;
	border-top: solid 1px #000000;
	padding: 5px;
	font-size:16px;
	font-family: Arial, Helvetica, Verdana, "ヒラギノ角ゴPro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "MS Pゴシック", sans-serif;
}
textarea {
	width:540px;
	height:100px;
}
</style>
</head>
<body>

<form method="post">
<textarea name="text" style='float:left'><?= $_POST['text'] ?></textarea>
<input type="submit" value="送信" style='float:left;'>
</form>
<br style='clear:left'><br>

<?
if ( $result != '' ) {
	$obj = json_decode($result);
	$cnt = count($obj);
	for( $i = 0; $i < $cnt; $i++ ) {
		if ( $i == 0 ) {
			print "<div class='entry'>";
			print "<img src='{$obj[$i]->user->profile_image_url}'>";
			print "</div>";
		}
		print "<div class='entry'";

		// ***************************************************
		// リツイート
		// ***************************************************
		if ( $obj[$i]->retweeted_status ) {
			print  " style='background-color:#D6B6CE'>";
			print "<div style='float:left;width:60px;'><img style='width:48px;' src='{$obj[$i]->retweeted_status->user->profile_image_url}'></div>";
			// URL をアンカーに変換
			$work = url2a($obj[$i]->text);
			print "<div style='float:left;width:530px'>{$work}</div>";
		}
		else {
			// *******************************************
			// 返信
			// *******************************************
			if ( $obj[$i]->in_reply_to_screen_name ) {
				print  " style='background-color:#B6D6D6'>";
			}
			// *******************************************
			// 通常
			// *******************************************
			else {
				print  ">";
			}
			// URL をアンカーに変換
			print url2a($obj[$i]->text);
			print "<br>";
		}

		// ***************************************************
		// 画像がある場合
		// ***************************************************
		if ( $obj[$i]->entities->media ) {
			print "<br>";
			print "<div style='clear:left;'><img src='{$obj[$i]->entities->media[0]->media_url}' style='width:200px;'></div>";
		}
		else {
			print "<br style='clear:left;'>";
		}

		print "</div>";
	}

print "<pre>";
//print_r($obj);
print "</pre>";

}
?>
</body>
</html>
<?

// **********************************************************
// AOuth 用の urlencode 関数
// **********************************************************
function urle( $str ) {
	// php 5.3.x 〜 ではこの変換は必要無い
	return str_replace('%7E', '~', rawurlencode($str));
}

function twitter_user_timeline( $apikey, $secret, $token, $token_secret, $userid ) {
	// **********************************************************
	// API
	// **********************************************************
	$twitter_url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';

	// **********************************************************
	// 認証データ
	// **********************************************************
	$oauth_consumer_key = $apikey;
	$oauth_consumer_secret = $secret;
	$oauth_token = $token;
	$oauth_secret = $token_secret;
	
	// 毎回変化するランダムな文字列
	$mt = microtime();
	$rand = mt_rand();
	$oauth_nonce = md5($mt . $rand);
	
	$oauth_signature_method = "HMAC-SHA1";
	$oauth_timestamp = time();
	$oauth_version = "1.0";
	
	// *********************************************************
	// シグネチャ用ベース文字列作成
	/*
	  httpMethod + "&" +
	  url_encode(  base_uri ) + "&" +
	  sorted_query_params.each  { | k, v |
	      url_encode ( k ) + "%3D" +
	      url_encode ( v )
	  }.join("%26")
	*/
	// *********************************************************
	$base_string = "GET";
	$base_string .= "&" . urle($twitter_url);
	$base_string .= "&";
	
	$base_string .= urle("oauth_consumer_key")."%3D".urle($oauth_consumer_key)."%26";
	$base_string .= urle("oauth_nonce")."%3D".urle($oauth_nonce)."%26";
	$base_string .= urle("oauth_signature_method")."%3D".urle($oauth_signature_method)."%26";
	$base_string .= urle("oauth_timestamp")."%3D".urle($oauth_timestamp)."%26";
	$base_string .= urle("oauth_token")."%3D".urle($oauth_token)."%26";
	$base_string .= urle("oauth_version")."%3D".urle($oauth_version);
	$base_string .= "%26screen_name%3D".urle($userid);
	
	// *********************************************************
	// シグネチャ作成
	/*
	url_encode( consumer_secret ) + "&" +
	url_encode( oauth_token_secret || nil )
	*/
	// *********************************************************
	$oauth_signature = 
	base64_encode( hash_hmac(
		"sha1",
		$base_string,
		$oauth_consumer_secret . "&" . $oauth_secret,
		true
	));
	
	// *********************************************************
	// curl 処理
	// *********************************************************
	$curl = curl_init();
	curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
	curl_setopt($curl, CURLOPT_HEADER, false);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);
	curl_setopt($curl, CURLOPT_URL, $twitter_url . "?screen_name=" . urle($userid));

	// *********************************************************
	// http ヘッダ作成
	// *********************************************************
	$header = array();
	$header[] = 'Expect:';
	
	$header[] = "Authorization: OAuth ".
	urle("oauth_consumer_key")."=\"".urle($oauth_consumer_key)."\",".
	urle("oauth_token")."=\"".urle($oauth_token)."\",".
	urle("oauth_nonce")."=\"".urle($oauth_nonce)."\",".
	urle("oauth_timestamp")."=\"".urle($oauth_timestamp)."\",".
	urle("oauth_signature_method")."=\"".urle($oauth_signature_method)."\",".
	urle("oauth_version")."=\"".urle($oauth_version)."\",".
	urle("oauth_signature")."=\"".urle($oauth_signature)."\"";
	
	curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
	
	// *********************************************************
	// https 用 ( https://api.twitter.com 利用時に必要 )
	// *********************************************************
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
	
	// *********************************************************
	// 戻された http ヘッダの出力
	// *********************************************************
	$handle = fopen("./header.txt", "w");
	curl_setopt($curl, CURLOPT_WRITEHEADER, $handle);
	
	// *********************************************************
	// 送信
	// *********************************************************
	$result = curl_exec($curl);
	
	// *********************************************************
	// 結果
	// *********************************************************
	$ret = true;
	if($result === false) {
		$ret = false;
	}
	curl_close($curl);

	return $result;

}

// URL をアンカーに変換
function url2a( $target ){

	$target = str_replace("http", " http", $target );

	if ( preg_match( "/http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/", $target ) ) {
		preg_match_all( "/http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/", $target, $pattarn );
		foreach ( $pattarn[0] as $key => $val ){
			$replace[] = "<a href=\"{$val}\" target=\"_blank\">{$val}</a>";
		}
		$target = str_replace( $pattarn[0], $replace, $target );
	}
	return $target;
}
?>

関連する記事

PHP + cURL で、Twitter API 1.1 の search/tweets を呼び出して、RSS に変換する



posted by lightbox at 2013-08-28 16:34 | PHP + Twitter | このブログの読者になる | 更新情報をチェックする

2013年08月24日


PHP : メール送信(unix : パスワード不要)

最近は、フリーのWEBサーバーでは迷惑メールに利用されるので、sendmail は使用できません。sakura 等、自分で借りているサーバーで動くはずです。また、このような URL を用意しておけば、Android 等のメール送信用の API としてテストに使う事ができます。
さくらインターネット、サーバコントロールパネル ( 作成直後は、容量制限が 200M なので 2M 程度に変更しておきます ) ❷ Gmail の GUI( メールヘッダ等を確認するのに役立ちます ) ❸ メーセージソースの一部
To: =?ISO-2022-JP?B?GyRCMDhAaBsoQg==?= <??????????@gmail.com>
Subject: =?ISO-2022-JP?B?GyRCJWEhPCVrJE4lPyUkJUgbKEI=?=
 =?ISO-2022-JP?B?GyRCJWskSCRKJGokXiQ5GyhCKGxpZ2h0Ym94KQ==?=
From: =?ISO-2022-JP?B?GyRCO2QbKEI=?= <phpuser@??????????.sakura.ne.jp>
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit

$BK\J8$r(B
$BAw?.$7$^$9(B
$B%+%J(B
$B%+%J(B

▼ ここからが本題です

PHP でメール処理をする場合、いろいろなキャラクタセットが入り組んで来るので注意が必要です。

ここではだいたいですが、このようになっています。

❶ 入力 HTML : EUC-JP
❷ PHP のソース : UTF-8
❸ PHP の内部エンコーディング : UTF-8
❹ メール内 : iso-2022-jp( 但しヘッダ内はBASE64変換 )

▼ mailtest.php
<?
header( "Content-Type: text/html; Charset=utf-8" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );

foreach( $_POST as $Key => $Value ) {
	$_POST[$Key] = str_replace("\\\\", "\\", $Value );
	$_POST[$Key] = str_replace("\\'", "'", $_POST[$Key] );
	$_POST[$Key] = str_replace("\\\"", "\"", $_POST[$Key] );
}

// ************************************************************************
// unix では、sendmail を使用します。
// 通常、この URL から信頼されているメールアドレス前提にしているので、
// セキュリティ情報は送る事ができません
// window 環境でもそれは同様なので、同一サーバーにメールサーバー
// を用意するか、COM+basp21でセキュリティ情報を使って送信する必要があります
// ************************************************************************

// mb_send_mail() は、e-mail をエンコードする際にこの設定を使用します
// ※ subject と body のみ
mb_language( "ja" );
mb_internal_encoding("utf-8");

// *********************************************************
// 使用するメールアドレス
// ※ $from_address は、この PHP があるサーバが
// ※ 管理しているメールサーバーのアドレスです
// ※ gmail に送ると、ヘッダーのソースを簡単に確認できます
// *********************************************************
$from_address = "phpuser@????????.sakura.ne.jp";
$to_address = "???????@gmail.com";

// *********************************************************
// 入力データが EUC-JP であると言う前提です
// ※ $subj はこの内部で設定しているので UTF-8 です。
// *********************************************************
$subj	= "メールのタイトルとなります(lightbox)";
$body	= mb_convert_encoding( $_POST['text'], mb_internal_encoding(), "EUC-JP" );
// *********************************************************
// 「半角カタカナ」を「全角カタカナ」に変換します
// *********************************************************
$body = mb_convert_kana($body,'K',mb_internal_encoding());


// *********************************************************
// 宛先と差出人は自分でエンコードする必要があります
// *********************************************************
$to	= mb_encode_mimeheader( mb_convert_encoding("宛先","iso-2022-jp", mb_internal_encoding()) ) . " <{$to_address}>";

// *********************************************************
// From: は追加ヘッダーとして設定しています
// *********************************************************
$from	= "From: " . mb_encode_mimeheader( mb_convert_encoding("私","iso-2022-jp", mb_internal_encoding()) ) . " <{$from_address}>";

$result = mb_send_mail($to, $subj, $body, $from );
if ( $result ) {
	$result = "成功";
}
else {
	$result = "失敗";
}

// ▼ この文字列は UTF-8 です
?>
メール送信が終了しました。<?= $result ?>


▼ 送信テスト用の HTML
<!doctype html>
<html lang="ja">
<head>
<title>sendmail</title>
<meta charset="euc-jp">
<style>
body {
	margin: 0px;
}
textarea {
	width:400px;
	height:200px;
}
</style>
</head>

<body>

<form method="post" action="mailtest.php">
<textarea name="text"></textarea>
<br>
<input type="submit" name="send" value="送信">
</form>

</body>
</html>



関連する記事 ( SQLの窓 )Windows PHP(Pear)で、Gmail(SSL/465)を使ってメールを送るPEAR の Mail パッケージによるメール送信IE 拡張メニューで取得したテキストをメールで送る


更新履歴
2009-05-06 : 初回投稿
2013-08-24 : WEB 経由の API として使う為に内容をチェック、整備



タグ:メール PHP
posted by lightbox at 2013-08-24 16:03 | PHP + 通信 | このブログの読者になる | 更新情報をチェックする

2013年08月22日


Twitter API の自分のアプリのトークンを使って投稿するだけの class VS2012_Twitter

SkyDrive に移動( Windows8 用テンプレート )

テンプレートの主な機能
❶ Twitter 投稿
❷ ダイアログボックス用クラス
❸ Http 用クラス( Twitter では無く一般的なもの )
❹ 新しいページをメモリ上に保持する為の実装( App.xaml.css )
Twitter API の自分のアプリのトークンを使って投稿するだけの class VS2010_Twitter とは、微妙に違います。 VS2012_Twitter.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;

namespace LBOX_Tool
{
	class VS2012_Twitter
	{
        private string _consumer_key;
        private string _consumer_secret;
        private string _token;
        private string _secret;

        private string _tweet_api = "https://api.twitter.com/1.1/statuses/update.json";

        public VS2012_Twitter (
            string consumer_key,
            string consumer_secret,
            string token,
            string secret
            ) {
                _consumer_key = consumer_key;
                _consumer_secret = consumer_secret;
                _token = token;
                _secret = secret;
        }

        public async Task<string> TweetAsync(string text)
        {

            // ソートされるリスト
			SortedDictionary<string, string> sd = new SortedDictionary<string, string>();
            sd.Add("oauth_consumer_key", _consumer_key);
            sd.Add("oauth_nonce", Nonce());
            sd.Add("oauth_signature_method", "HMAC-SHA1");
            sd.Add("oauth_timestamp", TimeStamp());
            sd.Add("oauth_token", _token);
            sd.Add("oauth_version", "1.0");
            sd.Add("status",  Uri.EscapeDataString(text));

            // http ヘッダ用シグネチャ作成
            string work = "";
            foreach (KeyValuePair<string, string> kvp in sd)
            {
                if (work != "")
                {
                    work += "&";
                }
                work += kvp.Key + "=" + kvp.Value;
            }

            string work2 = "";
            // メソッド
            work2 += "POST" + "&";
            // API URL
            work2 += Uri.EscapeDataString(_tweet_api) + "&";
            // Oauth + データ
            work2 += Uri.EscapeDataString(work);

            // OAuth tool チェック用
            Debug.WriteLine(work2);

            string oauth_signature = Signature(work2);

            // ヘッダ情報を作成
            work = "";
            foreach (KeyValuePair<string, string> kvp in sd)
            {
                // oauth_* のみを使用する
                if (work != "")
                {
                    if (kvp.Key.Substring(0, 6) == "oauth_") {
                        work += ", ";
                    }
                }
                if (kvp.Key.Substring(0, 6) == "oauth_")
                {
                    work += kvp.Key + "=" + Dd(kvp.Value);
                }
            }
            // シグネチャを追加( ヘッダーはソートの必要は無い )
            work += ", oauth_signature=" + Dd(Uri.EscapeDataString(oauth_signature));

            // OAuth tool チェック用
            Debug.WriteLine(work);

			string result = "";
			HttpResponseMessage response = null;
			HttpClient hc = new HttpClient();
			try
			{
				hc.MaxResponseContentBufferSize = int.MaxValue;
				hc.DefaultRequestHeaders.ExpectContinue = false;
				hc.DefaultRequestHeaders.Add("Authorization", "OAuth " + work);

				// 送信処理の準備
				HttpContent content = new FormUrlEncodedContent(new Dictionary<string, string>
                {
                    { "status",  text }
                });

				response = await hc.PostAsync( _tweet_api, content );
				result = await response.Content.ReadAsStringAsync();
				if (response.IsSuccessStatusCode)
				{
					if (result.Substring(0, 10) == "{\"errors\":")
					{
						result = "ERROR:" + response.StatusCode + ":" + result;
					}
				}
				else
				{
					result = "ERROR:" + response.StatusCode + ":2XX以外:" + result;
				}
			}
			catch (Exception ex)
			{
				result = "ERROR:" + ex.Message;
			}

			return result;

        }

        // ダブルクォートで挟む
        private string Dd(string base_string)
        {
            return "\"" + base_string + "\"";
        }

        private string Nonce()
        {
            Random rand = new Random();
            int nonce = rand.Next(1000000000);
            return nonce.ToString();
        }

        // タイムスタンプ
        private string TimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        // シグネチャ
        private string Signature(string target)
        {
			String signingKey = _consumer_secret + "&";
			signingKey += _secret;

			IBuffer keyMaterial = CryptographicBuffer.ConvertStringToBinary(signingKey, BinaryStringEncoding.Utf8);
			MacAlgorithmProvider hmacSha1Provider = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1");
			CryptographicKey macKey = hmacSha1Provider.CreateKey(keyMaterial);
			IBuffer dataToBeSigned = CryptographicBuffer.ConvertStringToBinary(target, BinaryStringEncoding.Utf8);
			IBuffer signatureBuffer = CryptographicEngine.Sign(macKey, dataToBeSigned);
			String signature = CryptographicBuffer.EncodeToBase64String(signatureBuffer);
			return signature;
		}
	}
}


MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Diagnostics;
using System.Threading.Tasks;
using LBOX_Tool;

namespace C_Sharp_Twitter1
{
	public sealed partial class MainPage : Page
	{
		public MainPage()
		{
			this.InitializeComponent();
		}

		protected override void OnNavigatedTo(NavigationEventArgs e)
		{
		}

		private async void  Button_Click_1(object sender, RoutedEventArgs e)
		{
			VS2012_Twitter twitter =
				new VS2012_Twitter(
		                    "Consumer key",
		                    "Consumer secret",
		                    "Access token",
		                    "Access token secret"
				);

			string result = await twitter.TweetAsync(this.tweet.Text);
			if (result.Substring(0, 6) != "ERROR:")
			{
				this.Response.Text = result;
			}
			else
			{
				Debug.WriteLine(result);
			}

		}
	}
}

関連する記事Twitter API の自分のアプリのトークンを使って投稿するだけの class VS2010_TwitterTwitter API の自分のアプリのトークンを使って投稿するだけの class Android_TwitterWSH : VBScript と JavaScript で Twitter に投稿するTwitter アプリの登録方法と、API キーの利用PHP : Twitter 投稿関数( twitter_update ) / cURL 関数

変更履歴
2013-07-09 : 初回投稿
2013-08-22 : ダウンロード(テンプレート追加)


タグ:twitter API
posted by lightbox at 2013-08-22 15:45 | Win8 ストアアプリ | このブログの読者になる | 更新情報をチェックする

2013年08月17日


SkyDrive の PowerPoint のアニメーションが3つしかありませんでした

ユーザからすれば、これは結構『トラブルレベル』だと思うのですが。ただ、ちょっとだけいい事もあるにはあって、Excel や Word に無い『図形描画』が用意されてました。と言っても、Google ドライブには全く及ばない現状ではあります。



ですが、WEB 上で公開するにあたって、複雑なアニメーションがはたして必要かどうかという事を切り口にすると、Office に馴染みの無い一般ユーザでもすぐ使えるようには成っているという考え方もあります。事実、Excel や Word でも同様の実装具合で、Word にいたっては、ルーラが無いのでそれに関する機能は無く、見たままでなんとかするようになっています。

そういう意味で、表の扱いは Word としては簡単になっていると思います。Excel は、グラフ作成に力が入っているようですが、WEB アプリとしてはかなりハイレベルなアプリとなっています。

▼ クリックするとオブジェクトのアニメーション



関連する記事

Google プレゼンテーションが結構ふつうに使えるという事の確認
NAVER での簡単な操作手順



posted by lightbox at 2013-08-17 17:22 | Microsoft Office | このブログの読者になる | 更新情報をチェックする

Google ドライブを使って WEB ページを公開する手順

いつもの事ですが、Google のサービスは解りにくい部分が多いので、脇道せずに手順を進めます。

公開用のフォルダを作成



▼ 内部でのフォルダ名を決定


フォルダに対して公開用の設定を行う







アップロードして公開したページを見る







※ host/ と /word.htm の間の文字列は、Google ドライブのシステム用のフォルダID です。



外部サービスを使って、URL を短くする

http://gdriv.es/ を使って、エイリアスを作成できます。マイナーなサービスみたいなので、今回簡単に『link』という別名が取れました。( ちなみに、このサービスはフレームを使っているだけです。)



Google の URL 短縮サービスを使ったほうが後々の信頼性は高いと思います。


関連する記事

Google ドライブに HTML を置いて WEBページが作れるようになりました


posted by lightbox at 2013-08-17 16:12 | WEBサービス | このブログの読者になる | 更新情報をチェックする

Google+ から見た『アルバム』と Picasa ウェブアルバム

もともと、Picasa ウェブアルバムが先に運営されていたサービスで、Google+ を立ち上げるにあたって統合されたようになっていますが、『画像』を WEB 上で再利用したい人にとっては、Picasa ウェブアルバム から管理したほうが優れています。ただ、現在、Picasa ウェブアルバムのドメイン(https://picasaweb.google.com/)にアクセスしようとすると、Google+ の UI へ飛ばされてしまいます。



ただ、ほんの少し待っていると画面中央に Picasa へ移動する為のリンクが浮き上がって来ます。常時 Picasa を使っているとそうでもありませんが、普通はこのような状態になります。

もちろん、元は同じなのでわざわざ Picasa へ移動する必要は無いように見えますが、実際は少し違います。Google+ から画像をアップロードしようとすると、毎回『共有(投稿)』作業用のポップアップウインドウが表示されてしまいます。



画像をアップロードして、他の WEB上で参照する事を目的としている場合、この UI の動きは邪魔でしかありません。しかも、Google+では、他の WEB上で参照する為の画像に対する URL が UI 上で公開されていません。つまりこれは、画像の URL を直接取り出して使用していると、いつ突然に仕様変更でその URL がリンク切れになるか解らない危険性を帯びている事になるわけです。

事実、Google+ から見た画像の URL と、Picasa から見た URL の仕様は違うのです。

その点、Picasa では、アルバムのプロパティを公開レベルにすると、画像に対する URL を使用する為の埋め込みコードが取得できるようになるので、そう簡単に仕様変更でリンク切れになる事は無いと考えられます。



実際は、このようなアルバムレベルの公開をしなくても、画像の URL は有効で外部から参照可能です。ただ、サービスとしてユーザが UI で画像 URL 正式に見れるかどうかという設定です(もちろん本来はアルバムを他の人が見れる設定ですが)

▼ 画像 URL の取得


★ この写真へのリンクは違うものです。このページの URL です。
★ 画像埋め込みから取得した IMG 要素の中の SRC 属性がそれですが、画像を右クリックしても基本同じ

▼ サイズ別に並べたもの


サイズは、URL の中の階層の一部で表現されます。最大、アップロード時のサイズまで指定可能で、そのサイズは右上に『写真の情報』として表示されています。但し、注意するべきは、このサイズは長辺側のサイズであるという事です。

その他の情報

Google+ に無制限で画像を保存できる事の説明
Picasa のサムネイルの使い方
Shadowbox 埋め込みコード作成 : 画像・WEBページ用


posted by lightbox at 2013-08-17 14:55 | WEBサービス | このブログの読者になる | 更新情報をチェックする

オンラインで『Word』( OneDrive )

『共有』で WEB ページとしてリンクを取得する事ができますが、埋め込みを使ったほうが汎用性があると思われます。ただ、まだ発展途上である事もありますし、Google ドライブとの機能競争によれば今後どうなるかはまだ良く解らないところではあります。





取得したコードは、高さにおいて1ページを全て一度に表示するには、550〜650ぐらいに変更するといいと思います。( 右下の『全画面表示アイコン』で WEBページとして表示可能になります )



▲ この画像の状態を WEB ページで表示します ( このWEB ページは Google ドライブですが )

注目は、右下の左側のメニューで、ここから『ダウンロード(アップロード時の形式)』と『PDF出力』と『埋め込みコードの取得』が行えます。最初の埋め込みコードは所有者でないとできませんが、この公開後はだれでも取得できます。

PDF 出力は、印刷方法としていったん PDF を作成するので、Google Chrome と Firefox の持つ PDF ビュアーではかなり UI が違うので注意が必要です( Google Chrome では印刷先を変更できるので、そこで PDF 出力にすると、PDF としてダウンロードできます )

▼ Google Chrome


▼ Firefox


IE10 について

昨今、Adobe 製品が原因でウィルスの被害が発生しているので、基本PC ら Adobe 製品のアドオンはインストールしないようにしています。ですからこちらの環境では、この処理で PDF は表示されずに直接ダウンロードするか、アプリケーションを起動するかを尋ねられます。

Microsoft と Google は一長一短

Word は、『印刷』を目的とした製品なので、完成された文書であれば PDF で出力させるというような『用紙』の目的であれば( 例えば履歴書 )この方法は便利ですが、内容を直接修正するとなると、現状ではオンラインでは『文字の均等割り付け』すら使う事ができないので実用的では無いようです。しかし、機能によっては Google ドライブの『Google ドキュメント』のほうが現在は機能は上のようです(見栄えは Microsoft が勝ってますが)。

ただ、Google も力の入れ方としては、プレゼンテーションが一番のようで、何より全般において『図形の挿入』が可能なのが使い勝手として格段上です。( Microsoft では、なんとか体裁を保っているというか、一般ユーザの為に Excelのグラフ関係は頑張ったようですが、そもそも、中級の機能がまだ実装されていません。)

▼ OneDrive の Excel のグラフ機能( 図形の挿入が無い )


▼ Google の図形挿入



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

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

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

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

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


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

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

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

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

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


Windows
container 終わり

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

Android SDK ポケットリファレンス
改訂版 Webデザイナーのための jQuery入門
今すぐ使えるかんたん ホームページ HTML&CSS入門
CSS ドロップシャドウの参考デモ
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり