3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。(jehupc.exblog.jpから移転中)

VBScriptでApacheOpenOffice3.41の設定用XMLファイルを変更して保存

ApacheOpenOffice3.41の設定を全ユーザで変更しようと思っています。 ApacheOpenOfficeの設定はXMLファイルなので、グループポリシーのログオンスクリプトを使い、VBScriptで設定ファイルを修正して保存するという仕掛けです。

なお、毎回ログオン時に適用すると、ユーザが意図的に設定を変更しても再ログイン時に元に戻ってしまうので、フラグを設定したテキストファイルを置いておき、既に過去のログオン時にスクリプト実行済みなら、実行しないということもさせようと思っています。

VBScriptでは Msxml2.DOMDocument を使ってXMLファイルの変更ができるようです。
ただ完全にDOMで一般的なメソッドに対応しているわけでは無いようで苦労しました。 (例えば、element.getAttribute メソッドなどはありません。element.Attributes をループさせて目的の属性までたどり着く必要があります)

Xpath(名前空間接頭辞)で直接ノードを指定したかったんですが、どうもVBScriptではうまくノードの指定が出来ませんした。 なので、ひたすらループでぶん回すという力技にしています。

とりあえずVBScriptをアップしておきます。

Option Explicit

'設定ファイルのバージョン。設定ファイルを修正し、適用させたい場合はインクリメントすること。
Const ver = 1

Dim objWshShell , objFileSys
Set objWshShell = WScript.CreateObject("WScript.Shell")
Dim stmSettingFile , stmVerFile
'設定ファイルのパス
stmSettingFile = objWshShell.SpecialFolders.Item("AppData") & "\OpenOffice.org\3\user\registrymodifications.xcu"
'バージョンファイルのパス
stmVerFile = objWshShell.SpecialFolders.Item("AppData") & "\OpenOffice.org\3\user\setting_ver.txt"

'設定値
Dim strSetNameValues(3)
'(0)item oor:pathの値  (1)prop oor:nameの値  (2)valueへの設定値  (3)設定が終わったかどうかのフラグ

strSetNameValues(0) = Array("/org.openoffice.Office.Linguistic/SpellChecking" , "IsSpellAuto","false",false)
strSetNameValues(1) = Array("/org.openoffice.Office.Common/Save/Document" , "AutoSave","true",false)
strSetNameValues(2) = Array("/org.openoffice.Office.Common/Save/Document" , "LoadPrinter","false",false)
strSetNameValues(3) = Array("/org.openoffice.Office.Common/Save/ODF" , "DefaultVersion","3",false)
strSetNameValues(4) = Array("/org.openoffice.Office.Common/Security/Scripting" , "MacroSecurityLevel","1",false)
strSetNameValues(5) = Array("/org.openoffice.Office.Common/AutoCorrect" , "CapitalAtStartSentence","false",false)
strSetNameValues(6) = Array("/org.openoffice.Office.Common/AutoCorrect" , "DoubleQuoteAtEnd","0",false)
strSetNameValues(7) = Array("/org.openoffice.Office.Common/AutoCorrect" , "DoubleQuoteAtStart","0",false)
strSetNameValues(8) = Array("/org.openoffice.Office.Common/AutoCorrect" , "DoubleQuoteAtStart","0",false)
strSetNameValues(9) = Array("/org.openoffice.Office.Common/AutoCorrect" , "SetInetAttribute","false",false)
strSetNameValues(10) = Array("/org.openoffice.Office.Common/AutoCorrect" , "SingleQuoteAtEnd","false",false)
strSetNameValues(11) = Array("/org.openoffice.Office.Common/AutoCorrect" , "SingleQuoteAtStart","false",false)
strSetNameValues(12) = Array("/org.openoffice.Office.Common/AutoCorrect" , "TwoCapitalsAtStart","false",false)


Set objFileSys = CreateObject("Scripting.FileSystemObject")

'設定ファイルの存在確認
SettingFileExistCheck objFileSys , stmSettingFile
'バージョンのチェック。
VersionCheck objFileSys

WScript.echo "バージョン古いから更新する。スクリプト続行。"

'XMLファイル更新
UpdateXMLNode  stmSettingFile 

'バージョンファイル書込
WriteVersionFile objFileSys

