-
Notifications
You must be signed in to change notification settings - Fork 159
Expand file tree
/
Copy pathObjectPropertyBagEntry.cs
More file actions
228 lines (197 loc) · 9.71 KB
/
Copy pathObjectPropertyBagEntry.cs
File metadata and controls
228 lines (197 loc) · 9.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
using Microsoft.SharePoint.Client;
using PnP.Framework.Diagnostics;
using PnP.Framework.Provisioning.Model;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PnP.Framework.Provisioning.ObjectHandlers
{
internal class ObjectPropertyBagEntry : ObjectHandlerBase
{
public override string Name
{
get { return "Property bag entries"; }
}
public override string InternalName => "PropertyBagEntries";
public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
{
using (var scope = new PnPMonitoredScope(this.Name))
{
var systemPropertyBagEntriesExclusions = new List<string>(new[]
{
"_",
"vti_",
"dlc_",
"ecm_",
"profileschemaversion",
"DesignPreview"
});
//modern header, footer, font settings have to be set with SetChromeOptions, Set[xxxx]FontPackage (pnpContext.Web.GetBrandingManager()) in order to work as expected
var brandingSettings = new List<string>(new[]
{
"headeroverlaycolor",
"headeroverlayopacity",
"headeroverlaygradientdirection",
"headercolorindexinlightmode",
"headercolorindexindarkmode",
"footeralignment",
"footeroverlaycolor",
"footeroverlayopacity",
"footeroverlaygradientdirection",
"footercolorindexinlightmode",
"footercolorindexindarkmode",
"fontoptionforsitetitle",
"fontoptionforsitenav",
"fontoptionforsitefootertitle",
"fontoptionforsitefooternav",
"footerbackgroundimageurl",
"footerbackgroundimagefocalpoint",
"backgroundimageurl"
});
// Check if this is not a noscript site as we're not allowed to write to the web property bag is that one
bool isNoScriptSite = web.IsNoScriptSite();
if (isNoScriptSite)
{
return parser;
}
// To handle situations where the propertybag is not updated fully when applying a theme,
// we need to create a new context and use that one. Reloading the propertybag does not solve this.
var webUrl = web.EnsureProperty(w => w.Url);
var newContext = web.Context.Clone(webUrl);
web = newContext.Web;
foreach (var propbagEntry in template.PropertyBagEntries.Where(p=> !brandingSettings.Contains(p.Key.ToLower())))
{
bool propExists = web.PropertyBagContainsKey(propbagEntry.Key);
if (propbagEntry.Overwrite)
{
var systemProp = systemPropertyBagEntriesExclusions.Any(k => propbagEntry.Key.StartsWith(k, StringComparison.OrdinalIgnoreCase));
if (!systemProp || (systemProp && applyingInformation.OverwriteSystemPropertyBagValues))
{
scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_PropertyBagEntries_Overwriting_existing_propertybag_entry__0__with_value__1_, propbagEntry.Key, propbagEntry.Value);
web.SetPropertyBagValue(propbagEntry.Key, parser.ParseString(propbagEntry.Value));
if (propbagEntry.Indexed)
{
web.AddIndexedPropertyBagKey(propbagEntry.Key);
}
}
}
else
{
if (!propExists)
{
scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_PropertyBagEntries_Creating_new_propertybag_entry__0__with_value__1__2_, propbagEntry.Key, propbagEntry.Value, propbagEntry.Indexed ? ",Indexed = true" : "");
web.SetPropertyBagValue(propbagEntry.Key, parser.ParseString(propbagEntry.Value));
if (propbagEntry.Indexed)
{
web.AddIndexedPropertyBagKey(propbagEntry.Key);
}
}
}
}
}
return parser;
}
public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo)
{
using (var scope = new PnPMonitoredScope(this.Name))
{
web.Context.Load(web, w => w.AllProperties, w => w.ServerRelativeUrl);
web.Context.ExecuteQueryRetry();
var entries = new List<PropertyBagEntry>();
var indexedProperties = web.GetIndexedPropertyBagKeys().ToList();
foreach (var propbagEntry in web.AllProperties.FieldValues)
{
var indexed = indexedProperties.Contains(propbagEntry.Key);
entries.Add(new PropertyBagEntry() { Key = propbagEntry.Key, Value = propbagEntry.Value.ToString(), Indexed = indexed });
}
template.PropertyBagEntries.Clear();
template.PropertyBagEntries.AddRange(entries);
// If a base template is specified then use that one to "cleanup" the generated template model
if (creationInfo.BaseTemplate != null)
{
template = CleanupEntities(template, creationInfo);
}
foreach (PropertyBagEntry propbagEntry in template.PropertyBagEntries)
{
propbagEntry.Value = Tokenize(propbagEntry.Value, web.ServerRelativeUrl);
}
}
return template;
}
private ProvisioningTemplate CleanupEntities(ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo)
{
ProvisioningTemplate baseTemplate = creationInfo.BaseTemplate;
foreach (var propertyBagEntry in baseTemplate.PropertyBagEntries)
{
int index = template.PropertyBagEntries.FindIndex(f => f.Key.Equals(propertyBagEntry.Key));
if (index > -1)
{
template.PropertyBagEntries.RemoveAt(index);
}
}
// Scan for "system" properties that should be removed as well. Below list contains
// prefixes of properties that will be dropped
List<string> systemPropertyBagEntriesExclusions = new List<string>(new string[]
{
"_",
"vti_",
"dlc_",
"ecm_",
"profileschemaversion",
"DesignPreview"
});
// Below property prefixes indicate properties that never can be dropped
List<string> systemPropertyBagEntriesInclusions = new List<string>(new string[]
{
"_PnP_"
});
systemPropertyBagEntriesInclusions.AddRange(creationInfo.PropertyBagPropertiesToPreserve);
var entriesToDelete = new List<PropertyBagEntry>();
// Prepare the list of property bag entries that will be dropped
foreach (var property in systemPropertyBagEntriesExclusions)
{
var results = from prop in template.PropertyBagEntries
where prop.Key.StartsWith(property, StringComparison.OrdinalIgnoreCase)
select prop;
entriesToDelete.AddRange(results);
}
// Remove the property bag entries that we want to forcifully keep
foreach (var property in systemPropertyBagEntriesInclusions)
{
var results = from prop in entriesToDelete
where prop.Key.StartsWith(property, StringComparison.OrdinalIgnoreCase)
select prop;
// Drop the found elements from the delete list
entriesToDelete = new List<PropertyBagEntry>(SymmetricExcept<PropertyBagEntry>(results, entriesToDelete));
}
// Delete the resulting list of property bag entries
foreach (var property in entriesToDelete)
{
template.PropertyBagEntries.Remove(property);
}
return template;
}
private static IEnumerable<T> SymmetricExcept<T>(IEnumerable<T> seq1, IEnumerable<T> seq2)
{
HashSet<T> hashSet = new HashSet<T>(seq1);
hashSet.SymmetricExceptWith(seq2);
return hashSet.Select(x => x);
}
public override bool WillProvision(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation applyingInformation)
{
if (!_willProvision.HasValue)
{
_willProvision = template.PropertyBagEntries.Any() && !web.IsNoScriptSite();
}
return _willProvision.Value;
}
public override bool WillExtract(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo)
{
if (!_willExtract.HasValue)
{
_willExtract = true;
}
return _willExtract.Value;
}
}
}