カテゴリー
プログラム 社内SE

NextCloudに外部からファイルをアップし内部リンクを取得する方法

別システムからバッチなどでファイルを作成、そのファイルをNextCloudにアップし、NextCloudの内部リンクを取得したいケースがあった。参考までに方法を示す。

CURLでNextCloudのAPIコマンドをたたき、アップしてFileIDを取得する。

PHPのプログラムで書くと以下の感じとなる。

/**
 * ローカルのファイルをNextCloudにアップロードし、内部リンクを取得する。
 *
 * @param string $upFileFullPath アップしたいファイルのフルパス
 * @param string $dstFolder NextClodu上のアップしたいフォルダー(ファイル名は$upFileFullPathのファイル名を使用)
 * @param string $nextCloudURL NextCloudのURL
 * @param string $userName NextCloudのログインユーザー
 * @param string $password NextCloudのログインユーザーのパスワード(アプリパスワード)
 * @return string|bool ファイルアップ後の内部リンクを返す。falseの場合はエラー
 */
function upFileAndGetInternalLink($upFileFullPath, $dstFolder, $nextCloudURL, $userName, $password){

	if (!is_file($upFileFullPath)){ return false; }
	$fileName = basename($upFileFullPath);
	if (substr($nextCloudURL, -1) != '/'){ $nextCloudURL .= '/'; }
	if (substr($dstFolder, -1) != '/'){ $dstFolder .= '/'; }

	$apiOfTheUpFile = $nextCloudURL . 'remote.php/dav/files/' . $dstFolder . $fileName;

	//libxml_use_internal_errors(true);

	$cmd = "curl -T '" . $upFileFullPath . "' -u '" . $userName . ":" . $password . "' '" . $apiOfTheUpFile . "'";
	echo $cmd;
	$ret = exec($cmd, $aryOutput, $resultCode);
	if ($ret === false || $resultCode != 0){ return false; }

	$cmd = "curl -u '" . $userName . ":" . $password . "' '" . $apiOfTheUpFile . "' -X PROPFIND --data '<?xml version=\"1.0\" encoding=\"UTF-8\"?>
		<d:propfind xmlns:d=\"DAV:\">
		<d:prop xmlns:oc=\"http://owncloud.org/ns\">
				<oc:fileid />
		</d:prop>
		</d:propfind>'";

	$ret = exec($cmd, $aryOutput, $resultCode);
	if ($ret === false || $resultCode != 0){ return false; }

	$ret = str_replace(':','',$ret);
	$xml = simplexml_load_string($ret);
	$fileId = (string)$xml->dresponse->dpropstat->dprop->ocfileid;

	return $nextCloudURL . "index.php/f/" . $fileId;

}

使用する感じは以下。

$upFileFullPath = '/td/dl/hogehoge.xlsx'; 
$nextCloudURL = 'https://yourNextCloud.com/'; 
$dstFolder = '/yourName/All/'; // NextCloudの"data"フォルダ以下を指定。ユーザ名が最初のフォルダ階層になるはず。
$userName = 'YourName';
$password = 'xH6Jk-Hkdje-OPalkd-J4PCn-mqxMW';

echo upFileAndGetInternalLink($upFileFullPath, $dstFolder, $nextCloudURL, $userName, $password);
カテゴリー
プログラム 社内SE

NextCloudで日本語(Shift-JIS)のテキストファイルを開く(簡単 1ファイル修正)

NextCloudをそのまま使うと日本語ファイルが文字化けすることがある。Windowsで昔作ったテキストファイルの文字コードがSJISになっているためである。

ググるとTEXTアプリのソースを2箇所修正する方法が出てくるのだが、最近のNextCloudのTEXTアプリは自前でエンコード変換するプログラムがあり、それを修正するほうが簡単で本筋である。(エンコード変換のプログラムで日本語の優先順が低いため、SJISのファイルがうまく変換されていないのである。)

修正ファイルは以下のファイルとなる。

(NextCloudインストールフォルダ)/apps/text/lib/Service/EncodingService.php

