SQLの窓

2020年07月31日


GAS : Classroom API で、コースに生徒を一括登録する

スクリプトエディタのリソースメニュー(Google の拡張サービス)より API を実行可能にしておく必要があります。



コースID を指定して、A 列に並べたユーザ文字列を元に、生徒として一括登録します

function createStudent() {

	var CourceId = "コースID";

	// 選択したシートを対象とします
	var spreadsheet = SpreadsheetApp.getActive();

	// 行番号
	var i = 1;

	while (true) {

		// 登録済のフォルダを排除する為に順に比較していく
		var targetRange = spreadsheet.getRange('A' + i);
		var cellWork = targetRange.getValue().toString();
		if (cellWork != '') {

			// 生徒作成用の JSON
			var json = {
				"userId": cellWork + "@ドメイン"
			};

			// 生徒を追加
			// ( 招待済でも確定します )
			try {
				Classroom.Courses.Students.create(json, CourceId);

			}
			catch(e) {
				GmailApp.sendEmail("メールアドレス", "Classroom 生徒登録エラー", JSON.stringify(json) + "\r\n" + e.message );

			}

			i++;

		}
		else {
			break;
		}
	}
}



関連する記事

GAS : Classroom API で、コース一覧と、コース毎のトピック一覧をスプレッドシートに出力




posted by lightbox at 2020-07-31 14:46 | GAS | このブログの読者になる | 更新情報をチェックする

GAS : Classroom API で、コース一覧と、コース毎のトピック一覧をスプレッドシートに出力

スクリプトエディタのリソースメニュー(Google の拡張サービス)より API を実行可能にしておく必要があります。



A に コースの ID、B にコース名、D にトピック ID、E にトピック名をセットします。

function listCourseAndTopic() {

	// 選択したシートを対象とします
	var spreadsheet = SpreadsheetApp.getActive();

	// コースの一覧( 1つ以上あるという前提 )
	var json = Classroom.Courses.list();

	var cnt = json.courses.length;
	
	var rowno = 0;

	for( var i = 0; i < cnt; i++ ) {
		var targetRange = spreadsheet.getRange('A' + (rowno + i + 1));
		targetRange.setValue(json.courses[i].id);
		targetRange = spreadsheet.getRange('B' + (rowno + i + 1));
		targetRange.setValue(json.courses[i].name);

		// トピックが無い場合、jsonTopic は {} となります
		jsonTopic = Classroom.Courses.Topics.list( json.courses[i].id );
		try {
			for ( var j = 0; j < jsonTopic.topic.length; j++ ) {
				var targetRange = spreadsheet.getRange('D' + (rowno + i + 1));
				targetRange.setValue(jsonTopic.topic[j].topicId);
				targetRange = spreadsheet.getRange('E' + (rowno + i + 1));
				targetRange.setValue(jsonTopic.topic[j].name);
				rowno++;
			}

			// トピックがあった場合一行空ける為にコメント
			// rowno--;
		}
		catch(e){
				// トピック無しでエラーの為ここで一行空けます
				rowno++;
		}

	}

}





posted by lightbox at 2020-07-31 14:20 | GAS | このブログの読者になる | 更新情報をチェックする

2020年07月25日


PHP : $.ajax でアップロード( 画像限定 ) / ファイルアップロード ver.3

PHP : 選択した画像の表示 / ファイルアップロード ver.2』 よりアップグレードします。

$.ajax と FormData オブジェクト を使用して画像をアップロードします。よって、HTML の FORM での送信は必要なくなります( HTML の入力チェックは必要時に使用する仕様なのでので FORM の記述は必要です )

control.php
      コントローラ

✅ model.php
      このバージョンでは使用しません

✅ view.php
      画面定義

✅ client.js
      FileReader オブジェクトを使用した、画像表示
      ※ 複数選択が可能になっています

✅ upload.js
      $.ajax と FormData オブジェクト 