'-----------------------------------------------------------
'XMLファイルノード更新。
'-----------------------------------------------------------
Sub UpdateXMLNode(stmSettingFile )
    Dim objXML,rtResult

    'XML操作オブジェクト
    Set objXML = CreateObject("Msxml2.DOMDocument.3.0")
    'XML読み込み
    rtResult = objXML.load( stmSettingFile  )
    If rtResult = False Then
      WScript.Echo "ノードなし"
    End If

    Dim nodeRoot ,  nodeItems , nodeItem , nodeProps ,nodeProp , nodeValue
    'rootノード取得
    Set nodeRoot = objXml.documentElement
    'itemノード取得
    Set nodeItems = nodeRoot.getElementsByTagName("item")

    'itemノードをループ
    For Each nodeItem In nodeItems
        Dim i , j
        'itemノードの属性をループ
        For i = 0 To nodeItem.Attributes.Length - 1
            'WScript.Echo "nodeItem:" & nodeItem.nodeName 
            '設定ファイル値の配列をループ
            For j = 0 To UBound(strSetNameValues) 
                'oor:pathがヒットした時
                If nodeItem.Attributes(i).Name = "oor:path" And  nodeItem.Attributes(i).Text = strSetNameValues(j)(0) Then
                    'propノードを取り出す
                    Set nodeProps = nodeItem.getElementsByTagName("prop")
                    'propノードをループ
                    For Each nodeProp In nodeProps
                        'oor:vがヒットした時
                        If nodeProp.Attributes(i).Name = "oor:name" And nodeProp.Attributes(i).Text = strSetNameValues(j)(1) Then
                            WScript.Echo nodeProp.Attributes(i).Name & "/" & nodeProp.Attributes(i).Text 
                            Set nodeValue = nodeProp.getElementsByTagName("value")
                            WScript.Echo "変更前 value = " & nodeValue(0).Text
                            '設定値更新
                            nodeValue(0).Text = strSetNameValues(j)(2)
                            '設定変えたフラグを設定
                            strSetNameValues(j)(3) = true
                            
                            WScript.Echo "変更後 value = " & nodeValue(0).Text
                        End If
                    Next
                End If
            Next
        Next
    Next


    '設定が終わっていない項目は、ノードが無いことになるので、ノード追加
    Dim strSetNameValue
    For Each strSetNameValue In strSetNameValues
        If strSetNameValue(3) = false Then
            CreateNode objXML, strSetNameValue(0) , strSetNameValue(1) , strSetNameValue(2) 
        End If
    Next

    ' XML を保存
    objXML.save( stmSettingFile )

End Sub

'-----------------------------------------------------------
'セッティングファイルの存在チェック。
'-----------------------------------------------------------
Sub SettingFileExistCheck(objFileSys , stmSettingFile)
    'セッティングファイルの存在確認
    If objFileSys.FileExists(stmSettingFile) = False Then
        WScript.echo "設定ファイル存在なし。AOO341未インストール。スクリプト終了"
        WScript.Quit
    End IF
End Sub


'-----------------------------------------------------------
'バージョンのチェック。
'-----------------------------------------------------------
Sub VersionCheck(objFileSys )
    Dim objTextStream,strVerText , blEndFlg
    blEndFlg = false
    'バージョンファイルの存在確認
    If objFileSys.FileExists(stmVerFile) = True Then
        'ファイルが存在すればバージョン確認
        'ファイル開く(読み取り専用)
        Set objTextStream = objFileSys.OpenTextFile(stmVerFile, 1 )
        'Do Until objTextStream.AtEndOfLine = True
        '最初の1行だけ読み込み
        If objTextStream.AtEndOfLine = False Then
            strVerText = objTextStream.ReadLine
        End If
        If CInt(strVerText) >= ver Then
            blEndFlg = true
        End If
        WScript.echo strVerText
        'ファイルを閉じる
        objTextStream.Close()
    Else
        'ファイルが無い場合は作成
        objFileSys.CreateTextFile stmVerFile
    End If
    Set objTextStream = Nothing
    'バージョンは同じなら設定しない
    If blEndFlg = true Then
        WScript.echo "バージョン新しいので更新しない。スクリプト終了。"
        WScript.Quit
    End If
End Sub

'-----------------------------------------------------------
'バージョンファイル書込
'-----------------------------------------------------------
Sub WriteVersionFile( objFileSys  )
    Dim objTextStream
    'バージョンファイル書き込み
    'ファイル開く(上書きモード)
    Set objTextStream = objFileSys.OpenTextFile(stmVerFile, 2 )
    objTextStream.WriteLine  Cstr(ver)
    objTextStream.Close()
    Set objTextStream = Nothing
End Sub

'-----------------------------------------------------------
'ノードが無い時に追加するサブプロシージャ
'-----------------------------------------------------------
Sub CreateNode(objXML , strItemAttName , strPropName , strValue )

    Dim objNewItem , objNewProp , objNewValue

    'itemノードの追加
    Set objNewItem = objXML.createElement("item")
    objNewItem.setAttribute "oor:path", strItemAttName
    objNewItem.appendChild(objXML.createTextNode(""))'vbCrLf & vbTab & vbTab))
    'propノードの追加
    Set objNewProp = objXML.createElement("prop")
    objNewProp.setAttribute "oor:name", strPropName
    objNewProp.setAttribute "oor:op", "fuse"
    objNewProp.appendChild(objXML.createTextNode(""))'vbCrLf & vbTab & vbTab & vbTab))
    'valueノードの追加
    Set objNewValue = objXML.createElement("value")
    objNewValue.Text = strValue
    'propノードにvalueノードを子として追加
    objNewProp.appendChild(objNewValue)
    objNewProp.appendChild(objXML.createTextNode(""))'vbCrLf & vbTab & vbTab))
    'itemノードにpropノードを子として追加
    objNewItem.appendChild(objNewProp)
    objNewItem.appendChild(objXML.createTextNode(""))'vbCrLf & vbTab))
    'ルートノードにitemノードを子として追加
    objXML.documentElement.appendChild(objNewItem)
    '最後に 改行
    objXML.documentElement.appendChild(objXML.createTextNode(""))'vbCrLf))
End Sub

参考:
VBScript の お勉強 - 松翠ソフトウェア - It’s About Helping Your Users Become Awesome
VBScriptからXMLデータを読む方法 - XML & SOA - @IT
@IT:Windows TIPS -- Tips:WSHからXMLファイルの設定情報を読み込む
XMLの値を取得 XPathでノードを指定する方法だけどうまくできなかった。
XML/XPath/XPathの書き方 - 俺の基地 使えなかったXpathの書き方。