このファイルの上部のエンコード一覧(変数名:COMMON_ENCODINGS)の配列の先頭にSJISを加える。(NextCloud 24.0.5では29行目)

// コメントが元のソース 'UTF-8'の前に、'SJIS-win', 'SJIS', を追加
// public const COMMON_ENCODINGS = [ 'UTF-8', 'GB2312', 'GBK', 'BIG-5', 'SJIS-win', 'EUC-JP', 'Windows-1252', 'ISO-8859-15', 'ISO-8859-1', 'ASCII'];
public const COMMON_ENCODINGS = [ 'SJIS-win', 'SJIS', 'UTF-8', 'GB2312', 'GBK', 'BIG-5', 'SJIS-win', 'EUC-JP', 'Windows-1252', 'ISO-8859-15', 'ISO-8859-1', 'ASCII'];

これでSJISのファイルが文字化けせずに表示されるようになる。(中国語のGB2312テキストファイルが今度は文字化けするかもしれないが、そこは考えない。考えるならブラウザの表示言語でエンコードの優先度を変更するなどで実用上は賄えそう。)

カテゴリー
プログラム 社内SE

Outlookのフォルダ構成ごとメールを取り出す方法

Outlookのメールだけならドラッグ&ドロップすれば、Windowsのフォルダへコピー可能だが、フォルダ毎ドラッグしてもWindowsのファイルシステムへコピーすることはできない。

これを実現するにはマクロでコピーするしかない。こんなこと通常やる必要は全くないのだが、ボケた上役の命令でやる機会があったので一応記録を残しておく。

Outlookでマクロを有効にための事前準備は以下。

  • [ファイル]-[オプション]-[トラストセンター]で[トラストセンターの設定]を開く
  • [マクロの設定]で[全てのマクロに対して警告を表示する]以下を選択する。
  • Outlookを再起動する。
  • [Alt]+[F11]でVBAの画面を開く
  • VBA画面で[ツール]-[参照設定]で開く画面から[Microsoft Scripting Runtime]を探し、チェックし[OK]をクリックする。
  • VBA画面で[挿入]-[標準モジュール]をクリックする。

ここまでで、左側のパネルでModule1という箇所が選択され、右側のパネルにVBAのプログラムが書けるようになるので、以下のプログラムをコピペする。

Dim objFSO As Scripting.FileSystemObject

Sub ExportOutlookFolders()
    Dim objFolder As Outlook.Folder
    Dim strFolderPath As String
    strFolderPath = SelectAnExportFolder()
    If strFolderPath = "" Then
        MsgBox "出力するフォルダを選択してください。", vbInformation + vbOKOnly, "フォルダ選択"
    Else
        Set objFSO = New Scripting.FileSystemObject
        Set objFolder = Outlook.Application.ActiveExplorer.CurrentFolder
        ExportAnOutlookFolder objFolder, strFolderPath
    End If
    Set objFolder = Nothing
    Set objFSO = Nothing
End Sub

Sub ExportAnOutlookFolder(ByVal OutlookFolder As Outlook.Folder, strFolderPath As String)
    Dim objSubFld As Outlook.Folder
    Dim objItem As Object
    Dim strPath As String
    Dim strFilePath As String
    Dim strSubject As String
    Dim strFilename As String
    Dim strRecievedTime As String
    Dim nCount As Long
    On Error Resume Next
    strPath = strFolderPath & "\" & ReplaceInvalidCharacters(OutlookFolder.Name)
    If Dir(strPath, 16) = Empty Then MkDir strPath

    nCount = 0

    For Each objItem In OutlookFolder.Items
        strSubject = ReplaceInvalidCharacters(objItem.Subject)
        If strSubject = "" Then
            strSubject = "notitle"
        End If
        strRecievedTime = Format(objItem.ReceivedTime, "yyyymmddhhnnss")
        strFilename = strRecievedTime & "_" & strSubject & ".msg"
        strFilePath = strPath & "\" & strFilename
        If objFSO.FileExists(strFilePath) Then
            nCount = nCount + 1
            strFilename = strRecievedTime & "_" & strSubject & " (" & nCount & ").msg"
            strFilePath = strPath & "\" & strFilename
        End If
        objItem.SaveAs strFilePath, olMSG
        
        DoEvents
    Next

    For Each objSubFld In OutlookFolder.Folders
        ExportAnOutlookFolder objSubFld, strPath
    Next
    Set OutlookFolder = Nothing
    Set objItem = Nothing
