SQLの窓

2015年05月28日


jQuery の $.get と $.post と PHP の json_encode で、フォームデータをサーバのテキストファイルとして読み書き

$.post は、$( "form" ).serialize() で一般的には容易にサーバへ入力されたデータを送る事ができます。読み込む場合も、jQuery では、JSON データ の MIME が application/json であれば自動的にオブジェクトとして受け取る事ができるので、$.post の第三引数に "json" を指定しなくてもそのまま for( prop in data ) で処理を開始できます。処理そのものも、ラジオボタンとチェックボックス以外は単純にセットする事ができます。

デモページ

jQuery の処理 / entry.js

jQuery で POST した後は、result.php が結果の HTML を返すので、用意しておいた IFRAME に document.write で書き込んでいます。( document.close しなければ、追加書き込みできます )
$(function(){

	// *****************************************************
	// 固定値
	// *****************************************************
	$("#get_default").click(function(){

		$.get( "data.json", function( data ) {
			// JSON から FORM へ自動セット
			for( prop in data ) {
				if ( prop == "in_radio" ) {
					if ( data[prop] == "1" ) {
						$( '[name="' + prop + '"]' ).eq(0).prop( "checked", true );
					}
					else {
						$( '[name="' + prop + '"]' ).eq(1).prop( "checked", true );
					}
					continue;
				}
				if ( prop == "smh" || prop == "gk" ) {
					$( '[name="' + prop + '"]' ).prop("checked", true );
					continue;
				}
				$( '[name="' + prop + '"]' ).val( data[prop] );
			};
		} );

	});

	// *****************************************************
	// JavaScript( jQuery ) で POST する
	// *****************************************************
	$("#post_form").click(function(){

		$.post( 
				"result.php",
				 $( "form" ).serialize(),	// FORM から 自動作成
				function( data ) {
					// 結果の HTML を IFRAME へ表示
					$( "iframe" ).get(0).contentWindow.document.write( data );
					$( "iframe" ).get(0).contentWindow.document.close();
				}
		);


	});

});


FORM の定義 / entry.php

ファイルに書き込むので、クライアント毎のファイルが必要になるので、セッションID をファイル毎に使用しています。

FORM 内のコントロールは、一般的なものを並べています。重要なのは、各コントロールに name 属性が必要なところぐらいです。( id は、jQuery の個別処理を想定して用意していますが、ここでは特に使用していません )

HTML の機能を使って送信する事を想定していないので、type="submit" のボタンはありませんし、method 属性の記述もありません。

※ デモの実行ボタンは、js_button.php の中に記述しています。
<?php
session_start();
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=1280,initial-scale=1.0">
<style>
body {
	line-height:150%;
	background-color: pink;
}
</style>

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/base/jquery-ui.css">
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/jquery-ui.min.js"></script>

<!-- 入力処理用の JavaScript -->
<script src="entry.js"></script>

</head>

<body>

<div style="position:relative;">
<?php require_once("js_button.php") ?>


<form>

	氏名 
	<input type="text"
		name="sei"
		id="sei"
		style="width:100px">

	<input type="text"
		name="mei"
		id="mei"
		style="width:100px">

	<br>住所 
	<input type="text"
		name="jyusyo"
		id="jyusyo"
		style="width:240px">

	<br>性別 
	<select name="seibetu"
		id="seibetu">
		<option value="man">男</option>
		<option value="woman">女</option>
	</select>

	<br>
	<label for="in_check1">スマホ</label>
	<input type="checkbox"
		name="smh"
		id="in_check1"
		value="smh">

	<label for="in_check2">ガラケー</label>
	<input type="checkbox"
		name="gk"
		id="in_check2"
		value="gk">
	<br>

	<label for="in_radio1">有り</label>
	<input type="radio"
		name="in_radio"
		id="in_radio1"
		value="1"
		checked>
	<label for="in_radio2">無し</label>
	<input type="radio"
		name="in_radio"
		id="in_radio2"
		value="0">
	<br>
	<input type="hidden" name="himitu" value="秘密">
	<input type="password" name="pass">

	<br>備考 
	<br>
	<textarea name="bikou"
		id="bikou"
		style="width:320px;height:100px;"></textarea>

	<br>
<?php if ( file_exists("data" . session_id() .".json") ) { ?>
	<a href="data<?= session_id() ?>.json" target="result">書き込まれた json</a>
<?php } ?>
	/ <a href="data.json" target="result">data.json</a>

</form>
</div>

<iframe
	src="about:blank"
	name="result"
	frameborder="1"
	scrolling="yes"
	width="600"
	height="450"
	style='border:solid 1px #000;margin-top:10px;'
></iframe>

</body>
</html>