✅ uplaod.php
      move_uploaded_file でアップロードされたファイルを保存するのが主な役割です。
      ( 結果は JSON フォーマットで $.ajax に返します )


control.php

$image_path は upload.php で使用するので、そちらへ移動しています。
( $result_message ももう使用しません )
<?php
// 不必要なエラー表示を行わない
error_reporting( E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED );
// ブラウザのキャッシュに保存しない
session_cache_limiter('nocache');
// セッションの開始
session_start();

// この ページの MIME( ページの種類 ) の設定
header( "Content-Type: text/html; charset=utf-8" );
// ***********************************************

// 固有の処理
require_once("model.php");


// $image_path = "./myimages";
// $result_message = "";

// 画面
require_once("view.php");

// デバッグ
//debug_print();

?>




model.php

ここは、もう使用しません( 標準化の為、model.php を削除する事はありません )
<?php
// ********************************************
// このアプリケーション専用の処理
// ********************************************

// *******************************************************
// デバッグ
// *******************************************************
function debug_print() {

	print "<pre>";
	print_r( $_GET );
	print_r( $_POST );
	print_r( $_SESSION );
	print_r( $_FILES );
	print "</pre>";

}
?>


view.php

選択するファイルを画像限定にする為に、type="file" の INPUT 要素の属性に accept=".png,.jpg,.jpeg,.gif" を追加しています。また、アップロードを $.ajax で行うので FORM の enctype="multipart/form-data"削除しています
<!DOCTYPE html>
<html>
<head>
	<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
	<meta charset="UTF-8">
	<title>画像ファイルアップロード</title>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css">

	<script src="client.js?_=<?= time() ?>"></script>
	<script src="upload.js?_=<?= time() ?>"></script>
</head>
<body>
	<div id="head">
		<div id="title">
			<a href="./">画像をアップロード</a>
		</div>
	</div>
	<div id="content">
		<form
			id="frm"
			method="POST">
			<p>
				<input type="hidden"
					name="MAX_FILE_SIZE"
					value="1000000">

				<input id="target"
					name="target"
					type="file"
					accept=".png,.jpg,.jpeg,.gif"
					class="ml-1 mt-3 btn btn-outline-primary">
			</p>
			<p>
				<input type="submit"
					name="send"
					value="アップロード"
					class="ml-1 btn btn-outline-primary">
				<a class="ml-4 btn btn-info btn-sm" href="<?= $_SERVER["PHP_SELF"] ?>">リロード</a>
			</p>
			<div id="image"></div>
		</form>
	</div>
	<div id="result"><?= $result_message ?></div>
</body>
</html>




client.js

この部分は、ver.2 では選択は1つだけでしたが、今回は複数選択が可能になっています( クリアのタイミングが違うだけです )。

$("#image").html(""); による画像クリアをここで実行していません。画像のクリアは、upload.js の #.ajax の正常終了時のイベントの最後で行っています。
$(function(){

	// INPUT type="file" のファイル選択後のイベント
	$("#target").on("change", function(){

		// 選択されたファイルの情報
		console.dir( this.files );

		// ファイル参照用のクラス : FileReader
		var reader = new FileReader();

		// 表示用にプロパティを追加
		reader.name = this.files[0].name;
		reader.type = this.files[0].type;

		// 画像が読み込まれると実行されるイベント
		$(reader).on("load", function () {

			// FileReader の内容
			console.dir( this );

			if ( this.type.indexOf("image/") == 0 ) {
				$("<img>").appendTo("#image")
					.prop( {"src": this.result, "title": this.name + " : " + this.type } )
					.css( {"width": "160px", "margin": "10px","border": "1px solid #c0c0c0" } );
			}
			else {
				$("<img>").appendTo("#image")
					.prop( {"src": "./notimage.png", "title": this.name + " : " + this.type } )
					.css( {"width": "160px", "margin": "10px","border": "1px solid #c0c0c0" } );
			}

		});

		if (this.files[0]) {
			// 画像を読み込み
			reader.readAsDataURL(this.files[0]);
		}

	});
	
});