End Sub

Function SelectAnExportFolder() As String
    Dim objSelFolder As Object
    Dim objShell As Object
    Set objShell = CreateObject("Shell.Application")
    Set objSelFolder = objShell.BrowseForFolder(0, "Select a folder", 0, 0)
    If Not TypeName(objSelFolder) = "Nothing" Then
        SelectAnExportFolder = objSelFolder.self.Path
    End If
    Set objSelFolder = Nothing
    Set objShell = Nothing
End Function

Function ReplaceInvalidCharacters(Str As String) As String
    Dim objRegEx As Object
    Set objRegEx = CreateObject("vbscript.regexp")
    objRegEx.Global = True
    objRegEx.IgnoreCase = False
    objRegEx.Pattern = "\||\/|\<|\>|""|:|\*|\\|\?"
    ReplaceInvalidCharacters = objRegEx.Replace(Str, "_")
    Set objRegEx = Nothing
End Function

ここまでが終わったら、

  • Outlookの画面に戻り、コピーしたいフォルダを選択する
  • VBAの画面に戻り、一番上の「ExportOutlookFolders」という関数にカーソルを合わせる。(下図のように上部に[General]と[ExportOutlookFolders]が選択されたらOK。)
  • その後、下図の左上、赤丸内の緑の再生ボタンをクリックすると、Windows側の出力先のフォルダ選択画面が表示されるので、選択するとフォルダ構成毎、Windows側にコピーされる。
マクロ実行方法

プログラムとしては、「今Outlookで選択されているフォルダを、指定のフォルダに出力する」という処理を行っている。出力されるファイル名は「メールの受け取り時刻+件名」になる。

カテゴリー
プログラム 社内SE

WordPress twenty- twentyのテーブル変更

WordPress twenty-twentyでこのブログを書いているのだが、付属のテーブルを少しだけおしゃれに外枠をなしにすることにした。

テーブルをブロックで作成した際に、[追加CSSクラス]に[is-style-regular my-style-stripes]とすると以下のような外枠なしの表ができる。

人生項目 内容
プログラム 仕事のために仕方なくやっている
家事 家族のために仕方なくやっている
DIY 家族のために仕方なくやっている
囲碁 ここ数年打っていない。打つ相手もいない。。。
サンプル

やり方は[外観]-[テーマエディター]でstyle.cssを選択し以下のCSSを最後に追加する。(もしくは、[外観]-[カスタマイズ]で左側ペインの[追加CSS]に追加。)

.my-style-stripes table{
	border:none;
}
.my-style-stripes table tr{
	border:none;
}
.my-style-stripes table tbody tr:nth-child(odd) {
	background: #dcd7ca;
}
.my-style-stripes table thead th {
	border-top:none;
	border-right: none;
	border-left: 1px solid #6d6d6d;
	border-bottom: 2px solid #6d6d6d;
}
.my-style-stripes table tbody td{
	border-top:none;
	border-right: none;
	border-left: 1px solid  #6d6d6d;
	border-bottom: 1px solid  #6d6d6d;
}
.my-style-stripes table thead th:first-child, .my-style-stripes tbody td:first-child{
	border-left: none;
}
.my-style-stripes tbody tr:last-child td {
	border-bottom: none;
}
カテゴリー
プログラム 社内SE

Roundcubeに任意のユーザ名とパスワードでログインするプラグイン

レガシーな意見を振り切れず、わずか20人の小売りの会社にメールサーバを一から構築した。その際、メールの自動フォルダ振り分けができるようdovecotにsieveを追加し、その操作のため、初めてRoundcube環境も構築したのだが、これがどうして、通常のメーラーとしてもなかなか良い。