$.post を受け取る PHP / result.php

ここでは、json_encode で $_POST を文字列に変換してファイルにそのまま書き込んでいます。テストがしやすいように、整形して日本語をそのまま読める状態にしています。( JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT )
<?php
session_start();
header("Content-Type: text/html; charset=UTF-8");

// 30分以上前に作成された JSON ファイルの削除
require_once("unlink.php");

// ***********************************************
// デバッグ表示
// ***********************************************
print "<pre>";
$work = print_r( $_POST, true );
print $work;
print "</pre>";

print "<hr>";

// ***********************************************
// JSONフォーマットファイルとして書き込み
// ***********************************************
$json = json_encode( $_POST, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
file_put_contents(
	"data" . session_id() . ".json",
	$json
);

?>




タグ:PHP jquery
posted by lightbox at 2015-05-28 22:54 | Ajax:jQuery | このブログの読者になる | 更新情報をチェックする

PHP : PHPコマンドラインから使用中の ini ファイルをチェック

php.exe --ini
( ※ PHP 5.2.3 以降で使用可能です )
他にも良く使いそうなのもののサンプルを以下に記述します
// バージョン表示
php -v
// 現在の日付
php -r "print date('Ymd');"
// タイムゾーンの確認
php --ri date

コマンドラインからの実行は、以下のようにエクスプローラで PHP フォルダを選択して、SHIFT しながら右クリックしてコマンドウインドウを開いて使用します。



PHP マニュアルへのリンク

PHP をコマンドラインから使用する
コマンドラインオプション
入出力ストリーム 


関連する記事


タグ:PHP
posted by lightbox at 2015-05-28 10:22 | PHP + ベーシック | このブログの読者になる | 更新情報をチェックする

Java 8 で、sun.jdbc.odbc.JdbcOdbcDriver を使う手順

さらに個別にしてテストしてみたところ、sun/jdbc と sun/security/action/LoadLibraryAction.class のみにして sun の見えるフォルダから、"C:\pleiades\java\8\bin\jar.exe" cvf jdbc.jar sun としても動作しました。あくまで対症療法なので、問題が出る可能性は残っています。ただ、sun の他のクラスを使っている場合はこの方法を取ったほうがいいと言うところです。
Java 7 までは、rt.jar の中に、sun/jdbc がありますが、java 8 では、jdbc を含めたいくつかが削除されています。そこで、 Java 7 の sun 以下を全て jdbc.jar として作成しなおして、プロジェクトのビルドパスで外部参照します。さらに、Java 7 までしかない JdbcOdbc.dll を Java 8 の jre/bin にコピーすると以前と同じように使えるはずです。Pleiades を使えば、java フォルダに 6 と 7 と 8 があるので、テストはすぐ可能です。 1) Java 7 の rt.jar を rt.zip にリネームして解凍 2) sun の見えるフォルダで、『"C:\pleiades\java\8\bin\jar.exe" cvf jdbc.jar sun』 ※ Pleiades の Java 8 の jar.exe を使用しています 3) ビルド・パスのライブラリタブの外部 jar で jdbc.jar を参照 4) JdbcOdbc.dll を Java 8 側にコピー 各 DB の JDBC ドライバ あればそちらを使うほうが今後は良いと思いますが、全てのデーベースに JDBC ドライバがあるとは限らないので、イザと言う時の為に検証しておいたほうがいいかもしれません MySQL でのテストコード ※ WindowBuilder の JFace のボタンクリックからの処理です
		Button btnNewButton = new Button(container, SWT.NONE);
		btnNewButton.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent ex) {

				boolean ret = true;

				Connection con = null;
				Statement stmt = null;
				ResultSet rset = null;

				String DriverName = "sun.jdbc.odbc.JdbcOdbcDriver";
				String connectionString =
						"Provider=MSDASQL" +
						";Driver={MySQL ODBC 5.3 Unicode Driver}" +
						";SERVER=localhost" +
						";DATABASE=lightbox" +
						";UID=root" +
						";PWD=パスワード文字列" +
						";Charset=cp932" +
						";";

				// 日本語設定用プロパティ
				Properties prop = new Properties();
				prop.put("charSet", "MS932");
				System.out.println(connectionString);

				// 接続
				try {
					Class.forName( DriverName );
					con = DriverManager.getConnection(
							"jdbc:odbc:" + connectionString,prop
					);

					stmt = con.createStatement();
				}
				catch( Exception e  ) {
					System.out.println(e.getMessage());
					ret = false;
				}
				// 接続失敗
				if ( !ret ) {
					return;
				}

				System.out.println("接続成功");

				// SQL 作成
				String Query = "select * from 社員マスタ where 社員コード = '0001'";
				try {
					rset = stmt.executeQuery ( Query );
					ret = rset.next();
				}
				catch( SQLException e ) {
					System.out.println(e.getMessage());
					ret = false;
				}

				// データが存在しないので処理終了
				if ( !ret ) {
					return;
				}

				System.out.println("SQL実行成功");

				try {
					System.out.println( rset.getString( "氏名" ) );
				}
				catch( SQLException e ) {
					System.out.println(e.getMessage());
				}

				System.out.println("列データ取得成功");

				try {
					stmt.close();
					con.close();
				}
				catch( SQLException e ) {
					System.out.println(e.getMessage());
				}

			}
		});

		btnNewButton.setBounds(205, 10, 81, 28);
		btnNewButton.setText("New Button");