upload.js

$.ajax と FormData オブジェクトの処理です。但し、アップロードするデータは type="file" を持つ INPUT 要素で作成されるファイルオブジェクトでは無く、FileReader オブジェクトで作成された base64 の文字列を持つ画像を使用します。
$(function(){

	// *************************************
	// アップロード処理
	// *************************************
	$("#frm").submit( function(event){

		// 本来の送信処理はキャンセルする
		event.preventDefault();

		// アップロードする画像があるかどうかのチェック
		if ( $("#image").html() == "" ) {
			options.error("アップロードする画像ファイルを選択して下さい");
			return;
		}

		if ( !confirm("アップロードを開始してもよろしいですか?") ) {
			return;
		}

		// 操作不可に設定
		$("#content input").prop("disabled", true);

		// 結果の表示エリアを全てクリア
		$("#result").html( "" );

		// **************************************
		// ファイルのアップロード
		// **************************************
		console.log("アップロード処理開始");

		var formData = new FormData();

		// 画像データサイズの制限
		formData.append("MAX_FILE_SIZE", 10000000);

		var file_cnt = 0;

		$("#image img").each( function() {

			var base64 = $(this).prop("src");
			var bin = atob(base64.split(',')[1]);
			var buffer = new Uint8Array(bin.length);
			for (var i = 0; i < bin.length; i++) {
				buffer[i] = bin.charCodeAt(i);
			}
			var blob = new Blob([buffer.buffer], {type: "application/octet-stream"});

			file_cnt++;
			var file_name = (new Date()).getTime();
			formData.append("image"+file_cnt, blob, file_name +"_"+file_cnt+".dat");

		});

		formData.append("FILE_COUNT", file_cnt );

		$.ajax({
			url: "./upload.php",
			type: "POST",
			data: formData,
			processData: false,  // jQuery がデータを処理しないよう指定
			contentType: false   // jQuery が contentType を設定しないよう指定
		})
		.done(function( data, textStatus ){
			console.log( "status:" + textStatus );
			console.log( "data:" + JSON.stringify(data, null, "    ") );
			alert("アップロード処理が完了しました");

			// アップロード結果の表示
			$.each(data, function( idx, image ){

				if ( image.error != 0 ) {
					$("#result").append("<span id=\"result" +idx+"\"></span><b style='color:red'>" + image.name+ " : " + image.result +"</b><br>");
				}
				else {
					$("#result").append("<span id=\"result" +idx+"\"></span>" + image.name + " : " + image.result + "<br>");
				}

			});

			// 画像表示のクリア
			$("#image").html("");

		})
		.fail(function(jqXHR, textStatus, errorThrown ){
			console.log( "status:" + textStatus );
			console.log( "errorThrown:" + errorThrown );
		})
		.always(function() {

			// 操作不可を解除
			$("#content input").prop("disabled", false);
		})
		;

	});

});


upload.php

サーバ側でファイルアップロードを担当する upload.php は、返すデータは JSON フォーマットであり、MIME は application/json です。これによって、$.ajax は直接 JSON オブジェクトを返す事になります。
<?php
// キャラクタセット
// *************************************
header( "Content-Type: application/json; charset=utf-8" );
// *************************************
// キャッシュ無効
// *************************************
session_cache_limiter('nocache');
session_start();

// ファイルを移動するフォルダ
$image_path = "./myimages";

// フォルダが無ければ作成
if ( !is_dir( $image_path ) ) {

	mkdir( $image_path );

}

$cnt = $_POST["FILE_COUNT"] + 0;

