├── .gitattributes
├── .gitignore
├── AX 2012 metadata scripts
├── _run.cmd
└── run_ax2012.ps1
├── ConvertHtmlToJson
├── .gitignore
├── CommandLine.dll
├── ConvertHtmlToJson.exe
├── HtmlAgilityPack.dll
└── Newtonsoft.Json.dll
├── Help Pane extension
├── AzureSearchCustomHelp
│ ├── AzureSearchCustomHelp.csproj
│ ├── AzureSearchCustomHelp.sln
│ ├── Client.cs
│ ├── Document.cs
│ ├── Exceptions
│ │ └── InvalidConfigurationException.cs
│ ├── HelppaneOption.axpp
│ ├── HelppaneOption
│ │ ├── HelppaneOption.rnrproj
│ │ └── HelppaneOption_on_box.rnrproj
│ ├── Language.config
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── UsersConfigMapSection.cs
│ └── packages.config
├── HelppaneOption.axpp
└── REST API CREATION.txt
├── HtmlLocaleChanger
├── .gitignore
├── CommandLine.dll
├── HtmlAgilityPack.dll
└── HtmlLocaleChanger.exe
├── LICENSE
├── README.md
├── SECURITY.md
└── SourceCode
├── ConvertHtmlToJson
├── ConvertHtmlToJson.sln
└── ConvertHtmlToJson
│ ├── App.config
│ ├── CommandLineOptions.cs
│ ├── ConvertHtmlToJson.csproj
│ ├── HTMLDirectory.cs
│ ├── HTMLtoJSONProcessor.cs
│ ├── JSONDirectory.cs
│ ├── Program.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ └── packages.config
└── HtmlLocaleChanger
├── HtmlLocaleChanger.sln
└── HtmlLocaleChanger
├── App.config
├── HTMLDirectory.cs
├── HtmlLocaleChanger.csproj
├── LocaleChangeProcessor.cs
├── Program.cs
├── Properties
└── AssemblyInfo.cs
└── packages.config
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # .NET Core
46 | project.lock.json
47 | project.fragment.lock.json
48 | artifacts/
49 | **/Properties/launchSettings.json
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # Visual Studio code coverage results
117 | *.coverage
118 | *.coveragexml
119 |
120 | # NCrunch
121 | _NCrunch_*
122 | .*crunch*.local.xml
123 | nCrunchTemp_*
124 |
125 | # MightyMoose
126 | *.mm.*
127 | AutoTest.Net/
128 |
129 | # Web workbench (sass)
130 | .sass-cache/
131 |
132 | # Installshield output folder
133 | [Ee]xpress/
134 |
135 | # DocProject is a documentation generator add-in
136 | DocProject/buildhelp/
137 | DocProject/Help/*.HxT
138 | DocProject/Help/*.HxC
139 | DocProject/Help/*.hhc
140 | DocProject/Help/*.hhk
141 | DocProject/Help/*.hhp
142 | DocProject/Help/Html2
143 | DocProject/Help/html
144 |
145 | # Click-Once directory
146 | publish/
147 |
148 | # Publish Web Output
149 | *.[Pp]ublish.xml
150 | *.azurePubxml
151 | # TODO: Comment the next line if you want to checkin your web deploy settings
152 | # but database connection strings (with potential passwords) will be unencrypted
153 | *.pubxml
154 | *.publishproj
155 |
156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
157 | # checkin your Azure Web App publish settings, but sensitive information contained
158 | # in these scripts will be unencrypted
159 | PublishScripts/
160 |
161 | # NuGet Packages
162 | *.nupkg
163 | # The packages folder can be ignored because of Package Restore
164 | **/packages/*
165 | # except build/, which is used as an MSBuild target.
166 | !**/packages/build/
167 | # Uncomment if necessary however generally it will be regenerated when needed
168 | #!**/packages/repositories.config
169 | # NuGet v3's project.json files produces more ignorable files
170 | *.nuget.props
171 | *.nuget.targets
172 |
173 | # Microsoft Azure Build Output
174 | csx/
175 | *.build.csdef
176 |
177 | # Microsoft Azure Emulator
178 | ecf/
179 | rcf/
180 |
181 | # Windows Store app package directories and files
182 | AppPackages/
183 | BundleArtifacts/
184 | Package.StoreAssociation.xml
185 | _pkginfo.txt
186 |
187 | # Visual Studio cache files
188 | # files ending in .cache can be ignored
189 | *.[Cc]ache
190 | # but keep track of directories ending in .cache
191 | !*.[Cc]ache/
192 |
193 | # Others
194 | ClientBin/
195 | ~$*
196 | *~
197 | *.dbmdl
198 | *.dbproj.schemaview
199 | *.jfm
200 | *.pfx
201 | *.publishsettings
202 | orleans.codegen.cs
203 |
204 | # Since there are multiple workflows, uncomment next line to ignore bower_components
205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
206 | #bower_components/
207 |
208 | # RIA/Silverlight projects
209 | Generated_Code/
210 |
211 | # Backup & report files from converting an old project file
212 | # to a newer Visual Studio version. Backup files are not needed,
213 | # because we have git ;-)
214 | _UpgradeReport_Files/
215 | Backup*/
216 | UpgradeLog*.XML
217 | UpgradeLog*.htm
218 |
219 | # SQL Server files
220 | *.mdf
221 | *.ldf
222 | *.ndf
223 |
224 | # Business Intelligence projects
225 | *.rdl.data
226 | *.bim.layout
227 | *.bim_*.settings
228 |
229 | # Microsoft Fakes
230 | FakesAssemblies/
231 |
232 | # GhostDoc plugin setting file
233 | *.GhostDoc.xml
234 |
235 | # Node.js Tools for Visual Studio
236 | .ntvs_analysis.dat
237 | node_modules/
238 |
239 | # Typescript v1 declaration files
240 | typings/
241 |
242 | # Visual Studio 6 build log
243 | *.plg
244 |
245 | # Visual Studio 6 workspace options file
246 | *.opt
247 |
248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
249 | *.vbw
250 |
251 | # Visual Studio LightSwitch build output
252 | **/*.HTMLClient/GeneratedArtifacts
253 | **/*.DesktopClient/GeneratedArtifacts
254 | **/*.DesktopClient/ModelManifest.xml
255 | **/*.Server/GeneratedArtifacts
256 | **/*.Server/ModelManifest.xml
257 | _Pvt_Extensions
258 |
259 | # Paket dependency manager
260 | .paket/paket.exe
261 | paket-files/
262 |
263 | # FAKE - F# Make
264 | .fake/
265 |
266 | # JetBrains Rider
267 | .idea/
268 | *.sln.iml
269 |
270 | # CodeRush
271 | .cr/
272 |
273 | # Python Tools for Visual Studio (PTVS)
274 | __pycache__/
275 | *.pyc
276 |
277 | # Cake - Uncomment if you are using it
278 | # tools/**
279 | # !tools/packages.config
280 |
281 | # Telerik's JustMock configuration file
282 | *.jmconfig
283 |
284 | # BizTalk build output
285 | *.btp.cs
286 | *.btm.cs
287 | *.odx.cs
288 | *.xsd.cs
289 |
--------------------------------------------------------------------------------
/AX 2012 metadata scripts/_run.cmd:
--------------------------------------------------------------------------------
1 | PowerShell.exe -File run_ax2012.ps1 "Shared" "NewShared"
2 |
3 | pause
--------------------------------------------------------------------------------
/AX 2012 metadata scripts/run_ax2012.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [string]$SourcePath = $( Read-Host "Please specify source path with AX 2012 HTML files" ),
3 | [string]$OutPath = $( Read-Host "Please specify destination path for processed AX 2012 HTML files" )
4 | )
5 |
6 | if(-not(Test-Path $SourcePath))
7 | {
8 | Write-Error -Message ("Error: The source path doesn't exist: " + $SourcePath) -Category ResourceUnavailable -CategoryReason "Specified path could not be found"
9 | return
10 | }
11 |
12 | if(-not(Test-Path $OutPath))
13 | {
14 | Write-Host "Creating directory" $OutPath
15 | New-Item -ItemType Directory -Force -Path $OutPath
16 | }
17 |
18 | function Process-Meta([string]$SourcePath, [string]$OutPath)
19 | {
20 | $Metas = @('',
21 | '',
22 | '',
23 | '',
24 | '')
25 |
26 | $PatternMetaWithSpaces = '\s*?'
27 | $PatternMeta = ''
28 | $PatternName = 'name=["|''](.*?)["|'']'
29 | $PatternContent = 'content=["|''](.*?)["|'']'
30 | $PatternGuid = '(^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})?;?(.*)'
31 | $PatternEnd1 = '(\.+)(?=[^.]*$)(.*[^:;\s])(?:[:;\s$])'
32 | $PatternEnd2 = '(\.+)(?=[^.]*$)(.*[^:;\s])(?:[:;\s$])?'
33 | $PatternHeadEnd = '\s*?<\/head>'
34 |
35 | $FilesProcessed = 0
36 | $FilesFailed = 0
37 |
38 | $Files = Get-ChildItem $SourcePath -Filter *.htm
39 | $Counter = 0
40 | Write-Host "Processing" $Files.Length "files. Please wait..."
41 | foreach ($File in $Files)
42 | {
43 | Write-Progress -Activity "Metadata processing" -Status "Current file" -CurrentOperation $File.FullName -PercentComplete (100 * $Counter/$Files.Length)
44 | $Content = [System.IO.File]::ReadAllText($File.FullName)
45 | if ($Content -match $PatternMetaWithSpaces)
46 | {
47 | $MetaString = $Matches[0]
48 |
49 | if ($Content -match $PatternHeadEnd)
50 | {
51 | $MetaString = $MetaString -replace "`n|`r"
52 | $SpacesCount = $MetaString.IndexOf("<")
53 | $SpacesForMetas = ""
54 | for ($i = 1; $i -le $SpacesCount; $i++)
55 | {
56 | $SpacesForMetas = $SpacesForMetas + " "
57 | }
58 |
59 | #Write-Host "META:" $MetaString
60 | if ($MetaString -match $PatternContent)
61 | {
62 | $AttrContent = $Matches[1]
63 | #Write-Host "ATTR.CONTENT:" $AttrContent
64 | $newAttrContent = ""
65 | if ($AttrContent -match $PatternGuid)
66 | {
67 | $newAttrContent = $Matches[1]
68 | #Write-Host "GUID:" $newAttrContent
69 | }
70 |
71 | $AfterGuid = $Matches[2]
72 |
73 | if ($AfterGuid -match $PatternEnd1)
74 | {
75 | $AfterGuid = $Matches[2]
76 | }
77 | elseif ($AfterGuid -match $PatternEnd2)
78 | {
79 | $AfterGuid = $Matches[2]
80 | }
81 |
82 | #Write-Host "AFTER GUID:" $AfterGuid
83 |
84 | if ($AfterGuid -is [string] -AND $AfterGuid.Trim() -ne "")
85 | {
86 | $newAttrContent = $newAttrContent + "; " + $AfterGuid;
87 | }
88 | #Write-Host "NEW ATTR. CONTENT:" $newAttrContent
89 |
90 | $MetaString = ($MetaString.Trim() -replace $PatternContent, ('content="' + $newAttrContent + '"'))
91 | $MetaString = ($MetaString -replace $PatternName, 'name="ms.search.form"')
92 | $Content = $Content -replace $PatternMeta, $MetaString
93 |
94 | if ($Content -match $PatternHeadEnd)
95 | {
96 | $HeadEndString = $Matches[0]
97 |
98 | $A = $Metas -join ("`n" + $SpacesForMetas)
99 | $Content = $Content -replace $PatternHeadEnd, ("`n" + $SpacesForMetas + $A + $HeadEndString)
100 | }
101 |
102 | $Content = $Content -replace ' doesn't exist" -Category InvalidData
117 | $FilesFailed++
118 | }
119 | }
120 | else
121 | {
122 | Write-Error -Message "Error: META doesn't exist" -Category InvalidData
123 | $FilesFailed++
124 | }
125 | $Counter++
126 | }
127 |
128 | Write-Host "Files processed:" $FilesProcessed
129 | if ($FilesFailed -gt 0)
130 | {
131 | Write-Host "Files failed:" $FilesFailed
132 | }
133 | }
134 |
135 | function Process-Htm([string]$OutPath)
136 | {
137 | Write-Host "Processing HTM links and file extensions"
138 | $Files = Get-ChildItem $OutPath | Where-Object {$_.Name.EndsWith(".htm")}
139 | $Counter = 0
140 | foreach ($File in $Files)
141 | {
142 | Write-Progress -Activity "Replace htm with html" -Status "Current file" -PercentComplete (100 * $Counter/$Files.Length) -CurrentOperation $File.FullName
143 | $updated = $false
144 | $Content = [System.IO.File]::ReadAllText($File.FullName)
145 | $Captured = $Content | Select-String -Pattern 'href\s*=\s*"(.+\.htm)"' -AllMatches
146 | for ($i = 0; $i -lt $Captured.Matches.Count; $i++)
147 | {
148 | $Uri = $null
149 | $Href = $Captured.Matches[$i].Groups[1].Value
150 | if (-not ([System.Uri]::TryCreate($Href, [System.UriKind]::Absolute, [ref] $Uri)))
151 | {
152 | $UpdatedHref = $Href + "l"# htm -> html
153 | $Content = $Content -replace $Href,$UpdatedHref
154 | $updated = $true
155 | }
156 | }
157 | if ($Updated)
158 | {
159 | $Content | Out-File -FilePath $File.FullName
160 | Write-Host ("Replaced HTM links in file: '{0}'" -f $File.FullName)
161 | }
162 |
163 | $Html = $File.FullName + "l"
164 | Move-Item -Path $File.FullName -Destination $Html -Force
165 | $Counter++
166 | }
167 | Write-Host "Processing complete"
168 | }
169 |
170 | Process-Meta -SourcePath $SourcePath -OutPath $OutPath
171 | Process-Htm -OutPath $OutPath
--------------------------------------------------------------------------------
/ConvertHtmlToJson/.gitignore:
--------------------------------------------------------------------------------
1 | ## Allow executable files in this folder
2 | !*.dll
3 | !*.exe
--------------------------------------------------------------------------------
/ConvertHtmlToJson/CommandLine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/dynamics365f-o-custom-help/308c1f7486211578590a93a1170f80bb4ec44d97/ConvertHtmlToJson/CommandLine.dll
--------------------------------------------------------------------------------
/ConvertHtmlToJson/ConvertHtmlToJson.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/dynamics365f-o-custom-help/308c1f7486211578590a93a1170f80bb4ec44d97/ConvertHtmlToJson/ConvertHtmlToJson.exe
--------------------------------------------------------------------------------
/ConvertHtmlToJson/HtmlAgilityPack.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/dynamics365f-o-custom-help/308c1f7486211578590a93a1170f80bb4ec44d97/ConvertHtmlToJson/HtmlAgilityPack.dll
--------------------------------------------------------------------------------
/ConvertHtmlToJson/Newtonsoft.Json.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/dynamics365f-o-custom-help/308c1f7486211578590a93a1170f80bb4ec44d97/ConvertHtmlToJson/Newtonsoft.Json.dll
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/AzureSearchCustomHelp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {61899F42-6EDF-47A9-BCD0-944F081A8276}
8 | Library
9 | Properties
10 | AzureSearchCustomHelp
11 | AzureSearchCustomHelp
12 | v4.5.2
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 | true
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | packages\Microsoft.Azure.Search.9.0.1\lib\net452\Microsoft.Azure.Search.dll
36 | True
37 |
38 |
39 | packages\Microsoft.Azure.Search.Common.9.0.1\lib\net452\Microsoft.Azure.Search.Common.dll
40 | True
41 |
42 |
43 | packages\Microsoft.Azure.Search.Data.9.0.1\lib\net452\Microsoft.Azure.Search.Data.dll
44 | True
45 |
46 |
47 | packages\Microsoft.Azure.Search.Service.9.0.1\lib\net452\Microsoft.Azure.Search.Service.dll
48 | True
49 |
50 |
51 | packages\Microsoft.Rest.ClientRuntime.2.3.21\lib\net452\Microsoft.Rest.ClientRuntime.dll
52 | True
53 |
54 |
55 | packages\Microsoft.Rest.ClientRuntime.Azure.3.3.19\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll
56 | True
57 |
58 |
59 | packages\Microsoft.Spatial.7.5.3\lib\portable-net45+win8+wpa81\Microsoft.Spatial.dll
60 | True
61 |
62 |
63 | packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
64 | True
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | Always
91 | Designer
92 |
93 |
94 |
95 |
96 |
103 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/AzureSearchCustomHelp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureSearchCustomHelp", "AzureSearchCustomHelp.csproj", "{61899F42-6EDF-47A9-BCD0-944F081A8276}"
7 | EndProject
8 | Project("{FC65038C-1B2F-41E1-A629-BED71D161FFF}") = "HelppaneOption (USR) [USRLayer]", "HelppaneOption\HelppaneOption.rnrproj", "{6CE73C61-AC88-49F8-AF1B-E7F38F2DFB93}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {61899F42-6EDF-47A9-BCD0-944F081A8276}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {61899F42-6EDF-47A9-BCD0-944F081A8276}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {61899F42-6EDF-47A9-BCD0-944F081A8276}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {61899F42-6EDF-47A9-BCD0-944F081A8276}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {6CE73C61-AC88-49F8-AF1B-E7F38F2DFB93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {6CE73C61-AC88-49F8-AF1B-E7F38F2DFB93}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {6CE73C61-AC88-49F8-AF1B-E7F38F2DFB93}.Release|Any CPU.ActiveCfg = Debug|Any CPU
23 | {6CE73C61-AC88-49F8-AF1B-E7F38F2DFB93}.Release|Any CPU.Build.0 = Debug|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/Client.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.Azure.Search;
5 | using Microsoft.Azure.Search.Models;
6 | using System.IO;
7 | using System.Reflection;
8 | using System.Net;
9 | using Newtonsoft.Json;
10 |
11 | namespace AzureSearchCustomHelp
12 | {
13 | public class Client
14 | {
15 | private string searchServiceName = string.Empty;
16 | private string queryApiKey = string.Empty;
17 | private SearchIndexClient primaryIndexClient = null;
18 | private SearchIndexClient parentIndexClient = null;
19 | private SearchIndexClient ultimateIndexClient = null;
20 | private string parentLanguage = string.Empty;
21 | private string ultimateLanguage = string.Empty;
22 |
23 | public Boolean SetConfig(string _serviceName, string _queryKey, string _primaryLanguage)
24 | {
25 | this.searchServiceName = _serviceName;
26 | this.queryApiKey = _queryKey;
27 | List indexList = GetIndexListFromAzure();
28 | if (indexList.Count > 0)
29 | {
30 | UsersConfigMapSection config = UsersConfigMapSection.Config;
31 | if (config != null)
32 | {
33 | List languages = config.SettingsList.ToList();
34 | if (languages != null)
35 | {
36 | try
37 | {
38 | SetUltimateIndex(indexList, languages);
39 | SetParentIndex(indexList, languages, _primaryLanguage);
40 | SetPrimaryIndex(indexList, languages, _primaryLanguage);
41 | }
42 | catch (InvalidConfigurationException)
43 | {
44 | return false;
45 | }
46 | }
47 | }
48 | }
49 | return primaryIndexClient != null || parentIndexClient != null || ultimateIndexClient != null;
50 | }
51 |
52 | private void SetUltimateIndex(List indexList, List languages)
53 | {
54 | UsersConfigMapConfigElement ultimateLanguageConfig = languages.Where(l => l.UltimateIndex != string.Empty).FirstOrDefault();
55 | if (ultimateLanguageConfig != null && (null != indexList.Where(i => i.name == ultimateLanguageConfig.UltimateIndex).FirstOrDefault()))
56 | {
57 | this.ultimateIndexClient = CreateSearchIndexClient(ultimateLanguageConfig.UltimateIndex);
58 | this.ultimateLanguage = ultimateLanguageConfig.PrimaryLanguage;
59 | }
60 | }
61 |
62 | private void SetPrimaryIndex(List indexList, List languages, string primaryLanguage)
63 | {
64 | UsersConfigMapConfigElement primaryLanguageConfig = languages.Where(l => l.PrimaryLanguage.ToLower() == primaryLanguage.ToLower()).FirstOrDefault();
65 | if (primaryLanguageConfig != null)
66 | {
67 | if (!string.IsNullOrEmpty(primaryLanguageConfig.Index) && (null != indexList.Where(i => i.name == primaryLanguageConfig.Index).FirstOrDefault()))
68 | {
69 | //Set primaryIndex to the value of @index
70 | this.primaryIndexClient = CreateSearchIndexClient(primaryLanguageConfig.Index);
71 | }
72 | }
73 | }
74 |
75 | private void SetParentIndex(List indexList, List languages, string primaryLanguage)
76 | {
77 | UsersConfigMapConfigElement primaryLanguageConfig = languages.Where(l => l.PrimaryLanguage.ToLower() == primaryLanguage.ToLower()).FirstOrDefault();
78 | if (primaryLanguageConfig != null)
79 | {
80 | if (!string.IsNullOrEmpty(primaryLanguageConfig.ParentIndex) && !string.IsNullOrEmpty(primaryLanguageConfig.ParentLanguage))
81 | {
82 | throw new InvalidConfigurationException("Both ParentIndex and ParentLanguage cannot be set for " + primaryLanguage);
83 | }
84 | if (!string.IsNullOrEmpty(primaryLanguageConfig.ParentIndex) && (null != indexList.Where(i => i.name == primaryLanguageConfig.ParentIndex).FirstOrDefault()))
85 | {
86 | //Set parentIndex to the value of @parentindex.
87 | this.parentIndexClient = CreateSearchIndexClient(primaryLanguageConfig.ParentIndex);
88 | this.parentLanguage = primaryLanguageConfig.PrimaryLanguage;
89 | }
90 | if (!string.IsNullOrEmpty(primaryLanguageConfig.ParentLanguage))
91 | {
92 | //Set the parentIndex to the value of @parentindex from the first ancestor that has @parentindex set.
93 | string ancestorLanguageWithParentIndex = GetAncestorWithParentIndexLanguage(languages, primaryLanguageConfig.ParentLanguage);
94 | if (!string.IsNullOrEmpty(ancestorLanguageWithParentIndex))
95 | {
96 | UsersConfigMapConfigElement ancestorLanguageConfig = languages.Where(l => l.PrimaryLanguage.ToLower() == ancestorLanguageWithParentIndex.ToLower()).FirstOrDefault();
97 | if (ancestorLanguageConfig != null && !string.IsNullOrEmpty(ancestorLanguageConfig.ParentIndex))
98 | {
99 | this.parentIndexClient = CreateSearchIndexClient(ancestorLanguageConfig.ParentIndex);
100 | this.parentLanguage = ancestorLanguageConfig.PrimaryLanguage;
101 | }
102 | }
103 | }
104 | }
105 | }
106 |
107 | private static string GetAncestorWithParentIndexLanguage(List languages, string parentLanguage)
108 | {
109 | UsersConfigMapConfigElement currentParentLanguageConfig = languages.Where(l => l.PrimaryLanguage.ToLower() == parentLanguage.ToLower()).FirstOrDefault();
110 | if (currentParentLanguageConfig != null)
111 | {
112 | if (!string.IsNullOrEmpty(currentParentLanguageConfig.ParentLanguage) && !string.IsNullOrEmpty(currentParentLanguageConfig.ParentIndex))
113 | {
114 | //Invalid config. @parentlanguage and @parentindex cannot both be set for @language.
115 | throw new InvalidConfigurationException("Both ParentIndex and ParentLanguage cannot be set for " + parentLanguage);
116 | }
117 | if (!string.IsNullOrEmpty(currentParentLanguageConfig.ParentLanguage))
118 | {
119 | //Look at the next ancestor.
120 | return GetAncestorWithParentIndexLanguage(languages, currentParentLanguageConfig.ParentLanguage);
121 | }
122 | if (!string.IsNullOrEmpty(currentParentLanguageConfig.ParentIndex))
123 | {
124 | //We've found an ancestor that has a ParentIndex set so return the language.
125 | return currentParentLanguageConfig.PrimaryLanguage;
126 | }
127 | //Invalid config. @parentlanguage has been set, but an ancestor with a @parentindex cannot be found.
128 | throw new InvalidConfigurationException("parentlanguage has been set, but an ancestor with a parentindex cannot be found");
129 | }
130 | throw new InvalidConfigurationException("Invalid configuration for " + parentLanguage);
131 | }
132 |
133 | private List GetIndexListFromAzure()
134 | {
135 | List azureIndexList = null;
136 | string indexListResults = null;
137 | string Url = string.Format(@"https://{0}.search.windows.net/indexes?api-version=2017-11-11&$select=name", this.searchServiceName);
138 |
139 | WebRequest request = WebRequest.Create(Url);
140 | request.Headers.Add("api-key", this.queryApiKey);
141 | request.ContentType = "application/json";
142 | WebResponse response = request.GetResponse();
143 |
144 | using (StreamReader sr = new StreamReader(response.GetResponseStream()))
145 | {
146 | indexListResults = sr.ReadToEnd();
147 | }
148 |
149 | //Convert Json Response Result to List.
150 | if (!string.IsNullOrEmpty(indexListResults))
151 | {
152 | azureIndexList = JsonConvert.DeserializeObject(indexListResults).value;
153 | }
154 | return azureIndexList;
155 | }
156 |
157 | #region Methods available in XPP
158 | public static string GetParentLanguage(string primaryLanguage)
159 | {
160 | UsersConfigMapSection config = UsersConfigMapSection.Config;
161 | if (config != null)
162 | {
163 | List languages = config.SettingsList.ToList();
164 | UsersConfigMapConfigElement primaryLanguageConfig = languages.Where(l => l.PrimaryLanguage.ToLower() == primaryLanguage.ToLower()).FirstOrDefault();
165 | if (primaryLanguageConfig != null)
166 | {
167 | if (!string.IsNullOrEmpty(primaryLanguageConfig.ParentLanguage) && !string.IsNullOrEmpty(primaryLanguageConfig.ParentIndex))
168 | {
169 | //Invalid config. @parentlanguage and @parentindex cannot both be set for @language.
170 | return string.Empty;
171 | }
172 | if (!string.IsNullOrEmpty(primaryLanguageConfig.ParentLanguage))
173 | {
174 | try
175 | {
176 | return GetAncestorWithParentIndexLanguage(languages, primaryLanguageConfig.ParentLanguage);
177 | }
178 | catch (InvalidConfigurationException)
179 | {
180 | return string.Empty;
181 | }
182 | }
183 | }
184 | }
185 | return string.Empty;
186 | }
187 |
188 | public static string GetUltimateLanguage()
189 | {
190 | UsersConfigMapSection config = UsersConfigMapSection.Config;
191 | if (config != null)
192 | {
193 | List languages = config.SettingsList.ToList();
194 | UsersConfigMapConfigElement ultimateLanguageConfig = languages.Where(l => l.UltimateIndex != string.Empty).FirstOrDefault();
195 | if (ultimateLanguageConfig != null)
196 | {
197 | return ultimateLanguageConfig.PrimaryLanguage;
198 | }
199 | }
200 | return string.Empty;
201 | }
202 |
203 |
204 | public static bool CheckFileExists(string file)
205 | {
206 | string FilePath = Path.Combine(Path.GetDirectoryName((new System.Uri(Assembly.GetExecutingAssembly().CodeBase)).LocalPath), file);
207 | return (File.Exists(FilePath));
208 | }
209 |
210 | #endregion
211 |
212 | /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
213 |
214 |
215 | private SearchIndexClient CreateSearchIndexClient(string searchServiceIndex)
216 | {
217 | return (!string.IsNullOrEmpty(searchServiceIndex)) ? new SearchIndexClient(this.searchServiceName, searchServiceIndex, new SearchCredentials(this.queryApiKey)) : null;
218 | }
219 |
220 | private DocumentSearchResult ProcessSearch(SearchIndexClient searchIndexClient, string _searchString, string filterLanguage, string filterAnotherLanguage = "")
221 | {
222 | SearchParameters parameters;
223 | DocumentSearchResult results = null;
224 | string _filter = string.Empty;
225 | if (string.IsNullOrEmpty(filterAnotherLanguage))
226 | {
227 | _filter = string.Format("ms_locale eq '{0}' or ms_locale eq '{1}' or ms_locale eq '{2}'", filterLanguage.ToLower(), filterLanguage.ToUpper(), filterLanguage);
228 | }
229 | else
230 | {
231 | _filter = string.Format("ms_locale eq '{0}' or ms_locale eq '{1}' or ms_locale eq '{2}' or ms_locale eq '{3}' or ms_locale eq '{4}' or ms_locale eq '{5}'", filterLanguage.ToLower(), filterLanguage.ToUpper(), filterAnotherLanguage.ToLower(), filterAnotherLanguage.ToUpper(), filterLanguage, filterAnotherLanguage);
232 | }
233 | parameters =
234 | new SearchParameters()
235 | {
236 | Filter = _filter,
237 | Select = new[] { "id", "ms_locale", "ms_search_region", "description", "title", "ms_search_form", "metadata_storage_path", "metadata_storage_content_type", "metadata_storage_name"},
238 | IncludeTotalResultCount = true
239 | };
240 | try
241 | {
242 | if (searchIndexClient != null)
243 | {
244 | results = searchIndexClient.Documents.Search(_searchString, parameters);
245 | }
246 | }
247 | catch (Exception ex)
248 | {
249 | throw new Exception(ex.Message);
250 | }
251 | return results;
252 | }
253 |
254 | public List searchResult(string _searchString, string _filter, string _primaryLanguage, bool _tIsUserSearch = true)
255 | {
256 | List amalgamatedDocuments = new List();
257 | if (primaryIndexClient != null)
258 | {
259 | if (parentIndexClient != null && String.Equals(primaryIndexClient.IndexName, parentIndexClient.IndexName, StringComparison.OrdinalIgnoreCase))
260 | {
261 | amalgamatedDocuments.AddRange(OrderAndProcessSearchResults(ProcessSearch(primaryIndexClient, _searchString, _primaryLanguage, parentLanguage)));
262 | }
263 | else
264 | {
265 | amalgamatedDocuments.AddRange(OrderAndProcessSearchResults(ProcessSearch(primaryIndexClient, _searchString, _primaryLanguage)));
266 | }
267 | }
268 | else if (parentIndexClient != null && String.Equals(_primaryLanguage, parentLanguage, StringComparison.OrdinalIgnoreCase))
269 | {
270 | //Search _primaryLanguage when it is a parent language
271 | amalgamatedDocuments.AddRange(OrderAndProcessSearchResults(ProcessSearch(parentIndexClient, _searchString, _primaryLanguage)));
272 | }
273 | else if (ultimateIndexClient != null && String.Equals(_primaryLanguage, ultimateLanguage, StringComparison.OrdinalIgnoreCase))
274 | {
275 | //Search _primaryLanguage when it is the ultimate language
276 | amalgamatedDocuments.AddRange(OrderAndProcessSearchResults(ProcessSearch(ultimateIndexClient, _searchString, _primaryLanguage)));
277 | }
278 | if (amalgamatedDocuments.Count == 0)
279 | {
280 | if (parentIndexClient != null)
281 | {
282 | amalgamatedDocuments.AddRange(OrderAndProcessSearchResults(ProcessSearch(parentIndexClient, _searchString, parentLanguage)));
283 | }
284 | if (ultimateIndexClient != null)
285 | {
286 | amalgamatedDocuments.AddRange(OrderAndProcessSearchResults(ProcessSearch(ultimateIndexClient, _searchString, ultimateLanguage)));
287 | }
288 | }
289 | return amalgamatedDocuments;
290 | }
291 |
292 | private List OrderAndProcessSearchResults(DocumentSearchResult resultDocuments)
293 | {
294 | List documents = new List();
295 | if (resultDocuments != null)
296 | {
297 | if (resultDocuments.Results.Count > 0)
298 | {
299 | documents = resultDocuments.Results.OrderByDescending(search => search.Score).Select(resultlist => resultlist.Document).ToList();
300 |
301 | foreach (Document doc in documents)
302 | {
303 | if (!string.IsNullOrEmpty(doc.metadata_storage_path) && doc.metadata_storage_path.Contains(".json") && !string.IsNullOrEmpty(doc.ms_locale))
304 | {
305 | doc.metadata_storage_path = doc.metadata_storage_path.ToLower();
306 | doc.metadata_storage_path = (doc.metadata_storage_path.Remove(0, doc.metadata_storage_path.IndexOf(doc.ms_locale.ToLower() + "/"))).Replace(".json", ".html");
307 | }
308 | }
309 | }
310 | }
311 | return documents;
312 | }
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/Document.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Azure.Search;
2 | using Microsoft.Azure.Search.Models;
3 |
4 | namespace AzureSearchCustomHelp
5 | {
6 | // The SerializePropertyNamesAsCamelCase attribute is defined in the Azure Search .NET SDK.
7 | // It ensures that Pascal-case property names in the model class are mapped to camel-case
8 | // field names in the index.
9 | [SerializePropertyNamesAsCamelCase]
10 | public partial class Document
11 | {
12 | [System.ComponentModel.DataAnnotations.Key]
13 | [IsFilterable, IsSortable, IsFacetable, IsSearchable]
14 | public string id { get; set; }
15 |
16 | [IsFilterable, IsSortable, IsFacetable, IsSearchable]
17 | public string title { get; set; }
18 |
19 | [IsFilterable, IsSortable, IsFacetable, IsSearchable]
20 | public string ms_search_form { get; set; }
21 |
22 | [IsFilterable, IsSortable, IsFacetable, IsSearchable]
23 | public string ms_search_region { get; set; }
24 |
25 | [IsFilterable, IsSortable, IsFacetable, IsSearchable]
26 | public string ms_locale { get; set; }
27 |
28 | [IsFilterable, IsSortable, IsFacetable, IsSearchable]
29 | public string metadata_storage_path { get; set; }
30 |
31 | [System.ComponentModel.DataAnnotations.Key]
32 | [IsSearchable]
33 | public string Microsoft_Help_Id { get; set; }
34 |
35 | [IsFilterable, IsSortable, IsFacetable]
36 | public string metadata_storage_content_type { get; set; }
37 |
38 | [IsFilterable, IsSortable, IsFacetable]
39 | public string metadata_storage_name { get; set; }
40 |
41 | [IsFilterable, IsSortable, IsFacetable, IsSearchable]
42 | public string description { get; set; }
43 |
44 | [IsFilterable, IsSortable, IsFacetable, IsSearchable]
45 | public string Content { get; set; }
46 |
47 |
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/Exceptions/InvalidConfigurationException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AzureSearchCustomHelp
4 | {
5 | class InvalidConfigurationException : Exception
6 | {
7 | ///
8 | /// Initializes a new instance of the class.
9 | ///
10 | public InvalidConfigurationException()
11 | {
12 | }
13 |
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// The message that describes the error.
18 | public InvalidConfigurationException(string message) : base(message)
19 | {
20 | }
21 |
22 | ///
23 | /// Initializes a new instance of the class.
24 | ///
25 | /// The error message that explains the reason for the exception.
26 | /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.
27 | public InvalidConfigurationException(string message, Exception innerException) : base(message, innerException)
28 | {
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/HelppaneOption.axpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/dynamics365f-o-custom-help/308c1f7486211578590a93a1170f80bb4ec44d97/Help Pane extension/AzureSearchCustomHelp/HelppaneOption.axpp
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/HelppaneOption/HelppaneOption.rnrproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | 2.0
6 | USRLayer
7 | True
8 | False
9 |
10 |
11 | initial
12 | AnyCPU
13 | bin
14 | v4.6
15 | False
16 | False
17 |
18 |
19 | true
20 | false
21 |
22 |
23 | {6ce73c61-ac88-49f8-af1b-e7f38f2dfb93}
24 | HelppaneOption
25 | HelppaneOption
26 | Form
27 | SysHelpPane (SysHelpPane.Extension_UsrLayer)
28 |
29 |
30 |
31 | Content
32 | SysCustomHelp__Extension
33 | SysCustomHelp__Extension
34 |
35 |
36 | Content
37 | SysHelpCustomSearchpane
38 | SysHelpCustomSearchpane
39 |
40 |
41 | Content
42 | SysHelpPane.Extension_UsrLayer
43 | SysHelpPane.Extension_UsrLayer
44 |
45 |
46 | Content
47 | Customhelppane_en-US
48 | Customhelppane_en-US
49 |
50 |
51 | Content
52 | HelppaneMacro
53 | HelppaneMacro
54 |
55 |
56 | Content
57 | SysCustomHelpMap
58 | SysCustomHelpMap
59 |
60 |
61 | Content
62 | TmpSysHelpCustomSearchResults
63 | TmpSysHelpCustomSearchResults
64 |
65 |
66 | Content
67 | Customhelppane.en-US.label.txt
68 | AxLabelFile\Customhelppane_en-US
69 |
70 |
71 |
72 |
73 | AzureSearchCustomHelp
74 | {61899f42-6edf-47a9-bcd0-944f081a8276}
75 | True
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/HelppaneOption/HelppaneOption_on_box.rnrproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | 2.0
6 | USRLayer
7 | True
8 | False
9 |
10 |
11 | initial
12 | AnyCPU
13 | bin
14 | v4.6
15 | False
16 | False
17 |
18 |
19 | true
20 | false
21 |
22 |
23 | {6ce73c61-ac88-49f8-af1b-e7f38f2dfb93}
24 | HelppaneOption_on_box
25 | HelppaneOption
26 | Form
27 | SysHelpPane (SysHelpPane.Extension_UsrLayer)
28 |
29 |
30 |
31 | Content
32 | SysCustomHelp__Extension
33 | SysCustomHelp__Extension
34 |
35 |
36 | Content
37 | SysHelpCustomSearchpane
38 | SysHelpCustomSearchpane
39 |
40 |
41 | Content
42 | SysHelpPane.Extension_UsrLayer
43 | SysHelpPane.Extension_UsrLayer
44 |
45 |
46 | Content
47 | Customhelppane_en-US
48 | Customhelppane_en-US
49 |
50 |
51 | Content
52 | HelppaneMacro
53 | HelppaneMacro
54 |
55 |
56 | Content
57 | SysCustomHelpMap
58 | SysCustomHelpMap
59 |
60 |
61 | Content
62 | TmpSysHelpCustomSearchResults
63 | TmpSysHelpCustomSearchResults
64 |
65 |
66 | Content
67 | Customhelppane.en-US.label.txt
68 | AxLabelFile\Customhelppane_en-US
69 |
70 |
71 |
72 |
73 | AzureSearchCustomHelp
74 | C:\Users\margoc\Source\Repos\DAX CP Code Projects2\AzureSearchCustomHelp\bin\Debug\AzureSearchCustomHelp.dll
75 |
76 |
77 | Microsoft.Azure.Search
78 | C:\Users\margoc\Source\Repos\DAX CP Code Projects2\AzureSearchCustomHelp\bin\Debug\Microsoft.Azure.Search.dll
79 |
80 |
81 | Microsoft.Rest.ClientRuntime.Azure
82 | C:\Users\margoc\Source\Repos\DAX CP Code Projects2\AzureSearchCustomHelp\bin\Debug\Microsoft.Rest.ClientRuntime.Azure.dll
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/Language.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("AzureSearchCustomHelp")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("AzureSearchCustomHelp")]
13 | [assembly: AssemblyCopyright("Copyright © 2019")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("61899f42-6edf-47a9-bcd0-944f081a8276")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/UsersConfigMapSection.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Configuration;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Reflection;
8 |
9 | namespace AzureSearchCustomHelp
10 | {
11 | public sealed class UsersConfigMapSection : ConfigurationSection
12 | {
13 | static string languageConfigFile = "Language.config";
14 | private static UsersConfigMapSection config;
15 |
16 | public static UsersConfigMapSection Config
17 | {
18 | get
19 | {
20 | try
21 | {
22 | ExeConfigurationFileMap customConfigFileMap = new ExeConfigurationFileMap();
23 | customConfigFileMap.ExeConfigFilename = Path.Combine(Path.GetDirectoryName((new System.Uri(Assembly.GetExecutingAssembly().CodeBase)).LocalPath), languageConfigFile);
24 | Configuration customConfig = ConfigurationManager.OpenMappedExeConfiguration(customConfigFileMap, ConfigurationUserLevel.None);
25 | config = customConfig.GetSection("languagesection") as UsersConfigMapSection;
26 | return config;
27 | }
28 | catch (Exception )
29 | {
30 | return null;
31 | }
32 | }
33 | }
34 |
35 | [ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
36 | private UsersConfigMapConfigElements Settings
37 | {
38 | get { return (UsersConfigMapConfigElements)this[""]; }
39 | set { this[""] = value; }
40 | }
41 |
42 | public IEnumerable SettingsList
43 | {
44 | get { return this.Settings.Cast(); }
45 | }
46 | }
47 |
48 | public sealed class UsersConfigMapConfigElements : ConfigurationElementCollection
49 | {
50 | protected override ConfigurationElement CreateNewElement()
51 | {
52 | return new UsersConfigMapConfigElement();
53 | }
54 | protected override object GetElementKey(ConfigurationElement element)
55 | {
56 | return ((UsersConfigMapConfigElement)element).PrimaryLanguage;
57 | }
58 | }
59 |
60 | public sealed class UsersConfigMapConfigElement : ConfigurationElement
61 | {
62 | [ConfigurationProperty("language", IsKey = true, IsRequired = true)]
63 | public string PrimaryLanguage
64 | {
65 | get { return (string)base["language"]; }
66 | set { base["language"] = value; }
67 | }
68 |
69 | [ConfigurationProperty("parentlanguage", IsRequired = false)]
70 | public string ParentLanguage
71 | {
72 | get { return (string)base["parentlanguage"]; }
73 | set { base["parentlanguage"] = value; }
74 | }
75 |
76 | [ConfigurationProperty("index", IsRequired = false)]
77 | public string Index
78 | {
79 | get { return (string)base["index"]; }
80 | set { base["index"] = value; }
81 | }
82 |
83 | [ConfigurationProperty("parentindex", IsRequired = false)]
84 | public string ParentIndex
85 | {
86 | get { return (string)base["parentindex"]; }
87 | set { base["parentindex"] = value; }
88 | }
89 |
90 | [ConfigurationProperty("ultimateindex", IsRequired = false)]
91 | public string UltimateIndex
92 | {
93 | get { return (string)base["ultimateindex"]; }
94 | set { base["ultimateindex"] = value; }
95 | }
96 | }
97 |
98 | public class RootObject
99 | {
100 | [JsonProperty("@odata.context")]
101 | public string context { get; set; }
102 | public List value { get; set; }
103 | }
104 | public class IndexName
105 | {
106 | public string name { get; set; }
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/Help Pane extension/AzureSearchCustomHelp/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Help Pane extension/HelppaneOption.axpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/dynamics365f-o-custom-help/308c1f7486211578590a93a1170f80bb4ec44d97/Help Pane extension/HelppaneOption.axpp
--------------------------------------------------------------------------------
/Help Pane extension/REST API CREATION.txt:
--------------------------------------------------------------------------------
1 | Requests to create data sources, indexes, and indexers using REST APIs
2 |
3 | Common values
4 | ===================================================================================================================================================================
5 | Auth: no auth
6 | Content-Type: Application\JSON
7 | api-key: [Azure Search Admin Key]
8 |
9 |
10 | Create datasource
11 | ===================================================================================================================================================================
12 | POST https://[AzureSearchServicename].search.windows.net/datasources?api-version=2017-11-11
13 |
14 | Request Body
15 | {
16 | "name" : "[DataSourceName]",
17 | "type" : "azureblob",
18 | "credentials" : { "connectionString" : "DefaultEndpointsProtocol=https;AccountName=[StorageAccountName];AccountKey=[key];EndpointSuffix=core.windows.net" },
19 | "container" : { "name" : "[JSONStorageContainerName]" }
20 | }
21 |
22 |
23 | Create index
24 | ===================================================================================================================================================================
25 | POST https://[AzureSearchServicename].search.windows.net/indexes?api-version=2017-11-11
26 |
27 | Request Body
28 | {
29 | "name" : "[IndexName]",
30 | "fields": [
31 | { "name": "id", "type": "Edm.String", "key": true,"searchable": true, "filterable": true, "sortable": true, "facetable": true },
32 | { "name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true, "analyzer": "[AnalyzerName]" },
33 | { "name": "ms_search_form", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true },
34 | { "name": "ms_search_region", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true },
35 | { "name": "ms_locale", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true },
36 | { "name": "metadata_storage_path", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true },
37 | { "name": "metadata_storage_name", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true },
38 | { "name": "metadata_storage_content_type", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": false, "facetable": false },
39 | { "name": "description", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true, "analyzer": "[AnalyzerName]" }
40 | ]
41 | }
42 |
43 |
44 | Create indexer with field mapping
45 | ===================================================================================================================================================================
46 | POST https://[AzureSearchServicename].search.windows.net/indexers?api-version=2017-11-11
47 |
48 | Request Body
49 | {
50 | "name" : "[IndexerName]",
51 | "dataSourceName" : "[DataSourceName]",
52 | "targetIndexName" : "[IndexName]",
53 | "schedule" : { "interval" : "PT10H" },
54 | "parameters" : { "configuration" : { "parsingMode" : "json" } },
55 | "fieldMappings" : [
56 | { "sourceFieldName" : "/title", "targetFieldName" : "title" },
57 | { "sourceFieldName" : "/ms.search.form", "targetFieldName" : "ms_search_form" },
58 | { "sourceFieldName" : "/ms.search.region", "targetFieldName" : "ms_search_region" },
59 | { "sourceFieldName" : "/ms.locale", "targetFieldName" : "ms_locale" },
60 | { "sourceFieldName" : "/description", "targetFieldName" : "description" }
61 | ]
62 | }
63 |
--------------------------------------------------------------------------------
/HtmlLocaleChanger/.gitignore:
--------------------------------------------------------------------------------
1 | ## Allow executable files in this folder
2 | !*.dll
3 | !*.exe
--------------------------------------------------------------------------------
/HtmlLocaleChanger/CommandLine.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/dynamics365f-o-custom-help/308c1f7486211578590a93a1170f80bb4ec44d97/HtmlLocaleChanger/CommandLine.dll
--------------------------------------------------------------------------------
/HtmlLocaleChanger/HtmlAgilityPack.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/dynamics365f-o-custom-help/308c1f7486211578590a93a1170f80bb4ec44d97/HtmlLocaleChanger/HtmlAgilityPack.dll
--------------------------------------------------------------------------------
/HtmlLocaleChanger/HtmlLocaleChanger.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/dynamics365f-o-custom-help/308c1f7486211578590a93a1170f80bb4ec44d97/HtmlLocaleChanger/HtmlLocaleChanger.exe
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Tools in the toolkit
3 |
4 | The toolkit is available at [https://github.com/microsoft/dynamics365f-o-custom-help/](https://github.com/microsoft/dynamics365f-o-custom-help/). The repo contains the following tools and the source code for the tools:
5 |
6 | ## ConvertHtmlToJson tool
7 |
8 | For more information, see https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/help/custom-help-toolkit-converthtmltojson
9 |
10 | ## HtmlLocaleChanger tool
11 |
12 | For more information, see https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/help/custom-help-toolkit-htmllocalechanger
13 |
14 | ## "Help Pane extension" Visual Studio project
15 |
16 | For more information, see https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/help/connect-help-pane
17 |
18 | ## AX 2012 metadata scripts
19 |
20 | For more information, see https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/help/migrate-dynamicsax2012
21 |
22 | # Contributing
23 |
24 | This project welcomes contributions and suggestions. Most contributions require you to agree to a
25 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
26 | the rights to use your contribution. For details, visit https://cla.microsoft.com.
27 |
28 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
29 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
30 | provided by the bot. You will only need to do this once across all repos using our CLA.
31 |
32 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
33 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
34 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
35 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.902
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConvertHtmlToJson", "ConvertHtmlToJson\ConvertHtmlToJson.csproj", "{1DDB3C92-B927-43C7-BCC1-6726ED32CE5C}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {1DDB3C92-B927-43C7-BCC1-6726ED32CE5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {1DDB3C92-B927-43C7-BCC1-6726ED32CE5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {1DDB3C92-B927-43C7-BCC1-6726ED32CE5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {1DDB3C92-B927-43C7-BCC1-6726ED32CE5C}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {FFDDCA0E-E578-4351-9758-DB67D790FE14}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson/CommandLineOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | ///
4 | /// Summary description for CommandLineOptions
5 | ///
6 | public class CommandLineOptions
7 | {
8 | public CommandLineOptions()
9 | {
10 | //
11 | // TODO: Add constructor logic here
12 | //
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson/ConvertHtmlToJson.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {1DDB3C92-B927-43C7-BCC1-6726ED32CE5C}
8 | Exe
9 | ConvertHtmlToJson
10 | ConvertHtmlToJson
11 | v4.6.1
12 | 512
13 | true
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\CommandLineParser.2.6.0\lib\net461\CommandLine.dll
38 |
39 |
40 | ..\packages\HtmlAgilityPack.1.11.16\lib\Net45\HtmlAgilityPack.dll
41 |
42 |
43 | ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson/HTMLDirectory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace ConvertHtmlToJson
9 | {
10 | class HTMLDirectory
11 | {
12 | private DirectoryInfo htmlDirectory;
13 |
14 | public HTMLDirectory(String htmlPath)
15 | {
16 | this.htmlDirectory = new DirectoryInfo(htmlPath);
17 | }
18 |
19 | public DirectoryInfo GetDirectoryInfo()
20 | {
21 | return this.htmlDirectory;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson/HTMLtoJSONProcessor.cs:
--------------------------------------------------------------------------------
1 | using HtmlAgilityPack;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using System;
5 | using System.IO;
6 | using System.Net;
7 |
8 |
9 | namespace ConvertHtmlToJson
10 | {
11 | class HTMLtoJSONProcessor
12 | {
13 | private readonly HTMLDirectory htmlDirectory;
14 | private readonly JSONDirectory jsonDirectory;
15 | private readonly bool verbose;
16 | private string firstLocale = string.Empty;
17 |
18 | public HTMLtoJSONProcessor(HTMLDirectory htmlDirectory, JSONDirectory jsonDirectory, bool verbose)
19 | {
20 | this.htmlDirectory = htmlDirectory;
21 | this.jsonDirectory = jsonDirectory;
22 | this.verbose = verbose;
23 | }
24 |
25 | public int Process()
26 | {
27 | return ProcessFilesInDirectory(htmlDirectory.GetDirectoryInfo());
28 | }
29 |
30 | private int ProcessFilesInDirectory(DirectoryInfo dirInfo)
31 | {
32 | int errorCount = 0;
33 | FileInfo[] dirFiles = dirInfo.GetFiles("*.html", SearchOption.TopDirectoryOnly);
34 | if (dirFiles.Length > 0)
35 | {
36 | DirectoryInfo targetDirectory = CreateTargetDirectory(dirInfo);
37 | foreach (FileInfo file in dirFiles)
38 | {
39 | if (file.FullName.IndexOf("toc.html", StringComparison.OrdinalIgnoreCase) == -1)
40 | {
41 | string jsonFilePath = Path.Combine(targetDirectory.FullName, Path.GetFileNameWithoutExtension(file.Name) + ".json");
42 | if (this.verbose)
43 | {
44 | Console.WriteLine("Processing " + file.FullName + " to " + jsonFilePath);
45 | }
46 | HtmlDocument htmlDoc = new HtmlDocument();
47 | htmlDoc.Load(file.FullName);
48 |
49 | JObject product = new JObject();
50 | HtmlNodeCollection nodeMeta = htmlDoc.DocumentNode.SelectNodes("//meta");
51 |
52 | bool hasTitle = false;
53 | bool hasDescription = false;
54 | bool hasMSLocale = false;
55 | foreach (HtmlNode _htm in nodeMeta)
56 | {
57 | string tag = string.Empty;
58 | string value = string.Empty;
59 | HtmlAttributeCollection attribColl = _htm.Attributes;
60 | if (attribColl[0].Value == "title")
61 | {
62 | tag = attribColl[0].Value;
63 | value = WebUtility.HtmlDecode(attribColl[1].Value.ToString());
64 | product.Add(new JProperty(tag, value));
65 | if (!String.IsNullOrWhiteSpace(value))
66 | {
67 | hasTitle = true;
68 | }
69 | }
70 | if (attribColl[0].Value == "description")
71 | {
72 | tag = attribColl[0].Value;
73 | value = WebUtility.HtmlDecode(attribColl[1].Value.ToString());
74 | product.Add(new JProperty(tag, value));
75 | if (!String.IsNullOrWhiteSpace(value))
76 | {
77 | hasDescription = true;
78 | }
79 | }
80 | if (attribColl[0].Value == "ms.locale")
81 | {
82 | if (String.IsNullOrWhiteSpace(firstLocale))
83 | {
84 | if (String.IsNullOrWhiteSpace(attribColl[1].Value))
85 | {
86 | Console.WriteLine("ms.locale requires a value in " + file.FullName);
87 | }
88 | else
89 | {
90 | firstLocale = attribColl[1].Value;
91 | }
92 | }
93 | tag = attribColl[0].Value;
94 | value = attribColl[1].Value.ToString();
95 | if (value.CompareTo(firstLocale) != 0)
96 | {
97 | Console.WriteLine("ms.locale metadata value (" + value + ") in " + file.FullName + " does not match the first ms.locale metadata value found (" + firstLocale + "). All ms.locale metadata values should be identical.");
98 | errorCount++;
99 | }
100 | product.Add(new JProperty(tag, value));
101 | if (!String.IsNullOrWhiteSpace(value))
102 | {
103 | hasMSLocale = true;
104 | }
105 | }
106 | if (attribColl[0].Value == "ms.search.form" || attribColl[0].Value == "ms.search.scope" || attribColl[0].Value == "ms.search.region")
107 | {
108 | tag = attribColl[0].Value;
109 | value = attribColl[1].Value.ToString();
110 | product.Add(new JProperty(tag, value));
111 | }
112 | }
113 | if (!hasTitle)
114 | {
115 | Console.WriteLine("title metadata missing in " + file.FullName);
116 | errorCount++;
117 | }
118 | if (!hasDescription)
119 | {
120 | Console.WriteLine("description metadata missing in " + file.FullName);
121 | errorCount++;
122 | }
123 | if (!hasMSLocale)
124 | {
125 | Console.WriteLine("ms.locale metadata missing in " + file.FullName);
126 | errorCount++;
127 | }
128 |
129 |
130 | HtmlNode node = htmlDoc.DocumentNode.SelectSingleNode("//body");
131 | product.Add(new JProperty("Content", node.InnerText));
132 |
133 | StreamWriter sysfile = File.CreateText(jsonFilePath);
134 | using (JsonTextWriter writer = new JsonTextWriter(sysfile))
135 | {
136 | product.WriteTo(writer);
137 | }
138 | }
139 | }
140 | }
141 | foreach (DirectoryInfo subdirectoryInfo in dirInfo.GetDirectories())
142 | {
143 | errorCount = errorCount + (ProcessFilesInDirectory(subdirectoryInfo));
144 | }
145 | return errorCount;
146 | }
147 |
148 | private DirectoryInfo CreateTargetDirectory(DirectoryInfo dirInfo)
149 | {
150 | DirectoryInfo targetDirectory = new DirectoryInfo(dirInfo.FullName.Replace(htmlDirectory.GetDirectoryInfo().FullName, jsonDirectory.GetDirectoryInfo().FullName));
151 | targetDirectory.Create();
152 | return targetDirectory;
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson/JSONDirectory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace ConvertHtmlToJson
9 | {
10 | class JSONDirectory
11 | {
12 | private DirectoryInfo jsonDirectory;
13 |
14 | public JSONDirectory(String jsonPath)
15 | {
16 | this.jsonDirectory = new DirectoryInfo(jsonPath);
17 | }
18 |
19 | public DirectoryInfo GetDirectoryInfo()
20 | {
21 | return this.jsonDirectory;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson/Program.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Globalization;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace ConvertHtmlToJson
11 | {
12 | public class Options
13 | {
14 | [Option('h', "htmlDirectory", Required = true, HelpText = "Directory containing the HTML source files.")]
15 | public string HtmlDirectory { get; set; }
16 |
17 | [Option('j', "jsonDirectory", Required = true, HelpText = "Directory where the JSON files should be created.")]
18 | public string JsonDirectory { get; set; }
19 |
20 | [Option('v', "verbose", Required = false, HelpText = "Detailed logging for each file processed.")]
21 | public bool Verbose { get; set; }
22 | }
23 | class Program
24 | {
25 | static void Main(string[] args)
26 | {
27 | Parser.Default.ParseArguments(args)
28 | .WithParsed(opts => RunOptions(opts));
29 | }
30 |
31 | private static void RunOptions(Options opts)
32 | {
33 | try
34 | {
35 | JSONDirectory jsonDirectory = new JSONDirectory(opts.JsonDirectory);
36 | HTMLDirectory htmlDirectory = new HTMLDirectory(opts.HtmlDirectory);
37 |
38 | HTMLtoJSONProcessor processor = new HTMLtoJSONProcessor(htmlDirectory, jsonDirectory, opts.Verbose);
39 | if (processor.Process() > 0)
40 | {
41 | Console.WriteLine("Please fix all of the errors listed and rerun ConvertHtmlToJson.");
42 | }
43 | }
44 | catch (Exception ex)
45 | {
46 | Console.WriteLine(ex);
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | [assembly: AssemblyTitle("ConvertHtmlToJson")]
6 | [assembly: AssemblyDescription("Utility that transforms HTML files into JSON files which can then be used by the Azure Search service to generate context-sensitive links for Help content.")]
7 | [assembly: AssemblyConfiguration("")]
8 | [assembly: AssemblyCompany("Microsoft(r) Corp.")]
9 | [assembly: AssemblyProduct("ConvertHtmlToJson")]
10 | [assembly: AssemblyCopyright("Copyright (C) 2019 Microsoft Corp. All Rights Reserved")]
11 | [assembly: AssemblyTrademark("")]
12 | [assembly: AssemblyCulture("")]
13 | [assembly: ComVisible(false)]
14 | [assembly: Guid("1ddb3c92-b927-43c7-bcc1-6726ed32ce5c")]
15 | [assembly: AssemblyVersion("1.0.0.0")]
16 | [assembly: AssemblyFileVersion("1.0.0.0")]
17 |
--------------------------------------------------------------------------------
/SourceCode/ConvertHtmlToJson/ConvertHtmlToJson/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/SourceCode/HtmlLocaleChanger/HtmlLocaleChanger.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.902
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlLocaleChanger", "HtmlLocaleChanger\HtmlLocaleChanger.csproj", "{01916328-CD9D-45B7-8687-C1C7C80D16A5}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {01916328-CD9D-45B7-8687-C1C7C80D16A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {01916328-CD9D-45B7-8687-C1C7C80D16A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {01916328-CD9D-45B7-8687-C1C7C80D16A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {01916328-CD9D-45B7-8687-C1C7C80D16A5}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {0EF30FC7-987D-49E5-99DF-AA596F05721D}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/SourceCode/HtmlLocaleChanger/HtmlLocaleChanger/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/SourceCode/HtmlLocaleChanger/HtmlLocaleChanger/HTMLDirectory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace HtmlLocaleChanger
9 | {
10 | class HTMLDirectory
11 | {
12 | private DirectoryInfo htmlDirectory;
13 |
14 | public HTMLDirectory(String htmlPath)
15 | {
16 | this.htmlDirectory = new DirectoryInfo(htmlPath);
17 | }
18 |
19 | public DirectoryInfo GetDirectoryInfo()
20 | {
21 | return this.htmlDirectory;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SourceCode/HtmlLocaleChanger/HtmlLocaleChanger/HtmlLocaleChanger.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {01916328-CD9D-45B7-8687-C1C7C80D16A5}
8 | Exe
9 | HtmlLocaleChanger
10 | HtmlLocaleChanger
11 | v4.6.1
12 | 512
13 | true
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\CommandLineParser.2.6.0\lib\net461\CommandLine.dll
38 |
39 |
40 | ..\packages\HtmlAgilityPack.1.11.17\lib\Net45\HtmlAgilityPack.dll
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/SourceCode/HtmlLocaleChanger/HtmlLocaleChanger/LocaleChangeProcessor.cs:
--------------------------------------------------------------------------------
1 | using HtmlAgilityPack;
2 | using System;
3 | using System.IO;
4 | using System.Text;
5 |
6 | namespace HtmlLocaleChanger
7 | {
8 | class LocaleChangeProcessor
9 | {
10 | private readonly HTMLDirectory htmlDirectory;
11 | private readonly string requestedLocale;
12 | private readonly bool verbose;
13 |
14 | public LocaleChangeProcessor(HTMLDirectory htmlDirectory, string requestedLocale, bool verbose)
15 | {
16 | this.htmlDirectory = htmlDirectory;
17 | this.requestedLocale = requestedLocale;
18 | this.verbose = verbose;
19 | }
20 |
21 | public void Process()
22 | {
23 | ProcessAllHTMLFiles(htmlDirectory.GetDirectoryInfo());
24 | }
25 |
26 | private void ProcessAllHTMLFiles(DirectoryInfo dirHTML)
27 | {
28 | FileInfo[] dirFiles = dirHTML.GetFiles("*.html", SearchOption.AllDirectories);
29 | if (dirFiles.Length > 0)
30 | {
31 | if (verbose)
32 | {
33 | Console.WriteLine(dirFiles.Length + " files to process.");
34 | }
35 |
36 | foreach (FileInfo file in dirFiles)
37 | {
38 | if (file.Name.ToLower() != "toc.html")
39 | {
40 | if (verbose)
41 | {
42 | Console.WriteLine("Processing " + file.FullName);
43 | }
44 |
45 | try
46 | {
47 | HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
48 | htmlDoc.Load(file.FullName, Encoding.UTF8);
49 | HtmlNodeCollection nodeMeta = htmlDoc.DocumentNode.SelectNodes("//meta");
50 | foreach (HtmlNode _htm in nodeMeta)
51 | {
52 | HtmlAttributeCollection attribColl = _htm.Attributes;
53 | if (attribColl[0].Value == "ms.locale")
54 | {
55 | attribColl[1].Value = requestedLocale;
56 | }
57 | }
58 | htmlDoc.Save(file.FullName, Encoding.UTF8);
59 | }
60 |
61 | catch (Exception ex)
62 | {
63 | Console.WriteLine("Error found while processing " + file.FullName + ": " + ex.Message);
64 | Environment.Exit(-1);
65 | }
66 | }
67 | }
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/SourceCode/HtmlLocaleChanger/HtmlLocaleChanger/Program.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Globalization;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace HtmlLocaleChanger
10 | {
11 | public class Options
12 | {
13 | [Option('h', "htmlDirectory", Required = true, HelpText = "Directory containing the HTML files.")]
14 | public string HtmlDirectory { get; set; }
15 |
16 | [Option('l', "locale", Required = true, HelpText = "New locale for the HTML files.")]
17 | public string Locale { get; set; }
18 |
19 | [Option('v', "verbose", Required = false, HelpText = "Detailed logging for each file processed.")]
20 | public bool Verbose { get; set; }
21 | }
22 | class Program
23 | {
24 | static void Main(string[] args)
25 | {
26 | Parser.Default.ParseArguments(args)
27 | .WithParsed(opts => RunOptions(opts));
28 | }
29 |
30 | private static void RunOptions(Options opts)
31 | {
32 | try
33 | {
34 | CultureInfo ci = new CultureInfo(opts.Locale);
35 | if (!CultureInfo.GetCultures(CultureTypes.AllCultures).Contains(ci))
36 | {
37 | throw new CultureNotFoundException();
38 | }
39 | }
40 | catch (CultureNotFoundException)
41 | {
42 | Console.WriteLine("Locale " + opts.Locale + " is not supported by this system. Do you want to proceed? (Y/N)");
43 | if (Console.ReadKey().Key != ConsoleKey.Y)
44 | {
45 | return;
46 | }
47 | }
48 | HTMLDirectory htmlDirectory = new HTMLDirectory(opts.HtmlDirectory);
49 | LocaleChangeProcessor processor = new LocaleChangeProcessor(htmlDirectory, opts.Locale, opts.Verbose);
50 | processor.Process();
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/SourceCode/HtmlLocaleChanger/HtmlLocaleChanger/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("HtmlLocaleChanger")]
9 | [assembly: AssemblyDescription("Utility that changes the ms.locale metadata attribute to the supplied locale.")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft(r) Corp.")]
12 | [assembly: AssemblyProduct("HtmlLocaleChanger")]
13 | [assembly: AssemblyCopyright("Copyright (C) 2019 Microsoft Corp. All Rights Reserved")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("01916328-cd9d-45b7-8687-c1c7c80d16a5")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/SourceCode/HtmlLocaleChanger/HtmlLocaleChanger/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------