Windows の日本語の環境の場合、ドライバ側がでいろいろ考慮されているようなので、キャラクタセットの指定が難解です。この環境では、MySQL が UTF-8 で、ソースコードが UTF8N ですが、prop.put("charSet", "MS932"); を指定しないと、日本語のテーブル名を理解してもらえません。まだ、ODBC ドライバ側の Charset=cp932 が無いと、読み込んだデータの日本語部分が使えません。他の組み合わせはうまく行きませんでした。おそらく、JdbcOdbc.dll の都合と、ODBC ドライバの都合だと思われます。

cp932 は、sjis でもかまいませんが、cp932 のほうが多くの文字をカバーするはずです。

関連する記事

Java sun.jdbc.odbc.JdbcOdbcDriver から Microsoft Access
Java sun.jdbc.odbc.JdbcOdbcDriver から Microsoft Excel

日本語 Eclipse 4.4 Pleiades で WindowBuilder


タグ:トラブル java
posted by lightbox at 2015-05-28 02:34 | java : データベース | このブログの読者になる | 更新情報をチェックする

2015年05月24日


Android Studio で AsyncTask / Android Studio ならではの操作でコードを楽々作成

処理そのものは簡単ですが、Android Studio ならではの、操作が満載です。

MainActivity.java

1) ボタンクリック
2) AsyncTask のインスタンスから、execute
3) doInBackground で非同期処理
4) onPostExecute で UI スレッド処理
package app.lightbox.winofsql.jp.httpget;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Button button = (Button) this.findViewById(R.id.button);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {

				new AsyncTask<String, Void, String>() {
					@Override
					protected String doInBackground(String... params) {
						HttpGet hg = new HttpGet();
						String result = hg.execute(params[0],params[1]);
						return result;
					}

					@Override
					protected void onPostExecute(String s) {
						super.onPostExecute(s);

						TextView tv = (TextView) MainActivity.this.findViewById(R.id.textView);
						tv.setText(s);

					}
				}.execute("http://weather.livedoor.com/forecast/webservice/json/v1?city=270000", "utf-8");

			}
		});

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.menu_main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();

		if (id == R.id.action_settings) {
			return true;
		}

		return super.onOptionsItemSelected(item);
	}
}

AsyncTask の概要
( AsyncTaskは、UIスレッドの適切かつ容易な使用を可能にします )

AsyncTask<String, Void, String> の一つ目の String は、doInBackground の引数の型です。実際には、文字列の配列で、doInBackground へ引き渡されます。最後の String は、onPostExecute の引数の型で、doInBackground の戻り値でもあります。つまり、非同期スレッドから、UI スレッドへの引渡しが String だと言う事です。

仕様として、インターネットへのアクセスは非同期処理で実行する必要があるので、doInBackground で行いますが、そこから直接画面の操作ができないので、onPostExecute で処理する事になります。onPostExecute で必要な情報は、基本的には doInBackground の戻り値でまかなうようになっています。

さらに、真ん中の Void は、onProgressUpdate と言う実行中の進行状況の処理に使われますが、ほぼ使わないので Void にしています。面倒ならば、AsyncTask<String, String, String> でもいいです。

関連情報

▼ HttpGet クラスについてはこちら
Eclipse + JFace : HttpURLConnection で GET

▼ 呼び出している API についてはこちら
お天気Webサービス仕様 - Weather Hacks - livedoor 天気情報

▼ API をもっとテストしやすくする情報はこちら
Livedoor の お天気Webサービス API は、JSON のテストするのに重宝しています。

インターネットにアクセスするので、AndroidManifest を変更



<u と入力すると候補が表示されます( CTRL + SPACE でも表示されます )



uses-permission を選択すると、次は属性値の候補



終了タグ無しで閉じたい( つまり入力の完了 )は、CTRL + SHIFT + ENTER です。



入力が完了しました

styles.xml (v21) の変更 

