├── .editorconfig ├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── WebDeployParametersToolkit.Tests ├── ParametersXmlReaderTests │ └── ReadShould.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── Resources │ ├── BasicParameters.xml │ ├── EmptySettings.config │ ├── LocationSimpleSettings.config │ └── SimpleSettings.config ├── WebConfigSample.cs ├── WebConfigSettingsAsserts.cs ├── WebConfigSettingsReaderTests │ ├── ReadApplicationSettingsShould.cs │ ├── ReadCompilationDebugSettingsShould.cs │ ├── ReadMailSettingsShould.cs │ └── ReadSessionStateSettingsShould.cs ├── WebDeployParameterAsserts.cs ├── WebDeployParametersToolkit.Tests.csproj ├── WebDeployParametersToolkit.Tests.ruleset ├── XmlNodeExtensionsTests │ └── GetFullPathShould.cs └── packages.config ├── WebDeployParametersToolkit.sln ├── WebDeployParametersToolkit ├── Commands │ ├── AddParameterizationTargetCommand.cs │ ├── ApplyMissingParametersCommand.cs │ ├── GenerateParametersCommand.cs │ ├── GenerateSetParametersCommand.cs │ └── NestCommand.cs ├── Dialogs │ ├── FileNameDialog.xaml │ ├── FileNameDialog.xaml.cs │ └── OptionsPageGrid.cs ├── Extensions │ ├── ProjectExtensions.cs │ ├── SolutionExplorerExtensions.cs │ ├── XmlDocumentExtensions.cs │ └── XmlNodeExtensions.cs ├── GlobalSuppressions.cs ├── Nester.cs ├── ParameterizationProject.cs ├── Properties │ └── AssemblyInfo.cs ├── Resources │ ├── AddParameterizationTargetCommand.png │ ├── ApplyMissingParametersCommand.png │ ├── GenerateParametersCommand.png │ ├── GenerateSetParametersCommand.png │ ├── NestCommand.png │ ├── PackageIcon.png │ └── PreviewImage.png ├── Utilities │ ├── ParametersXmlReader.cs │ ├── SetParametersXmlReader.cs │ ├── WebConfigSetting.cs │ ├── WebConfigSettingsReader.cs │ ├── WebDeployParameter.cs │ └── WebDeployParameterEntry.cs ├── VSCommandTable.cs ├── VSCommandTable.vsct ├── VSPackage.cs ├── WebDeployParametersToolkit.csproj ├── WebDeployParametersToolkit.ruleset ├── source.extension.cs ├── source.extension.ico └── source.extension.vsixmanifest ├── appveyor.yml ├── art ├── OptionsDialog.png ├── ParametersXmlContextMenu.png ├── SetParametersImportMenu.png ├── SetParametersNameDialog.png ├── SetParametersNestMenu.png └── WebConfigContextMenu.png └── publishManifest.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Default settings: 4 | # A newline ending every file 5 | # Use 4 spaces as indentation 6 | [*] 7 | indent_style = space 8 | trim_trailing_whitespace = true 9 | 10 | [*.csproj] 11 | indent_size = 2 12 | 13 | # C# files 14 | [*.cs] 15 | indent_size = 4 16 | 17 | # New line preferences 18 | end_of_line = crlf 19 | insert_final_newline = false 20 | 21 | #### .NET Coding Conventions #### 22 | 23 | # Organize usings 24 | dotnet_separate_import_directive_groups = false 25 | dotnet_sort_system_directives_first = true 26 | 27 | # this. and Me. preferences 28 | dotnet_style_qualification_for_event = false:silent 29 | dotnet_style_qualification_for_field = false:silent 30 | dotnet_style_qualification_for_method = false:silent 31 | dotnet_style_qualification_for_property = false:silent 32 | 33 | # Language keywords vs BCL types preferences 34 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 35 | dotnet_style_predefined_type_for_member_access = true:silent 36 | 37 | # Parentheses preferences 38 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 39 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 40 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 41 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 42 | 43 | # Modifier preferences 44 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 45 | 46 | # Field preferences 47 | dotnet_style_readonly_field = true:suggestion 48 | 49 | # Parameter preferences 50 | dotnet_code_quality_unused_parameters = all:suggestion 51 | 52 | # var preferences 53 | csharp_style_var_elsewhere = true:warning 54 | csharp_style_var_for_built_in_types = true:warning 55 | csharp_style_var_when_type_is_apparent = true:warning 56 | 57 | # Expression-bodied members 58 | csharp_style_expression_bodied_accessors = true:silent 59 | csharp_style_expression_bodied_constructors = false:silent 60 | csharp_style_expression_bodied_indexers = true:silent 61 | csharp_style_expression_bodied_lambdas = true:silent 62 | csharp_style_expression_bodied_local_functions = false:silent 63 | csharp_style_expression_bodied_methods = false:silent 64 | csharp_style_expression_bodied_operators = false:silent 65 | csharp_style_expression_bodied_properties = true:silent 66 | 67 | # Pattern matching preferences 68 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 69 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 70 | csharp_style_prefer_switch_expression = true:suggestion 71 | 72 | # Null-checking preferences 73 | csharp_style_conditional_delegate_call = true:suggestion 74 | 75 | # Modifier preferences 76 | csharp_prefer_static_local_function = true:suggestion 77 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async 78 | 79 | # Code-block preferences 80 | csharp_prefer_braces = true:suggestion 81 | csharp_prefer_simple_using_statement = true:suggestion 82 | 83 | # Expression-level preferences 84 | csharp_prefer_simple_default_expression = true:suggestion 85 | csharp_style_deconstructed_variable_declaration = true:suggestion 86 | csharp_style_inlined_variable_declaration = true:suggestion 87 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 88 | csharp_style_prefer_index_operator = true:suggestion 89 | csharp_style_prefer_range_operator = true:suggestion 90 | csharp_style_throw_expression = true:suggestion 91 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 92 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 93 | 94 | # 'using' directive preferences 95 | csharp_using_directive_placement = outside_namespace:warning 96 | 97 | # Expression-level preferences 98 | dotnet_style_coalesce_expression = true:suggestion 99 | dotnet_style_collection_initializer = true:suggestion 100 | dotnet_style_explicit_tuple_names = true:suggestion 101 | dotnet_style_null_propagation = true:suggestion 102 | dotnet_style_object_initializer = true:suggestion 103 | dotnet_style_prefer_auto_properties = true:suggestion 104 | dotnet_style_prefer_compound_assignment = true:suggestion 105 | dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion 106 | dotnet_style_prefer_conditional_expression_over_return = true:suggestion 107 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:silent 108 | dotnet_style_prefer_inferred_tuple_names = true:silent 109 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 110 | 111 | # Namespace preferences 112 | csharp_style_namespace_declarations = file_scoped:suggestion 113 | 114 | # New line preferences 115 | csharp_new_line_before_catch = true 116 | csharp_new_line_before_else = true 117 | csharp_new_line_before_finally = true 118 | csharp_new_line_before_members_in_anonymous_types = true 119 | csharp_new_line_before_members_in_object_initializers = true 120 | csharp_new_line_before_open_brace = all 121 | csharp_new_line_between_query_expression_clauses = true 122 | 123 | # Indentation preferences 124 | csharp_indent_block_contents = true 125 | csharp_indent_braces = false 126 | csharp_indent_case_contents = true 127 | csharp_indent_case_contents_when_block = false 128 | csharp_indent_labels = flush_left 129 | csharp_indent_switch_labels = true 130 | 131 | # Space preferences 132 | csharp_space_after_cast = false 133 | csharp_space_after_colon_in_inheritance_clause = true 134 | csharp_space_after_comma = true 135 | csharp_space_after_dot = false 136 | csharp_space_after_keywords_in_control_flow_statements = true 137 | csharp_space_after_semicolon_in_for_statement = true 138 | csharp_space_around_binary_operators = before_and_after 139 | csharp_space_around_declaration_statements = false 140 | csharp_space_before_colon_in_inheritance_clause = true 141 | csharp_space_before_comma = false 142 | csharp_space_before_dot = false 143 | csharp_space_before_open_square_brackets = false 144 | csharp_space_before_semicolon_in_for_statement = false 145 | csharp_space_between_empty_square_brackets = false 146 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 147 | csharp_space_between_method_call_name_and_opening_parenthesis = false 148 | csharp_space_between_method_call_parameter_list_parentheses = false 149 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 150 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 151 | csharp_space_between_method_declaration_parameter_list_parentheses = false 152 | csharp_space_between_parentheses = false 153 | csharp_space_between_square_brackets = false 154 | 155 | # Wrapping preferences 156 | csharp_preserve_single_line_blocks = true 157 | csharp_preserve_single_line_statements = false 158 | 159 | #### Naming styles #### 160 | 161 | # Naming rules 162 | 163 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning 164 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 165 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 166 | 167 | dotnet_naming_rule.private_or_internal_field_should_be_camel_case_name.severity = suggestion 168 | dotnet_naming_rule.private_or_internal_field_should_be_camel_case_name.symbols = private_or_internal_field 169 | dotnet_naming_rule.private_or_internal_field_should_be_camel_case_name.style = camel_case_name 170 | 171 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 172 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 173 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 174 | 175 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 176 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 177 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 178 | 179 | # Symbol specifications 180 | 181 | dotnet_naming_symbols.interface.applicable_kinds = interface 182 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 183 | dotnet_naming_symbols.interface.required_modifiers = 184 | 185 | dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field 186 | dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected 187 | dotnet_naming_symbols.private_or_internal_field.required_modifiers = 188 | 189 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 190 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 191 | dotnet_naming_symbols.types.required_modifiers = 192 | 193 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 194 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 195 | dotnet_naming_symbols.non_field_members.required_modifiers = 196 | 197 | # Naming styles 198 | 199 | dotnet_naming_style.pascal_case.required_prefix = 200 | dotnet_naming_style.pascal_case.required_suffix = 201 | dotnet_naming_style.pascal_case.word_separator = 202 | dotnet_naming_style.pascal_case.capitalization = pascal_case 203 | 204 | dotnet_naming_style.begins_with_i.required_prefix = I 205 | dotnet_naming_style.begins_with_i.required_suffix = 206 | dotnet_naming_style.begins_with_i.word_separator = 207 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 208 | 209 | dotnet_naming_style.camel_case_name.required_prefix = 210 | dotnet_naming_style.camel_case_name.required_suffix = 211 | dotnet_naming_style.camel_case_name.word_separator = 212 | dotnet_naming_style.camel_case_name.capitalization = camel_case 213 | 214 | 215 | # NOTE: Requires **VS2019 16.3** or later 216 | 217 | # Code files 218 | 219 | # SCS0018: Potential Path Traversal vulnerability was found where '{0}' in '{1}' may be tainted by user-controlled data from '{2}' in method '{3}'. 220 | dotnet_diagnostic.SCS0018.severity = suggestion 221 | 222 | [*.{cs,vb}] 223 | 224 | # CA1001: Types that own disposable fields should be disposable 225 | dotnet_diagnostic.CA1001.severity = warning 226 | 227 | # CA1014: Mark assemblies with CLSCompliant 228 | dotnet_diagnostic.CA1014.severity = none 229 | 230 | # CA1303: Do not pass literals as localized parameters 231 | dotnet_diagnostic.CA1303.severity = silent 232 | 233 | # CA1305: Specify IFormatProvider 234 | dotnet_diagnostic.CA1305.severity = silent 235 | 236 | # CA1704: Identifiers should be spelled correctly 237 | dotnet_diagnostic.CA1704.severity = none 238 | 239 | # CA1711: Identifiers should not have incorrect suffix 240 | dotnet_diagnostic.CA1711.severity = silent 241 | 242 | # CA1727: Use PascalCase for named placeholders 243 | dotnet_diagnostic.CA1727.severity = warning 244 | 245 | # CA1821: Remove empty Finalizers 246 | dotnet_diagnostic.CA1821.severity = warning 247 | 248 | # CA1848: Use the LoggerMessage delegates 249 | dotnet_diagnostic.CA1848.severity = suggestion 250 | 251 | # CA2213: Disposable fields should be disposed 252 | dotnet_diagnostic.CA2213.severity = warning 253 | 254 | # CA2231: Overload operator equals on overriding value type Equals 255 | dotnet_diagnostic.CA2231.severity = warning 256 | 257 | # CA2283: Named placeholders should not be numeric values 258 | dotnet_diagnostic.CA2253.severity = warning 259 | 260 | #CA5391: Use antiforgery token in ASP.NET Core MVC controllers - Duplicate of SCS0016 261 | dotnet_diagnostic.CA5391.severity = none 262 | 263 | # CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' 264 | dotnet_diagnostic.CS1591.severity = silent 265 | 266 | # S100: Methods and properties should be named in PascalCase 267 | dotnet_diagnostic.S100.severity = warning 268 | 269 | # S109: Magic numbers should not be used 270 | dotnet_diagnostic.S109.severity = silent 271 | 272 | # S121: Control structures should use curly braces 273 | dotnet_diagnostic.S121.severity = suggestion 274 | 275 | # S1128: Unused "using" should be removed 276 | dotnet_diagnostic.S1128.severity = warning 277 | 278 | # S1135: Track uses of "TODO" tags 279 | dotnet_diagnostic.S1135.severity = suggestion 280 | 281 | # S1450: Private fields only used as local variables in methods should become local variables 282 | dotnet_diagnostic.S1450.severity = none 283 | 284 | # S1659: Multiple variables should not be declared on the same line 285 | dotnet_diagnostic.S1659.severity = warning 286 | 287 | # S1858: "ToString()" calls should not be redundant 288 | dotnet_diagnostic.S1858.severity = warning 289 | 290 | # S2156: "sealed" classes should not have "protected" members 291 | dotnet_diagnostic.S2156.severity = warning 292 | 293 | # S2357: Fields should be private 294 | dotnet_diagnostic.S2357.severity = warning 295 | 296 | # S2931: Classes with "IDisposable" members should implement "IDisposable" 297 | dotnet_diagnostic.S2931.severity = warning 298 | 299 | # S3216: "ConfigureAwait(false)" should be used 300 | dotnet_diagnostic.S3216.severity = suggestion 301 | 302 | # S3267: Loops should be simplified with "LINQ" expressions 303 | dotnet_diagnostic.S3267.severity = suggestion 304 | 305 | # SA0001: XML comment analysis disabled 306 | dotnet_diagnostic.SA0001.severity = none 307 | 308 | # SA1101: Prefix local calls with this 309 | dotnet_diagnostic.SA1101.severity = none 310 | 311 | # SA1120: Comments should contain text 312 | dotnet_diagnostic.SA1120.severity = silent 313 | 314 | # SA1121: Use built-in type alias 315 | dotnet_diagnostic.SA1121.severity = silent 316 | 317 | # SA1200: Using directives should be placed correctly 318 | dotnet_diagnostic.SA1200.severity = none 319 | 320 | # SA1303: Const field names should begin with upper-case letter 321 | dotnet_diagnostic.SA1303.severity = none 322 | 323 | # SA1311: Static readonly fields should begin with upper-case letter 324 | dotnet_diagnostic.SA1311.severity = none 325 | 326 | # SA1600: Elements should be documented 327 | dotnet_diagnostic.SA1600.severity = none 328 | 329 | # SA1601: Partial elements should be documented 330 | dotnet_diagnostic.SA1601.severity = none 331 | 332 | # SA1602: Enumeration items should be documented 333 | dotnet_diagnostic.SA1602.severity = none 334 | 335 | # SA1633: File should have header 336 | dotnet_diagnostic.SA1633.severity = none 337 | 338 | # SCS0005: Weak random number generator 339 | # Justification: Duplicate of CA5394 340 | dotnet_diagnostic.SCS0005.severity = none 341 | 342 | # VSTHRD200: Use "Async" suffix for async methods 343 | dotnet_diagnostic.VSTHRD200.severity = none -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | packages 2 | 3 | # User files 4 | *.suo 5 | *.user 6 | *.sln.docstates 7 | .vs/ 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Rr]elease/ 12 | x64/ 13 | [Bb]in/ 14 | [Oo]bj/ 15 | 16 | # MSTest test Results 17 | [Tt]est[Rr]esult*/ 18 | [Bb]uild[Ll]og.* 19 | 20 | # NCrunch 21 | *.ncrunchsolution 22 | *.ncrunchproject 23 | _NCrunch_WebCompiler 24 | /UpgradeLog.htm 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | - [ ] Add explicit menu option to update Parameters.xml 3 | - [ ] SetParameters.xml intellisense 4 | 5 | Features that have a checkmark are complete and available for 6 | download in the 7 | [CI build](http://vsixgallery.com/extension/6435437e-72fb-4626-9a47-865f185ce258/). 8 | 9 | # Changelog 10 | 11 | These are the changes to each version that has been released 12 | on the official Visual Studio extension gallery. 13 | 14 | ## 4.0 15 | **2023-12-27** 16 | - [x] Visual Studio 2022 support 17 | 18 | ## 3.2 19 | **2020-7-18** 20 | - [x] Fix for Visual Studio 2017 support 21 | 22 | ## 3.1 23 | **2020-5-2** 24 | - [x] Added asynchronous loading support 25 | 26 | ## 3.0 27 | **2018-12-9** 28 | - [x] Visual Studio 2019 support 29 | - [x] Web.config "location" element support 30 | 31 | ## 2.0 32 | **2017-3-5** 33 | - [x] Option to set default parameters to values from web.config or to tokenized values 34 | 35 | ## 1.8 36 | **2017-3-4** 37 | - [x] Generate parameters for appSettings 38 | - [x] Open files in editor window after being generated/updated 39 | 40 | ## 1.7 41 | **2017-2-18** 42 | - [x] Skip generating parameters for StringCollections and any other application settings not serialized as strings 43 | 44 | ## 1.6 45 | **2017-1-10** 46 | - [x] Visual Studio 2017 support 47 | 48 | ## 1.5 49 | **2016-11-5** 50 | - [x] User configurable options for which parameters to generate 51 | 52 | ## 1.4 53 | **2016-10-15** 54 | - [x] Generate parameter for compilation debug attribute 55 | - [x] Generate parameters for mailSettings 56 | - [x] Generate parameters for session state settings 57 | - [x] Bug fix for *Import Missing Parameters* 58 | 59 | ## 1.3 60 | **2016-9-26** 61 | 62 | - [x] Configure project to copy SetParameters.xml files to drop location during build 63 | 64 | 65 | ## 1.2 66 | **2016-09-07** 67 | 68 | - [x] Handling missing connectionStrings node in web.config file 69 | 70 | ## 1.1 71 | 72 | **2016-09-02** 73 | 74 | - [x] Fix for *setParameters* element being wrongly named *parameters* instead when generating SetParameters.xml files 75 | 76 | ## 1.0 77 | 78 | **2016-08-31** 79 | 80 | - [x] Initial release 81 | - [x] Generate Parameters.xml from web.config 82 | - [x] Generate SetParameters.xml files from web.config 83 | - [x] Import missing parameters into SetParameters.xml file 84 | - [x] Nest SetParameters.xml files under Parameters.xml file 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Looking to contribute something? **Here's how you can help.** 4 | 5 | Please take a moment to review this document in order to make the contribution 6 | process easy and effective for everyone involved. 7 | 8 | Following these guidelines helps to communicate that you respect the time of 9 | the developers managing and developing this open source project. In return, 10 | they should reciprocate that respect in addressing your issue or assessing 11 | patches and features. 12 | 13 | 14 | ## Using the issue tracker 15 | 16 | The issue tracker is the preferred channel for [bug reports](#bug-reports), 17 | [features requests](#feature-requests) and 18 | [submitting pull requests](#pull-requests), but please respect the 19 | following restrictions: 20 | 21 | * Please **do not** use the issue tracker for personal support requests. Stack 22 | Overflow is a better place to get help. 23 | 24 | * Please **do not** derail or troll issues. Keep the discussion on topic and 25 | respect the opinions of others. 26 | 27 | * Please **do not** open issues or pull requests which *belongs to* third party 28 | components. 29 | 30 | 31 | ## Bug reports 32 | 33 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 34 | Good bug reports are extremely helpful, so thanks! 35 | 36 | Guidelines for bug reports: 37 | 38 | 1. **Use the GitHub issue search** — check if the issue has already been 39 | reported. 40 | 41 | 2. **Check if the issue has been fixed** — try to reproduce it using the 42 | latest `master` or development branch in the repository. 43 | 44 | 3. **Isolate the problem** — ideally create an 45 | [SSCCE](http://www.sscce.org/) and a live example. 46 | Uploading the project on cloud storage (OneDrive, DropBox, et el.) 47 | or creating a sample GitHub repository is also helpful. 48 | 49 | 50 | A good bug report shouldn't leave others needing to chase you up for more 51 | information. Please try to be as detailed as possible in your report. What is 52 | your environment? What steps will reproduce the issue? What browser(s) and OS 53 | experience the problem? Do other browsers show the bug differently? What 54 | would you expect to be the outcome? All these details will help people to fix 55 | any potential bugs. 56 | 57 | Example: 58 | 59 | > Short and descriptive example bug report title 60 | > 61 | > A summary of the issue and the Visual Studio, browser, OS environments 62 | > in which it occurs. If suitable, include the steps required to reproduce the bug. 63 | > 64 | > 1. This is the first step 65 | > 2. This is the second step 66 | > 3. Further steps, etc. 67 | > 68 | > `` - a link to the project/file uploaded on cloud storage or other publicly accessible medium. 69 | > 70 | > Any other information you want to share that is relevant to the issue being 71 | > reported. This might include the lines of code that you have identified as 72 | > causing the bug, and potential solutions (and your opinions on their 73 | > merits). 74 | 75 | 76 | ## Feature requests 77 | 78 | Feature requests are welcome. But take a moment to find out whether your idea 79 | fits with the scope and aims of the project. It's up to *you* to make a strong 80 | case to convince the project's developers of the merits of this feature. Please 81 | provide as much detail and context as possible. 82 | 83 | 84 | ## Pull requests 85 | 86 | Good pull requests, patches, improvements and new features are a fantastic 87 | help. They should remain focused in scope and avoid containing unrelated 88 | commits. 89 | 90 | **Please ask first** before embarking on any significant pull request (e.g. 91 | implementing features, refactoring code, porting to a different language), 92 | otherwise you risk spending a lot of time working on something that the 93 | project's developers might not want to merge into the project. 94 | 95 | Please adhere to the [coding guidelines](#code-guidelines) used throughout the 96 | project (indentation, accurate comments, etc.) and any other requirements 97 | (such as test coverage). 98 | 99 | Adhering to the following process is the best way to get your work 100 | included in the project: 101 | 102 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 103 | and configure the remotes: 104 | 105 | ```bash 106 | # Clone your fork of the repo into the current directory 107 | git clone https://github.com//.git 108 | # Navigate to the newly cloned directory 109 | cd 110 | # Assign the original repo to a remote called "upstream" 111 | git remote add upstream https://github.com/madskristensen/.git 112 | ``` 113 | 114 | 2. If you cloned a while ago, get the latest changes from upstream: 115 | 116 | ```bash 117 | git checkout master 118 | git pull upstream master 119 | ``` 120 | 121 | 3. Create a new topic branch (off the main project development branch) to 122 | contain your feature, change, or fix: 123 | 124 | ```bash 125 | git checkout -b 126 | ``` 127 | 128 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 129 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 130 | or your code is unlikely be merged into the main project. Use Git's 131 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 132 | feature to tidy up your commits before making them public. Also, prepend name of the feature 133 | to the commit message. For instance: "SCSS: Fixes compiler results for IFileListener.\nFixes `#123`" 134 | 135 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 136 | 137 | ```bash 138 | git pull [--rebase] upstream master 139 | ``` 140 | 141 | 6. Push your topic branch up to your fork: 142 | 143 | ```bash 144 | git push origin 145 | ``` 146 | 147 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 148 | with a clear title and description against the `master` branch. 149 | 150 | 151 | ## Code guidelines 152 | 153 | - Always use proper indentation. 154 | - In Visual Studio under `Tools > Options > Text Editor > C# > Advanced`, make sure 155 | `Place 'System' directives first when sorting usings` option is enabled (checked). 156 | - Before committing, organize usings for each updated C# source file. Either you can 157 | right-click editor and select `Organize Usings > Remove and sort` OR use extension 158 | like [BatchFormat](http://visualstudiogallery.msdn.microsoft.com/a7f75c34-82b4-4357-9c66-c18e32b9393e). 159 | - Before committing, run Code Analysis in `Debug` configuration and follow the guidelines 160 | to fix CA issues. Code Analysis commits can be made separately. 161 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Installed product versions 2 | - Visual Studio: [example 2015 Professional] 3 | - This extension: [example 1.1.21] 4 | 5 | ### Description 6 | Replace this text with a short description 7 | 8 | ### Steps to recreate 9 | 1. Replace this 10 | 2. text with 11 | 3. the steps 12 | 4. to recreate 13 | 14 | ### Current behavior 15 | Explain what it's doing and why it's wrong 16 | 17 | ### Expected behavior 18 | Explain what it should be doing after it's fixed. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Kyle Herzog 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web Deploy Parameters Toolkit 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/pjlmikxue6721vab/branch/master?svg=true)](https://ci.appveyor.com/project/kyleherzog/webdeployparameterstoolkit/branch/master) 4 | 5 | Download this extension from the [VS Marketplace](https://marketplace.visualstudio.com/items?itemName=kherzog.WebDeployParametersToolkit) 6 | or get the [CI build](http://vsixgallery.com/extension/6435437e-72fb-4626-9a47-865f185ce258/). 7 | 8 | --------------------------------------- 9 | 10 | Tools to make it easy to work with parameters in Web Deploy (MSDeploy). 11 | 12 | See the [changelog](CHANGELOG.md) for changes and roadmap. 13 | 14 | ## Features 15 | 16 | - Generate Parameters.xml from Web.Config 17 | - Generate SetParameters[Environment].xml 18 | - Import missing parameters into SetParameters.xml files 19 | - Nest SetParameters.xml files under Parameters.xml 20 | 21 | 22 | ### Generate Parameters.xml from Web.Config 23 | Select the web.config in solution explorer and right-click. 24 | 25 | ![Web Config Context Menu](art/WebConfigContextMenu.png) 26 | 27 | Then select *Generate Parameters.xml* to have a Parameters.xml file automatically generated from settings in the web.config file. If a Parameters.xml file already exists in the project, missing parameters will be merged into the existing Parameters.xml file. 28 | 29 | To control which settings are automatically generated, adjust the settings in the Visual Studio Tools > Options dialog. 30 | ![Options Dialog](art/OptionsDialog.png) 31 | 32 | Starting in version 2.0, the options dialog also allows the user to select how the default values are generated in the Parametrs.xml file. The default style is clone, which will read the value from the web.config file and copy it into the default value attribute. The other option is tokenize. Tokenize will generate the default value in the format of \_\_PROPERTYNAME__. 33 | 34 | ### Generate SetParameters[Environment].xml 35 | Select the Parameters.xml file in solution explorer and right-click. 36 | 37 | ![Parameters XML Context Menu](art/ParametersXmlContextMenu.png) 38 | 39 | Then select *Generate SetParameters.xml*. 40 | 41 | A dialog will then give you the option to add an environment name to the new SetParameters.xml file to be generated. 42 | 43 | ![Set Parameters Name Dialog](art/SetParametersNameDialog.png) 44 | 45 | Either enter an environment name or leave it blank and then click *Create*. Then the new SetPameters.xml file is created. 46 | 47 | ### Import Missing Parameters into SetParameters.xml files 48 | If parameters have been added to the Parameters.xml file, the new parameters can be automatically added to a SetParameters.xml file. Just select the SetParameters.xml file in solution explorer and right-click. 49 | 50 | ![Set Parameters Import Menu](art/SetParametersImportMenu.png) 51 | 52 | Then select *Import Missing Parameters* to have any missing parameters automatically added to the selected file. 53 | 54 | ### Nest SetParameters.xml files under Parameters.xml 55 | SetParameters*.xml files will automatically be nested under the Parameters.xml file. 56 | 57 | To nest a SetParameters*.xml file that was created prior to installing this extension. Select the SetParameters*.xml file in solution explorer and right-click. 58 | 59 | ![Set Parameters Nest Menu](art/SetParametersNestMenu.png) 60 | 61 | Then select *Nest Under Parameters.xml* to have the selected file nested under Parameters.xml. 62 | 63 | ### Parameterization Build Action Support 64 | When generating or nesting SetParameters*.xml files, the build action on those files will be set to *Parameterization*. 65 | 66 | This setting will result in these files being copied to the package location and excluded from the package itself. 67 | 68 | 69 | ## Contribute 70 | For cloning and building this project yourself, make sure 71 | to install the 72 | [Extensibility Tools](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.ExtensibilityTools) 73 | extension for Visual Studio which enables some features 74 | used by this project. 75 | 76 | ## License 77 | [Apache 2.0](LICENSE) -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/ParametersXmlReaderTests/ReadShould.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using WebDeployParametersToolkit.Utilities; 5 | 6 | namespace WebDeployParametersToolkit.Tests.ParametersXmlReaderTests 7 | { 8 | [TestClass] 9 | public class ReadShould 10 | { 11 | private const string defaultProjectName = "MyTestProject"; 12 | 13 | [TestMethod] 14 | public void ReturnExpectedValuesWhenUsingEmptySettings() 15 | { 16 | var expected = GetBasicParameters(false); 17 | var reader = new ParametersXmlReader(Properties.Resources.BasicParameters, defaultProjectName, Properties.Resources.EmptySettings); 18 | var parameters = reader.Read(); 19 | parameters.AssertHasSameItems(expected); 20 | } 21 | 22 | [TestMethod] 23 | public void ReturnExpectedValuesWhenUsingSimpleSettings() 24 | { 25 | var expected = GetBasicParameters(true); 26 | var reader = new ParametersXmlReader(Properties.Resources.BasicParameters, defaultProjectName, Properties.Resources.SimpleSettings); 27 | var parameters = reader.Read(); 28 | parameters.AssertHasSameItems(expected); 29 | } 30 | 31 | [TestMethod] 32 | [ExpectedException(typeof(FileFormatException))] 33 | public void ThrowExceptionIfRootElementNotParameters() 34 | { 35 | var reader = new ParametersXmlReader("", defaultProjectName, null); 36 | reader.Read(); 37 | } 38 | 39 | private static ICollection GetAutoParameters() 40 | { 41 | var results = new List 42 | { 43 | new WebDeployParameter 44 | { 45 | Name = "FirstConnectionString-Web.config Connection String", 46 | DefaultValue = "server=localhost;database=FirstDb;uid=myUser;password=myPass;", 47 | Description = "FirstConnectionString Connection String used in web.config by the application to access the database.", 48 | Entries = new List 49 | { 50 | new WebDeployParameterEntry() 51 | { 52 | Kind = "XmlFile", 53 | Match = "/configuration/connectionStrings/add[@name='FirstConnectionString']/@connectionString", 54 | Scope = @"\\web.config$", 55 | }, 56 | }, 57 | }, 58 | new WebDeployParameter 59 | { 60 | Name = "SecondConnectionString-Web.config Connection String", 61 | DefaultValue = "server=localhost;database=SecondDb;uid=myUser;password=myPass;", 62 | Description = "SecondConnectionString Connection String used in web.config by the application to access the database.", 63 | Entries = new List 64 | { 65 | new WebDeployParameterEntry 66 | { 67 | Kind = "XmlFile", 68 | Match = "/configuration/connectionStrings/add[@name='SecondConnectionString']/@connectionString", 69 | Scope = @"\\web.config$", 70 | }, 71 | }, 72 | }, 73 | }; 74 | 75 | return results; 76 | } 77 | 78 | private static ICollection GetBasicParameters(bool includeAutoConnectionParameters) 79 | { 80 | var results = new List 81 | { 82 | new WebDeployParameter 83 | { 84 | Name = "FirstParameter", 85 | DefaultValue = "FirstValue", 86 | Description = "Description of FirstParameter", 87 | Entries = new List 88 | { 89 | new WebDeployParameterEntry 90 | { 91 | Kind = "XmlFile", 92 | Match = "/configuration/appSettings/add[@key='FirstAppSetting']/@value", 93 | Scope = @"\\web.config$", 94 | }, 95 | }, 96 | }, 97 | new WebDeployParameter 98 | { 99 | Name = "SecondParameter", 100 | DefaultValue = "SecondValue", 101 | Description = "Description of SecondParameter", 102 | Entries = new List 103 | { 104 | new WebDeployParameterEntry 105 | { 106 | Kind = "XmlFile", 107 | Match = "/configuration/appSettings/add[@key='SecondAppSetting']/@value", 108 | Scope = @"\\web.config$", 109 | }, 110 | }, 111 | }, 112 | 113 | new WebDeployParameter() 114 | { 115 | Name = "IIS Web Application Name", 116 | DefaultValue = defaultProjectName, 117 | }, 118 | }; 119 | 120 | if (includeAutoConnectionParameters) 121 | { 122 | results.AddRange(GetAutoParameters()); 123 | } 124 | 125 | return results; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("WebDeployParametersToolkit.Tests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("WebDeployParametersToolkit.Tests")] 10 | [assembly: AssemblyCopyright("Copyright © 2017")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | [assembly: ComVisible(false)] 14 | [assembly: Guid("dddc2cb3-cd23-4167-886c-e4063cbac3cd")] 15 | 16 | // [assembly: AssemblyVersion("1.0.*")] 17 | [assembly: AssemblyVersion("1.0.0.0")] 18 | [assembly: AssemblyFileVersion("1.0.0.0")] 19 | [assembly: NeutralResourcesLanguage("en-US")] 20 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WebDeployParametersToolkit.Tests.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WebDeployParametersToolkit.Tests.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> 65 | ///<parameters> 66 | /// <parameter defaultvalue="FirstValue" description="Description of FirstParameter" name="FirstParameter"> 67 | /// <parameterentry kind="XmlFile" match="/configuration/appSettings/add[@key='FirstAppSetting']/@value" scope="\\web.config$" /> 68 | /// </parameter> 69 | /// <parameter defaultvalue="SecondValue" description="Description of SecondParameter" name="SecondParameter"> 70 | /// <parameterentry kind="XmlFile" match="/configuration/appSettings/add[@key='SecondAppSetting']/@v [rest of string was truncated]";. 71 | /// 72 | internal static string BasicParameters { 73 | get { 74 | return ResourceManager.GetString("BasicParameters", resourceCulture); 75 | } 76 | } 77 | 78 | /// 79 | /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> 80 | ///<configuration> 81 | /// <configSections> 82 | /// </configSections> 83 | /// <system.web> 84 | /// 85 | /// </system.web> 86 | ///</configuration>. 87 | /// 88 | internal static string EmptySettings { 89 | get { 90 | return ResourceManager.GetString("EmptySettings", resourceCulture); 91 | } 92 | } 93 | 94 | /// 95 | /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> 96 | ///<configuration> 97 | /// <configSections> 98 | /// <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 99 | /// <section name="TestApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> 100 | /// </sectionGroup> 101 | /// </configSections> 102 | /// <location [rest of string was truncated]";. 103 | /// 104 | internal static string LocationSimpleSettings { 105 | get { 106 | return ResourceManager.GetString("LocationSimpleSettings", resourceCulture); 107 | } 108 | } 109 | 110 | /// 111 | /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> 112 | ///<configuration> 113 | /// <configSections> 114 | /// <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 115 | /// <section name="TestApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> 116 | /// </sectionGroup> 117 | /// </configSections> 118 | /// <appSettin [rest of string was truncated]";. 119 | /// 120 | internal static string SimpleSettings { 121 | get { 122 | return ResourceManager.GetString("SimpleSettings", resourceCulture); 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 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 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\resources\basicparameters.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 123 | 124 | 125 | ..\resources\emptysettings.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 126 | 127 | 128 | ..\resources\simplesettings.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 129 | 130 | 131 | ..\resources\locationsimplesettings.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 132 | 133 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/Resources/BasicParameters.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/Resources/EmptySettings.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/Resources/LocationSimpleSettings.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | String value is here. 16 | 17 | 18 | True 19 | 20 | 21 | 22 | 24 | 1 here 25 | And other here. 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/Resources/SimpleSettings.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | String value is here. 15 | 16 | 17 | True 18 | 19 | 20 | 21 | 23 | 1 here 24 | And other here. 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/WebConfigSample.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Xml; 4 | using WebDeployParametersToolkit.Utilities; 5 | 6 | namespace WebDeployParametersToolkit.Tests 7 | { 8 | internal class WebConfigSample 9 | { 10 | public WebConfigSample(string xml) 11 | { 12 | Document = new XmlDocument { XmlResolver = null }; 13 | 14 | var sreader = new System.IO.StringReader(xml); 15 | using var reader = new XmlTextReader(sreader) { DtdProcessing = DtdProcessing.Prohibit }; 16 | Document.Load(reader); 17 | 18 | ExpectedSettings = new List(); 19 | } 20 | 21 | public XmlDocument Document { get; } 22 | 23 | public IList ExpectedSettings { get; } 24 | 25 | public static WebConfigSample GetEmptySettings() 26 | { 27 | var result = new WebConfigSample(Properties.Resources.EmptySettings); 28 | return result; 29 | } 30 | 31 | public static WebConfigSample GetLocationSimpleSettings() 32 | { 33 | var result = new WebConfigSample(Properties.Resources.LocationSimpleSettings); 34 | return result; 35 | } 36 | 37 | public static WebConfigSample GetSimpleApplicationSettings(string pathRoot, ParametersGenerationStyle style) 38 | { 39 | var result = new WebConfigSample(Properties.Resources.SimpleSettings); 40 | 41 | var appSettingsPathFormat = $"{pathRoot}appSettings/add[@key='{{0}}']/@value"; 42 | result.AddExpectedApplicationSetting("AppSettingsKey", string.Format(CultureInfo.InvariantCulture, appSettingsPathFormat, "AppSettingsKey"), "0123", style); 43 | 44 | var applicationSettingsPathFormat = $"{pathRoot}applicationSettings/TestApp.Properties.Settings/setting[@name='{{0}}']/value/text()"; 45 | result.AddExpectedApplicationSetting("SomeString", string.Format(CultureInfo.InvariantCulture, applicationSettingsPathFormat, "SomeString"), "String value is here.", style); 46 | result.AddExpectedApplicationSetting("SomeBoolean", string.Format(CultureInfo.InvariantCulture, applicationSettingsPathFormat, "SomeBoolean"), "True", style); 47 | 48 | return result; 49 | } 50 | 51 | public static WebConfigSample GetSimpleCompilationDebugSettings(ParametersGenerationStyle style) 52 | { 53 | var result = new WebConfigSample(Properties.Resources.SimpleSettings); 54 | 55 | result.AddExpectedApplicationSetting("Compilation.Debug", "/configuration/system.web/compilation/@debug", "true", style); 56 | return result; 57 | } 58 | 59 | public static WebConfigSample GetSimpleMailSettings(ParametersGenerationStyle style) 60 | { 61 | var result = new WebConfigSample(Properties.Resources.SimpleSettings); 62 | 63 | result.AddExpectedApplicationSetting("Smtp.NetworkHost", "/configuration/system.net/mailSettings/smtp/network/@host", "localhost", style); 64 | result.AddExpectedApplicationSetting("Smtp.DeliveryMethod", "/configuration/system.net/mailSettings/smtp/@deliveryMethod", "Network", style); 65 | 66 | return result; 67 | } 68 | 69 | public static WebConfigSample GetSimpleSessionStateSettings(ParametersGenerationStyle style) 70 | { 71 | var result = new WebConfigSample(Properties.Resources.SimpleSettings); 72 | 73 | result.AddExpectedApplicationSetting("SessionState.Mode", "/configuration/system.web/sessionState/@mode", "SQLServer", style); 74 | result.AddExpectedApplicationSetting("SessionState.ConnectionString", "/configuration/system.web/sessionState/@sqlConnectionString", "Data Source=myserver;Initial catalog=ASPState;User ID=aspsession;Password=passwordhere", style); 75 | 76 | return result; 77 | } 78 | 79 | public void AddExpectedApplicationSetting(string name, string nodePath, string value, ParametersGenerationStyle style) 80 | { 81 | var setting = new WebConfigSetting() { Name = name, NodePath = nodePath }; 82 | if (style == ParametersGenerationStyle.Tokenize) 83 | { 84 | setting.Value = $"__{setting.Name.ToUpperInvariant()}__"; 85 | } 86 | else 87 | { 88 | setting.Value = value; 89 | } 90 | 91 | ExpectedSettings.Add(setting); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/WebConfigSettingsAsserts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using WebDeployParametersToolkit.Utilities; 6 | 7 | namespace WebDeployParametersToolkit.Tests 8 | { 9 | public static class WebConfigSettingsAsserts 10 | { 11 | public static void AssertHasSameItems(this IEnumerable source, WebConfigSetting target) 12 | { 13 | AssertHasSameItems(source, new List() { target }); 14 | } 15 | 16 | public static void AssertHasSameItems(this IEnumerable source, IEnumerable target) 17 | { 18 | if (source == null) 19 | { 20 | throw new ArgumentNullException(nameof(source)); 21 | } 22 | 23 | if (target == null) 24 | { 25 | throw new ArgumentNullException(nameof(target)); 26 | } 27 | 28 | if (source.Count() == target.Count()) 29 | { 30 | foreach (var sourceItem in source) 31 | { 32 | var targetItem = target.FirstOrDefault(t => t.NodePath == sourceItem.NodePath); 33 | if (targetItem == null) 34 | { 35 | throw new AssertFailedException($"A target item with a NodePath of {sourceItem.NodePath} could not be found."); 36 | } 37 | else if (targetItem.Name != sourceItem.Name) 38 | { 39 | throw new AssertFailedException($"The {sourceItem.NodePath} item source name ({sourceItem.Name}) does not match target name ({targetItem.Name})."); 40 | } 41 | else if (targetItem.Value != sourceItem.Value) 42 | { 43 | throw new AssertFailedException($"The {sourceItem.NodePath} item source name ({sourceItem.Value}) does not match target name ({targetItem.Value})."); 44 | } 45 | } 46 | } 47 | else 48 | { 49 | throw new AssertFailedException($"Number of source items({source.Count()}) does not match number of target items({target.Count()})."); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/WebConfigSettingsReaderTests/ReadApplicationSettingsShould.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using WebDeployParametersToolkit.Utilities; 3 | 4 | namespace WebDeployParametersToolkit.Tests.WebConfigSettingsReaderTests 5 | { 6 | [TestClass] 7 | public class ReadApplicationSettingsShould 8 | { 9 | private const string standardSettingsRootPath = "/configuration/"; 10 | private const string nestedSettingsRootPath = "/configuration/location[@path=\"subfolder\"]/"; 11 | 12 | [TestMethod] 13 | public void ReturnAllTokenizedGivenTokenizeStyle() 14 | { 15 | var style = ParametersGenerationStyle.Tokenize; 16 | var simpleSettings = WebConfigSample.GetSimpleApplicationSettings(standardSettingsRootPath, style); 17 | var results = WebConfigSettingsReader.ReadApplicationSettings(simpleSettings.Document, true, true, style); 18 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 19 | } 20 | 21 | [TestMethod] 22 | public void ReturnAllWithValuesGivenCloneStyle() 23 | { 24 | var style = ParametersGenerationStyle.Clone; 25 | var simpleSettings = WebConfigSample.GetSimpleApplicationSettings(standardSettingsRootPath, style); 26 | var results = WebConfigSettingsReader.ReadApplicationSettings(simpleSettings.Document, true, true, style); 27 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 28 | } 29 | 30 | [TestMethod] 31 | public void ReturnTokenizedValuesGivenTokenizeStyleAndSettingsNestedUnderLocationTag() 32 | { 33 | var style = ParametersGenerationStyle.Tokenize; 34 | var simpleSettings = WebConfigSample.GetSimpleApplicationSettings(nestedSettingsRootPath, style); 35 | var locationSimpleSettings = WebConfigSample.GetLocationSimpleSettings(); 36 | var results = WebConfigSettingsReader.ReadApplicationSettings(locationSimpleSettings.Document, true, true, style); 37 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 38 | } 39 | 40 | [TestMethod] 41 | public void ReturnValuesGivenCloneStyleAndSettingsNestedUnderLocationTag() 42 | { 43 | var style = ParametersGenerationStyle.Clone; 44 | var simpleSettings = WebConfigSample.GetSimpleApplicationSettings(nestedSettingsRootPath, style); 45 | var locationSimpleSettings = WebConfigSample.GetLocationSimpleSettings(); 46 | var results = WebConfigSettingsReader.ReadApplicationSettings(locationSimpleSettings.Document, true, true, style); 47 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 48 | } 49 | 50 | [TestMethod] 51 | public void ReturnZeroResultsGivenEmptySettings() 52 | { 53 | var emptySettings = WebConfigSample.GetEmptySettings(); 54 | var results = WebConfigSettingsReader.ReadApplicationSettings(emptySettings.Document, true, true, ParametersGenerationStyle.Tokenize); 55 | emptySettings.ExpectedSettings.AssertHasSameItems(results); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/WebConfigSettingsReaderTests/ReadCompilationDebugSettingsShould.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using WebDeployParametersToolkit.Utilities; 3 | 4 | namespace WebDeployParametersToolkit.Tests.WebConfigSettingsReaderTests 5 | { 6 | [TestClass] 7 | public class ReadCompilationDebugSettingsShould 8 | { 9 | [TestMethod] 10 | public void ReturnNullGivenEmptySettings() 11 | { 12 | var style = ParametersGenerationStyle.Tokenize; 13 | var emptySettings = WebConfigSample.GetEmptySettings(); 14 | var result = WebConfigSettingsReader.ReadCompilationDebugSettings(emptySettings.Document, style); 15 | Assert.IsNull(result); 16 | } 17 | 18 | [TestMethod] 19 | public void ReturnTokenizedResultsGivenTokenizeStyle() 20 | { 21 | var style = ParametersGenerationStyle.Tokenize; 22 | var simpleSettings = WebConfigSample.GetSimpleCompilationDebugSettings(style); 23 | var results = WebConfigSettingsReader.ReadCompilationDebugSettings(simpleSettings.Document, style); 24 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 25 | } 26 | 27 | [TestMethod] 28 | public void ReturnValuesGivenCloneStyle() 29 | { 30 | var style = ParametersGenerationStyle.Clone; 31 | var simpleSettings = WebConfigSample.GetSimpleCompilationDebugSettings(style); 32 | var results = WebConfigSettingsReader.ReadCompilationDebugSettings(simpleSettings.Document, style); 33 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 34 | } 35 | 36 | [TestMethod] 37 | public void ReturnTokenizedValuesGivenTonenizeStyleAndSettingsNestedUnderLocationTag() 38 | { 39 | var style = ParametersGenerationStyle.Tokenize; 40 | var simpleSettings = WebConfigSample.GetSimpleCompilationDebugSettings(style); 41 | var locationSimpleSettings = WebConfigSample.GetLocationSimpleSettings(); 42 | var results = WebConfigSettingsReader.ReadCompilationDebugSettings(locationSimpleSettings.Document, style); 43 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 44 | } 45 | 46 | [TestMethod] 47 | public void ReturnValuesGivenCloneStyleAndSettingsNestedUnderLocationTag() 48 | { 49 | var style = ParametersGenerationStyle.Clone; 50 | var simpleSettings = WebConfigSample.GetSimpleCompilationDebugSettings(style); 51 | var locationSimpleSettings = WebConfigSample.GetLocationSimpleSettings(); 52 | var results = WebConfigSettingsReader.ReadCompilationDebugSettings(locationSimpleSettings.Document, style); 53 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/WebConfigSettingsReaderTests/ReadMailSettingsShould.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using WebDeployParametersToolkit.Utilities; 3 | 4 | namespace WebDeployParametersToolkit.Tests.WebConfigSettingsReaderTests 5 | { 6 | [TestClass] 7 | public class ReadMailSettingsShould 8 | { 9 | [TestMethod] 10 | public void ReturnAllTokenizedGivenTokenizeStyle() 11 | { 12 | var style = ParametersGenerationStyle.Tokenize; 13 | var simpleSettings = WebConfigSample.GetSimpleMailSettings(style); 14 | var results = WebConfigSettingsReader.ReadMailSettings(simpleSettings.Document, style); 15 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 16 | } 17 | 18 | [TestMethod] 19 | public void ReturnAllWithValuesGivenCloneStyle() 20 | { 21 | var style = ParametersGenerationStyle.Clone; 22 | var simpleSettings = WebConfigSample.GetSimpleMailSettings(style); 23 | var results = WebConfigSettingsReader.ReadMailSettings(simpleSettings.Document, style); 24 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 25 | } 26 | 27 | [TestMethod] 28 | public void ReturnZeroResultsGivenEmptySettings() 29 | { 30 | var emptySettings = WebConfigSample.GetEmptySettings(); 31 | var results = WebConfigSettingsReader.ReadMailSettings(emptySettings.Document, ParametersGenerationStyle.Tokenize); 32 | emptySettings.ExpectedSettings.AssertHasSameItems(results); 33 | } 34 | 35 | [TestMethod] 36 | public void ReturnTokenizedValuesGivenTokenizeStyleAndSettingsNestedUnderLocationTag() 37 | { 38 | var style = ParametersGenerationStyle.Tokenize; 39 | var simpleSettings = WebConfigSample.GetSimpleMailSettings(style); 40 | var locationSimpleSettings = WebConfigSample.GetLocationSimpleSettings(); 41 | var results = WebConfigSettingsReader.ReadMailSettings(locationSimpleSettings.Document, style); 42 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 43 | } 44 | 45 | [TestMethod] 46 | public void ReturnValuesGivenCloneStyleAndSettingsNestedUnderLocationTag() 47 | { 48 | var style = ParametersGenerationStyle.Clone; 49 | var simpleSettings = WebConfigSample.GetSimpleMailSettings(style); 50 | var locationSimpleSettings = WebConfigSample.GetLocationSimpleSettings(); 51 | var results = WebConfigSettingsReader.ReadMailSettings(locationSimpleSettings.Document, style); 52 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/WebConfigSettingsReaderTests/ReadSessionStateSettingsShould.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using WebDeployParametersToolkit.Utilities; 3 | 4 | namespace WebDeployParametersToolkit.Tests.WebConfigSettingsReaderTests 5 | { 6 | [TestClass] 7 | public class ReadSessionStateSettingsShould 8 | { 9 | [TestMethod] 10 | public void ReturnTokenizedValuesGivenTokenizeStyle() 11 | { 12 | var style = ParametersGenerationStyle.Tokenize; 13 | var simpleSettings = WebConfigSample.GetSimpleSessionStateSettings(style); 14 | var results = WebConfigSettingsReader.ReadSessionStateSettings(simpleSettings.Document, style); 15 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 16 | } 17 | 18 | [TestMethod] 19 | public void ReturnTokenizedValuesGivenTokenizeStyleAndSettingsNestedUnderLocationTag() 20 | { 21 | var style = ParametersGenerationStyle.Tokenize; 22 | var simpleSettings = WebConfigSample.GetSimpleSessionStateSettings(style); 23 | var locationSimpleSettings = WebConfigSample.GetLocationSimpleSettings(); 24 | var results = WebConfigSettingsReader.ReadSessionStateSettings(locationSimpleSettings.Document, style); 25 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 26 | } 27 | 28 | [TestMethod] 29 | public void ReturnValuesGivenCloneStyleAndSettingsNestedUnderLocatinoTag() 30 | { 31 | var style = ParametersGenerationStyle.Clone; 32 | var simpleSettings = WebConfigSample.GetSimpleSessionStateSettings(style); 33 | var locationSimpleSettings = WebConfigSample.GetLocationSimpleSettings(); 34 | var results = WebConfigSettingsReader.ReadSessionStateSettings(locationSimpleSettings.Document, style); 35 | simpleSettings.ExpectedSettings.AssertHasSameItems(results); 36 | } 37 | 38 | [TestMethod] 39 | public void ReturnZeroResultsGivenEmptySettings() 40 | { 41 | var emptySettings = WebConfigSample.GetEmptySettings(); 42 | var results = WebConfigSettingsReader.ReadSessionStateSettings(emptySettings.Document, ParametersGenerationStyle.Tokenize); 43 | emptySettings.ExpectedSettings.AssertHasSameItems(results); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/WebDeployParameterAsserts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using WebDeployParametersToolkit.Utilities; 6 | 7 | namespace WebDeployParametersToolkit.Tests 8 | { 9 | public static class WebDeployParameterAsserts 10 | { 11 | public static void AssertHasSameItems(this IEnumerable source, IEnumerable target) 12 | { 13 | if (source == null) 14 | { 15 | throw new ArgumentNullException(nameof(source)); 16 | } 17 | 18 | if (target == null) 19 | { 20 | throw new ArgumentNullException(nameof(target)); 21 | } 22 | 23 | if (source.Count() == target.Count()) 24 | { 25 | foreach (var sourceItem in source) 26 | { 27 | var targetItem = target.FirstOrDefault(t => t.Name == sourceItem.Name); 28 | if (targetItem == null) 29 | { 30 | throw new AssertFailedException($"A target item with a {nameof(WebDeployParameter.Name)} of {sourceItem.Name} could not be found."); 31 | } 32 | else if (targetItem.DefaultValue != sourceItem.DefaultValue) 33 | { 34 | throw new AssertFailedException($"Non-matching properties({nameof(WebDeployParameter.DefaultValue)}) on parameter named '{sourceItem.Name}' source({sourceItem.DefaultValue}) vs. target({targetItem.DefaultValue})."); 35 | } 36 | else if (targetItem.Description != sourceItem.Description) 37 | { 38 | throw new AssertFailedException($"Non-matching properties({nameof(WebDeployParameter.Description)}) on parameter named '{sourceItem.Name}' source({sourceItem.Description}) vs. target({targetItem.Description})."); 39 | } 40 | else if ((sourceItem.Entries != null && (targetItem.Entries == null || sourceItem.Entries.Count() != targetItem.Entries.Count())) 41 | || (sourceItem.Entries == null && targetItem.Entries != null)) 42 | { 43 | throw new AssertFailedException($"Non-matching {nameof(WebDeployParameter.Entries)} count on parameter named '{sourceItem.Name}' source({sourceItem.Entries?.Count()}) vs. target({targetItem.Entries?.Count()})."); 44 | } 45 | else if (sourceItem.Entries != null) 46 | { 47 | foreach (var sourceEntry in sourceItem.Entries) 48 | { 49 | if (!targetItem.Entries.Any(e => e.Kind == sourceEntry.Kind && e.Match == sourceEntry.Match && e.Scope == sourceEntry.Scope)) 50 | { 51 | throw new AssertFailedException($"Unable to find matching entry on parameter named '{sourceItem.Name}' (Match = '{sourceEntry.Match}', Kind = '{sourceEntry.Kind}', Scope = '{sourceEntry.Scope}')."); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | else 58 | { 59 | throw new AssertFailedException($"Number of source items({source.Count()}) does not match number of target items({target.Count()})."); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/WebDeployParametersToolkit.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | 8.0 7 | AnyCPU 8 | {DDDC2CB3-CD23-4167-886C-E4063CBAC3CD} 9 | Library 10 | Properties 11 | WebDeployParametersToolkit.Tests 12 | WebDeployParametersToolkit.Tests 13 | v4.7.2 14 | 512 15 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | 15.0 17 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 18 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 19 | False 20 | UnitTest 21 | 22 | 23 | 24 | 25 | 26 | true 27 | full 28 | false 29 | bin\Debug\ 30 | DEBUG;TRACE 31 | prompt 32 | 4 33 | WebDeployParametersToolkit.Tests.ruleset 34 | bin\Debug\WebDeployParametersToolkit.Tests.xml 35 | CS1591 36 | 37 | 38 | pdbonly 39 | true 40 | bin\Release\ 41 | TRACE 42 | prompt 43 | 4 44 | WebDeployParametersToolkit.Tests.ruleset 45 | 46 | 47 | 48 | ..\packages\MSTest.TestFramework.3.1.1\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll 49 | 50 | 51 | ..\packages\MSTest.TestFramework.3.1.1\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | True 62 | True 63 | Resources.resx 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Designer 79 | 80 | 81 | Designer 82 | 83 | 84 | Designer 85 | 86 | 87 | Designer 88 | 89 | 90 | 91 | 92 | ResXFileCodeGenerator 93 | Resources.Designer.cs 94 | Designer 95 | 96 | 97 | 98 | 99 | {05907860-bd17-4400-9b35-6bb7456373d9} 100 | WebDeployParametersToolkit 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/WebDeployParametersToolkit.Tests.ruleset: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/XmlNodeExtensionsTests/GetFullPathShould.cs: -------------------------------------------------------------------------------- 1 | using System.Xml; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using WebDeployParametersToolkit.Extensions; 4 | 5 | namespace WebDeployParametersToolkit.Tests.XmlNodeExtensionsTests 6 | { 7 | [TestClass] 8 | public class GetFullPathShould 9 | { 10 | private const string xml = ""; 11 | 12 | [TestMethod] 13 | public void ReturnTheFullPathGivenNoAttributeIdentifiersSpecified() 14 | { 15 | var document = new XmlDocument() { XmlResolver = null }; 16 | document.SafeLoadXml(xml); 17 | 18 | var node = document.SelectSingleNode("/root//level2a"); 19 | var nodePath = node.GetFullPath(); 20 | 21 | var expectedResult = "/root/level1a/level2a"; 22 | Assert.AreEqual(expectedResult, nodePath); 23 | } 24 | 25 | [TestMethod] 26 | public void ReturnTheFullPathIncludingAttributeGivenAttributeIdentifierSpecified() 27 | { 28 | var document = new XmlDocument() { XmlResolver = null }; 29 | document.SafeLoadXml(xml); 30 | 31 | var node = document.SelectSingleNode("/root//level2a"); 32 | var nodePath = node.GetFullPath(new string[] { "path" }); 33 | 34 | var expectedResult = "/root/level1a[@path=\"folderNameHere\"]/level2a"; 35 | Assert.AreEqual(expectedResult, nodePath); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34330.188 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C93BE754-731F-424C-82AC-B4C4BE19C901}" 7 | ProjectSection(SolutionItems) = preProject 8 | .editorconfig = .editorconfig 9 | appveyor.yml = appveyor.yml 10 | CHANGELOG.md = CHANGELOG.md 11 | publishManifest.json = publishManifest.json 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebDeployParametersToolkit", "WebDeployParametersToolkit\WebDeployParametersToolkit.csproj", "{05907860-BD17-4400-9B35-6BB7456373D9}" 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebDeployParametersToolkit.Tests", "WebDeployParametersToolkit.Tests\WebDeployParametersToolkit.Tests.csproj", "{DDDC2CB3-CD23-4167-886C-E4063CBAC3CD}" 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Release|Any CPU = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {05907860-BD17-4400-9B35-6BB7456373D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {05907860-BD17-4400-9B35-6BB7456373D9}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {05907860-BD17-4400-9B35-6BB7456373D9}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {05907860-BD17-4400-9B35-6BB7456373D9}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {DDDC2CB3-CD23-4167-886C-E4063CBAC3CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {DDDC2CB3-CD23-4167-886C-E4063CBAC3CD}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {DDDC2CB3-CD23-4167-886C-E4063CBAC3CD}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {DDDC2CB3-CD23-4167-886C-E4063CBAC3CD}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | GlobalSection(SolutionProperties) = preSolution 35 | HideSolutionNode = FALSE 36 | EndGlobalSection 37 | GlobalSection(ExtensibilityGlobals) = postSolution 38 | SolutionGuid = {5D6F3A30-863F-4D25-B421-9C0499D51E64} 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit/Commands/AddParameterizationTargetCommand.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Company. All rights reserved. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using System; 8 | using System.ComponentModel.Design; 9 | using System.IO; 10 | using EnvDTE; 11 | using Microsoft.VisualStudio.Shell; 12 | using WebDeployParametersToolkit.Extensions; 13 | 14 | namespace WebDeployParametersToolkit 15 | { 16 | /// 17 | /// Command handler 18 | /// 19 | internal sealed class AddParameterizationTargetCommand 20 | { 21 | /// 22 | /// Command ID. 23 | /// 24 | public const int CommandId = 260; 25 | 26 | /// 27 | /// Command menu group (command set GUID). 28 | /// 29 | public static readonly Guid CommandSet = new Guid("b451bf5d-476a-43b7-8a00-11671601fdaa"); 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// Adds our command handlers for menu (commands must exist in the command table file) 34 | /// 35 | private AddParameterizationTargetCommand(OleMenuCommandService commandService) 36 | { 37 | if (commandService != null) 38 | { 39 | var menuCommandID = new CommandID(CommandSet, CommandId); 40 | var menuItem = new OleMenuCommand(MenuItemCallback, menuCommandID); 41 | menuItem.BeforeQueryStatus += MenuItem_BeforeQueryStatus; 42 | commandService.AddCommand(menuItem); 43 | } 44 | } 45 | 46 | /// 47 | /// Gets the instance of the command. 48 | /// 49 | public static AddParameterizationTargetCommand Instance 50 | { 51 | get; 52 | private set; 53 | } 54 | 55 | /// 56 | /// Initializes the singleton instance of the command. 57 | /// 58 | /// Owner package, not null. 59 | public static async System.Threading.Tasks.Task InitializeAsync(AsyncPackage package) 60 | { 61 | // Switch to the main thread - the call to AddCommand in SampleCommand's constructor requires 62 | // the UI thread. 63 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 64 | 65 | var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(true) as OleMenuCommandService; 66 | Instance = new AddParameterizationTargetCommand(commandService); 67 | } 68 | 69 | private static bool NeedsInitialization() 70 | { 71 | ThreadHelper.ThrowIfNotOnUIThread(); 72 | var filePath = SolutionExplorerExtensions.SelectedItemPath; 73 | 74 | if (!string.IsNullOrEmpty(filePath) && "Parameters.xml".Equals(Path.GetFileName(SolutionExplorerExtensions.SelectedItemPath), StringComparison.OrdinalIgnoreCase)) 75 | { 76 | var projectFullName = VSPackage.DteInstance.Solution.FindProjectItem(filePath).ContainingProject.FullName; 77 | var project = new ParameterizationProject(projectFullName); 78 | return project.NeedsInitialization; 79 | } 80 | 81 | return false; 82 | } 83 | 84 | private void MenuItem_BeforeQueryStatus(object sender, EventArgs e) 85 | { 86 | var menuItem = (OleMenuCommand)sender; 87 | menuItem.Visible = false; 88 | 89 | SolutionExplorerExtensions.LoadSelectedItemPath(); 90 | ThreadHelper.ThrowIfNotOnUIThread(); 91 | if (NeedsInitialization()) 92 | { 93 | menuItem.Visible = true; 94 | } 95 | } 96 | 97 | /// 98 | /// This function is the callback used to execute the command when the menu item is clicked. 99 | /// See the constructor to see how the menu item is associated with this function using 100 | /// OleMenuCommandService service and MenuCommand class. 101 | /// 102 | /// Event sender. 103 | /// Event args. 104 | private void MenuItemCallback(object sender, EventArgs e) 105 | { 106 | ThreadHelper.ThrowIfNotOnUIThread(); 107 | var filePath = SolutionExplorerExtensions.SelectedItemPath; 108 | 109 | if (!string.IsNullOrEmpty(filePath) && "Parameters.xml".Equals(Path.GetFileName(SolutionExplorerExtensions.SelectedItemPath), StringComparison.OrdinalIgnoreCase)) 110 | { 111 | var projectFullName = VSPackage.DteInstance.Solution.FindProjectItem(filePath).ContainingProject.FullName; 112 | var project = new ParameterizationProject(projectFullName); 113 | project.Initialize(); 114 | 115 | var parent = VSPackage.DteInstance.Solution.FindProjectItem(filePath); 116 | 117 | foreach (var item in parent.ProjectItems) 118 | { 119 | if (item is ProjectItem child) 120 | { 121 | child.Properties.Item("ItemType").Value = "Parameterization"; 122 | } 123 | } 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit/Commands/ApplyMissingParametersCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Design; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Xml; 6 | using EnvDTE; 7 | using Microsoft.VisualStudio.Shell; 8 | using Microsoft.VisualStudio.Shell.Interop; 9 | using WebDeployParametersToolkit.Extensions; 10 | using WebDeployParametersToolkit.Utilities; 11 | 12 | namespace WebDeployParametersToolkit 13 | { 14 | /// 15 | /// Command handler 16 | /// 17 | internal sealed class ApplyMissingParametersCommand 18 | { 19 | /// 20 | /// Command ID. 21 | /// 22 | public const int CommandId = 258; 23 | 24 | /// 25 | /// Command menu group (command set GUID). 26 | /// 27 | public static readonly Guid CommandSet = new Guid("b451bf5d-476a-43b7-8a00-11671601fdaa"); 28 | 29 | /// 30 | /// VS Package that provides this command, not null. 31 | /// 32 | private readonly AsyncPackage package; 33 | 34 | /// 35 | /// Initializes a new instance of the class. 36 | /// Adds our command handlers for menu (commands must exist in the command table file) 37 | /// 38 | /// Owner package, not null. 39 | private ApplyMissingParametersCommand(AsyncPackage package, OleMenuCommandService commandService) 40 | { 41 | this.package = package ?? throw new ArgumentNullException(nameof(package)); 42 | 43 | if (commandService != null) 44 | { 45 | var menuCommandID = new CommandID(CommandSet, CommandId); 46 | var menuItem = new OleMenuCommand(MenuItemCallback, menuCommandID); 47 | menuItem.BeforeQueryStatus += MenuItem_BeforeQueryStatus; 48 | commandService.AddCommand(menuItem); 49 | } 50 | } 51 | 52 | /// 53 | /// Gets the instance of the command. 54 | /// 55 | public static ApplyMissingParametersCommand Instance 56 | { 57 | get; 58 | private set; 59 | } 60 | 61 | private ProjectItem ParametersXmlItem { get; set; } 62 | 63 | /// 64 | /// Gets the service provider from the owner package. 65 | /// 66 | private IServiceProvider ServiceProvider 67 | { 68 | get 69 | { 70 | return package; 71 | } 72 | } 73 | 74 | /// 75 | /// Initializes the singleton instance of the command. 76 | /// 77 | /// Owner package, not null. 78 | public static async System.Threading.Tasks.Task InitializeAsync(AsyncPackage package) 79 | { 80 | // Switch to the main thread - the call to AddCommand in SampleCommand's constructor requires 81 | // the UI thread. 82 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 83 | 84 | var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(true) as OleMenuCommandService; 85 | Instance = new ApplyMissingParametersCommand(package, commandService); 86 | } 87 | 88 | private bool CanGenerateApplyMissingParameters() 89 | { 90 | ThreadHelper.ThrowIfNotOnUIThread(); 91 | var item = SolutionExplorerExtensions.SelectedItemPath; 92 | if (string.IsNullOrEmpty(item)) 93 | { 94 | return false; 95 | } 96 | 97 | var fileName = Path.GetFileName(item); 98 | var extension = Path.GetExtension(item); 99 | var directory = Path.GetDirectoryName(item); 100 | ParametersXmlItem = VSPackage.DteInstance.Solution.FindProjectItem(Path.Combine(directory, "Parameters.xml")); 101 | 102 | return fileName.StartsWith("setparameters", StringComparison.OrdinalIgnoreCase) && extension.Equals(".xml", StringComparison.OrdinalIgnoreCase) && ParametersXmlItem != null; 103 | } 104 | 105 | private void MenuItem_BeforeQueryStatus(object sender, EventArgs e) 106 | { 107 | var menuItem = (OleMenuCommand)sender; 108 | menuItem.Visible = false; 109 | 110 | SolutionExplorerExtensions.LoadSelectedItemPath(); 111 | 112 | ThreadHelper.ThrowIfNotOnUIThread(); 113 | 114 | if (CanGenerateApplyMissingParameters()) 115 | { 116 | menuItem.Visible = true; 117 | } 118 | } 119 | 120 | /// 121 | /// This function is the callback used to execute the command when the menu item is clicked. 122 | /// See the constructor to see how the menu item is associated with this function using 123 | /// OleMenuCommandService service and MenuCommand class. 124 | /// 125 | /// Event sender. 126 | /// Event args. 127 | private void MenuItemCallback(object sender, EventArgs e) 128 | { 129 | ThreadHelper.ThrowIfNotOnUIThread(); 130 | try 131 | { 132 | var fileName = SolutionExplorerExtensions.SelectedItemPath; 133 | 134 | var projectName = VSPackage.DteInstance.Solution.FindProjectItem(fileName).ContainingProject.Name; 135 | var reader = new ParametersXmlReader(ParametersXmlItem.FileNames[0], projectName); 136 | var allParameters = reader.Read(); 137 | var existingParameters = SetParametersXmlReader.GetParameters(fileName); 138 | 139 | var missingParameters = allParameters.Where(p => !existingParameters.Keys.Contains(p.Name)).ToList(); 140 | 141 | if (missingParameters.Count == 0) 142 | { 143 | ShowMessage("Parameters Already Updated", "No missing parameters found."); 144 | } 145 | else 146 | { 147 | var document = new XmlDocument { XmlResolver = null }; 148 | var text = File.ReadAllText(SolutionExplorerExtensions.SelectedItemPath); 149 | var sreader = new StringReader(text); 150 | var xmlReader = new XmlTextReader(sreader) { DtdProcessing = DtdProcessing.Prohibit }; 151 | document.Load(xmlReader); 152 | 153 | var parametersNode = document.SelectSingleNode("/parameters"); 154 | 155 | foreach (var parameter in missingParameters) 156 | { 157 | var node = document.CreateElement("setParameter"); 158 | node.SetAttribute("name", parameter.Name); 159 | node.SetAttribute("value", parameter.DefaultValue); 160 | parametersNode.AppendChild(node); 161 | } 162 | 163 | document.Save(fileName); 164 | VSPackage.DteInstance.Solution.FindProjectItem(fileName).Open().Visible = true; 165 | } 166 | } 167 | catch (Exception ex) 168 | { 169 | ShowMessage("Parameters Error", ex.Message); 170 | } 171 | } 172 | 173 | private void ShowMessage(string title, string message) 174 | { 175 | VsShellUtilities.ShowMessageBox( 176 | ServiceProvider, 177 | message, 178 | title, 179 | OLEMSGICON.OLEMSGICON_INFO, 180 | OLEMSGBUTTON.OLEMSGBUTTON_OK, 181 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit/Commands/GenerateParametersCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Design; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Xml; 8 | using Microsoft.VisualStudio.Shell; 9 | using Microsoft.VisualStudio.Shell.Interop; 10 | using Microsoft.VisualStudio.Threading; 11 | using WebDeployParametersToolkit.Extensions; 12 | using WebDeployParametersToolkit.Utilities; 13 | 14 | namespace WebDeployParametersToolkit 15 | { 16 | /// 17 | /// Command handler 18 | /// 19 | internal sealed class GenerateParametersCommand 20 | { 21 | /// 22 | /// Command ID. 23 | /// 24 | public const int CommandId = 259; 25 | 26 | /// 27 | /// Command menu group (command set GUID). 28 | /// 29 | public static readonly Guid CommandSet = new Guid("b451bf5d-476a-43b7-8a00-11671601fdaa"); 30 | 31 | /// 32 | /// VS Package that provides this command, not null. 33 | /// 34 | private readonly AsyncPackage package; 35 | 36 | /// 37 | /// Initializes a new instance of the class. 38 | /// Adds our command handlers for menu (commands must exist in the command table file) 39 | /// 40 | /// Owner package, not null. 41 | private GenerateParametersCommand(AsyncPackage package, OleMenuCommandService commandService) 42 | { 43 | this.package = package ?? throw new ArgumentNullException(nameof(package)); 44 | 45 | if (commandService != null) 46 | { 47 | var menuCommandID = new CommandID(CommandSet, CommandId); 48 | var menuItem = new OleMenuCommand(MenuItemCallback, menuCommandID); 49 | menuItem.BeforeQueryStatus += MenuItem_BeforeQueryStatus; 50 | 51 | commandService.AddCommand(menuItem); 52 | } 53 | } 54 | 55 | /// 56 | /// Gets the instance of the command. 57 | /// 58 | public static GenerateParametersCommand Instance 59 | { 60 | get; 61 | private set; 62 | } 63 | 64 | /// 65 | /// Gets the service provider from the owner package. 66 | /// 67 | private IServiceProvider ServiceProvider 68 | { 69 | get 70 | { 71 | return package; 72 | } 73 | } 74 | 75 | /// 76 | /// Initializes the singleton instance of the command. 77 | /// 78 | /// Owner package, not null. 79 | public static async System.Threading.Tasks.Task InitializeAsync(AsyncPackage package) 80 | { 81 | // Switch to the main thread - the call to AddCommand in SampleCommand's constructor requires 82 | // the UI thread. 83 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 84 | 85 | var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(true) as OleMenuCommandService; 86 | Instance = new GenerateParametersCommand(package, commandService); 87 | } 88 | 89 | private static bool CanGenerateParameters() 90 | { 91 | var filename = Path.GetFileName(SolutionExplorerExtensions.SelectedItemPath); 92 | return filename.Equals("web.config", StringComparison.OrdinalIgnoreCase); 93 | } 94 | 95 | private static void WriteParameters(IEnumerable settings, XmlWriter writer) 96 | { 97 | foreach (var setting in settings) 98 | { 99 | writer.WriteStartElement("parameter"); 100 | writer.WriteAttributeString("name", setting.Name); 101 | writer.WriteAttributeString("defaultvalue", setting.Value); 102 | writer.WriteAttributeString("description", $"{setting.Name} description."); 103 | writer.WriteAttributeString("tags", string.Empty); 104 | 105 | writer.WriteStartElement("parameterentry"); 106 | writer.WriteAttributeString("kind", "XmlFile"); 107 | writer.WriteAttributeString("match", setting.NodePath); 108 | writer.WriteAttributeString("scope", @"\\web.config$"); 109 | writer.WriteEndElement(); 110 | 111 | writer.WriteEndElement(); 112 | } 113 | } 114 | 115 | private void CreateParametersXml(IEnumerable settings, string fileName) 116 | { 117 | var writer = XmlWriter.Create(fileName, new XmlWriterSettings() { Indent = true }); 118 | 119 | try 120 | { 121 | writer.WriteStartDocument(); 122 | writer.WriteStartElement("parameters"); 123 | WriteParameters(settings, writer); 124 | writer.WriteEndDocument(); 125 | } 126 | finally 127 | { 128 | writer.Close(); 129 | } 130 | } 131 | 132 | private void EnsureUniqueSettingsNames(List settings, ICollection usedNames) 133 | { 134 | foreach (var setting in settings) 135 | { 136 | if (usedNames.Any(n => n == setting.Name)) 137 | { 138 | setting.Name = GetUniqueName(setting.Name, usedNames, 2); 139 | } 140 | 141 | usedNames.Add(setting.Name); 142 | } 143 | } 144 | 145 | private async System.Threading.Tasks.Task GenerateFileAsync(string fileName) 146 | { 147 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 148 | var settings = GetWebConfigSettings(fileName); 149 | 150 | var folder = Path.GetDirectoryName(fileName); 151 | var targetName = Path.Combine(folder, "Parameters.xml"); 152 | if (File.Exists(targetName)) 153 | { 154 | if (VsShellUtilities.PromptYesNo( 155 | "Merge missing settings into existing Parameters.xml file?", 156 | "Update File?", 157 | OLEMSGICON.OLEMSGICON_QUERY, 158 | VSPackage.Shell)) 159 | { 160 | await UpdateParametersXmlAsync(settings, targetName).ConfigureAwait(true); 161 | } 162 | } 163 | else 164 | { 165 | CreateParametersXml(settings, targetName); 166 | 167 | var project = VSPackage.DteInstance.Solution.FindProjectItem(fileName).ContainingProject; 168 | project.ProjectItems.AddFromFile(targetName); 169 | } 170 | 171 | VSPackage.DteInstance.Solution.FindProjectItem(targetName).Open().Visible = true; 172 | } 173 | 174 | private string GetUniqueName(string baseName, IEnumerable currentNames, int nextIndex) 175 | { 176 | if (currentNames.Any(n => n == $"{baseName}{nextIndex}")) 177 | { 178 | return GetUniqueName(baseName, currentNames, nextIndex + 1); 179 | } 180 | else 181 | { 182 | return $"{baseName}{nextIndex}"; 183 | } 184 | } 185 | 186 | private IEnumerable GetWebConfigSettings(string fileName) 187 | { 188 | var reader = new WebConfigSettingsReader(fileName) 189 | { 190 | IncludeApplicationSettings = VSPackage.OptionsPage.IncludeApplicationSettings, 191 | IncludeAppSettings = VSPackage.OptionsPage.IncludeAppSettings, 192 | IncludeCompilationDebug = VSPackage.OptionsPage.IncludeCompilationDebug, 193 | IncludeMailSettings = VSPackage.OptionsPage.IncludeMailSettings, 194 | IncludeSessionStateSettings = VSPackage.OptionsPage.IncludeSessionStateSettings, 195 | ValuesStyle = VSPackage.OptionsPage.DefaultValueStyle 196 | }; 197 | 198 | return reader.Read(); 199 | } 200 | 201 | private void MenuItem_BeforeQueryStatus(object sender, EventArgs e) 202 | { 203 | var menuItem = (OleMenuCommand)sender; 204 | menuItem.Visible = false; 205 | 206 | ThreadHelper.ThrowIfNotOnUIThread(); 207 | SolutionExplorerExtensions.LoadSelectedItemPath(); 208 | 209 | if (CanGenerateParameters()) 210 | { 211 | menuItem.Visible = true; 212 | } 213 | } 214 | 215 | /// 216 | /// This function is the callback used to execute the command when the menu item is clicked. 217 | /// See the constructor to see how the menu item is associated with this function using 218 | /// OleMenuCommandService service and MenuCommand class. 219 | /// 220 | /// Event sender. 221 | /// Event args. 222 | private void MenuItemCallback(object sender, EventArgs e) 223 | { 224 | try 225 | { 226 | var fileName = SolutionExplorerExtensions.SelectedItemPath; 227 | ThreadHelper.JoinableTaskFactory.Run(async () => 228 | { 229 | await GenerateFileAsync(fileName).ConfigureAwait(true); 230 | }); 231 | } 232 | catch (Exception ex) 233 | { 234 | ShowMessage("Error Generating File", ex.Message); 235 | } 236 | } 237 | 238 | private void ShowMessage(string title, string message) 239 | { 240 | VsShellUtilities.ShowMessageBox( 241 | ServiceProvider, 242 | message, 243 | title, 244 | OLEMSGICON.OLEMSGICON_INFO, 245 | OLEMSGBUTTON.OLEMSGBUTTON_OK, 246 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); 247 | } 248 | 249 | private async System.Threading.Tasks.Task UpdateParametersXmlAsync(IEnumerable settings, string fileName) 250 | { 251 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 252 | var projectName = VSPackage.DteInstance.Solution.FindProjectItem(fileName).ContainingProject.Name; 253 | var reader = new ParametersXmlReader(fileName, projectName); 254 | var parameters = reader.Read(); 255 | var matches = new List(); 256 | foreach (var parameter in parameters) 257 | { 258 | if (parameter.Entries != null) 259 | { 260 | matches.AddRange(parameter.Entries.Select(e => e.Match)); 261 | } 262 | } 263 | 264 | var missingSettings = settings.Where(s => !matches.Exists(p => p == s.NodePath)).ToList(); 265 | 266 | EnsureUniqueSettingsNames(missingSettings, parameters.Select(p => p.Name).ToList()); 267 | 268 | var document = new XmlDocument { XmlResolver = null }; 269 | var text = File.ReadAllText(fileName); 270 | var sreader = new StringReader(text); 271 | var xmlReader = new XmlTextReader(sreader) { DtdProcessing = DtdProcessing.Prohibit }; 272 | document.Load(xmlReader); 273 | 274 | var builder = new StringBuilder(); 275 | var writer = XmlWriter.Create(builder, new XmlWriterSettings() { Indent = true, OmitXmlDeclaration = true, ConformanceLevel = ConformanceLevel.Fragment }); 276 | try 277 | { 278 | WriteParameters(missingSettings, writer); 279 | } 280 | finally 281 | { 282 | writer.Close(); 283 | } 284 | 285 | var missingParametersNodes = document.CreateDocumentFragment(); 286 | missingParametersNodes.InnerXml = builder.ToString(); 287 | 288 | var parametersNode = document.SelectSingleNode("/parameters"); 289 | parametersNode.AppendChild(missingParametersNodes); 290 | 291 | document.Save(fileName); 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit/Commands/GenerateSetParametersCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Design; 4 | using System.IO; 5 | using System.Windows.Interop; 6 | using System.Xml; 7 | using Microsoft.VisualStudio.Shell; 8 | using Microsoft.VisualStudio.Shell.Interop; 9 | using WebDeployParametersToolkit.Extensions; 10 | using WebDeployParametersToolkit.Utilities; 11 | 12 | namespace WebDeployParametersToolkit 13 | { 14 | /// 15 | /// Command handler 16 | /// 17 | internal sealed class GenerateSetParametersCommand 18 | { 19 | /// 20 | /// Command ID. 21 | /// 22 | public const int CommandId = 0x0100; 23 | 24 | /// 25 | /// Command menu group (command set GUID). 26 | /// 27 | public static readonly Guid CommandSet = new Guid("b451bf5d-476a-43b7-8a00-11671601fdaa"); 28 | 29 | /// 30 | /// VS Package that provides this command, not null. 31 | /// 32 | private readonly AsyncPackage package; 33 | 34 | /// 35 | /// Initializes a new instance of the class. 36 | /// Adds our command handlers for menu (commands must exist in the command table file) 37 | /// 38 | /// Owner package, not null. 39 | private GenerateSetParametersCommand(AsyncPackage package, OleMenuCommandService commandService) 40 | { 41 | this.package = package ?? throw new ArgumentNullException(nameof(package)); 42 | 43 | if (commandService != null) 44 | { 45 | var menuCommandID = new CommandID(CommandSet, CommandId); 46 | 47 | var menuItem = new OleMenuCommand(MenuItemCallback, menuCommandID); 48 | menuItem.BeforeQueryStatus += MenuItem_BeforeQueryStatus; 49 | commandService.AddCommand(menuItem); 50 | } 51 | } 52 | 53 | /// 54 | /// Gets the instance of the command. 55 | /// 56 | public static GenerateSetParametersCommand Instance 57 | { 58 | get; 59 | private set; 60 | } 61 | 62 | /// 63 | /// Initializes the singleton instance of the command. 64 | /// 65 | /// Owner package, not null. 66 | public static async System.Threading.Tasks.Task InitializeAsync(AsyncPackage package) 67 | { 68 | // Switch to the main thread - the call to AddCommand in SampleCommand's constructor requires 69 | // the UI thread. 70 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 71 | 72 | var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(true) as OleMenuCommandService; 73 | Instance = new GenerateSetParametersCommand(package, commandService); 74 | } 75 | 76 | private static bool CanGenerateSetParameters() 77 | { 78 | return !string.IsNullOrEmpty(SolutionExplorerExtensions.SelectedItemPath) && "Parameters.xml".Equals(Path.GetFileName(SolutionExplorerExtensions.SelectedItemPath), StringComparison.OrdinalIgnoreCase); 79 | } 80 | 81 | private void CreateSetXml(IEnumerable webDeployParameters, string fileName) 82 | { 83 | var writer = XmlWriter.Create(fileName, new XmlWriterSettings() { Indent = true }); 84 | 85 | try 86 | { 87 | writer.WriteStartDocument(); 88 | writer.WriteStartElement("parameters"); 89 | foreach (var parameter in webDeployParameters) 90 | { 91 | writer.WriteStartElement("setParameter"); 92 | writer.WriteAttributeString("name", parameter.Name); 93 | writer.WriteAttributeString("value", parameter.DefaultValue); 94 | writer.WriteEndElement(); 95 | } 96 | 97 | writer.WriteEndDocument(); 98 | } 99 | finally 100 | { 101 | writer.Close(); 102 | } 103 | } 104 | 105 | private void GenerateFile(string fileName) 106 | { 107 | ThreadHelper.ThrowIfNotOnUIThread(); 108 | 109 | var sourceName = SolutionExplorerExtensions.SelectedItemPath; 110 | var folder = Path.GetDirectoryName(sourceName); 111 | var targetName = Path.Combine(folder, fileName); 112 | 113 | if (File.Exists(targetName)) 114 | { 115 | ShowMessage("Duplicate File", "The file name specified already exits."); 116 | return; 117 | } 118 | 119 | var projectFullName = VSPackage.DteInstance.Solution.FindProjectItem(sourceName).ContainingProject.FullName; 120 | 121 | var parameterizationProject = new ParameterizationProject(projectFullName); 122 | if (parameterizationProject.Initialize()) 123 | { 124 | var projectName = VSPackage.DteInstance.Solution.FindProjectItem(sourceName).ContainingProject.Name; 125 | var parameters = ParseParameters(sourceName, projectName); 126 | CreateSetXml(parameters, targetName); 127 | var parent = VSPackage.DteInstance.Solution.FindProjectItem(sourceName); 128 | var item = parent.ProjectItems.AddFromFile(targetName); 129 | item.Properties.Item("ItemType").Value = "Parameterization"; 130 | item.Open().Visible = true; 131 | } 132 | } 133 | 134 | private void MenuItem_BeforeQueryStatus(object sender, EventArgs e) 135 | { 136 | var menuItem = (OleMenuCommand)sender; 137 | menuItem.Visible = false; 138 | 139 | ThreadHelper.ThrowIfNotOnUIThread(); 140 | SolutionExplorerExtensions.LoadSelectedItemPath(); 141 | 142 | if (CanGenerateSetParameters()) 143 | { 144 | menuItem.Visible = true; 145 | } 146 | } 147 | 148 | /// 149 | /// This function is the callback used to execute the command when the menu item is clicked. 150 | /// See the constructor to see how the menu item is associated with this function using 151 | /// OleMenuCommandService service and MenuCommand class. 152 | /// 153 | /// Event sender. 154 | /// Event args. 155 | private void MenuItemCallback(object sender, EventArgs e) 156 | { 157 | ThreadHelper.ThrowIfNotOnUIThread(); 158 | var dialog = new FileNameDialog(); 159 | var hwnd = VSPackage.DteInstance.MainWindow.HWnd; 160 | var window = (System.Windows.Window)HwndSource.FromHwnd(hwnd).RootVisual; 161 | dialog.Owner = window; 162 | 163 | var result = dialog.ShowDialog(); 164 | if (result.HasValue && result.Value) 165 | { 166 | GenerateFile(dialog.Input); 167 | } 168 | } 169 | 170 | private IEnumerable ParseParameters(string fileName, string projectName) 171 | { 172 | IEnumerable results = null; 173 | try 174 | { 175 | var reader = new ParametersXmlReader(fileName, projectName); 176 | results = reader.Read(); 177 | } 178 | catch (Exception ex) 179 | { 180 | ShowMessage("Parameters.xml Error", ex.Message); 181 | } 182 | 183 | return results; 184 | } 185 | 186 | private void ShowMessage(string title, string message) 187 | { 188 | VsShellUtilities.ShowMessageBox( 189 | package, 190 | message, 191 | title, 192 | OLEMSGICON.OLEMSGICON_INFO, 193 | OLEMSGBUTTON.OLEMSGBUTTON_OK, 194 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /WebDeployParametersToolkit/Commands/NestCommand.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Company. All rights reserved. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using System; 8 | using System.ComponentModel.Design; 9 | using System.IO; 10 | using System.Linq; 11 | using EnvDTE; 12 | using Microsoft.VisualStudio.Shell; 13 | using WebDeployParametersToolkit.Extensions; 14 | 15 | namespace WebDeployParametersToolkit 16 | { 17 | /// 18 | /// Command handler 19 | /// 20 | internal sealed class NestCommand 21 | { 22 | /// 23 | /// Command ID. 24 | /// 25 | public const int CommandId = 257; 26 | 27 | /// 28 | /// Command menu group (command set GUID). 29 | /// 30 | public static readonly Guid CommandSet = new Guid("b451bf5d-476a-43b7-8a00-11671601fdaa"); 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// Adds our command handlers for menu (commands must exist in the command table file) 35 | /// 36 | private NestCommand(OleMenuCommandService commandService) 37 | { 38 | if (commandService != null) 39 | { 40 | var menuCommandID = new CommandID(CommandSet, CommandId); 41 | var menuItem = new OleMenuCommand(MenuItemCallback, menuCommandID); 42 | menuItem.BeforeQueryStatus += MenuItem_BeforeQueryStatus; 43 | commandService.AddCommand(menuItem); 44 | } 45 | } 46 | 47 | /// 48 | /// Gets the instance of the command. 49 | /// 50 | public static NestCommand Instance 51 | { 52 | get; 53 | private set; 54 | } 55 | 56 | /// 57 | /// Initializes the singleton instance of the command. 58 | /// 59 | /// Owner package, not null. 60 | public static async System.Threading.Tasks.Task InitializeAsync(AsyncPackage package) 61 | { 62 | // Switch to the main thread - the call to AddCommand in SampleCommand's constructor requires 63 | // the UI thread. 64 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 65 | 66 | var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(false) as OleMenuCommandService; 67 | 68 | Instance = new NestCommand(commandService); 69 | } 70 | 71 | private static bool CanNestInParameters() 72 | { 73 | ThreadHelper.ThrowIfNotOnUIThread(); 74 | var itemPath = SolutionExplorerExtensions.SelectedItemPath; 75 | if (string.IsNullOrEmpty(itemPath)) 76 | { 77 | return false; 78 | } 79 | 80 | var fileName = Path.GetFileName(itemPath); 81 | var extension = Path.GetExtension(itemPath); 82 | var directory = Path.GetDirectoryName(itemPath); 83 | var parametersItem = VSPackage.DteInstance.Solution.FindProjectItem(Path.Combine(directory, "Parameters.xml")); 84 | 85 | var setParametersItem = VSPackage.DteInstance.Solution.FindProjectItem(itemPath); 86 | 87 | var currentParent = setParametersItem?.Collection?.Parent as ProjectItem; 88 | 89 | return fileName.StartsWith("setparameters", StringComparison.OrdinalIgnoreCase) 90 | && extension.Equals(".xml", StringComparison.OrdinalIgnoreCase) 91 | && parametersItem != null 92 | && setParametersItem != null 93 | && (currentParent == null || currentParent.Name != parametersItem.Name); 94 | } 95 | 96 | private void MenuItem_BeforeQueryStatus(object sender, EventArgs e) 97 | { 98 | var menuItem = (OleMenuCommand)sender; 99 | menuItem.Visible = false; 100 | 101 | SolutionExplorerExtensions.LoadSelectedItemPath(); 102 | 103 | ThreadHelper.ThrowIfNotOnUIThread(); 104 | 105 | if (CanNestInParameters()) 106 | { 107 | menuItem.Visible = true; 108 | } 109 | } 110 | 111 | /// 112 | /// This function is the callback used to execute the command when the menu item is clicked. 113 | /// See the constructor to see how the menu item is associated with this function using 114 | /// OleMenuCommandService service and MenuCommand class. 115 | /// 116 | /// Event sender. 117 | /// Event args. 118 | private void MenuItemCallback(object sender, EventArgs e) 119 | { 120 | ThreadHelper.ThrowIfNotOnUIThread(); 121 | var dte = VSPackage.DteInstance; 122 | 123 | // save node name details to maintain selection in solution explorer 124 | var selectedItem = ((Array)dte.ToolWindows.SolutionExplorer.SelectedItems).Cast().First(); 125 | 126 | var selectedParent = selectedItem.Collection.Parent as UIHierarchyItem; 127 | var parentNodeName = selectedParent.NodeName(); 128 | var nodeName = selectedItem.Name; 129 | var itemPath = SolutionExplorerExtensions.SelectedItemPath; 130 | 131 | var fileName = SolutionExplorerExtensions.SelectedItemPath; 132 | var projectFullName = VSPackage.DteInstance.Solution.FindProjectItem(fileName).ContainingProject.FullName; 133 | 134 | var parameterizationProject = new ParameterizationProject(projectFullName); 135 | if (parameterizationProject.Initialize()) 136 | { 137 | var projectItem = VSPackage.DteInstance.Solution.FindProjectItem(fileName); 138 | projectItem.Properties.Item("ItemType").Value = "Parameterization"; 139 | Nester.ApplyNesting(itemPath); 140 | 141 | System.Threading.Thread.Sleep(1000); 142 | dte.ToolWindows.SolutionExplorer.GetItem($"{parentNodeName}\\Parameters.xml").UIHierarchyItems.Expanded = true; 143 | dte.ToolWindows.SolutionExplorer.GetItem($"{parentNodeName}\\Parameters.xml\\{nodeName}").Select(vsUISelectionType.vsUISelectionTypeSelect); 144 | } 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /WebDeployParametersToolkit/Dialogs/FileNameDialog.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |