1param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost, [switch]$Remote)
2Set-StrictMode -Version Latest
3$ErrorActionPreference = 'Stop'
4if (-not $Remote) { & "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost }
5$app = New-Object -ComObject OneNote.Application
6$notebook = ''
7$ns = 'http://schemas.microsoft.com/office/onenote/2010/onenote'
8try {
9 $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebook, 0)
10 $section = ''
11 $app.OpenHierarchy('synthetic.one', $notebook, [ref]$section, 0)
12 if (-not $Remote) {
13 $cases = @(
14 @{ name = 'Split remote prefix'; operation = 'split'; change = 'prefix' },
15 @{ name = 'Split remote boundary'; operation = 'split'; change = 'boundary' },
16 @{ name = 'Split remote child'; operation = 'split'; change = 'child' },
17 @{ name = 'Split remote list'; operation = 'split'; change = 'list' },
18 @{ name = 'Split remote tag'; operation = 'split'; change = 'tag' },
19 @{ name = 'Split remote format'; operation = 'split'; change = 'format' },
20 @{ name = 'Split remote sibling'; operation = 'split'; change = 'sibling' },
21 @{ name = 'Join remote prefix'; operation = 'join'; change = 'prefix' },
22 @{ name = 'Join left boundary'; operation = 'join'; change = 'boundary' },
23 @{ name = 'Join right boundary'; operation = 'join'; change = 'right-boundary' },
24 @{ name = 'Join remote child'; operation = 'join'; change = 'child' },
25 @{ name = 'Join remote list'; operation = 'join'; change = 'list' },
26 @{ name = 'Join remote tag'; operation = 'join'; change = 'tag' },
27 @{ name = 'Join remote format'; operation = 'join'; change = 'format' },
28 @{ name = 'Join remote sibling'; operation = 'join'; change = 'sibling' },
29 @{ name = 'Join empty adoption'; operation = 'join'; change = 'adoption' }
30 )
31 foreach ($case in $cases) {
32 $page = ''
33 $app.CreateNewPage($section, [ref]$page, 0)
34 $left = if ($case.change -eq 'adoption') { '' } else { 'ab' + [char]::ConvertFromUtf32(0x1F980) + 'cd' }
35 $body = '<one:OE><one:T><![CDATA[<b>' + $left + '</b>]]></one:T></one:OE>'
36 if ($case.operation -eq 'join') { $body += '<one:OE><one:T><![CDATA[<b>Right</b>]]></one:T></one:OE>' }
37 $body += '<one:OE><one:T>Preserved sibling</one:T></one:OE>'
38 $xml = '<one:Page xmlns:one="' + $ns + '" ID="' + $page + '"><one:Title><one:OE><one:T>' + $case.name + '</one:T></one:OE></one:Title><one:Outline><one:Position x="72" y="108"/><one:OEChildren>' + $body + '</one:OEChildren></one:Outline></one:Page>'
39 $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $true)
40 }
41 $cases | ConvertTo-Json -Depth 6 | Set-Content (Join-Path $Root 'cases.json') -Encoding UTF8
42 } else {
43 $cases = Get-Content (Join-Path $Root 'cases.json') -Raw | ConvertFrom-Json
44 $hierarchy = ''
45 $deadline = [DateTime]::UtcNow.AddSeconds(30)
46 do {
47 $app.GetHierarchy($section, 4, [ref]$hierarchy, 1)
48 [xml]$tree = $hierarchy
49 $manager = New-Object Xml.XmlNamespaceManager($tree.NameTable)
50 $manager.AddNamespace('one', $ns)
51 if (@($tree.SelectNodes('//one:Page', $manager)).Count -eq $cases.Count + 1) { break }
52 Start-Sleep -Milliseconds 200
53 } while ([DateTime]::UtcNow -lt $deadline)
54 $updates = Join-Path $Root 'remote-updates'
55 [void](New-Item -ItemType Directory -Path $updates -Force)
56 foreach ($case in $cases) {
57 $page = @($tree.SelectNodes('//one:Page', $manager) | Where-Object { $_.GetAttribute('name') -eq $case.name })
58 if ($page.Count -ne 1) { throw ('Expected one native control page: ' + $case.name) }
59 $content = ''
60 $app.GetPageContent($page[0].GetAttribute('ID'), [ref]$content, 0, 1)
61 [xml]$xml = $content
62 $manager = New-Object Xml.XmlNamespaceManager($xml.NameTable)
63 $manager.AddNamespace('one', $ns)
64 $outline = $xml.SelectSingleNode('/one:Page/one:Outline/one:OEChildren', $manager)
65 $paragraphs = @($outline.SelectNodes('one:OE', $manager))
66 $left = $paragraphs[0]
67 $target = if ($case.operation -eq 'join') { $paragraphs[1] } else { $left }
68 switch ($case.change) {
69 'prefix' {
70 $text = $left.SelectSingleNode('one:T', $manager)
71 $text.InnerText = 'X' + $text.InnerText
72 if ($case.operation -eq 'join') {
73 $text = $target.SelectSingleNode('one:T', $manager)
74 $text.InnerText += 'Y'
75 }
76 }
77 'boundary' {
78 $text = $left.SelectSingleNode('one:T', $manager)
79 if ($case.operation -eq 'split') {
80 if (-not $text.InnerText.Contains('ab')) { throw 'The split boundary control lost its text' }
81 $text.InnerText = $text.InnerText.Replace('ab', 'abX')
82 } else { $text.InnerText += 'X' }
83 }
84 'right-boundary' {
85 $text = $target.SelectSingleNode('one:T', $manager)
86 $text.InnerText = 'X' + $text.InnerText
87 }
88 'child' {
89 $children = $xml.CreateElement('one', 'OEChildren', $ns)
90 $child = $xml.CreateElement('one', 'OE', $ns)
91 $text = $xml.CreateElement('one', 'T', $ns)
92 $text.InnerText = 'Native child'
93 [void]$child.AppendChild($text)
94 [void]$children.AppendChild($child)
95 [void]$target.AppendChild($children)
96 }
97 'list' {
98 $list = $xml.CreateElement('one', 'List', $ns)
99 $bullet = $xml.CreateElement('one', 'Bullet', $ns)
100 $bullet.SetAttribute('bullet', '3')
101 $bullet.SetAttribute('fontSize', '11')
102 [void]$list.AppendChild($bullet)
103 [void]$target.PrependChild($list)
104 }
105 'tag' {
106 if (-not $xml.SelectSingleNode('/one:Page/one:TagDef[@index="0"]', $manager)) {
107 $definition = $xml.CreateDocumentFragment()
108 $definition.InnerXml = '<one:TagDef xmlns:one="' + $ns + '" index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Native task"/>'
109 [void]$xml.DocumentElement.PrependChild($definition)
110 }
111 $tag = $xml.CreateElement('one', 'Tag', $ns)
112 foreach ($pair in @{ index='0'; completed='false'; disabled='false'; creationDate='2020-01-02T03:04:05.000Z' }.GetEnumerator()) { $tag.SetAttribute($pair.Key, $pair.Value) }
113 [void]$target.PrependChild($tag)
114 }
115 'format' {
116 $text = $target.SelectSingleNode('one:T', $manager)
117 $plain = if ($case.operation -eq 'join') { 'Right' } else { 'ab' + [char]::ConvertFromUtf32(0x1F980) + 'cd' }
118 $text.InnerText = '<i>' + $plain + '</i>'
119 }
120 'sibling' { $paragraphs[-1].SelectSingleNode('one:T', $manager).InnerText = 'Native sibling' }
121 'adoption' { $left.SelectSingleNode('one:T', $manager).InnerText = 'X' }
122 default { throw ('Unknown native control: ' + $case.change) }
123 }
124 $xml.Save((Join-Path $updates ($case.name + '.xml')))
125 Write-Output ('Updating: ' + $case.name)
126 $app.UpdatePageContent($xml.OuterXml, [DateTime]::MinValue, 1, $true)
127 Write-Output ('Applied: ' + $case.name)
128 }
129 }
130 $app.SyncHierarchy($notebook)
131} finally {
132 if ($notebook) { $app.CloseNotebook($notebook, $false) }
133 [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
134 $app = $null
135 [GC]::Collect()
136 [GC]::WaitForPendingFinalizers()
137}