最小バージョンを Android 5.1.1 にしていると、画面のタイトル部分が白くなってコンテンツとの境界がわかり難いので変更します。最小バージョンを Android 4 にしていると、そうはならないと思います。( 古いほうに合わされるようです )

▼ AndroidManifest の要領で以下のように変更します。


ボタンイベントの作成 

基本的に問題は『ALT + Enter』で解決して行きます。ほぼ勝手に import とかしてくれますが、候補が出る事もあります。



上では、まだ Button をタイプしたけれど、import はされていません。しかし、ALT + Enter で import されます。



上の状態では、import は終わっていますが、お約束のキャストがされていません。左に表示されているアイコンを開くと、キャストの選択肢が現れます。

▼ イベント作成


イベントの setOnClickListener を候補から選択します。



引数の中で new と入力すると、候補が現れて、選択すると自動的に処理用のブロックが作成されます



記述完了です


AsyncTask の入力も基本的には同じようなものです。ただ、@Override の選択は、ブロック内の挿入可能箇所で、CTRL + O で表示されるダイアログで選択します。




さて、画面作成



ボタンの下に ScrollView を配置して、その中に TextView を使って長い JSON の文字列をスクロールして表示できるようにしています
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"/>

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/scrollView"
        android:layout_alignParentStart="false"
        android:layout_below="@+id/button"
        android:fillViewport="false">
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:id="@+id/textView"
            android:textSize="16sp"/>

    </ScrollView>
</RelativeLayout>

ボタン

ボタンは、デザイナで適当に配置して、wrap_content( 表示内容に合わせる ) です。

ScrollView

その下にデザイナから ScrollView を配置すると、android:layout_below が設定されますが、これはプロパティからは layout:alignComponent の中で設定されています。実際の内容と、Android Studio のプロパティ表現が違う場合があるので注意です。さらに、プロパティからは、fill_parent( 親に対して最も大きく ) を設定して、ボタンの下いっぱいのスクロールビューを完成させます。

TextView

デサイナからはうまく入らないかもしれないですが、エディタで ScrollView 内に配置すればいいと思います。横幅のみ、fill_parent にして、高さは、wrap_content です。

テキストサイズは数字を入力すると、Android Studio の仕様だと思いますが、単位が dp になります。しかし、結局左側に問題解決用のアイコンが出て、sp に変更するようになります。


タグ:Android Studio
posted by lightbox at 2015-05-24 21:49 | 1 Android Studio | このブログの読者になる | 更新情報をチェックする

2015年05月23日


PHP の ImageMagick で作成した PNG 画像にオフセットが設定されてしまった場合の対応方法


 

GIMP でこのような表示がされてしまいました。無視すれば意図した状態にはなりますが、適用すると妙な事になります。( WEB上で表示するぶんには問題なさそうです )

何故このような事が起こるのかは解りませんが、背景を透過した時に画像の全体のサイズが調整されてしまってこのようになってしまいました。同様のトラブルも発見しましたが、それはコマンドラインの ImageMagick のお話でした。

ただ、リンク先の情報から、『+repage』というキーワードを知る事ができたので、それで調べると PHP のマニュアルの Imagick::trimImage というメソッドの User Contributed Notes に解答がありました。
<?php 
$im->trimImage(0); 
$im->setImagePage(0, 0, 0, 0); 
?>
上記処理で出なくなりました。



posted by lightbox at 2015-05-23 22:04 | PHP + 特記事項 | このブログの読者になる | 更新情報をチェックする

2015年05月22日


Android Studio : 現在の時間を ライフサイクルのアクティブな時に処理される onStart で表示する( 5.1.1 )

 
 

実行後、現在の時刻を表示します。
アプリがアクティブになった場合にも、その時の現在の時刻を表示します
MainActivity.java
package app.lightbox.winofsql.jp.whattime;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;


public class MainActivity extends Activity {

	// *************************************
	// 自動作成( onCreate )
	// *************************************
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Log.i("lightbox", "開始");

	}

	// *************************************
	// CTRL + O から追加( onStart, onStop, onPause )
	// *************************************
	@Override
	protected void onStart() {
		super.onStart();

		TimeZone tz = TimeZone.getTimeZone("Asia/Tokyo");
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("HH'時'mm'分'ss'秒'");
		sdf.setTimeZone(tz);
		String time = sdf.format(date);
		Log.i("lightbox", time);

		TextView tv = (TextView) this.findViewById(R.id.text);
		tv.setText(time);

	}

	@Override
	protected void onStop() {
		super.onStop();

		Log.i("lightbox", "画面が切り替わりました");

	}

	@Override
	protected void onPause() {
		super.onPause();

		Log.i("lightbox", "画面が切り替わろうとしています");

	}

	// *************************************
	// 自動作成( onCreateOptionsMenu )
	// *************************************
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.menu_main, menu);
		return true;
	}

	// *************************************
	// 自動作成( onOptionsItemSelected )
	// *************************************
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();

		if (id == R.id.action_settings) {

			Log.i("lightbox", "メニュー");

			// 第二画面へ移動
			Intent intent = new Intent(MainActivity.this, NextPage.class);
			startActivity(intent);

			return true;
		}

		return super.onOptionsItemSelected(item);
	}
}

