WordPressとFlashの連携(コンタクトフォーム)

  • 2009 年 4月 7 日
  • kosuke

WordPressとFlashを連携させてコンタクトフォームを試した記録。
これまで通りWordPressをベースにして極力サーバー側には触らないアプローチで考えます。


かねてより自分的にコンタクトフォームの類いをFlashで作ってもメリットは微妙だよなぁと思ってます。ま、FULL FLASHのサイトでしか使わないだろうから統一感の点は評価できる。クライアント側での入力チェックやトラフィックの軽減といったAjax同様の効果も期待できる。
しかしまぁ、使う側が使いやすいかというと使い慣れたブラウザのフォームが一番な気がするし、実案件ではサーバー環境の制約があったりセキュリティやスパムの対応が必要だとかDBと連動とかFlash側だけではいかんともしがたい条件になることもしばしば。
そういった煩雑な条件になってくるとFlashで作るって以外の交通整理に時間をとられる。交通整理に各方面と連携が必要なわけだけれど、そういった行程やコストもデメリットとなってくるんだよね。

前置きが長くなりすぎました。というわけで、ここではややこしい複雑なことは考えずにコンタクトフォームを考えてみる。方法はWPのプラグインを使ってフォーム作る。出力結果をFlashに読み込み、ごにょごにょするっていう手法。XHTMLに準拠したソースで動いているものならFlashに読み込ませて大抵の処理は出来ると思う。そりゃもう頑張り次第で….。

では早速。
フォームのプラグインは沢山あったのだけれど、日本語環境で評判のよいContact Form 7を使いました。

インストールしたらツールに「Contact Form 7」のメニューが出来るのでここから必要なフォームエレメントと送信メールを設定します。

まずコントロールパネル右上の「タグの作成」で必要なタグを作成。

このタグを「フォーム」に記述していきます。このフォームの記述に応じて、後で設定するページのxhtmlが出力されるので出力結果と見比べて設定を繰り返します。

メールには送信メールを設定。フォームに入力された値をメールに記載するには[key]のように[]でフォーム欄に入力したタグのキーを囲みます。入力したユーザーにコピーを送るなど、別のメールを送りたい場合はメール(2)も使えます。

メッセージでエラーや完了などの通知メッセージを設定できる。
これはこのまま使ってもいいし、使いやすいように数字とかにしちゃってもいいと思う。これもxhtmlの出力と見比べながら設定していく感じで。

設定が済んだらいつものようにページを作る。
ページの本文にコンタクトフォームのコードを記述します。

コンタクトフォームのコードはhtmlタグに置き換わるので、ページのテンプレートは何も記載しない感じ。Safariだと中途半端なhtmlでもレンダリングされたのでヘッダをhtmlにしています。

<?php /*
Template Name:cms-form
*/ ?>
<?php
header( "Content-Type: text/html; charset=" . get_option( 'blog_charset' ) );
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<?php if (have_posts()) : the_post(); ?>
	<?php the_content(); ?>
<?php endif; ?>

このページをブラウザで表示すると、http://sample.nipx.jp/wordpress/?page_id=125(ブラウザによっては表示できないかも?)のように表示される

表示の参考

これをFlashで読み込んで表示する。

出来上がったのがこれ。Flashのコンタクトフォーム

入力しても送信しないようにしているので操作して確認できます。

xhtmlのフォームエレメントをflashのフォームエレメントに置き換えているだけなのだけれど、まったく整理していない&このxhtml専用の俺俺具合で恥ずかしいくらいごちゃごちゃなソースを晒す…。
ここでは動的にフォームエレメントを生成していますが、設定できるものは通常ステージに配置してコンポーネントインスペクタで設定した方が楽だと思う。