ただ、Roundcubeのログインがメールアドレスとパスワードでしかできないのが自分の環境的に使いにくい。出来れば、自分で設定したユーザ名とパスワードでログインしたいため、ファイルに設定したユーザ名とパスワードでログインできるようなプラグインを作成してみた。また、20人分の設定も面倒なので、CSVにした一覧から設定ファイルを生成するツールも作成した。

インストール方法

  1. RoundcubeのPluginsフォルダーにGithubからソースをダウンロードする
  2. Roundcubeのconfigにvirtuserpassを設定

Linuxなら以下のような感じです。

cd (Roundcubeをインストールしたフォルダ)/plugins
git clone https://github.com/y-toy/virtuserpass virtuserpass

vim ../config/config.inc.php
---
# "virtuserpass" を $config['plugins'] に追加
$config['plugins'] =  array('virtuserpass');
# もしくはファイルの一番下に以下を追加
# $config['plugins'][] =  'virtuserpass';

設定

“(Roundcubeをインストールしたフォルダ)/plugins/virtuserpass/config”の下に”config.inc.php”を作成し、以下のような設定をします。

// 主な設定
$config['virtuserpass'] = array(
	'taro'=>array('967Joe123','taro@biggy.co.jp','emailPass1234'),
	'ziro'=>array('pass1234','ziro@biggy.co.jp','emailPass5678'),
	'saburo'=>array('1234test','saburo@biggy.co.jp','emailPass7777'),
);

// オプション
$config['virtuserpass_scramble']=false;
$config['virtuserpass_email_scramble']=false;
$config['virtuserpass_allow_email_login']=true;

上の設定では、”taro”、”ziro”、”saburo”が設定してあり、”taro”さんはアカウント”taro”とパスワード”967Joe123″でログインできます。(対応するメールアドレスとパスワードは’taro@biggy.co.jp’と’emailPass1234’。)

$config[‘virtuserpass’]

ログインするアカウントの設定をします。ログインするユーザ名とパスワード、内部で変換されるメールアドレスとパスワードを設定します。

$config['virtuserpass'] = array(
	'ユーザ名' => array('パスワード', 'メールアドレス', 'メールのパスワード'),
	// いくつでも設定できます。
);

$config[‘virtuserpass_scramble’]

trueを設定するとconfigファイルのユーザのパスワードがMD5でハッシュされているものとして扱います。パスワードが外部に漏れない用のセキュリティ考慮です。後から説明するツール”makeConfigFileFromCSV.php”で設定できます。

$config[‘virtuserpass_email_scramble’]

trueを設定するとconfigファイルのメールパスワードが暗号化されているものとして扱います。ないよりましでしょの気休めセキュリティです。後から説明するツール”makeConfigFileFromCSV.php”で設定します。

$config[‘virtuserpass_allow_email_login’]

falseを設定するとメールアドレスを使ったログインが出来なくなります。

ツール

CSVファイルから一括でユーザをconifgに設定できるようにツールも作っています。

使い方

php ./makeConfigFileFromCSV.php csv-file-path [1|0] [1|0] [1|0]

引数 1 :CSVファイルのパス
引数 2 : 1を設定するとログイン時のパスワードがハッシュされて設定されます。
引数 3 : 1を設定するとメールアドレスのパスワードが暗号化されて設定されます。
引数 4 : 0を設定するとメールアドレスでのログインが出来なくなります。

Configファイル生成の仕方

  1. CSVファイルを作成します。CSVファイルは以下の4列で作成してください。ログインユーザ名,ログインパスワード, メールアドレス, メールアドレスのパスワード。このプラグインのconfigフォルダーの下にsample.csvファイルがありますので参考ください。
  2. 下のような感じでこのツールを実行します。このプラグイン用のconfigファイルが作成されます。
cd (Roundcubeをインストールしたフォルダ)/plugins/virtuserpass
php ./makeConfigFileFromCSV.php 作ったCSVファイルのパス 1 1 1

