@@ -17,25 +17,31 @@ namespace Umbraco.Core.Services
17
17
internal class SectionService : ISectionService
18
18
{
19
19
private readonly IUserService _userService ;
20
+ private IEnumerable < Section > _allAvailableSections ;
20
21
private readonly IApplicationTreeService _applicationTreeService ;
22
+ private readonly IDatabaseUnitOfWorkProvider _uowProvider ;
21
23
private readonly CacheHelper _cache ;
24
+ internal const string AppConfigFileName = "applications.config" ;
25
+ private static string _appConfig ;
26
+ private volatile bool _isInitialized = false ;
27
+ private static readonly object Locker = new object ( ) ;
22
28
23
29
public SectionService (
24
30
IUserService userService ,
25
- IApplicationTreeService applicationTreeService ,
31
+ IApplicationTreeService applicationTreeService ,
32
+ IDatabaseUnitOfWorkProvider uowProvider ,
26
33
CacheHelper cache )
27
34
{
28
35
if ( applicationTreeService == null ) throw new ArgumentNullException ( "applicationTreeService" ) ;
29
36
if ( cache == null ) throw new ArgumentNullException ( "cache" ) ;
30
37
31
38
_userService = userService ;
32
39
_applicationTreeService = applicationTreeService ;
40
+ _uowProvider = uowProvider ;
33
41
_cache = cache ;
34
42
}
35
43
36
- internal const string AppConfigFileName = "applications.config" ;
37
- private static string _appConfig ;
38
- private static readonly object Locker = new object ( ) ;
44
+
39
45
40
46
/// <summary>
41
47
/// gets/sets the application.config file path
@@ -57,59 +63,92 @@ internal static string AppConfigFilePath
57
63
}
58
64
59
65
/// <summary>
60
- /// Ensures all available sections exist in the storage medium
66
+ /// Initializes the service with all available application plugins
61
67
/// </summary>
62
- /// <param name="existingSections"></param>
63
- public void Initialize ( IEnumerable < Section > existingSections )
68
+ /// <param name="allAvailableSections">
69
+ /// All application plugins found in assemblies
70
+ /// </param>
71
+ /// <remarks>
72
+ /// This is used to populate the app.config file with any applications declared in plugins that don't exist in the file
73
+ /// </remarks>
74
+ public void Initialize ( IEnumerable < Section > allAvailableSections )
64
75
{
65
- LoadXml ( doc =>
66
- {
67
- foreach ( var attr in existingSections )
68
- {
69
- doc . Root . Add ( new XElement ( "add" ,
70
- new XAttribute ( "alias" , attr . Alias ) ,
71
- new XAttribute ( "name" , attr . Name ) ,
72
- new XAttribute ( "icon" , attr . Icon ) ,
73
- new XAttribute ( "sortOrder" , attr . SortOrder ) ) ) ;
74
- }
75
- } , true ) ;
76
+ _allAvailableSections = allAvailableSections ;
76
77
}
77
78
78
79
/// <summary>
79
80
/// The cache storage for all applications
80
81
/// </summary>
81
82
public IEnumerable < Section > GetSections ( )
82
83
{
83
- return _cache . GetCacheItem (
84
+ return _cache . RuntimeCache . GetCacheItem < IEnumerable < Section > > (
84
85
CacheKeys . ApplicationsCacheKey ,
85
86
( ) =>
86
87
{
87
88
////used for unit tests
88
89
//if (_testApps != null)
89
90
// return _testApps;
90
91
91
- var tmp = new List < Section > ( ) ;
92
+ var list = ReadFromXmlAndSort ( ) ;
92
93
93
- LoadXml ( doc =>
94
+ //On first access we need to do some initialization
95
+ if ( _isInitialized == false )
96
+ {
97
+ lock ( Locker )
94
98
{
95
- foreach ( var addElement in doc . Root . Elements ( "add" ) . OrderBy ( x =>
96
- {
97
- var sortOrderAttr = x . Attribute ( "sortOrder" ) ;
98
- return sortOrderAttr != null ? Convert . ToInt32 ( sortOrderAttr . Value ) : 0 ;
99
- } ) )
99
+ if ( _isInitialized == false )
100
100
{
101
- var sortOrderAttr = addElement . Attribute ( "sortOrder" ) ;
102
- tmp . Add ( new Section ( addElement . Attribute ( "name" ) . Value ,
103
- addElement . Attribute ( "alias" ) . Value ,
104
- addElement . Attribute ( "icon" ) . Value ,
105
- sortOrderAttr != null ? Convert . ToInt32 ( sortOrderAttr . Value ) : 0 ) ) ;
101
+ //now we can check the non-volatile flag
102
+ if ( _allAvailableSections != null )
103
+ {
104
+ var hasChanges = false ;
105
+
106
+ LoadXml ( doc =>
107
+ {
108
+ //Now, load in the xml structure and update it with anything that is not declared there and save the file.
109
+
110
+ //NOTE: On the first iteration here, it will lazily scan all apps, etc... this is because this ienumerable is lazy
111
+ // based on the ApplicationRegistrar - and as noted there this is not an ideal way to do things but were stuck like this
112
+ // currently because of the legacy assemblies and types not in the Core.
113
+
114
+ //Get all the trees not registered in the config
115
+ var unregistered = _allAvailableSections
116
+ . Where ( x => list . Any ( l => l . Alias == x . Alias ) == false )
117
+ . ToArray ( ) ;
118
+
119
+ hasChanges = unregistered . Any ( ) ;
120
+
121
+ var count = 0 ;
122
+ foreach ( var attr in unregistered )
123
+ {
124
+ doc . Root . Add ( new XElement ( "add" ,
125
+ new XAttribute ( "alias" , attr . Alias ) ,
126
+ new XAttribute ( "name" , attr . Name ) ,
127
+ new XAttribute ( "icon" , attr . Icon ) ,
128
+ new XAttribute ( "sortOrder" , attr . SortOrder ) ) ) ;
129
+ count ++ ;
130
+ }
131
+
132
+ //don't save if there's no changes
133
+ return count > 0 ;
134
+ } , true ) ;
135
+
136
+ if ( hasChanges )
137
+ {
138
+ //If there were changes, we need to re-read the structures from the XML
139
+ list = ReadFromXmlAndSort ( ) ;
140
+ }
141
+ }
106
142
}
107
- } , false ) ;
108
- return tmp ;
143
+ }
144
+ }
145
+
146
+ return list ;
147
+
109
148
} ) ;
110
149
}
111
150
112
- internal void LoadXml ( Action < XDocument > callback , bool saveAfterCallback )
151
+ internal void LoadXml ( Func < XDocument , bool > callback , bool saveAfterCallbackIfChanged )
113
152
{
114
153
lock ( Locker )
115
154
{
@@ -119,9 +158,9 @@ internal void LoadXml(Action<XDocument> callback, bool saveAfterCallback)
119
158
120
159
if ( doc . Root != null )
121
160
{
122
- callback . Invoke ( doc ) ;
161
+ var changed = callback . Invoke ( doc ) ;
123
162
124
- if ( saveAfterCallback )
163
+ if ( saveAfterCallbackIfChanged && changed )
125
164
{
126
165
//ensure the folder is created!
127
166
Directory . CreateDirectory ( Path . GetDirectoryName ( AppConfigFilePath ) ) ;
@@ -194,6 +233,7 @@ public void MakeNew(string name, string alias, string icon, int sortOrder)
194
233
new XAttribute ( "name" , name ) ,
195
234
new XAttribute ( "icon" , icon ) ,
196
235
new XAttribute ( "sortOrder" , sortOrder ) ) ) ;
236
+ return true ;
197
237
} , true ) ;
198
238
199
239
//raise event
@@ -209,7 +249,7 @@ public void DeleteSection(Section section)
209
249
lock ( Locker )
210
250
{
211
251
//delete the assigned applications
212
- ApplicationContext . Current . DatabaseContext . Database . Execute (
252
+ _uowProvider . GetUnitOfWork ( ) . Database . Execute (
213
253
"delete from umbracoUser2App where app = @appAlias" ,
214
254
new { appAlias = section . Alias } ) ;
215
255
@@ -222,14 +262,41 @@ public void DeleteSection(Section section)
222
262
223
263
LoadXml ( doc =>
224
264
{
225
- doc . Root . Elements ( "add" ) . Where ( x => x . Attribute ( "alias" ) != null && x . Attribute ( "alias" ) . Value == section . Alias ) . Remove ( ) ;
265
+ doc . Root . Elements ( "add" ) . Where ( x => x . Attribute ( "alias" ) != null && x . Attribute ( "alias" ) . Value == section . Alias )
266
+ . Remove ( ) ;
267
+
268
+ return true ;
226
269
} , true ) ;
227
270
228
271
//raise event
229
272
OnDeleted ( section , new EventArgs ( ) ) ;
230
273
}
231
274
}
232
275
276
+ private List < Section > ReadFromXmlAndSort ( )
277
+ {
278
+ var tmp = new List < Section > ( ) ;
279
+
280
+ LoadXml ( doc =>
281
+ {
282
+ foreach ( var addElement in doc . Root . Elements ( "add" ) . OrderBy ( x =>
283
+ {
284
+ var sortOrderAttr = x . Attribute ( "sortOrder" ) ;
285
+ return sortOrderAttr != null ? Convert . ToInt32 ( sortOrderAttr . Value ) : 0 ;
286
+ } ) )
287
+ {
288
+ var sortOrderAttr = addElement . Attribute ( "sortOrder" ) ;
289
+ tmp . Add ( new Section ( addElement . Attribute ( "name" ) . Value ,
290
+ addElement . Attribute ( "alias" ) . Value ,
291
+ addElement . Attribute ( "icon" ) . Value ,
292
+ sortOrderAttr != null ? Convert . ToInt32 ( sortOrderAttr . Value ) : 0 ) ) ;
293
+ }
294
+ return false ;
295
+ } , false ) ;
296
+
297
+ return tmp ;
298
+ }
299
+
233
300
internal static event TypedEventHandler < Section , EventArgs > Deleted ;
234
301
private static void OnDeleted ( Section app , EventArgs args )
235
302
{
0 commit comments