package form{
 
	import caurina.transitions.properties.FilterShortcuts;
	FilterShortcuts.init();
 
	import fl.controls.Button;
	import fl.controls.CheckBox;
	import fl.controls.ComboBox;
	import fl.controls.Label;
	import fl.controls.RadioButton;
	import fl.controls.RadioButtonGroup;
	import fl.controls.TextArea;
	import fl.controls.TextInput;
 
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.text.TextFormat;
	import flash.text.TextFieldAutoSize;
	import flash.utils.Dictionary;
 
	import jp.progression.casts.*;
	import jp.progression.commands.*;
	import jp.progression.scenes.SceneObject;
 
	import index.loading.Loading;
 
	public class FormScene extends SceneObject{
 
		public function FormScene(name:String=null, initObject:Object=null){
			super(name, initObject);
 
			this.onLoad		= this.onLoadHandler;
 
			this.container	= new CastSprite( { x:10, y:60 } );
			this.loading	= new Loading( { x:CastDocument.centerX, y:CastDocument.centerY } );	//ローディングを画面中央に生成
		}
 
		public var request:URLRequest;
		public var dictionary:Dictionary;
		public var container:CastSprite;
		public var loading:Loading;
 
		protected function onLoadHandler():void{
			this.addCommand(
				new AddChild( this.progression.container, this.container ),
				new LoadURL( new URLRequest( "http://sample.nipx.jp/wordpress/?page_id=125" ) ),
				new Func( function():void{
					setup( new XML( this.latestData ), this );
				})
			)
		}
 
		public function dummySend(doc:XML):Boolean{
			var span:XMLList = doc.descendants("span");
			var n:uint = 0;
			for each(var node:XML in span){
				if( node.@["class"] == "wpcf7-not-valid-tip-no-ajax" ){
					n++;
				}
			}
			return ( n == 1 );
		}
 
		//WordPressからのxhtmlを受け取りフォーム画面を生成 //doc = wpからのxhtml, comm = このメソッドはFuncコマンドから実行。そのFuncへの参照
		public function setup( doc:XML, comm:Func=null ):void{
			//trace( doc )
			//フォームの送信先を設定
			request	= new URLRequest( "http://sample.nipx.jp" + doc.form.@action );
			request.method = ( doc.form.@method == "post" ) ? URLRequestMethod.POST : URLRequestMethod.GET;
 
			//送信時使う値を格納するDictionary
			dictionary = new Dictionary( true );
 
			//フォーム生成に利用する変数
			var xml:XML;		//xml
			var x:Number = 0;	//エレメントのx座標
			var y:Number = 0;	//エレメントのy座標
			var h:Number = 0;	//エレメントの高さを一時的に格納
 
			//フロートメッセージ
			if( doc.div.text().length() ){	//メッセージがあれば処理する
				//表示メッセージを生成
				var format:TextFormat		= new TextFormat( "ヒラギノ角ゴ Pro W6", 16, 0x333333 );
				var field:CastTextField		= new CastTextField( { x:0, y:0 } );
					field.defaultTextFormat	= format;
					field.autoSize			= TextFieldAutoSize.LEFT;
					field.text				= doc.div.text();
					field.x					= CastDocument.centerX - field.width /2;
					field.y					= CastDocument.centerY - field.height/2;
					//送信された時
					//if(field.text == "あなたのメッセージは送信されました。ありがとうございました。"){	//正しいif文
					if(field.text == "入力内容に不備があります。確認してもう一度送信してください。" && dummySend( doc ) ){	//ダミー対応
						field.text = "以上のようにFlashからにフォームを利用できます。ありがとうございました。";				//ダミー対応
						field.x = CastDocument.centerX - field.width /2;										//ダミー対応
						field.y = CastDocument.centerY - field.height/2;										//ダミー対応
						//メッセージを表示、フォームを非表示、以降のコマンドを削除をコマンドリストに追加
						comm.parent.insertCommand(
							new AddChild( progression.stage, field, { autoAlpha:250 } ),
							new DoTweener( this.container, { alpha:0, transition:"easeOutSine", time:1 } ),
							new Wait( 2000 ),
							new RemoveChild( progression.stage, field, { autoAlpha:250 } ),
							new Break()
						);		
						return void;
					}
					//送信されない時
					else{
						//メッセージを表示し再度フォームを表示をコマンドリストん追加
						comm.parent.insertCommand(
							new AddChild( progression.stage, field, { autoAlpha:250 } ),
							new Wait( 2000 ),
							new RemoveChild( progression.stage, field, { autoAlpha:250 } )
						);
					}		
			}	
 
			//以前のフォームエレメントを削除
			this.container.removeAllChildren();
 
			//hiddenの設定 wpから得るxhtmlの冒頭にhiddenを格納
			var hidden:XMLList	= doc.descendants("input");
				hidden = hidden.( @type == "hidden" );
			for each( xml in hidden){
				dictionary[ xml.@name.toString() ] = xml.@value;
			}
 
			//フォーム要素の設定 wpから得るxhtmlについてpタグ毎にエレメントを納めている
			var p:XMLList		= doc.descendants("p");
			//エレメントの数繰り返す
			for each( xml in p ){
				if( xml.@["class"] == "captcha1" || xml.@["class"] == "captcha2" ){
					break;
				}
				x = 150;			//左の位置を初期化
				h = 0;				//エレメントの高さを初期化
				//ラベルがあれば表示、ラベルはpタグのテキスト要素として記述されている
				if( xml.text() ){
					var label:Label = new Label();
						label.textField.autoSize = TextFieldAutoSize.LEFT;
						label.text = xml.text();
						label.y = y;
					new AddChild( container, label ).execute();
				}
				//エレメントの種類によりタグが異なる為、必要な判別値を用意
				var className:String = xml.span.span.@["class"];
				var input:XMLList = xml.descendants("input");
				var node:XML;	//繰り返し時にxmlを格納する変数
 
				//ラジオボタンの場合(class名wpcf7-radioのspanに<input type="radio">が囲まれている)
				if( className == "wpcf7-radio" ){
					var group:RadioButtonGroup;		//ラジオボタンのグループ
					for each( node in input ){
						var radio:RadioButton = new RadioButton();
							radio.x = x;
							radio.y = y;
							group = ( group == null ) ? new RadioButtonGroup( node.@name ) : group;
							radio.group = group;
							radio.label = node.@value;
							radio.value = node.@value;
							radio.selected = (node.@checked == "checked");
							new AddChild( container, radio ).execute();
							radio.textField.autoSize = TextFieldAutoSize.LEFT;
							x+= radio.textField.width + 25;		//次のラジオボタンのx位置を設定
							h = radio.height;
					}
					dictionary[ node.@name.toString() ] = group;
				}
				//チェックボックスの場合
				else if( className == "wpcf7-checkbox" ){
					var arr:Array = [];				//このpタグのチェックボックスのインスタンスを格納
					for each( node in input ){
						var checkbox:CheckBox = new CheckBox();
							checkbox.x = x;
							checkbox.y = y;
							checkbox.label = node.@value;
							checkbox.selected = (node.@checked == "checked");
							new AddChild( container, checkbox ).execute();
							arr.push( checkbox );
							checkbox.textField.autoSize = TextFieldAutoSize.LEFT;
							x+= checkbox.textField.width + 25;		//次のチェックボックスのx位置を設定
							h = checkbox.height;
					}
					dictionary[ node.@name.toString() ] = arr;		//Dictionaryに登録
				}
				else{
					for each( node in input ){
						//テキストの場合
						if( node.@type == "text" ){
							var text:TextInput = new TextInput();
								text.x = x;
								text.y = y;
								text.width = 250;
								text.text = node.@value;
							new AddChild( container, text ).execute();
							dictionary[ node.@name.toString() ] = text;
							x+= text.width;
							h = text.height;
						}
						//送信ボタンの場合
						else if( node.@type == "submit" ){
							var submit:Button = new Button();
								submit.x = x;
								submit.y = y;
								submit.width = 50;
								submit.label = node.@value;
							new AddChild( container, submit ).execute();
							//dictionary[ node.@name.toString() ] = submit;
							submit.addEventListener( MouseEvent.MOUSE_DOWN, mouseDownHandler, false, 0, true );
							h = submit.height;
						}
					}
					//セレクトボックスの場合(ここではプルダウンのみの想定)
					var select:XMLList = xml.descendants("select");
					if( select ){
						for each( node in select ){
							var combobox:ComboBox = new ComboBox();
								combobox.x = x;
								combobox.y = y;
								combobox.width = 75;
								var option:XMLList = node.option;
								var num:uint = 0;
								for( var i=0; i<option.length(); i++ ){
									var op:XML = option[i];
									num = ( op.@selected == "selected" ) ? i : num;
									combobox.addItem( { label:op.text(), data:op.@value } )
								}
								combobox.selectedIndex = num;
							new AddChild( container, combobox ).execute();
							dictionary[ node.@name.toString() ] = combobox;
							x+= combobox.width;
							h = combobox.height;
						}
					}
					//テキストエリアの場合
					var textareaList:XMLList = xml.descendants("textarea");
					if( textareaList ){
						for each( node in textareaList ){
							var textarea:TextArea = new TextArea();
								textarea.x = x;
								textarea.y = y;
								textarea.width = 250;
								textarea.height= 100;
								textarea.text= node.text();
							new AddChild( container, textarea ).execute();
							dictionary[ node.@name.toString() ] = textarea;
							x+= textarea.width;
							h = textarea.height;
						}
					}
					//エラーメッセージがあればフォームエレメントの右に表示
					var span:XMLList	= xml.descendants("span");
						for each( node in span ){
							if( node.(@["class"] == "wpcf7-not-valid-tip-no-ajax" ) ){
								var msg:Label = new Label();
									msg.textField.autoSize = TextFieldAutoSize.LEFT;
									msg.text = node.text();
									msg.x = x;
									msg.y = y;
								new AddChild( container, msg ).execute();
							}
						}
 
 
				}
				//次の座標を設定
				y+=h;
				y+=10;
			}
		}
 
		//送信ボタンを押したら送信実行
		public function mouseDownHandler(e:MouseEvent):void{
			//変数オブジェクトを生成
			var vars:URLVariables = new URLVariables();
			//dictionaryを参照しキーと値を設定
			for( var i:String in dictionary ){
				var target	= dictionary[i];
				if( target is TextInput || target is TextArea ){	//TextInputかTextAreaならtextを格納
					vars[ i ] = target.text;
				}
				else if( target is Array ){	//Array(チェックボックス)なら配列にチェック済みの値(ラベル)を格納
					var arr:Array = [];
					for( var a=0; a<target.length; a++ ){
						var checkbox:CheckBox = target[a] as CheckBox;
						if( checkbox.selected ){
							arr.push( checkbox.label )
						}
					}
					vars[ i ] = arr;
				}
				else if( target is RadioButtonGroup ){	//ラジオボタンなら選択されている値を格納
					if( target.selection ){
						vars[ i ] = target.selection.value;
					}
				}
				else if( target is ComboBox ){			//セレクトボックスなら選択されている値を格納
					vars[ i ] = target.selectedItem.data;
				}
				else{	//それ以外(ここではhidden)
					vars[ i ] = target;
				}
 
			}
			//変数をリクエストデータに登録
			this.request.data = vars;
			//WPに送信
			var comm:SerialList = new SerialList();
				comm.addCommand(
					new AddChild( this.progression.container, this.loading ),
					new DoTweener( this.container, { _Blur_blurX:20, _Blur_blurY:20, _Blur_quality:3, alpha:0.5, transition:"easeOutSine", time:0.5 } ),
					new LoadURL( this.request ),
					new RemoveChild( this.progression.container, this.loading ),
					new Func( function():void{
						setup( new XML( this.latestData ), this );
					}),
					new DoTweener( this.container, { _Blur_blurX:0, _Blur_blurY:0, _Blur_quality:3, alpha:1, transition:"easeOutSine", time:0.5 } ),
					new Prop( this.container, { filters:null } )
				)
				comm.execute();
		}
 
	}
}

結論。フォームはHTMLがいいと思う。

“WordPressとFlashの連携(コンタクトフォーム)” に コメントはありません

コメントをどうぞ