TimeZone

エミュレータで、TimeZone.getDefault() は正しい時間がとれませんでした。確実に、『Asia/Tokyo』を指定するのが良いと思います。

onStart

onCreate では、アプリの開始時にしか実行されないので画面が切り替わって戻って来た時にもその時点での時間を表示する為に、onStart で実行しています。

Activity のライフサイクル( Android Developers )

画面の切り替えは、エミュレータのコントロールで出来ますが、内部のメニューから次の画面に遷移するようにして状況を確認できるようにしています。次画面は、Activity を継承したクラスの onCreate で初期画面( 元々のHello World ) を表示しています。



Activity の追加( AndroidManifest )

その為に必要な Activity の定義は、AndroidManifest にも追加しています。その際の入力は、Android Studio が、.NextPage を候補として表示してくれました。



Override

Override するメソッドは、必要なところにカーソルを置いて、CTRL + O でダイアログが表示されるので、とても便利です。



Project Structure





 

Min SDK Version と Max SDK Version は同じで一番新しいものを、プロジェクト作成時に指定しています。ただ、これの影響でスキンのタイトルが白くなるので、styles.xml( v21 ) の内容を変更しています。
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
    </style>
</resources>

画面の TextView
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context=".MainActivity">

    <TextView
        android:id="@+id/text"
        android:text="@string/hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="40dp"
        android:textColor="#ff000000"/>

</RelativeLayout>

プロジェクト作成時の HelloWorld をそのまま使っていますが、テキストを中央に表示する為に、以下のプロパティで both を指定します。



あとは、テキストサイズと、テキストの色と id をプロパティから変更しています



タグ:Android Studio
posted by lightbox at 2015-05-22 19:39 | 1 Android Studio | このブログの読者になる | 更新情報をチェックする

2015年05月15日


連想配列のキー部分がプロパティと一致したらセットする( メール送信用クラス )

▼ 以下の記事のメール送信部分に汎用性を持たせる為に、作成しました。ブラウザからは、FORM で直接呼び出してもいいですが、jQuery で .serialize() から .post() を使用する方法もあります。

@BBS(アットビービーエス) のサービスが終了するので、管理人宛のメールサービスをほぼ同じ画面デザインで作成しました。( ご自由にお使い下さい )

UTF-8 前提です
<?php

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

$mail = new mail_data();

$_GET['from_address'] = "このPHPが置かれるサーバで使用できるメールアドレス";
$_GET['from_text'] ="日本語差出人";
$_GET['to_address'] ="送り先のメールアドレス";
$_GET['to_text'] ="日本語受取人";
$_GET['subject'] ="件名";
$_GET['body'] ="本文1\n本文2\n";

$_GET['another'] ="その他";

$mail->send_mail_set($_GET);

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

$mail->send_mail();

// ***********************************************
// メール処理用クラス
// ***********************************************
class mail_data {

	public $from_address;
	public $from_text;
	public $to_address;
	public $to_text;
	public $subject;
	public $body;

	// ***********************************************
	// 連想配列のキー部分がプロパティと一致したらセット
	// ***********************************************
	public function send_mail_set( $data ) {

		foreach( $data as $k => $v ) {
			if ( property_exists( $this, $k ) ) {
				$this->{$k} = $v;
			}
		}

	}

	// ***********************************************
	// サーバー(メールサーバ)送信処理
	// ***********************************************
	public function send_mail() {

		$from = mb_convert_encoding( $this->from_text, "JIS", "utf-8" );
		$from = "From: =?ISO-2022-JP?B?" . base64_encode($from) . "?= <{$this->from_address}>";
		$to = mb_convert_encoding( $this->to_text, "JIS", "utf-8" );
		$to = "To: =?ISO-2022-JP?B?" . base64_encode($to) . "?= <{$this->to_address}>";
		mb_send_mail($to, $this->subject, $this->body, $from );
	}

}

?>

$_GET を使っているのは、テスト段階なのが理由です。実装では $_POST が望ましいです。


posted by lightbox at 2015-05-15 17:14 | PHP + 通信 | このブログの読者になる | 更新情報をチェックする

@BBS(アットビービーエス) のサービスが終了するので、管理人宛のメールサービスをほぼ同じ画面デザインで作成しました。( ご自由にお使い下さい )