3. 作成したCSVファイルは念のため削除するか、公開されていないフォルダに移動します。

カテゴリー
プログラム 社内SE

sshのconfigのhostをリスト表示し、そのままssh

sshコマンドを打つときに.configに設定したホスト名を忘れて躓く方のための補助用のスクリプトです。
Windows Powershell版Linux bash版それぞれあります。

使い方

コマンド:sshfc [ホスト名の一部]

sshfcとだけ打つとconfigファイルに設定した全ホスト名が表示されます。
sshfc sysのように第一引数をつけて打つとホスト名にsysを含むSSH先が表示されます。続けて番号選択でそのままSSH接続です。
コマンド名は”ssh from config” の頭をとってsshfcとしています。以下使用例になります。

C:\> sshfc sys
1) system
2) test_system
3) dev_system
SSHするホストの番号を入力してください。[c]を押すとキャンセルされます。: 3
ssh dev_system
hogehoge@192.168.1.100's password:
....

Linux Bash版 インストール方法

以下の要領で、githubからスクリプトをダウンロードしてPATHが通っているフォルダに置いてください

git clone https://github.com/y-toy/linux_sshfc.git
chmod +x ./linux_sshfc/sshfc
# 以下環境に合わせて実行ください。
mv ./linux_sshfc/sshfc /usr/local/bin/

Windows Powershell版 インストール

以下の要領で、”ユーザフォルダ\bin”をPATHに追加して、githubからファイルをダウンロードします。

New-Item ($HOME + "\bin") -ItemType Directory
[System.Environment]::SetEnvironmentVariable("Path", $env:Path + ";%USERPROFILE%\bin", "User")

git clone https://github.com/y-toy/windows_sshfc.git
cd windows_sshfc
Move-Item ./sshfc.ps1 ($HOME + "\bin")

スクリプト自体が実行できない場合は以下コマンドを実行してみてください。スクリプトの実行制限を緩めます。

Set-ExecutionPolicy Unrestricted -scope CurrentUser

上記は確実ですが、上記をやらなくても、githubからダウンロードしたファイルを、空行を入れるなど意味のない編集をメモ帳などでして、BOM付UTF-8で保存すると実行できるようになるはずです。(普通のUTF-8はNGです。メモ帳の[名前を付けて保存]で UTF-8 BOM付きを選択。)

雑記

最近、WindowsのsshクライアントをMicrosoft謹製の「Windows terminal」に変更しました。 「Windows terminal」 自体は秀逸なのですが、以前使用していたsshクライアントと比べ若干使いずらいのが、SSHの接続先の選択方法。なのでツールを作ってカバーした感じです。

作ったツールが思いのほか便利だったので、bash版も作成しサーバに入った後のSSHも同様にできるようにしました。「.configに設定したホスト名なんだっけ?」と考えることが無くなりました。

Powershell のスクリプトを書くのは初めて。こういった作業効率化のちょっとしたコードを書く分には構文に慣れている分、過去のvbscriptで良いかなあという印象です。bashのスクリプトも簡単な数行ものを除けば、めったに書かないのでいつも構文忘れます。

Powershell版は日英つけていますがps1ファイルは配布が難しいので意味ないです。Bash版は面倒なので英語だけです。

カテゴリー
プログラム 社内SE

PHP ヤマト配送状況取得 2021年サイト変更対応

2021年9月 クロネコヤマトの荷物問い合わせシステムのページレイアウトが変更になって、送り状番号から荷物の配送状況を取得していたバッチがエラーとなった。
APIを使用すればよいという話だが、ヤマトビジネスメンバーズは他部署で契約&契約した人は退職で詳細不明になっている。まあ、面倒なので、PHPで問い合わせページから荷物状況を取得するプログラムを書いた。

実際はベタ書きしているが、かっこ悪いので、公開に当たりクラス化。1回テスト確認はしたが、使っていないので、エラーがああるかも。PHP5.4だが、7でも8でも動くと思う。取得した状況には「▶」が付いているので不要であれば消して使ってください。
内部でサイト解析のためDOMDocument使っているので使える環境のこと。以下、使い方とクラス。