for( $i = 0; $i < $cnt; $i++ ) {

	$image_target = "image".($i+1);

	if ( $_FILES[$image_target]["error"] == 0 ) {

		// *************************************
		// 1) 画像フォーマットの取得
		// *************************************
		$type_string = image_type_to_mime_type( exif_imagetype( $_FILES[$image_target]['tmp_name'] ) );

		// *************************************
		// 2) オリジナルファイル名の取得
		// *************************************
		$file = explode(".", $_FILES[$image_target]['name']);

		// *************************************
		// 3) 日本語ファイル名対応
		// *************************************
		$file_name = urlencode( $file[0] );

		// *************************************
		// 4) 保存ファイル名を作成
		//   a) 拡張子決定
		//   b) uniqid() でファイル目をユニーク
		// *************************************
		$target = "";
		if ( $type_string == "image/jpeg" ) {
			$target = uniqid() . "_{$file_name}.jpg";
		}
		if ( $type_string == "image/gif" ) {
			$target = uniqid() . "_{$file_name}.gif";
		}
		if ( $type_string == "image/png" ) {
			$target = uniqid() . "_{$file_name}.png";
		}
		if ( $target == "" ) {
			$_FILES["image"]["result"][] = "アップロードできないフォーマットです";
		}
		else {
			// *************************************
			// アップロードファイルの保存
			// *************************************
			if ( @move_uploaded_file( $_FILES[$image_target]['tmp_name'], "{$image_path}/{$target}" ) ) {
				$_FILES[$image_target]["result"] = "アップロードに成功しました";
			}
			else {
				// なんらかの環境エラー
				$_FILES[$image_target]["result"] = "アップロードに失敗しました";
			}
			
		}
	}
	else {
		switch($_FILES[$image_target]["error"]){
			case 1:
				$_FILES[$image_target]["result"] = "php.ini の upload_max_filesize ディレクティブの値を超えています";
				break;
			case 2:
				$_FILES[$image_target]["result"] = "HTML フォームで指定された MAX_FILE_SIZE を超えています";
				break;
			case 3:
				$_FILES[$image_target]["result"] = "一部のみしかアップロードされていません";
				break;
			case 4:
				$_FILES[$image_target]["result"] = "アップロードされませんでした";
				break;
			case 6:
				$_FILES[$image_target]["result"] = "テンポラリフォルダがありません";
				break;
			case 7:
				$_FILES[$image_target]["result"] = "ディスクへの書き込みに失敗しました";
				break;
			case 8:
				$_FILES[$image_target]["result"] = "PHP の拡張モジュールがファイルのアップロードを中止しました";
				break;
			default:
				$_FILES[$image_target]["result"] = "不明なエラーです";
		}
		
	}

}

print json_encode($_FILES)


?>




posted by lightbox at 2020-07-25 19:54 | PHP + WEBアプリ | このブログの読者になる | 更新情報をチェックする

ロリポップのモジュール版 PHP でエラーを出力する方法




実行する PHP の先頭で、『ini_set("display_errors", 1);』を実行して、処理は全て別のファイルに書いて require_once() します。こうする事によって、PHP は以下の例の action1.php 内で発生したエラーに対して error_reporting の設定に基づいてエラーを表示します。
<?php
ini_set("display_errors", 1);

require_once("action1.php");

?>

action1.php のサンプル
<?php
common_action();

$a = 1;

function common_action() {

    error_reporting( E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED );
    session_cache_limiter('nocache');
    session_start();

    header( "Content-Type: text/html; charset=utf-8" );
}


?>
<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="utf-8">
<title>メール送信</title>
<head>
<body>
<div>メール送信</div>
</body>
</html>

このソースで、$a = 1; のセミコロンを削除して実行してみると以下のようになるはずです
Parse error: syntax error, unexpected 'function' (T_FUNCTION) in /home/users/1/yourdomain/web/yourapp/path/action1.php on line 6



そして、action1.php を直接実行すると Google Chrome では以下のようになります




では、WordPress の場合はどうするかというと

※ wp-config.php の先頭に 『ini_set("display_errors", 1);』を記述してください



posted by lightbox at 2020-07-25 15:12 | PHP | このブログの読者になる | 更新情報をチェックする
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 終わり