デモページ


フォントは変更し、必要無い文言は削除しています。
( オリジナルは SHIFT_JIS でしたが、全て UTF-8 に変更しました )



自分が管理するサーバー(レンタルサーバー)があって、そのサーバーから PHP の mb_send_mail 関数で呼び出す事のできるメールアカウントを使って利用可能です。

common.php
<?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" );

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

function admin_mail( $user_body, $user_mail, $user_subject ) {
	$from_address = "<このPHPが置かれるサーバで使用できるメールアドレス>";
	$from = "日本語の差出人";
	$to_address = "<管理人のメールアドレス>";
	$to = "管理人宛";

	$from = mb_convert_encoding( $from, "JIS", "utf-8" );
	$from	= "From: =?ISO-2022-JP?B?" . base64_encode($from) . "?= $from_address";
	$to = mb_convert_encoding( $to, "JIS", "utf-8" );
	$to	= "To: =?ISO-2022-JP?B?" . base64_encode($to) . "?= $to_address";

	$mail_body = "------------------------------------------------\n";
	$mail_body .= "(注)このメールに直接返信しないでください。\n";
	$mail_body .= "送信元は投稿者ではありません。\n";
	$mail_body .= "下記掲載の「投稿者のメールアドレス」に返信してください。\n";
	$mail_body .= "------------------------------------------------\n";
	$mail_body .= "投稿者のIP:{$_SERVER["REMOTE_ADDR"]}\n";
	$mail_body .= "投稿者のメールアドレス:{$user_mail}\n";
	$mail_body .= "内容:\n";
	$mail_body .= $user_body;

	mb_send_mail($to, $user_subject, $mail_body, $from );

}
?>

カスタマイズしやすいように、画面処理は 3つ の PHP ファイルで遷移させています。

1) 入力 : adminmail_1.php
2) 確認 : adminmail_2.php
3) 終了 : adminmail_3.php

その為、通常のデータの受け渡しは、POST を使用していますが、エラー処理の画面遷移では GET を使用しています。

セッションを使うともっと簡潔になりますが、ここでは使用していません。

adminmail_1.php

このページに サーバーから $_POST が送られる事はありませんが、画面遷移を1つのページに仕様変更する際には必要なので、便宜上記述しています。

str_replace の処理は、入力値を HTML 内に問題無く埋め込めるようにする為に、HTML の < と > と 属性の値に使われる "(ダブルクォーテーション) を変換しています。
<?php
require_once( "common.php" );

foreach($_POST as $k => $v) {
	$v = str_replace("\"","&#34;",$v);
	$v = str_replace("<","&lt;",$v);
	$_POST[$k] = str_replace(">","&gt;",$v);
}
foreach($_GET as $k => $v) {
	$v = str_replace("\"","&#34;",$v);
	$v = str_replace("<","&lt;",$v);
	$_GET[$k] = str_replace(">","&gt;",$v);
}

$message = "";
if ( $_GET['e1'] == "1" ) {
	$message .= "<br>※ 件名を入力してください";
}
if ( $_GET['e2'] == "1" ) {
	$message .= "<br>※ メールアドレスを入力してください";
}
if ( $_GET['e2'] == "2" ) {
	$message .= "<br>※ メールアドレスが正しく無いようです";
}
if ( $_GET['e3'] == "1" ) {
	$message .= "<br>※ 本文を入力して下さい";
}

if ( $message != "" ) {
	foreach($_GET as $k => $v) {
		$_POST[$k] = $_GET[$k];
	}
}

?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>管理人へメールを送る</title>

<link type="text/css" rel="stylesheet" href="adminmail.css">

</head>
<body>

<div id="header" style="text-align:left;">
	<a href="adminmail_1.php">リロード</a>
</div>

<div id="container">
	
	<div id="top">
	  <h1>管理人へメールを送る</h1>
	</div>
	<div id="message">
		<?= $message ?>
	</div>	

	<div id="content">
		<fieldset>
			<legend>送信情報</legend>
			
			<div class="explain">
				<ol>
						<li>下記のフォームを埋めて投稿してください。</li>
						<li>メールアドレスは正しく入力してください。</li>
					</ol>
				<p></p>
			</div>


			<form action="adminmail_2.php" method="post" class="form">
				
				<p><label for="subject">件名</label></p>
				
				<p><input type="text" name="subject" value="<?= $_POST["subject"] ?>" id="subject" size="60"></p>
				
				<p><label for="usermail">メールアドレス</label></p>
				
				<p><input type="text" name="usermail" value="<?= $_POST["usermail"] ?>" id="usermail" size="60"></p>
				
				<p><label for="body">本文</label></p>
				
				<p><textarea cols="50" rows="5" name="body" id="body"><?= $_POST["body"] ?></textarea></p>
				
				<input type="submit" name="confirm" value="確認" class="buttons">
				
			</form>
		</fieldset>
	</div>
	
	<div class="clear"></div>
	