使い方

$truckNumbers = array(
'444486855064',
'4444-9099-9090',
'444490999101',
); // いくつでも可

$objYamato = new clsGetYamatoDeliverStatus();
$aryDelivStatus = $objYamato->getYamatoDeliverStatus($truckNumbers);
if ($aryDelivStatus === false){ /* エラー ヤマト追跡番号 状況取得失敗 */ }

var_dump($aryDelivStatus);
/*
結果は[
(‐無しの追跡番号),
(‐有りの追跡番号),
状況日,
配達状況,
問い合わせ時間]の配列
array(3) {
  [0] =>
  array(5) {
    [0] =>
    string(12) "444486855064"
    [1] =>
    string(14) "4444-8685-5064"
    [2] =>
    string(5) "10/20"
    [3] =>
    string(16) "▶ 配達完了"
    [4] =>
    string(19) "2021/10/12 09:59:10"
  }
  [1] =>
  array(5) {
    [0] =>
    string(12) "444490999090"
    [1] =>
    string(14) "4444-9099-9090"
    [2] =>
    string(5) "10/20"
    [3] =>
    string(16) "▶ 配達完了"
    [4] =>
    string(19) "2021/10/12 09:59:10"
  }
  [2] =>
  array(5) {
    [0] =>
    string(12) "444490999101"
    [1] =>
    string(14) "4444-9099-9101"
    [2] =>
    string(5) "10/20"
    [3] =>
    string(16) "▶ 配達完了"
    [4] =>
    string(19) "2021/10/12 09:59:10"
  }
}
*/

荷物状況取得 クラス

<?php

class clsGetYamatoDeliverStatus{

	// 配列$truckNumbersに指定された追跡番号の状況を全て取得する。$truckNumbersは‐有でも無しでも可。
	// 戻り値は次の配列の配列 ["123456781234"(‐無しの追跡番号),"1234-5678-1234"(‐有りの追跡番号),mm/dd 状況日,配達状況,Y/m/d H:i:s 問い合わせ時間]
	// 戻り値がfalseの場合、エラー(ヤマト追跡サイトにアクセス出来ない)
	public function getYamatoDeliverStatus($truckNumbers){
		// 10件ずつヤマトのサイトからトラッキング情報を取得する。
		$numnerCnt = count($truckNumbers);
		$getTruckNumbers = array();
		$aryAllStatusInfo = array();
		for ($i=0;$i < $numnerCnt;$i++){
			$getTruckNumbers[] = $truckNumbers[$i];
			if (count($getTruckNumbers) == 10){
				$aryRetStatus = $this->getYamatoDeliverStatusCore($getTruckNumbers);
				$aryTemp = array_merge($aryAllStatusInfo, $aryRetStatus);
				if ($aryTemp === false){ return false; }
				$aryAllStatusInfo = $aryTemp;
				$getTruckNumbers = array();
			}
		}

		// 残っている場合
		if (count($getTruckNumbers) > 0){
			$aryRetStatus = $this->getYamatoDeliverStatusCore($getTruckNumbers);
			$aryTemp = array_merge($aryAllStatusInfo, $aryRetStatus);
			if ($aryTemp === false){ return false; }
			$aryAllStatusInfo = $aryTemp;
		}

		return $aryAllStatusInfo;
	}

