├── src
├── Transform.UnitTests
│ ├── packages.config
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Program_Tests.cs
│ └── Transform.UnitTests.csproj
├── Sitecore.Configuration.Roles
│ ├── packages.config
│ ├── Sitecore.Configuration.Roles.nuspec
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── BooleanLogicParser
│ │ ├── Tokens.cs
│ │ ├── Parser.cs
│ │ └── Tokenizer.cs
│ ├── SC.Sitecore.Configuration.Roles.nuspec
│ ├── RoleConfigReader.cs
│ ├── Sitecore.Configuration.Roles.csproj
│ ├── RoleConfigurationHelper.cs
│ └── RoleXmlPatchHelper.cs
├── Sitecore.Configuration.Roles.UnitTests
│ ├── packages.config
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── RoleConfigurationHelper_ValidateRoles.cs
│ ├── Sitecore.Configuration.Roles.UnitTests.csproj
│ └── RoleXmlPatchHelperTests.cs
├── Transform
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Transform.csproj
│ └── Program.cs
└── Sitecore.Configuration.Roles.sln
├── .gitignore
└── readme.md
/src/Transform.UnitTests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pubxml
2 | *.suo
3 | *.user
4 | src/.vs/
5 | src/Sitecore.Configuration.Roles.UnitTests/bin/
6 | src/Sitecore.Configuration.Roles.UnitTests/obj/
7 | src/Sitecore.Configuration.Roles/bin/
8 | src/Sitecore.Configuration.Roles/obj/
9 | src/Website/bin/
10 | src/Website/obj/
11 | src/packages/
12 | src/TestResults/
13 | src/Transform.UnitTests/bin/
14 | src/Transform.UnitTests/obj/
15 | src/Transform/bin/
16 | src/Transform/obj/
17 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles.UnitTests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/Transform/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | [assembly: ComVisible(false)]
6 |
7 | [assembly: AssemblyTitle("Transform")]
8 | [assembly: AssemblyProduct("Sitecore.Configuration.Roles")]
9 |
10 | [assembly: AssemblyVersion("1.3.0.0")]
11 | [assembly: AssemblyFileVersion("1.3.0.0")]
12 |
13 | [assembly: InternalsVisibleTo("Transform.UnitTests")]
--------------------------------------------------------------------------------
/src/Transform.UnitTests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | [assembly: AssemblyTitle("Transform.UnitTests")]
6 | [assembly: AssemblyDescription("")]
7 | [assembly: AssemblyConfiguration("")]
8 | [assembly: AssemblyCompany("")]
9 | [assembly: AssemblyProduct("Transform.UnitTests")]
10 | [assembly: AssemblyCopyright("Copyright © 2017")]
11 | [assembly: AssemblyTrademark("")]
12 | [assembly: AssemblyCulture("")]
13 |
14 | [assembly: ComVisible(false)]
15 |
16 | [assembly: Guid("9f9f65c7-9816-4e51-8a0b-4f5509682708")]
17 |
18 | // [assembly: AssemblyVersion("1.0.*")]
19 | [assembly: AssemblyVersion("1.0.0.0")]
20 | [assembly: AssemblyFileVersion("1.0.0.0")]
21 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/Sitecore.Configuration.Roles.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sitecore.Configuration.Roles
5 | 1.1.0
6 | Alen Pelin
7 | Sitecore
8 | false
9 | Configuration Role Engine for Sitecore.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | [assembly: AssemblyTitle("Sitecore.Configuration.Roles")]
6 | [assembly: AssemblyProduct("Sitecore.Configuration.Roles")]
7 | [assembly: AssemblyDescription("Extends default configuration engine with role:require attribute.")]
8 |
9 | [assembly: AssemblyCompany("Sitecore")]
10 | [assembly: AssemblyCopyright("Copyright © Sitecore 2015")]
11 | [assembly: ComVisible(false)]
12 |
13 | [assembly: InternalsVisibleTo("Sitecore.Configuration.Roles.UnitTests")]
14 |
15 | [assembly: AssemblyVersion("1.3.0.0")]
16 | [assembly: AssemblyFileVersion("1.3.0.0")]
17 | [assembly: AssemblyInformationalVersion("1.2 rev. 170405")]
18 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles.UnitTests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | [assembly: AssemblyTitle("Sitecore.Configuration.Roles.UnitTests")]
6 | [assembly: AssemblyDescription("")]
7 | [assembly: AssemblyConfiguration("")]
8 | [assembly: AssemblyCompany("")]
9 | [assembly: AssemblyProduct("Sitecore.Configuration.Roles.UnitTests")]
10 | [assembly: AssemblyCopyright("Copyright © 2017")]
11 | [assembly: AssemblyTrademark("")]
12 | [assembly: AssemblyCulture("")]
13 |
14 | [assembly: ComVisible(false)]
15 |
16 | [assembly: Guid("66c9dfb2-5c20-4dff-9062-50d8a6de50ff")]
17 |
18 | // [assembly: AssemblyVersion("1.0.*")]
19 | [assembly: AssemblyVersion("1.0.0.0")]
20 | [assembly: AssemblyFileVersion("1.0.0.0")]
21 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/BooleanLogicParser/Tokens.cs:
--------------------------------------------------------------------------------
1 | namespace Sitecore.Configuration.Roles.BooleanLogic
2 | {
3 | internal class OperandToken : Token
4 | {
5 | }
6 |
7 | internal class OrToken : OperandToken
8 | {
9 | }
10 |
11 | internal class AndToken : OperandToken
12 | {
13 | }
14 |
15 | internal class BooleanValueToken : Token
16 | {
17 | }
18 |
19 | internal class FalseToken : BooleanValueToken
20 | {
21 | }
22 |
23 | internal class TrueToken : BooleanValueToken
24 | {
25 | }
26 |
27 | internal class ParenthesisToken : Token
28 | {
29 | }
30 |
31 | internal class ClosedParenthesisToken : ParenthesisToken
32 | {
33 | }
34 |
35 | internal class OpenParenthesisToken : ParenthesisToken
36 | {
37 | }
38 |
39 | internal class NegationToken : Token
40 | {
41 | }
42 |
43 | internal abstract class Token
44 | {
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/SC.Sitecore.Configuration.Roles.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SC.Sitecore.Configuration.Roles
5 | 1.0.0
6 | Alen Pelin
7 | Sitecore
8 | false
9 | Configuration Role Engine for Sitecore.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles.UnitTests/RoleConfigurationHelper_ValidateRoles.cs:
--------------------------------------------------------------------------------
1 | namespace Sitecore.Configuration.Roles.UnitTests
2 | {
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using FluentAssertions;
5 |
6 | [TestClass]
7 | public class RoleConfigurationHelper_ValidateRoles
8 | {
9 | [TestMethod]
10 | public void Authoring()
11 | {
12 | RoleConfigurationHelper.ValidateRoles(new[] { "authoring" })
13 | .Should().BeNull();
14 | }
15 |
16 | [TestMethod]
17 | public void Processing()
18 | {
19 | RoleConfigurationHelper.ValidateRoles(new[] { "processing" })
20 | .Should().BeNull();
21 | }
22 |
23 | [TestMethod]
24 | public void Reporting()
25 | {
26 | RoleConfigurationHelper.ValidateRoles(new[] { "reporting" })
27 | .Should().BeNull();
28 | }
29 |
30 | [TestMethod]
31 | public void Delivery()
32 | {
33 | RoleConfigurationHelper.ValidateRoles(new[] { "delivery" })
34 | .Should().BeNull();
35 | }
36 |
37 | [TestMethod]
38 | public void AuthoringDelivery()
39 | {
40 | RoleConfigurationHelper.ValidateRoles(new[] { "authoring", "delivery" })
41 | .Should().BeNull();
42 | }
43 |
44 | [TestMethod]
45 | public void AuthoringDeliveryOne()
46 | {
47 | RoleConfigurationHelper.ValidateRoles(new[] { "authoring", "delivery" })
48 | .Should().BeNull();
49 | }
50 |
51 | [TestMethod]
52 | public void AllInOneDeliveryOne()
53 | {
54 | RoleConfigurationHelper.ValidateRoles(new[] { "authoring", "processing", "reporting", "delivery" })
55 | .Should().BeNull();
56 | }
57 |
58 | [TestMethod]
59 | public void AuthoringProcessingReporting()
60 | {
61 | RoleConfigurationHelper.ValidateRoles(new[] { "authoring", "processing", "reporting" })
62 | .Should().BeNull();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/BooleanLogicParser/Parser.cs:
--------------------------------------------------------------------------------
1 | namespace Sitecore.Configuration.Roles.BooleanLogic
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | // Expression := [ "!" ] { } ...
7 | // Boolean := | | "(" ")"
8 | // BooleanOperator := "And" | "Or"
9 | // BooleanConstant := "True" | "False"
10 | internal class Parser
11 | {
12 | private readonly IEnumerator _tokens;
13 |
14 | internal Parser(IEnumerable tokens)
15 | {
16 | _tokens = tokens.GetEnumerator();
17 | _tokens.MoveNext();
18 | }
19 |
20 | internal bool Parse()
21 | {
22 | while (_tokens.Current != null)
23 | {
24 | var isNegated = _tokens.Current is NegationToken;
25 | if (isNegated)
26 | {
27 | _tokens.MoveNext();
28 | }
29 |
30 | var boolean = ParseBoolean();
31 | if (isNegated)
32 | {
33 | boolean = !boolean;
34 | }
35 |
36 | while (_tokens.Current is OperandToken)
37 | {
38 | var operand = _tokens.Current;
39 | if (!_tokens.MoveNext())
40 | {
41 | throw new Exception("Missing expression after operand");
42 | }
43 |
44 | var nextBoolean = ParseBoolean();
45 |
46 | if (operand is AndToken)
47 | {
48 | boolean = boolean && nextBoolean;
49 | }
50 | else
51 | {
52 | boolean = boolean || nextBoolean;
53 | }
54 | }
55 |
56 | return boolean;
57 | }
58 |
59 | throw new Exception("Empty expression");
60 | }
61 |
62 | private bool ParseBoolean()
63 | {
64 | if (_tokens.Current is BooleanValueToken)
65 | {
66 | var current = _tokens.Current;
67 | _tokens.MoveNext();
68 |
69 | return current is TrueToken;
70 | }
71 |
72 | if (_tokens.Current is OpenParenthesisToken)
73 | {
74 | _tokens.MoveNext();
75 |
76 | var expInPars = Parse();
77 |
78 | if (_tokens.Current is ClosedParenthesisToken)
79 | {
80 | _tokens.MoveNext();
81 |
82 | return expInPars;
83 | }
84 |
85 | throw new Exception("Expecting Closing Parenthesis");
86 | }
87 |
88 | if (_tokens.Current is ClosedParenthesisToken)
89 | {
90 | throw new Exception("Unexpected Closed Parenthesis");
91 | }
92 |
93 | // since its not a BooleanConstant or Expression in parenthesis, it must be a expression again
94 | var ret = Parse();
95 |
96 | return ret;
97 | }
98 | }
99 | }
--------------------------------------------------------------------------------
/src/Transform/Transform.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {D0C52BE8-194D-400C-BC15-6600315BA39E}
8 | Exe
9 | Transform
10 | Transform
11 | v4.5
12 | 512
13 |
14 |
15 | AnyCPU
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | AnyCPU
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | BooleanLogicParser\Parser.cs
46 |
47 |
48 | BooleanLogicParser\Tokenizer.cs
49 |
50 |
51 | BooleanLogicParser\Tokens.cs
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/RoleConfigReader.cs:
--------------------------------------------------------------------------------
1 | namespace Sitecore.Configuration.Roles
2 | {
3 | using System;
4 | using System.Collections;
5 | using System.IO;
6 | using System.Xml;
7 | using Sitecore.Diagnostics;
8 | using Sitecore.Xml.Patch;
9 |
10 | [UsedImplicitly]
11 | public class RoleConfigReader : ConfigReader
12 | {
13 | [NotNull]
14 | private readonly string[] IncludeOverride = ReadIncludeOverride();
15 |
16 | private readonly RoleConfigurationHelper RoleConfigurationHelper;
17 |
18 | private readonly XmlPatcher Patcher;
19 |
20 | public RoleConfigReader()
21 | {
22 | RoleConfigurationHelper = new RoleConfigurationHelper();
23 | Patcher = new XmlPatcher("http://www.sitecore.net/xmlconfig/set/", "http://www.sitecore.net/xmlconfig/", new RoleXmlPatchHelper(RoleConfigurationHelper));
24 | }
25 |
26 | protected override void ExpandIncludeFiles([NotNull] XmlNode rootNode, [NotNull] Hashtable cycleDetector)
27 | {
28 | Assert.ArgumentNotNull(rootNode, "rootNode");
29 | Assert.ArgumentNotNull(cycleDetector, "cycleDetector");
30 |
31 | RoleConfigurationHelper.LoadAppSetting();
32 |
33 | base.ExpandIncludeFiles(rootNode, cycleDetector);
34 | }
35 |
36 | protected override void ReplaceGlobalVariables([NotNull] XmlNode rootNode)
37 | {
38 | Assert.ArgumentNotNull(rootNode, "rootNode");
39 |
40 | base.ReplaceGlobalVariables(rootNode);
41 |
42 | RoleConfigurationHelper.Validate();
43 | }
44 |
45 | [NotNull]
46 | protected override ConfigPatcher GetConfigPatcher([NotNull] XmlNode element)
47 | {
48 | Assert.ArgumentNotNull(element, "element");
49 |
50 | return new ConfigPatcher(element, this.Patcher);
51 | }
52 |
53 | protected override void LoadAutoIncludeFiles(XmlNode element)
54 | {
55 | Assert.ArgumentNotNull(element, "element");
56 |
57 | if (IncludeOverride.Length == 0)
58 | {
59 | base.LoadAutoIncludeFiles(element);
60 |
61 | return;
62 | }
63 |
64 | var configPatcher = GetConfigPatcher(element);
65 | LoadAutoIncludeFiles(configPatcher, MainUtil.MapPath("/App_Config/Sitecore/Components"));
66 |
67 | foreach (var path in IncludeOverride)
68 | {
69 | Assert.IsTrue(Directory.Exists(path), "The include:override setting points to non-existing folder: {0}", path);
70 | Assert.IsTrue(Path.IsPathRooted(path), "The include:override setting points to non-rooted path: {0}", path);
71 |
72 | LoadAutoIncludeFiles(configPatcher, path);
73 | }
74 | }
75 |
76 | [NotNull]
77 | private static string[] ReadIncludeOverride()
78 | {
79 | var includeOverride = System.Configuration.ConfigurationManager.AppSettings["include:override"];
80 | if (string.IsNullOrEmpty(includeOverride))
81 | {
82 | return new string[0];
83 | }
84 |
85 | return includeOverride.Split(";|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26430.6
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sitecore.Configuration.Roles", "Sitecore.Configuration.Roles\Sitecore.Configuration.Roles.csproj", "{6A31C120-8BA2-42A3-9C9B-4D40B626EC3A}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sitecore.Configuration.Roles.UnitTests", "Sitecore.Configuration.Roles.UnitTests\Sitecore.Configuration.Roles.UnitTests.csproj", "{66C9DFB2-5C20-4DFF-9062-50D8A6DE50FF}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Transform", "Transform\Transform.csproj", "{D0C52BE8-194D-400C-BC15-6600315BA39E}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{E882ABF3-83DC-447A-89B7-F90AC957F08A}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Transform.UnitTests", "Transform.UnitTests\Transform.UnitTests.csproj", "{9F9F65C7-9816-4E51-8A0B-4F5509682708}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {6A31C120-8BA2-42A3-9C9B-4D40B626EC3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {6A31C120-8BA2-42A3-9C9B-4D40B626EC3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {6A31C120-8BA2-42A3-9C9B-4D40B626EC3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {6A31C120-8BA2-42A3-9C9B-4D40B626EC3A}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {66C9DFB2-5C20-4DFF-9062-50D8A6DE50FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {66C9DFB2-5C20-4DFF-9062-50D8A6DE50FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {66C9DFB2-5C20-4DFF-9062-50D8A6DE50FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {66C9DFB2-5C20-4DFF-9062-50D8A6DE50FF}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {D0C52BE8-194D-400C-BC15-6600315BA39E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {D0C52BE8-194D-400C-BC15-6600315BA39E}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {D0C52BE8-194D-400C-BC15-6600315BA39E}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {D0C52BE8-194D-400C-BC15-6600315BA39E}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {9F9F65C7-9816-4E51-8A0B-4F5509682708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {9F9F65C7-9816-4E51-8A0B-4F5509682708}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {9F9F65C7-9816-4E51-8A0B-4F5509682708}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {9F9F65C7-9816-4E51-8A0B-4F5509682708}.Release|Any CPU.Build.0 = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | GlobalSection(NestedProjects) = preSolution
43 | {D0C52BE8-194D-400C-BC15-6600315BA39E} = {E882ABF3-83DC-447A-89B7-F90AC957F08A}
44 | {9F9F65C7-9816-4E51-8A0B-4F5509682708} = {E882ABF3-83DC-447A-89B7-F90AC957F08A}
45 | EndGlobalSection
46 | EndGlobal
47 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/Sitecore.Configuration.Roles.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {6A31C120-8BA2-42A3-9C9B-4D40B626EC3A}
8 | Library
9 | Properties
10 | Sitecore.Configuration.Roles
11 | Sitecore.Configuration.Roles
12 | v4.5
13 | 512
14 |
15 |
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 | ..\packages\SC.Sitecore.Kernel.8.1.2\lib\Sitecore.Kernel.dll
37 | False
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 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}.
61 |
62 |
63 |
64 |
71 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/BooleanLogicParser/Tokenizer.cs:
--------------------------------------------------------------------------------
1 | namespace Sitecore.Configuration.Roles.BooleanLogic
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 |
9 | internal class Tokenizer
10 | {
11 | private readonly StringReader _reader;
12 | private string _text;
13 | private readonly string[] _trueAliases;
14 |
15 | internal Tokenizer(string text)
16 | {
17 | _text = text;
18 | _reader = new StringReader(text);
19 | _trueAliases = new string[0];
20 | }
21 |
22 | internal Tokenizer(string text, params string[] trueAliases)
23 | {
24 | _text = text;
25 | _reader = new StringReader(text);
26 | _trueAliases = trueAliases;
27 | }
28 |
29 | internal IEnumerable Tokenize()
30 | {
31 | var tokens = new List();
32 | while (_reader.Peek() != -1)
33 | {
34 | while (char.IsWhiteSpace((char)_reader.Peek()))
35 | {
36 | _reader.Read();
37 | }
38 |
39 | if (_reader.Peek() == -1)
40 | {
41 | break;
42 | }
43 |
44 | var c = (char)_reader.Peek();
45 | switch (c)
46 | {
47 | case '&':
48 | tokens.Add(new AndToken());
49 | _reader.Read();
50 | break;
51 |
52 | case '|':
53 | tokens.Add(new OrToken());
54 | _reader.Read();
55 | break;
56 |
57 | case '!':
58 | tokens.Add(new NegationToken());
59 | _reader.Read();
60 | break;
61 |
62 | case '(':
63 | tokens.Add(new OpenParenthesisToken());
64 | _reader.Read();
65 | break;
66 |
67 | case ')':
68 | tokens.Add(new ClosedParenthesisToken());
69 | _reader.Read();
70 | break;
71 |
72 | default:
73 | if (IsValidTokenCharacter(c))
74 | {
75 | var token = ParseKeyword();
76 | tokens.Add(token);
77 | }
78 | else
79 | {
80 | var remainingText = _reader.ReadToEnd() ?? string.Empty;
81 | throw new Exception(string.Format("Unknown grammar found at position {0} : '{1}'", _text.Length - remainingText.Length, remainingText));
82 | }
83 | break;
84 | }
85 | }
86 |
87 | return tokens;
88 | }
89 |
90 | private static bool IsValidTokenCharacter(char c)
91 | {
92 | return char.IsLetter(c) || char.IsDigit(c) || c == '-' || c == '_' || c == '.' || c == '/';
93 | }
94 |
95 | private Token ParseKeyword()
96 | {
97 | var text = new StringBuilder();
98 | while (IsValidTokenCharacter((char)_reader.Peek()))
99 | {
100 | text.Append((char)_reader.Read());
101 | }
102 |
103 | var potentialKeyword = text.ToString().ToLower();
104 |
105 | var aliases = _trueAliases.Length > 0;
106 | if (_trueAliases.Any(x => x.Equals(potentialKeyword, StringComparison.OrdinalIgnoreCase)))
107 | {
108 | return new TrueToken();
109 | }
110 |
111 | switch (potentialKeyword)
112 | {
113 | case "true":
114 | return new TrueToken();
115 |
116 | case "false":
117 | return new FalseToken();
118 |
119 | case "and":
120 | return new AndToken();
121 |
122 | case "or":
123 | return new OrToken();
124 |
125 | default:
126 | if (aliases)
127 | {
128 | return new FalseToken();
129 | }
130 | else
131 | {
132 | throw new Exception("Expected keyword (True, False, And, Or) but found " + potentialKeyword);
133 | }
134 | }
135 | }
136 | }
137 | }
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/RoleConfigurationHelper.cs:
--------------------------------------------------------------------------------
1 | namespace Sitecore.Configuration.Roles
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Configuration;
6 | using System.Linq;
7 | using System.Xml;
8 | using BooleanLogic;
9 | using Sitecore.Diagnostics;
10 | using Sitecore.Xml.Patch;
11 |
12 | ///
13 | /// The configuration roles helper.
14 | ///
15 | public class RoleConfigurationHelper
16 | {
17 | [CanBeNull]
18 | private string[] definedRoles;
19 |
20 | public RoleConfigurationHelper()
21 | {
22 | }
23 |
24 | internal RoleConfigurationHelper(string[] roles)
25 | {
26 | definedRoles = roles;
27 | }
28 |
29 | ///
30 | /// List of defined roles.
31 | ///
32 | ///
33 | /// The defined roles.
34 | ///
35 | [NotNull]
36 | public IEnumerable DefinedRoles
37 | {
38 | get
39 | {
40 | return (definedRoles ?? new string[0]).ToArray();
41 | }
42 | }
43 |
44 | [CanBeNull]
45 | internal string DefinedRolesSource { get; private set; }
46 |
47 | [CanBeNull]
48 | internal string DefinedRolesErrorSource { get; private set; }
49 |
50 | [CanBeNull]
51 | private string DefinedRolesErrorMessage { get; set; }
52 |
53 | internal void LoadAppSetting()
54 | {
55 | var roleDefine = System.Configuration.ConfigurationManager.AppSettings["role:define"];
56 | if (!string.IsNullOrEmpty(roleDefine))
57 | {
58 | DefineRolesOnce(roleDefine, "web.config");
59 | }
60 | }
61 |
62 | internal void Validate()
63 | {
64 | if (string.IsNullOrEmpty(DefinedRolesErrorSource) && string.IsNullOrEmpty(DefinedRolesErrorMessage))
65 | {
66 | return;
67 | }
68 |
69 | throw new ConfigurationErrorsException(DefinedRolesErrorMessage, DefinedRolesErrorSource, 0);
70 | }
71 |
72 | internal bool ProcessRolesNamespace([NotNull] IXmlNode attribute)
73 | {
74 | Assert.ArgumentNotNull(attribute, "node");
75 | Assert.ArgumentCondition(attribute.NodeType == XmlNodeType.Attribute, "attribute", "The attribute node is not an XmlNodeType.Attribute");
76 |
77 | var name = attribute.LocalName;
78 | var value = attribute.Value;
79 | switch (name)
80 | {
81 | case "r":
82 | case "require":
83 | if (!string.IsNullOrEmpty(value))
84 | {
85 | var tokens = new Tokenizer(value, DefinedRoles.ToArray()).Tokenize();
86 | var ret = new Parser(tokens).Parse();
87 |
88 | return ret;
89 | }
90 |
91 | break;
92 | }
93 |
94 | return true;
95 | }
96 |
97 | private void DefineRolesOnce([NotNull] string value, [NotNull] IXmlNode node)
98 | {
99 | Assert.ArgumentNotNull(value, "value");
100 | Assert.ArgumentNotNull(node, "node");
101 |
102 | var source = (IXmlSource)node;
103 | var sourceName = source.SourceName;
104 |
105 | DefineRolesOnce(value, sourceName);
106 | }
107 |
108 | private void DefineRolesOnce([NotNull] string value, [NotNull] string sourceName)
109 | {
110 | Assert.ArgumentNotNull(value, "value");
111 | Assert.ArgumentNotNull(sourceName, "sourceName");
112 |
113 | if (definedRoles != null && DefinedRolesSource != sourceName)
114 | {
115 | DefinedRolesErrorSource = sourceName;
116 | DefinedRolesErrorMessage = string.Format(
117 | "Current set of roles defined in the \"{0}\" file was attempted to be modified in the \"{1}\" file. " +
118 | "This is not allowed to prevent unintended configuration changes. " +
119 | "If roles from both files are valid, they need to be merged into a single file.",
120 | DefinedRolesSource,
121 | DefinedRolesErrorSource);
122 |
123 | return;
124 | }
125 |
126 | var roles = value.Split("|,;".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
127 | .Select(x => x.ToLowerInvariant())
128 | .Distinct()
129 | .ToList();
130 |
131 | var error = ValidateRoles(roles);
132 | if (!string.IsNullOrEmpty(error))
133 | {
134 | DefinedRolesErrorMessage = error;
135 | DefinedRolesErrorSource = sourceName;
136 |
137 | return;
138 | }
139 |
140 | definedRoles = roles.ToArray();
141 | DefinedRolesSource = sourceName;
142 | }
143 |
144 | internal static string ValidateRoles(ICollection roles)
145 | {
146 | return null;
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/Transform.UnitTests/Program_Tests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace Transform.UnitTests
5 | {
6 | using System.Linq;
7 | using System.Xml;
8 |
9 | [TestClass]
10 | public class Program_Tests
11 | {
12 | [TestMethod]
13 | public void ParseRole()
14 | {
15 | // arrange
16 | var webConfig = ParseXml(@"
17 |
18 |
19 |
20 |
21 | ");
22 |
23 | // act
24 | var role = Program.ParseRole(webConfig);
25 |
26 | // assert
27 | Assert.AreEqual("abc", role);
28 | }
29 |
30 | [TestMethod]
31 | public void ProcessFile_Partial()
32 | {
33 | // arrange
34 | var xml = ParseXml(@"
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | ");
48 |
49 | var expected = ParseXml(@"
50 |
51 |
52 |
53 |
54 |
55 |
56 | ");
57 |
58 | // act
59 | var result = Program.ProcessFile(xml.DocumentElement, "Authoring".Split());
60 |
61 | // assert
62 | CompareElements(expected.DocumentElement, xml.DocumentElement, "/configuration", 0);
63 | Assert.IsTrue(result);
64 | }
65 |
66 | [TestMethod]
67 | public void ProcessFile_Empty()
68 | {
69 | // arrange
70 | var xml = ParseXml(@"
71 |
72 |
73 |
74 |
75 | ");
76 |
77 | var expected = ParseXml(@"
78 |
79 | ");
80 |
81 | // act
82 | var result = Program.ProcessFile(xml.DocumentElement, "Authoring".Split());
83 |
84 | // assert
85 | CompareElements(expected.DocumentElement, xml.DocumentElement, "/configuration", 0);
86 | Assert.IsFalse(result);
87 | }
88 |
89 | private void CompareElements(XmlElement expected, XmlElement actual, string path, int childNumber)
90 | {
91 | CompareAttributes(expected.Attributes, actual.Attributes, path, childNumber);
92 | for (var i = 0; i < Math.Max(expected.ChildNodes.Count, actual.ChildNodes.Count); ++i)
93 | {
94 | var exp = i < expected.ChildNodes.Count ? (XmlElement)expected.ChildNodes[i] : null;
95 | var act = i < actual.ChildNodes.Count ? (XmlElement)actual.ChildNodes[i] : null;
96 | if (exp == null)
97 | {
98 | if (act == null)
99 | {
100 | throw new NotImplementedException("This cannot be");
101 | }
102 |
103 | Assert.Fail($"Unexpected xml element by path {path}[{childNumber}]: {act}");
104 | }
105 | else if (act == null)
106 | {
107 | Assert.Fail($"Missing xml element by path {path}[{childNumber}]: {exp}");
108 | }
109 | else
110 | {
111 | CompareElements(exp, act, path + "/" + exp.Name, i);
112 | }
113 | }
114 | }
115 |
116 | private void CompareAttributes(XmlAttributeCollection expected1, XmlAttributeCollection actual1, string path, int childNumber)
117 | {
118 | var expected = expected1.OfType().ToList();
119 | var actual = actual1.OfType().ToList();
120 | foreach (XmlAttribute exp in expected1)
121 | {
122 | expected.Remove(exp);
123 | var act = actual.FirstOrDefault(x => x.Name == exp.Name);
124 | Assert.IsNotNull(act, $"{path}[{childNumber}][@{exp.Name}]");
125 |
126 | Assert.AreEqual(exp.Value, act.Value);
127 | actual.Remove(act);
128 | }
129 |
130 | foreach (var act in actual)
131 | {
132 | Assert.Fail($"Unexpected attr {path}[{childNumber}][@{act.Name}='{act.Value}']");
133 | }
134 | }
135 |
136 | private static XmlDocument ParseXml(string xml)
137 | {
138 | var webConfig = new XmlDocument();
139 | webConfig.LoadXml(xml.Replace("'", "\"").Trim());
140 | return webConfig;
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/Transform.UnitTests/Transform.UnitTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {9F9F65C7-9816-4E51-8A0B-4F5509682708}
8 | Library
9 | Properties
10 | Transform.UnitTests
11 | Transform.UnitTests
12 | v4.5
13 | 512
14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 15.0
16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
18 | False
19 | UnitTest
20 |
21 |
22 |
23 |
24 | true
25 | full
26 | false
27 | bin\Debug\
28 | DEBUG;TRACE
29 | prompt
30 | 4
31 |
32 |
33 | pdbonly
34 | true
35 | bin\Release\
36 | TRACE
37 | prompt
38 | 4
39 |
40 |
41 |
42 | ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
43 |
44 |
45 | ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | {D0C52BE8-194D-400C-BC15-6600315BA39E}
61 | Transform
62 |
63 |
64 |
65 |
66 |
67 |
68 | 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}.
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles.UnitTests/Sitecore.Configuration.Roles.UnitTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {66C9DFB2-5C20-4DFF-9062-50D8A6DE50FF}
8 | Library
9 | Properties
10 | Sitecore.Configuration.Roles.UnitTests
11 | Sitecore.Configuration.Roles.UnitTests
12 | v4.6.2
13 | 512
14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 15.0
16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
18 | False
19 | UnitTest
20 |
21 |
22 |
23 |
24 | true
25 | full
26 | false
27 | bin\Debug\
28 | DEBUG;TRACE
29 | prompt
30 | 4
31 |
32 |
33 | pdbonly
34 | true
35 | bin\Release\
36 | TRACE
37 | prompt
38 | 4
39 |
40 |
41 |
42 | ..\packages\FluentAssertions.4.19.2\lib\net45\FluentAssertions.dll
43 |
44 |
45 | ..\packages\FluentAssertions.4.19.2\lib\net45\FluentAssertions.Core.dll
46 |
47 |
48 | ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
49 |
50 |
51 | ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
52 |
53 |
54 | ..\packages\SC.Sitecore.Kernel.8.1.2\lib\Sitecore.Kernel.dll
55 | True
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | {6a31c120-8ba2-42a3-9c9b-4d40b626ec3a}
73 | Sitecore.Configuration.Roles
74 |
75 |
76 |
77 |
78 |
79 |
80 | 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}.
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/src/Transform/Program.cs:
--------------------------------------------------------------------------------
1 | namespace Transform
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Xml;
8 | using Sitecore.Configuration.Roles.BooleanLogic;
9 |
10 | public static class Program
11 | {
12 | public static void Main(string[] args)
13 | {
14 | if (args.Length == 0)
15 | {
16 | Console.WriteLine("Transform.exe - the tool is part of Sitecore Configuration Roles 1.2");
17 | Console.WriteLine();
18 | Console.WriteLine("The only purpose is to convert roles-enabled configuration files and");
19 | Console.WriteLine("downgrade them to regular configuration files: ");
20 | Console.WriteLine(" * update web.config file with stock config provider");
21 | Console.WriteLine(" * update all include files by removing all sections that do not comply ");
22 | Console.WriteLine(" with roles specified in role:define setting in web.config file and all");
23 | Console.WriteLine(" signs of configuration roles used.");
24 | Console.WriteLine();
25 | Console.WriteLine("Usage: ");
26 | Console.WriteLine(" > Transform.exe []");
27 | Console.WriteLine(" (if output folder omitted, creates Include_ folder instead");
28 |
29 | return;
30 | }
31 |
32 | var filePath = GetWebConfigPath(args);
33 | var webConfig = ReadWebConfigFile(filePath);
34 | var role = ParseRole(webConfig);
35 | if (string.IsNullOrEmpty(role))
36 | {
37 | throw new NotSupportedException("Cannot find child element of , or the value is empty");
38 | }
39 |
40 | var timestamp = $"{DateTime.Now:yyyyMMdd-HHmmss}";
41 | ChangeConfigProvider(webConfig, $"{filePath}_{timestamp}");
42 |
43 | var folderPath = Path.GetDirectoryName(filePath);
44 | folderPath = Path.Combine(folderPath, "App_Config\\Include");
45 |
46 | var outputDir = GetOutputDirectoryPath(args);
47 | outputDir = string.IsNullOrEmpty(outputDir) ? $"{folderPath}_{timestamp}" : Path.Combine(outputDir, "App_Config\\Include");
48 | Directory.CreateDirectory(outputDir);
49 |
50 | var files = Directory.GetFiles(folderPath, "*.config", SearchOption.AllDirectories);
51 | foreach (var file in files)
52 | {
53 | ProcessFile(file, folderPath, outputDir, role.Split("|;,".ToCharArray()));
54 | }
55 | }
56 |
57 | private static void ProcessFile(string filePath, string sourceFolderPath, string outputFolderPath, string[] roles)
58 | {
59 | var relativePath = filePath.Substring(sourceFolderPath.Length).TrimStart("\\/".ToCharArray());
60 | var newFilePath = Path.Combine(outputFolderPath, relativePath);
61 | Directory.CreateDirectory(Path.GetDirectoryName(newFilePath));
62 |
63 | var outputStream = File.OpenWrite(newFilePath);
64 | try
65 | {
66 | var xml = new XmlDocument();
67 | xml.Load(filePath);
68 |
69 | // do work
70 | if (!ProcessFile(xml.DocumentElement, roles))
71 | {
72 | // delete (do not copy to output folder) include file if it does't have element
73 | return;
74 | }
75 |
76 | using (var writer = new XmlTextWriter(outputStream, Encoding.Unicode))
77 | {
78 | xml.WriteTo(writer);
79 | }
80 | }
81 | finally
82 | {
83 | outputStream.Close();
84 | }
85 | }
86 |
87 | internal static bool ProcessFile(XmlElement xml, string[] roles)
88 | {
89 | ProcessElement(xml, roles);
90 | StripNamespace(xml);
91 |
92 | return xml.SelectSingleNode("sitecore") != null;
93 | }
94 |
95 | private static void StripNamespace(XmlElement xml)
96 | {
97 | // remove namespace
98 | xml.Attributes
99 | .OfType()
100 | .Where(x => x.Prefix == "xmlns" && x.Value == "http://www.sitecore.net/xmlconfig/role/")
101 | .ToList()
102 | .ForEach(x =>
103 | x.OwnerElement.Attributes.Remove(x));
104 |
105 | foreach (var child in xml.ChildNodes.OfType())
106 | {
107 | StripNamespace(child);
108 | }
109 | }
110 |
111 | private static void ProcessElement(XmlElement xml, string[] roles)
112 | {
113 | var require = xml.Attributes["require", "http://www.sitecore.net/xmlconfig/role/"];
114 | if (require != null)
115 | {
116 | var tokens = new Tokenizer(require.Value, roles).Tokenize();
117 | if (!new Parser(tokens).Parse())
118 | {
119 | xml.ParentNode.RemoveChild(xml);
120 | return;
121 | }
122 | }
123 |
124 | if (require != null)
125 | {
126 | xml.Attributes.Remove(require);
127 | }
128 |
129 | // ToArray is important as the collection can be modified
130 | foreach (var child in xml.ChildNodes.OfType().ToArray())
131 | {
132 | ProcessElement(child, roles);
133 | }
134 | }
135 |
136 | internal static string ParseRole(XmlDocument webConfig)
137 | {
138 | return ((XmlElement)webConfig.DocumentElement.SelectSingleNode("appSettings/add[@key='role:define']"))?.GetAttribute("value");
139 | }
140 |
141 | internal static void ChangeConfigProvider(XmlDocument webConfig, string filePath)
142 | {
143 | var section = ((XmlElement)webConfig.DocumentElement.SelectSingleNode("configSections/section[@name='sitecore']"));
144 | if (!section.GetAttribute("type").StartsWith("Sitecore.Configuration.Roles.RoleConfigReader"))
145 | {
146 | return;
147 | }
148 |
149 | section.SetAttribute("type", "Sitecore.Configuration.ConfigReader, Sitecore.Kernel");
150 | webConfig.Save(filePath);
151 | }
152 |
153 | private static XmlDocument ReadWebConfigFile(string filePath)
154 | {
155 | var webConfig = new XmlDocument();
156 | webConfig.Load(filePath);
157 |
158 | return webConfig;
159 | }
160 |
161 | private static string GetWebConfigPath(string[] args)
162 | {
163 | return GetFirstArgument(args);
164 | }
165 |
166 | private static string GetFirstArgument(string[] args)
167 | {
168 | return args[0];
169 | }
170 |
171 | private static string GetOutputDirectoryPath(string[] args)
172 | {
173 | return args.Skip(1).FirstOrDefault();
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles.UnitTests/RoleXmlPatchHelperTests.cs:
--------------------------------------------------------------------------------
1 | namespace Sitecore.Configuration.Roles.UnitTests
2 | {
3 | using System.Xml;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using Sitecore.Data.Items;
6 | using Sitecore.Xml.Patch;
7 |
8 | [TestClass]
9 | public class RoleXmlPatchHelperTests
10 | {
11 | [TestMethod]
12 | public void MergeChildrenTest_Root()
13 | {
14 | var sut = new RoleXmlPatchHelperEx();
15 | var target = ParseXml("");
16 | var patch = ParseIXml("");
17 |
18 | sut.MergeChildren(target, patch, false);
19 |
20 | Assert.AreEqual(0, target.ChildNodes.Count);
21 | }
22 |
23 | [TestMethod]
24 | public void MergeChildrenTest_AddChild()
25 | {
26 | var sut = new RoleXmlPatchHelperEx();
27 | var target = ParseXml("");
28 | var patch = ParseIXml("");
29 |
30 | sut.MergeChildren(target, patch, false);
31 |
32 | Assert.AreEqual(1, target.ChildNodes.Count);
33 | Assert.AreEqual("child", target.ChildNodes[0].Name);
34 | }
35 |
36 | [TestMethod]
37 | public void MergeChildrenTest_AddChildren()
38 | {
39 | var sut = new RoleXmlPatchHelperEx();
40 | var target = ParseXml("");
41 | var patch = ParseIXml("");
42 |
43 | sut.MergeChildren(target, patch, false);
44 |
45 | Assert.AreEqual(2, target.ChildNodes.Count);
46 | Assert.AreEqual("child1", target.ChildNodes[0].Name);
47 | Assert.AreEqual("child2", target.ChildNodes[1].Name);
48 | }
49 |
50 | [TestMethod]
51 | public void MergeChildrenTest_RemoveChild()
52 | {
53 | var sut = new RoleXmlPatchHelperEx();
54 | var target = ParseXml(
55 | "" +
56 | " " +
57 | " " +
58 | " " +
59 | "");
60 | var patch = ParseIXml(
61 | "" +
62 | " " +
63 | " " +
64 | " " +
65 | " " +
66 | " " +
67 | "");
68 |
69 | sut.MergeChildren(target, patch, false);
70 |
71 | Assert.AreEqual(0, target.ChildNodes[0].ChildNodes.Count);
72 | }
73 |
74 | [TestMethod]
75 | public void MergeChildrenTest_RemoveChild_Role1()
76 | {
77 | var sut = new RoleXmlPatchHelperEx("role1");
78 | var target = ParseXml(
79 | "" +
80 | " " +
81 | " " +
82 | " " +
83 | " " +
84 | " " +
85 | "");
86 |
87 | var patch = ParseIXml(
88 | "" +
89 | " " +
90 | " " +
91 | " " +
92 | " " +
93 | " " +
94 | " " +
95 | " " +
96 | " " +
97 | " " +
98 | " " +
99 | " " +
100 | "");
101 |
102 | sut.MergeChildren(target, patch, false);
103 |
104 | Assert.AreEqual(2, target.ChildNodes[0].ChildNodes.Count);
105 | Assert.AreEqual("child2", target.ChildNodes[0].ChildNodes[0].Name);
106 | Assert.AreEqual("child3", target.ChildNodes[0].ChildNodes[1].Name);
107 | }
108 |
109 | [TestMethod]
110 | public void MergeChildrenTest_RemoveChild_Role2()
111 | {
112 | var sut = new RoleXmlPatchHelperEx("role2");
113 | var target = ParseXml(
114 | "" +
115 | " " +
116 | " " +
117 | " " +
118 | " " +
119 | " " +
120 | "");
121 |
122 | var patch = ParseIXml(
123 | "" +
124 | " " +
125 | " " +
126 | " " +
127 | " " +
128 | " " +
129 | " " +
130 | " " +
131 | " " +
132 | " " +
133 | " " +
134 | " " +
135 | "");
136 |
137 | sut.MergeChildren(target, patch, false);
138 |
139 | Assert.AreEqual(2, target.ChildNodes[0].ChildNodes.Count);
140 | Assert.AreEqual("child1", target.ChildNodes[0].ChildNodes[0].Name);
141 | Assert.AreEqual("child3", target.ChildNodes[0].ChildNodes[1].Name);
142 | }
143 |
144 | [TestMethod]
145 | public void MergeChildrenTest_RemoveChild_Role3()
146 | {
147 | var sut = new RoleXmlPatchHelperEx("role3");
148 | var target = ParseXml(
149 | "" +
150 | " " +
151 | " " +
152 | " " +
153 | " " +
154 | " " +
155 | "");
156 |
157 | var patch = ParseIXml(
158 | "" +
159 | " " +
160 | " " +
161 | " " +
162 | " " +
163 | " " +
164 | " " +
165 | " " +
166 | " " +
167 | " " +
168 | " " +
169 | " " +
170 | "");
171 |
172 | sut.MergeChildren(target, patch, false);
173 |
174 | Assert.AreEqual(2, target.ChildNodes[0].ChildNodes.Count);
175 | Assert.AreEqual("child1", target.ChildNodes[0].ChildNodes[0].Name);
176 | Assert.AreEqual("child2", target.ChildNodes[0].ChildNodes[1].Name);
177 | }
178 |
179 | private IXmlElement ParseIXml(string xml)
180 | {
181 | return new XmlDomSource(ParseXml(xml));
182 | }
183 |
184 | private XmlElement ParseXml(string xml)
185 | {
186 | var doc = new XmlDocument();
187 | doc.LoadXml(xml);
188 |
189 | return doc.DocumentElement;
190 | }
191 |
192 | private class RoleXmlPatchHelperEx : RoleXmlPatchHelper
193 | {
194 | private XmlPatchNamespaces Namespace { get; }
195 | = new XmlPatchNamespaces
196 | {
197 | PatchNamespace = "http://www.sitecore.net/xmlconfig/",
198 | SetNamespace = "http://www.sitecore.net/xmlconfig/set/"
199 | };
200 |
201 | internal RoleXmlPatchHelperEx(params string[] roles) : base(new RoleConfigurationHelper(roles))
202 | {
203 | }
204 |
205 | internal new void MergeChildren(XmlNode target, IXmlElement patch, bool targetWasInserted)
206 | {
207 | base.MergeChildren(target, patch, Namespace, targetWasInserted);
208 | }
209 | }
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Sitecore Configuration Roles
2 |
3 | This document describes how to configure a Sitecore instance to use one of the pre-defined server roles. After you install a Sitecore instance, the only changes you need to make are to install the module and update settings that define which server role it will have.
4 |
5 | ## Available in Sitecore 9.0 out-of-box
6 |
7 | This project has become deprecated since **Sitecore 9.0.0** release, which offers same functionality with extra benefits such as:
8 |
9 | * refactored configuration files (stock `App_Config/Include/**` contents was moved to `App_Config/Sitecore`)
10 | * pre-configured roles (`App_Config/Sitecore.config` is annotated as well as `/App_Config/Sitecore/**`)
11 | * search engine support (`Lucene`, `Solr`, `Azure`)
12 | * custom prefixes (you can add as many `something:define="option1"` and `something:require="option2"` as you want)
13 | * layers of configuration via `App_Config/Layers.config` (added `App_Config/Environment` and `App_Config/Modules`)
14 |
15 | ### Index
16 |
17 | ##### [Prerequsites](#prerequsites)
18 |
19 | ##### [How To](#how-to)
20 | 1. [Install NuGet Package](#1-install-nuget-package)
21 | 2. [Replace Include Configuration Files](#2-replace-include-configuration-files)
22 | 3. [Deploy](#3-deploy)
23 | 4. [Update web.config files](#4-update-webconfig-files)
24 | 5. [Verify if it works](#5-verify-if-it-works)
25 |
26 | ##### [Details](#details)
27 | 1. [Define Role Command](#1--define-role-command)
28 | 2. [Require Role Command](#2--require-role-command)
29 | 3. [Modified configuration files](#3--modified-configuration-files)
30 |
31 | ##### [Comments](#comments)
32 |
33 | ## Prerequsites
34 |
35 | * **Sitecore CMS 8.1 rev. 160302 (Update-2)**
36 | * **Sitecore CMS 8.1 rev. 160302 (Update-3)**
37 | * **Sitecore CMS 8.2 - all releases**
38 |
39 | In this project Sitecore configuration engine was extended with two simple commands
40 | and modified configuration files that use them. It is distributed as a module which
41 | is based on the `Sitecore CMS 8.1 rev. 160302 (Update-2)` which now allows patching
42 | configuration engine.
43 |
44 | ## How to
45 |
46 | ### 1. Install NuGet Package
47 |
48 | Install the `Sitecore.Configuration.Roles` NuGet package:
49 | ```ps
50 | PS> Install-Package Sitecore.Configuration.Roles
51 | ```
52 | Alternatively, you can [download it here](https://github.com/Sitecore/Sitecore-Configuration-Roles/releases) and unpack to the `bin` folder.
53 |
54 | ### 2. Replace Include Configuration Files
55 |
56 | Replace default Sitecore configuration files in `App_Config/Include` folder with annotated ones.
57 |
58 | Delete entire contents of `App_Config/Include` folder (**except `DataFolder.config` file and your custom files**) and replace with files from one of the branches:
59 | * Sitecore 8.1 Update-3 - [configuration/8.1.3](https://github.com/Sitecore/Sitecore-Configuration-Roles/tree/configuration/8.1.3)
60 | * Sitecore 8.2 Initial Release - [configuration/8.2.0](https://github.com/Sitecore/Sitecore-Configuration-Roles/tree/configuration/8.2.0)
61 | * Sitecore 8.2 Update-1 - [configuration/8.2.1](https://github.com/Sitecore/Sitecore-Configuration-Roles/tree/configuration/8.2.1)
62 | * Sitecore 8.2 Update-2 - [configuration/8.2.2](https://github.com/Sitecore/Sitecore-Configuration-Roles/tree/configuration/8.2.2)
63 | * Sitecore 8.2 Update-3 - [configuration/8.2.3](https://github.com/Sitecore/Sitecore-Configuration-Roles/tree/configuration/8.2.3)
64 | * Sitecore 8.2 Update-4 - [configuration/8.2.4](https://github.com/Sitecore/Sitecore-Configuration-Roles/tree/configuration/8.2.4)
65 | * Sitecore 8.2 Update-5 - [configuration/8.2.5](https://github.com/Sitecore/Sitecore-Configuration-Roles/tree/configuration/8.2.5)
66 | * Sitecore 8.2 Update-6 - [configuration/8.2.6](https://github.com/Sitecore/Sitecore-Configuration-Roles/tree/configuration/8.2.6)
67 | * Sitecore 8.2 Update-7 - [configuration/8.2.7](https://github.com/Sitecore/Sitecore-Configuration-Roles/tree/configuration/8.2.7)
68 |
69 | Go through your custom configuration files and annotate configuration nodes that must be presented only in certain kind of instances.
70 |
71 | For example, the item saved event handlers in `Customization.config` file to be used only in the `ContentManagement` environment:
72 |
73 | ```xml
74 |
75 |
76 |
77 |
78 |
79 | ```
80 |
81 | ### 3. Deploy
82 |
83 | Deploy the files to both `ContentManagement` and `ContentDelivery` Sitecore instances:
84 | ```
85 | App_Config/Include/**/*
86 | bin/Sitecore.Configuration.Roles.dll
87 | ```
88 |
89 | ### 4. Update web.config files
90 |
91 | Change `web.config` files of `ContentManagement` and `ContentDelivery` Sitecore instances so they are aware of their role.
92 |
93 | #### ContentManagement
94 |
95 | ```xml
96 | ...
97 |
98 |
99 | ...
100 |
101 |
102 | ...
103 |
104 |
105 | ...
106 | ```
107 |
108 | #### ContentDelivery
109 |
110 | ```xml
111 | ...
112 |
113 |
114 | ...
115 |
116 |
117 | ...
118 |
119 |
120 | ...
121 | ```
122 |
123 | ### 5. Verify if it works
124 |
125 | (Optional) Verify actual configuration:
126 | * navigate to the `/sitecore/admin/showconfig.aspx` page of the `ContentDelivery` instance
127 | * make sure that the definition of the `sitecore_master_index` index configuration element is not presented on the page
128 |
129 | ## Details
130 |
131 | ### 1. Define Role Command
132 |
133 | The `role:define` command defines pipe-separated list of configuration roles the given Sitecore instance has.
134 |
135 | The role name can be any string that matches the `[a-zA-Z0-9]+` pattern, however
136 | there are several commonly used **conventional role names** to use:
137 |
138 | * Standalone
139 | * ContentManagement
140 | * Reporting
141 | * Processing
142 | * ContentDelivery
143 |
144 | These roles are described below.
145 |
146 | #### Example
147 |
148 | ```xml
149 |
150 | ...
151 |
152 | ...
153 |
154 |
155 | ...
156 |
157 | ```
158 |
159 | ### 2. Require Role Command
160 |
161 | When `role:require` command is applied to a XML configuration node within Sitecore include config file
162 | the node will be ignored if the boolean expression is false. When the expression is evaluated, every
163 | configuration role that is defined by the `role:define` command is being transformed into "true" and
164 | all undefined role names are transformed into "false" condition.
165 |
166 | ### 3. Modified configuration files
167 |
168 | The module is shipped with modified stock configuration files to make Sitecore
169 | pre-configured to serve each of these configuration roles.
170 |
171 | #### Standalone
172 |
173 | Defines Standalone role that is the same as Sitecore pre-configured out of box.
174 | It allows only single-server set up.
175 |
176 | #### ContentManagement
177 |
178 | Defines Content Management (CM) role that allows editors to use editing
179 | applications like Content Editor, Page Editor etc.
180 |
181 | #### Reporting
182 |
183 | Defines xDB Reporting (Rep) role that fetches reporting data from various
184 | data sources to use in Sitecore reporting applications. It can be enabled
185 | on the same instance with other roles or on a dedicated Sitecore instance.
186 |
187 | #### Processing
188 |
189 | Defines xDB Processing (Proc) role. It can be enabled on the same instance
190 | with other roles or on a dedicated Sitecore instance.
191 |
192 | #### ContentDelivery
193 |
194 | Defines Content Delivery (CD) role that assumes current Sitecore instance
195 | is accessed only by end-users and Sitecore administrators. It cannot be
196 | enabled on the same instance with other roles.
197 |
198 | ## Examples
199 |
200 | EXAMPLE 1
201 | Here is an example of Sitecore solution with single Sitecore instance.
202 |
203 | - SRV-01: Standalone
204 |
205 | EXAMPLE 2
206 | Here is an example of Sitecore solution with 2 Sitecore instances:
207 | one is multipurpose, another is delivery only - both serve front-end users.
208 |
209 | - SRV-01: ContentManagement|Processing|Reporting
210 | - SRV-02: ContentDelivery
211 |
212 | EXAMPLE 3
213 | Here is an example of Sitecore solution with 5 Sitecore instances,
214 | one content-management only, one processing and reporting and 3 delivery.
215 |
216 | - SRV-01: ContentManagement
217 | - SRV-02: Processing|Reporting
218 | - SRV-03: ContentDelivery
219 | - SRV-04: ContentDelivery
220 | - SRV-05: ContentDelivery
221 |
--------------------------------------------------------------------------------
/src/Sitecore.Configuration.Roles/RoleXmlPatchHelper.cs:
--------------------------------------------------------------------------------
1 | namespace Sitecore.Configuration.Roles
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Xml;
7 | using Sitecore.Diagnostics;
8 | using Sitecore.Xml.Patch;
9 |
10 | public class RoleXmlPatchHelper : XmlPatchHelper
11 | {
12 | private const string RoleNamespace = "http://www.sitecore.net/xmlconfig/role/";
13 |
14 | private readonly RoleConfigurationHelper RoleConfigurationHelper;
15 |
16 | public RoleXmlPatchHelper(RoleConfigurationHelper roleConfigurationHelper)
17 | {
18 | RoleConfigurationHelper = roleConfigurationHelper;
19 | }
20 |
21 | public override void CopyAttributes([NotNull] XmlNode target, [NotNull] IXmlElement patch, [NotNull] XmlPatchNamespaces ns)
22 | {
23 | Assert.ArgumentNotNull(target, "target");
24 | Assert.ArgumentNotNull(patch, "patch");
25 | Assert.ArgumentNotNull(ns, "ns");
26 |
27 | var attributes = patch.GetAttributes().Where(a => a.NamespaceURI != ns.PatchNamespace && (a.NamespaceURI != RoleNamespace) && a.NamespaceURI != "http://www.w3.org/2000/xmlns/");
28 | var values = attributes.Select(a => ParseXmlNodeInfo(ns, a)).ToArray();
29 |
30 | if (!values.Any())
31 | {
32 | return;
33 | }
34 |
35 | this.AssignAttributes(target, values);
36 | this.AssignSource(target, patch, ns);
37 | }
38 |
39 | public override void MergeNodes([NotNull] XmlNode target, [NotNull] IXmlElement patch, [NotNull] XmlPatchNamespaces ns)
40 | {
41 | Assert.ArgumentNotNull(target, "target");
42 | Assert.ArgumentNotNull(patch, "patch");
43 | Assert.ArgumentNotNull(ns, "ns");
44 |
45 | if (target.NamespaceURI != patch.NamespaceURI || target.LocalName != patch.LocalName)
46 | {
47 | return;
48 | }
49 |
50 | var exit = false;
51 | foreach (var attribute in patch.GetAttributes())
52 | {
53 | if (exit)
54 | {
55 | continue;
56 | }
57 |
58 | if (attribute.NamespaceURI == RoleNamespace && !RoleConfigurationHelper.ProcessRolesNamespace(attribute))
59 | {
60 | // we need to finish enumerating attributes to avoid reader problem
61 | exit = true;
62 | }
63 | }
64 |
65 | if (exit)
66 | {
67 | foreach (var node in patch.GetChildren())
68 | {
69 | // we need to get children to avoid reader problem
70 | }
71 |
72 | return;
73 | }
74 |
75 | base.MergeNodes(target, patch, ns);
76 | }
77 |
78 | protected override void MergeChildren(XmlNode target, IXmlElement patch, XmlPatchNamespaces ns, bool targetWasInserted)
79 | {
80 | Assert.ArgumentNotNull(target, "target");
81 | Assert.ArgumentNotNull(patch, "patch");
82 | Assert.ArgumentNotNull(ns, "ns");
83 |
84 | string savedComment = null;
85 | var pendingOperations = new Stack();
86 |
87 | // copy child nodes
88 | foreach (IXmlElement node in patch.GetChildren())
89 | {
90 | if (node.NodeType == XmlNodeType.Text)
91 | {
92 | target.InnerText = node.Value;
93 | continue;
94 | }
95 |
96 | if (node.NodeType == XmlNodeType.Comment)
97 | {
98 | savedComment = node.Value;
99 | continue;
100 | }
101 |
102 | if (node.NodeType != XmlNodeType.Element)
103 | {
104 | continue;
105 | }
106 |
107 | if (node.NamespaceURI == ns.PatchNamespace)
108 | {
109 | ProcessConfigNode(target, node);
110 |
111 | continue;
112 | }
113 |
114 | var queryAttributes = new List();
115 | var setAttributes = new List();
116 |
117 | InsertOperation operation = null;
118 |
119 | var exit = false;
120 | foreach (IXmlNode attribute in node.GetAttributes())
121 | {
122 | if (exit)
123 | {
124 | continue;
125 | }
126 |
127 | if (attribute.NamespaceURI == RoleNamespace)
128 | {
129 | if (!RoleConfigurationHelper.ProcessRolesNamespace(attribute))
130 | {
131 | // we need to finish enumerating attributes to avoid reader problem
132 | exit = true;
133 | pendingOperations.Clear();
134 | }
135 |
136 | continue;
137 | }
138 |
139 | if (attribute.NamespaceURI == ns.PatchNamespace)
140 | {
141 | switch (attribute.LocalName)
142 | {
143 | case "b":
144 | case "before":
145 | case "a":
146 | case "after":
147 | case "i":
148 | case "instead":
149 | operation = new InsertOperation
150 | {
151 | Reference = attribute.Value,
152 | Disposition = attribute.LocalName[0]
153 | };
154 | break;
155 | }
156 |
157 | continue;
158 | }
159 |
160 | if (attribute.NamespaceURI == ns.SetNamespace)
161 | {
162 | setAttributes.Add(new XmlNodeInfo
163 | {
164 | NodeType = attribute.NodeType,
165 | NamespaceURI = string.Empty, // "set" NS translates into an empty NS
166 | LocalName = attribute.LocalName,
167 | Prefix = string.Empty,
168 | Value = attribute.Value
169 | });
170 | continue;
171 | }
172 |
173 | if (attribute.Prefix != "xmlns")
174 | {
175 | queryAttributes.Add(new XmlNodeInfo
176 | {
177 | NodeType = attribute.NodeType,
178 | NamespaceURI = attribute.NamespaceURI,
179 | LocalName = attribute.LocalName,
180 | Prefix = attribute.Prefix,
181 | Value = attribute.Value
182 | });
183 | }
184 | }
185 |
186 | if (exit)
187 | {
188 | continue;
189 | }
190 |
191 | var nsManager = new XmlNamespaceManager(new NameTable());
192 |
193 | var predicateBuilder = new StringBuilder();
194 | var added = false;
195 | foreach (var a in queryAttributes)
196 | {
197 | if (added)
198 | {
199 | predicateBuilder.Append(" and ");
200 | }
201 |
202 | if (a.Prefix != null && string.IsNullOrEmpty(nsManager.LookupPrefix(a.Prefix)))
203 | {
204 | nsManager.AddNamespace(a.Prefix, a.NamespaceURI);
205 | }
206 |
207 | predicateBuilder.Append("@" + MakeName(a.Prefix, a.LocalName) + "=\"" + a.Value + "\"");
208 | added = true;
209 | }
210 |
211 | if (node.Prefix != null && string.IsNullOrEmpty(nsManager.LookupPrefix(node.Prefix)))
212 | {
213 | nsManager.AddNamespace(node.Prefix, node.NamespaceURI);
214 | }
215 |
216 | XmlNode targetChild = null;
217 | bool created = false;
218 |
219 | if (!targetWasInserted)
220 | {
221 | string predicate = MakeName(node.Prefix, node.LocalName);
222 |
223 | var expression = predicateBuilder.ToString();
224 | if (expression.Length > 0)
225 | {
226 | predicate = predicate + "[" + expression + "]";
227 | }
228 |
229 | targetChild = target.SelectSingleNode(predicate, nsManager);
230 | }
231 |
232 | if (targetChild == null)
233 | {
234 | Assert.IsNotNull(target.OwnerDocument, "document");
235 | targetChild = target.OwnerDocument.CreateElement(MakeName(node.Prefix, node.LocalName), node.NamespaceURI);
236 | created = true;
237 | if (!InsertChild(target, targetChild, operation) && operation != null)
238 | {
239 | operation.Node = targetChild;
240 | pendingOperations.Push(operation);
241 | }
242 |
243 | AssignAttributes(targetChild, queryAttributes);
244 | }
245 | else if (operation != null)
246 | {
247 | if (!InsertChild(target, targetChild, operation))
248 | {
249 | operation.Node = targetChild;
250 | pendingOperations.Push(operation);
251 | }
252 | }
253 |
254 | if (savedComment != null)
255 | {
256 | Assert.IsNotNull(targetChild.OwnerDocument, "document");
257 | XmlComment comment = targetChild.OwnerDocument.CreateComment(savedComment);
258 | Assert.IsNotNull(targetChild.ParentNode, "parent");
259 | targetChild.ParentNode.InsertBefore(comment, targetChild);
260 | savedComment = null;
261 | }
262 |
263 | AssignAttributes(targetChild, setAttributes);
264 | MergeChildren(targetChild, node, ns, /*targetWasInserted = */ created);
265 | if ((created || setAttributes.Any()) && !targetWasInserted)
266 | {
267 | AssignSource(targetChild, node, ns);
268 | }
269 | }
270 |
271 | while (pendingOperations.Count > 0)
272 | {
273 | InsertOperation operation = pendingOperations.Pop();
274 | Assert.IsNotNull(operation.Node.ParentNode, "parent");
275 | InsertChild(operation.Node.ParentNode, operation.Node, operation);
276 | }
277 | }
278 |
279 | [NotNull]
280 | private static IXmlNode ParseXmlNodeInfo([NotNull] XmlPatchNamespaces ns, [NotNull] IXmlNode a)
281 | {
282 | Assert.ArgumentNotNull(ns, "ns");
283 | Assert.ArgumentNotNull(a, "a");
284 |
285 | var targetNamespace = a.NamespaceURI == ns.SetNamespace ? string.Empty : a.NamespaceURI;
286 |
287 | return new XmlNodeInfo
288 | {
289 | NodeType = a.NodeType,
290 | NamespaceURI = targetNamespace,
291 | LocalName = a.LocalName,
292 | Value = a.Value,
293 | Prefix = a.Prefix
294 | };
295 | }
296 | }
297 | }
--------------------------------------------------------------------------------