</div>


</body>
</html>


adminmail_2.php

メールアドレスのフォーマットチェックには、filter_var 関数(PHP 5 >= 5.2.0) を使用しています。
<?php
require_once( "common.php" );

$err = false;
$param = "";

// 入力チェック
if( trim( $_POST['subject'] ) == "") {
	$err = true;
	if ( $param != "" ) {
		$param .= "&";
	}
	$param .= "e1=1";
}
if( trim( $_POST['usermail'] ) == "") {
	$err = true;
	if ( $param != "" ) {
		$param .= "&";
	}
	$param .= "e2=1";
}
else {
	if( !filter_var($_POST['usermail'], FILTER_VALIDATE_EMAIL)) {
		$err = true;
		if ( $param != "" ) {
			$param .= "&";
		}
		$param .= "e2=2";
	}
}
if( trim( $_POST['body'] ) == "") {
	$err = true;
	if ( $param != "" ) {
		$param .= "&";
	}
	$param .= "e3=1";
}

if ( $err ) {
	$url = "adminmail_1.php?{$param}";
	$url .= "&subject=" . urlencode($_POST['subject']);
	$url .= "&usermail=" . $_POST['usermail'];
	$url .= "&body=" . urlencode($_POST['body']);
	header("Location: {$url}");
	exit();
}

foreach($_POST as $k => $v) {
	$v = str_replace("\"","&#34;",$v);
	$v = str_replace("<","&lt;",$v);
	$_POST[$k] = str_replace(">","&gt;",$v);
}

$_POST["body_html"] = str_replace("\r\n","<br>",$_POST["body"]);
$_POST["body_html"] = str_replace("\n","<br>",$_POST["body_html"]);


?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>管理人へメールを送る</title>

<link type="text/css" rel="stylesheet" href="adminmail.css">

</head>
<body>

<div id="header" style="text-align:left;">
	<a href="adminmail_1.php">リロード</a>
</div>

<div id="container">
	
	<div id="top">
	  <h1>管理人へメールを送る</h1>
	</div>
	
		
	<div id="content">
		<fieldset>
			<legend>送信情報</legend>
			
			<div class="explain">
				<ol>
						<li>下記のフォームを埋めて投稿してください。</li>
						<li>メールアドレスは正しく入力してください。</li>
					</ol>
				<p></p>
			</div>
			
			
			<form action="adminmail_3.php" method="post" class="form">
				<p><label>件名</label></p>
				
				<p><?= $_POST["subject"] ?></p>
				<input type="hidden" name="subject" value="<?= $_POST["subject"] ?>">
				<p><label>メールアドレス</label></p>
				
				<p><?= $_POST["usermail"] ?></p>
				<input type="hidden" name="usermail" value="<?= $_POST["usermail"] ?>">
				<p><label>本文</label></p>
				
				<p><?= $_POST["body_html"] ?></p>
				<input type="hidden" name="body" value="<?= $_POST["body"] ?>">
				<input type="submit" name="edit" value="<< 修正" class="buttons" formaction="adminmail_1.php">
				<input type="submit" name="sendmail" value="送信" class="buttons">
			</form>
		</fieldset>
	</div>
	
	<div class="clear"></div>
	
</div>


</body>
</html>


adminmail_3.php

ここでメール送信を実行しています。送信後、この画面にリダイレクトしているので、ブラウザで再表示しても再度送信しようとする事はありません。
<?php
require_once( "common.php" );

if ($_SERVER["REQUEST_METHOD"] == "POST") {

	admin_mail( $_POST["body"], $_POST["usermail"], $_POST["subject"]);

	header("Location: adminmail_3.php");
	exit();

}

?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>管理人へメールを送る</title>

<link type="text/css" rel="stylesheet" href="adminmail.css">

</head>
<body>

<div id="header" style="text-align:left;">
	<a href="adminmail_1.php">リロード</a>
</div>

<div id="container">
	
	<div id="top">
	  <h1>管理人へメールを送る</h1>
	</div>
	
		
	<div id="content">
		<fieldset>
			<legend>送信情報</legend>
			
			<div class="explain">
				<ol>
						<li>下記のフォームを埋めて投稿してください。</li>
						<li>メールアドレスは正しく入力してください。</li>
					</ol>
				<p></p>
			</div>
			
			
					<p style="color:#336699; font-weight:bold;">メールを送信しました。</p>
			<p><a href="adminmail_1.php">メール作成フォームへ戻る</a></p>
					<div class="clear"></div>
		</fieldset>
	</div>
	
	<div class="clear"></div>
	