	// ヤマトの追跡サイトにアクセスし、10件づつ結果を取得する。
	// 引数の$truckNumbersは配列で10件まで。
	// 戻り値は次の配列の配列 ["123456781234"(‐無しの追跡番号),"1234-5678-1234"(‐有りの追跡番号),mm/dd 状況日,配達状況,Y/m/d H:i:s 問い合わせ時間]
	// 戻り値がfalseの場合エラー(サイトにアクセス出来ない場合のみ)
	private function getYamatoDeliverStatusCore($truckNumbers){
		$aryRet = array();

		$url = 'https://toi.kuronekoyamato.co.jp/cgi-bin/tneko';
		$tnumCnt = count($truckNumbers);

		$aryPOST = array(
			'mypagesession' => '',
			'backaddress' => '',
			'backrequest' => '',
			'number00' => 2
		);
		$j=0;
		for ($i=1;$i<=10;$i++){
			$postName = 'number';
			if ($i < 10){
				$postName .= '0' . $i;
			}else{
				$postName .= $i;
			}
			if ($j < $tnumCnt){
				$aryPOST[$postName] = $truckNumbers[$j];
			}else{
				$aryPOST[$postName] = '';
			}
			$j++;
		}

		$http_build = http_build_query($aryPOST,'','&');
		$header = array(
			"Content-Type: application/x-www-form-urlencoded",
			"Content-Length: ".strlen($http_build)
		);
		// オプション
		$opts = array("http" => array(
			"method" => "POST",
			"header"  => implode("\r\n", $header),
			"content" => $http_build)
		);
		$scc = stream_context_create($opts);

		// 読み取り
		$get_html = file_get_contents($url, false, $scc);
		if ($get_html === false){ return false; }

		// staus取得時間
		$getTime = Date('Y/m/d H:i:s');

		$dom = new DOMDocument();
		$ret = $dom->loadHTML($get_html);

		$divResult = $this->getElementsByClassName($dom, 'tracking-box-area', 'div');
		$divLen = count($divResult);
		for($i=0;$i < $divLen;$i++){
			// このdivの下のinputのvalueを取得
			$domInputs = $this->getDomNodeFromTagName($divResult[$i], 'input');
			if (count($domInputs) == 0){ continue; }
			$trNum = $domInputs[0]->attributes->getNamedItem('value')->nodeValue;
			if ($trNum == ''){ continue; }

			// このdiv以下の日付を取得
			$date = '';
			$dateNodes = $this->getDomNodeFromClassName($divResult[$i], 'data date');
			if (count($dateNodes) == 0){ continue; }
			$date = $dateNodes[0]->nodeValue;

			// このdiv以下の状況を取得
			$state = '';
			$dateStates = $this->getDomNodeFromClassName($divResult[$i], 'data state');
			if (count($dateStates) == 0){ continue; }
			$state = $dateStates[0]->nodeValue;

			$aryRet[] = array(
				str_replace('-','',$trNum), // ‐なし
				$trNum, // ‐あり
				$date, // 状況になった日付
				$state, // 状況
				$getTime // 取得時刻
			);

		}

		return $aryRet;
	}

	private function getElementsByClassName($dom, $ClassName, $tagName=null) {
		if($tagName){
			$Elements = $dom->getElementsByTagName($tagName);
		}else {
			$Elements = $dom->getElementsByTagName("*");
		}
		$Matched = array();
		for($i=0;$i<$Elements->length;$i++) {
			if($Elements->item($i)->attributes->getNamedItem('class')){
				if(strpos($Elements->item($i)->attributes->getNamedItem('class')->nodeValue, $ClassName) !== false) {
					$Matched[]=$Elements->item($i);
				}
			}
		}
		return $Matched;
	}

	// 子ノード以下に指定のタグ名を持つdomNodeの一覧を返す。
	private function getDomNodeFromTagName($domNode, $tagName){
		$aryRet = array();
		foreach ( $domNode->childNodes as $chiledNode ){
			if ($chiledNode->nodeName == $tagName){
				$aryRet[] = $chiledNode;
			}
			if ($chiledNode->hasChildNodes()){
				$aryTemp = $this->getDomNodeFromTagName($chiledNode, $tagName);
				foreach($aryTemp as $temp){
					$aryRet[] = $temp;
				}
			}
		}
		return $aryRet;
	}