</div>


</body>
</html>

送り先が固定なのでここでは実装していませんが、セッションを使えば最初の画面表示から、メール送信までの時間間隔が一定以上必要になるようにする事ができます。


posted by lightbox at 2015-05-15 07:44 | PHP + 通信 | このブログの読者になる | 更新情報をチェックする

2015年05月08日


Android の Twitter のブラウザが、『rem』という単位を理解できずに、巨大なサイズになってしまった件

▼ こんな感じになります。


下のほうの薄いのは広告が表示される瞬間でキャプチャしたのでこんな感じになっています。

環境は、Seesaa のスマホ用のものです。最初何の事がさっぱり解らなかったのですが、古いスマホページのデザインではならずに最新のものだとこうなります。Android の Chrome ではなりません。iPhone( 8.3 ) の Twitter アプリではならないです。Android(4.4.4) Twitter(5.57.0) の環境でなります。

どうも BODY の CSS が怪しいと思い、見てみると以下のようになっていました。
body {
  font-size: 14rem;
  line-height: 1.4;
  color: #333333;
  background: #F1F1F1;
}
え? 『rem』? という事で、CSS 内の rem を全て px に変更すると正しく表示されるようになりました。結局 Twitter アプリのバグっぽいですね。

※ ちなみにハードは KYOCERA DIGUNA U です。



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

2015年05月05日


Java : Google gson 2.3.1 で JSON 文字列を定義済みのクラスを使ってオブジェクト化

処理としては、以下の記事の続きになります。

Eclipse + JFace : HttpURLConnection で GET

※ Google gson のダウンロードはこちら
※ Google gson の実装はこちら
※ オンラインドキュメントはこちら

まずは、JSON データとして WEB API を

Livedoor の お天気Webサービスを使用します。地域id を指定するだけで、その地域の天気データを取得できる API です。

▼ 大阪
http://weather.livedoor.com/forecast/webservice/json/v1?city=270000

JSON のチェックと整形は JSONLint サービスでどうぞ


Google gson 用のクラス定義

JSON のフォーマットに合わせて定義しますが、順序を合わす必要は無く、名前(変数名)を一致させるようにします。サンプルでは、文字列と整数を使っていますが、JsonElement で可能な変換はできると思います。

Getter と Setter でアプリケーションに必要な変換をそこで定義してしまってもいいと思います。

public class Weather {

	class PinpointLocation {
		String link;
		String name;
	}

	class Text {
		String text;
		String publicTime;
	}

	class Location {
		String city;
		String area;
		String prefecture;
	}

	class IntTest {
		ImageSize image;
	}

	class ImageSize {
		int width;
		int height;
	}

	PinpointLocation[] pinpointLocations;
	Location location;
	IntTest copyright;
	Text description;

	// Getter と Setter で処理する
	private String publicTime;

	String getPublicTime() {
		return publicTime.substring(0, 10);
	}
	void setPublicTime(String publicTime) {
		this.publicTime = publicTime;
	}

}


実行
	/**
	 * Create contents of the application window.
	 * @param parent
	 */
	@Override
	protected Control createContents(Composite parent) {
		Composite container = new Composite(parent, SWT.NONE);
		container.setLayout(null);
		{
			Button btnNewButton = new Button(container, SWT.NONE);
			btnNewButton.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					System.out.println("ボタンの処理");
					HttpGet hg = new HttpGet();
					String result =
						hg.execute(
							"http://weather.livedoor.com/forecast/webservice/json/v1?city=270000",
							"utf-8"
							);
					System.out.println(result);

					Gson gson = new Gson();
					Weather json = gson.fromJson(result,Weather.class);

					// 内部クラスの変数として定義されたものをそのまま利用
					System.out.println(json.location.city);
					System.out.println(json.location.area);
					System.out.println(json.location.prefecture);
					System.out.println(json.description.text);
					System.out.println(json.description.publicTime);

					// Setter でセットされたものを Getter で取得
					System.out.println(json.getPublicTime());

					// クラスの配列として定義していたものを使用
					for( int i = 0; i < json.pinpointLocations.length; i++ ) {
						System.out.println(json.pinpointLocations[i].link);
						System.out.println(json.pinpointLocations[i].name);
					}

					// 数値変数
					System.out.println(json.copyright.image.width);
					System.out.println(json.copyright.image.height);

				}
			});
			btnNewButton.setBounds(10, 10, 81, 28);
			btnNewButton.setText("New Button");
		}

		return container;
	}



タグ:java JSON
posted by lightbox at 2015-05-05 07:15 | Java | このブログの読者になる | 更新情報をチェックする
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 終わり