	// 子ノード以下に指定のクラス名を持つdomNodeの一覧を返す。
	private function getDomNodeFromClassName($domNode, $className){
		$aryRet = array();
		foreach ( $domNode->childNodes as $chiledNode ){
			if ($chiledNode->hasAttributes()){
				$nodAttr = $chiledNode->attributes->getNamedItem('class');
				if ($nodAttr != null){
					$nodeClasses = $nodAttr->nodeValue;
					if (strpos($nodeClasses, $className) !== false){
						$aryRet[] = $chiledNode;
					}
				}
			}
			if ($chiledNode->hasChildNodes()){
				$aryTemp = $this->getDomNodeFromClassName($chiledNode, $className);
				foreach($aryTemp as $temp){
					$aryRet[] = $temp;
				}
			}
		}
		return $aryRet;
	}
}
カテゴリー
プログラム 社内SE

Pear Mailで送信したメールを 送信済みフォルダに設置する。

PHPのPear::Mailでシステムからメールを送信し、送信したメールを送信済みフォルダに入れたい事案があった。
解決方法がググっても出てこなかったので、自己解決。

基本的には送信したデータをIMAP関数で送信済みのフォルダに設置するだけ。

以下ソース。

include_once("Mail.php");
include_once("Mail/mime.php");

mb_language("uni");
mb_internal_encoding("UTF-8");

// 宛先とヘッダーとボディを作る。省略。以下mimeの例
$mime = new Mail_mime();
$mime->setHTMLBody('something<br />anything');
$body = $mime->get(array(
  'text_encoding' => '7bit',
  'text_charset' => 'UTF-8',
  'html_charset' => 'UTF-8',
  'head_charset' => 'UTF-8'));
$headers = $mime->headers(array(/*省略*/));
$recipients = array(/*省略*/);

// メール送信
$mailObject = Mail::factory("smtp", Array(/*省略*/)); 
$send = $mailObject->send($recipients, $headers, $body);
if (PEAR::isError($send)){ return 'Mail send error! ' . $send->getMessage(); }

////////////////////////////////////////////////////////////////
// IMAPフォルダに設定。

// 送信済みフォルダに設置するデータ作成
$message = '';
foreach($headers as $key => $value){
  $message .= $key . ': ' . $value . "\r\n";
}
$message .= "\r\n" . $body;
// 設置フォルダー
$folderName = '送信済み'; // 自分のIMAPの送信済みフォルダ
$encodeFolder = mb_convert_encoding($folderName, 'UTF7-IMAP', 'UTF-8'); // エンコード
// imap_openの説明を読んで。SSLならポート以降を:993/imap/ssl
$path = "{imap.hogehoge.com:143/imap/notls}INBOX." . $encodeFolder;
$imapStream = imap_open($path,'username@hogehoge.com','password');
if ($imapStream === FALSE){ die(); }
$result = imap_append($imapStream, $path, $message, "\\Seen");
if ($result === FALSE){ die(); }
imap_close($imapStream);

/* end */

以上

カテゴリー
プログラム 社内SE

cordova-plugin-buildinfoのiOSでDebugがfalseになる。

Cordovaのplugin cordova-plugin-buildinfoを利用した際に、iOSでdebugビルドしても”BuildInfo.debug”がfalseになって困っていたところ、以下の方法で解決できたので、備忘録として残しておく。

1)プラグインのバージョンを4.0以上に上げる。
関係ないが、4.0以下だと「このプラグインのせいでiOSのアプリが異常に遅くなることがある」との投稿があったので、とりあえず4.0以上に上げた。

2)XCODEでプロジェクトを開きBuild Settingsから以下2点を変更する。

・Apple LLVM 9.0 – Preprocessing -> Preprocessor Macrosの下にdebugという項目があるので、そこに”DEBUG=1″を追加する。(”Macros”で設定項目を検索するとすぐ見つかる)
・Swift compiler – Custom Flags -> Other Swift Flags
の下にdebugという項目があるので、”-D DEBUG”を追加する。
(”Swift”で設定項目を検索すると見つかる。)

※XCodeプロジェクトの開き方は、Cordova環境直下の./platforms/iosの下に拡張子”.xcworkspace”のファイルがあるのでこれを開く。同じようなファイルで拡張子”.xcodeproj”があるが、こちらではないので注意。

これで、デバッグビルドの場合のみ[BuildInfo.debug]がfalseとなった。