Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ThemeConfiguration.cs
1using System;
3using System.IO;
4using System.Xml;
5using System.Xml.Schema;
6using System.Threading.Tasks;
7using Waher.Content;
9using Waher.Events;
17
19{
24 {
25 private readonly static Dictionary<string, ThemeDefinition> themeDefinitions = new Dictionary<string, ThemeDefinition>();
26 private static ThemeConfiguration instance = null;
27 private HttpResource setTheme = null;
28
29 private string themeId = string.Empty;
30 private Dictionary<string, object> themeIdByAlternativeHost;
31
36 : base()
37 {
38 }
39
43 public static ThemeConfiguration Instance => instance;
44
48 [DefaultValueStringEmpty]
49 public string ThemeId
50 {
51 get => this.themeId;
52 set => this.themeId = value;
53 }
54
60 public string GetThemeId(IHostReference HostReference)
61 {
62 string Host = DomainSettings.IsAlternativeDomain(HostReference.Host);
63 if (string.IsNullOrEmpty(Host))
64 return this.themeId;
65
66 lock (themeDefinitions)
67 {
68 if (themeDefinitions.TryGetValue(Host, out ThemeDefinition Def))
69 return Def.Id;
70 else
71 return this.themeId;
72 }
73 }
74
78 public override string Resource => "/Settings/Theme.md";
79
83 public override int Priority => 500;
84
90 public override Task<string> Title(Runtime.Language.Language Language)
91 {
92 return Language.GetStringAsync(typeof(Gateway), 5, "Theme");
93 }
94
98 public override Task ConfigureSystem()
99 {
100 if (!string.IsNullOrEmpty(this.themeId) && themeDefinitions.TryGetValue(this.themeId, out ThemeDefinition Def))
101 Theme.CurrentTheme = Def;
102
103 // TODO: GraphViz, PlantUml, LayoutXml for alternative domains.
104
105 return Task.CompletedTask;
106 }
107
112 public override void SetStaticInstance(ISystemConfiguration Configuration)
113 {
114 instance = Configuration as ThemeConfiguration;
115 }
116
117 private void CheckLoaded()
118 {
119 if (themeDefinitions.Count > 0)
120 return;
121
122 string ThemesFolder = Path.Combine(Gateway.AppDataFolder, "Root", "Themes");
123 if (!Directory.Exists(ThemesFolder))
124 return;
125
126 XmlSchema Schema = XSL.LoadSchema(typeof(Gateway).Namespace + ".Schema.Theme.xsd", typeof(Gateway).Assembly);
127 ThemeDefinition Def;
128
129 foreach (string FileName in Directory.GetFiles(ThemesFolder, "*.xml", SearchOption.AllDirectories))
130 {
131 try
132 {
133 XmlDocument Doc = XML.LoadFromFile(FileName, true);
134
135 XSL.Validate(FileName, Doc, "Theme", "http://waher.se/Schema/Theme.xsd", Schema);
136
137 Def = new ThemeDefinition(Doc);
138 themeDefinitions[Def.Id] = Def;
139 }
140 catch (Exception ex)
141 {
142 Log.Exception(ex, FileName);
143 continue;
144 }
145 }
146 }
147
152 public override async Task InitSetup(HttpServer WebServer)
153 {
154 await base.InitSetup(WebServer);
155
156 await WebServer.SetETagSalt(this.Updated.Ticks.ToString());
157
158 this.CheckLoaded();
159
160 bool Update = false;
161
162 if (!string.IsNullOrEmpty(this.themeId) && !themeDefinitions.ContainsKey(this.themeId))
163 {
164 this.themeId = string.Empty;
165 this.Step = 0;
166 this.Completed = DateTime.MinValue;
167 this.Complete = false;
168
169 Update = true;
170 }
171
172 if (string.IsNullOrEmpty(this.themeId) && themeDefinitions.Count == 1)
173 {
174 foreach (ThemeDefinition Def2 in themeDefinitions.Values)
175 {
176 this.themeId = Def2.Id;
177
178 await this.MakeCompleted();
179 Update = false;
180
181 break;
182 }
183 }
184
185 if (Update)
186 {
187 this.Updated = DateTime.Now;
188 await Database.Update(this);
189 }
190
191 if (!string.IsNullOrEmpty(this.themeId) && themeDefinitions.TryGetValue(this.themeId, out ThemeDefinition Def))
192 Theme.CurrentTheme = Def;
193 else if (themeDefinitions.TryGetValue("CactusRose", out Def))
194 Theme.CurrentTheme = Def;
195 else
196 {
197 foreach (ThemeDefinition Def2 in themeDefinitions.Values)
198 {
199 Theme.CurrentTheme = Def2;
200 break;
201 }
202 }
203
204 this.themeIdByAlternativeHost = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
205 foreach (KeyValuePair<string, object> P in await HostSettings.GetHostValuesAsync("ThemeId"))
206 this.themeIdByAlternativeHost[P.Key] = P.Value;
207
208 foreach (KeyValuePair<string, object> P in this.themeIdByAlternativeHost)
209 {
210 if (P.Value is string ThemeId && themeDefinitions.TryGetValue(ThemeId, out Def))
211 Theme.SetTheme(P.Key, Def);
212 }
213
214 this.setTheme = WebServer.Register("/Settings/SetTheme", null, this.SetTheme, true, false, true);
215 }
216
221 public override Task UnregisterSetup(HttpServer WebServer)
222 {
223 WebServer.Unregister(this.setTheme);
224
225 return base.UnregisterSetup(WebServer);
226 }
227
231 protected override string ConfigPrivilege => "Admin.Presentation.Theme";
232
233 private async Task SetTheme(HttpRequest Request, HttpResponse Response)
234 {
235 Gateway.AssertUserAuthenticated(Request, this.ConfigPrivilege);
236
237 if (!Request.HasData)
238 {
239 await Response.SendResponse(new BadRequestException());
240 return;
241 }
242
243 ContentResponse Content = await Request.DecodeDataAsync();
244 if (Content.HasError || !(Content.Decoded is string ThemeId))
245 {
246 await Response.SendResponse(new BadRequestException());
247 return;
248 }
249
250 string TabID = Request.Header["X-TabID"];
251 if (string.IsNullOrEmpty(TabID))
252 {
253 await Response.SendResponse(new BadRequestException());
254 return;
255 }
256
257 if (!themeDefinitions.TryGetValue(ThemeId, out ThemeDefinition Def))
258 {
259 await Response.SendResponse(new NotFoundException("Theme not found: " + ThemeId));
260 return;
261 }
262
263 string Host = DomainSettings.IsAlternativeDomain(Request.Host);
264 if (string.IsNullOrEmpty(Host))
265 {
266 Theme.CurrentTheme = Def;
267
268 this.themeId = Def.Id;
269
270 if (this.Step <= 0)
271 this.Step = 1;
272 }
273 else
274 {
275 lock (this.themeIdByAlternativeHost)
276 {
277 this.themeIdByAlternativeHost[Host] = Def;
278 }
279
280 await HostSettings.SetAsync(Host.ToLower(), "ThemeId", Def.Id);
281
282 Theme.SetTheme(Host, Def);
283
284 // TODO: GraphViz, PlantUml, LayoutXml colors.
285 }
286
287 this.Updated = DateTime.Now;
288 await Database.Update(this);
289
290 await Gateway.HttpServer.SetETagSalt(this.Updated.Ticks.ToString());
291
292 await ClientEvents.PushEvent(new string[] { TabID }, "ThemeOk", JSON.Encode(new KeyValuePair<string, object>[]
293 {
294 new KeyValuePair<string, object>("themeId", Def.Id),
295 new KeyValuePair<string, object>("cssUrl", Def.CSSX),
296 }, false), true, "User");
297
298 Response.StatusCode = 200;
299 Response.StatusMessage = "OK";
300 }
301
307 {
308 ThemeDefinition[] Result = new ThemeDefinition[themeDefinitions.Count];
309 themeDefinitions.Values.CopyTo(Result, 0);
310
311 Array.Sort(Result, (t1, t2) => t1.Title.CompareTo(t2.Title));
312
313 return Result;
314 }
315
322 public static bool TryGetTheme(string ThemeId, out ThemeDefinition Definition)
323 {
324 return themeDefinitions.TryGetValue(ThemeId, out Definition);
325 }
326
330 public const string GATEWAY_THEME_ID = nameof(GATEWAY_THEME_ID);
331
336 public override Task<bool> EnvironmentConfiguration()
337 {
338 string Value = Environment.GetEnvironmentVariable(GATEWAY_THEME_ID);
339 if (string.IsNullOrEmpty(Value))
340 return Task.FromResult(false);
341
342 this.CheckLoaded();
343
344 if (!themeDefinitions.ContainsKey(Value))
345 {
346 this.LogEnvironmentError("Theme not found.", GATEWAY_THEME_ID, Value);
347 return Task.FromResult(false);
348 }
349
350 this.themeId = Value;
351
352 return Task.FromResult(true);
353 }
354
355 }
356}
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Helps with common JSON-related tasks.
Definition: JSON.cs:16
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:537
Helps with common XML-related tasks.
Definition: XML.cs:21
static XmlDocument LoadFromFile(string FileName)
Loads an XML document from a file.
Definition: XML.cs:1808
Static class managing loading of XSL resources stored as embedded resources or in content files.
Definition: XSL.cs:16
static XmlSchema LoadSchema(string ResourceName)
Loads an XML schema from an embedded resource.
Definition: XSL.cs:24
static void Validate(string ObjectID, XmlDocument Xml, params XmlSchema[] Schemas)
Validates an XML document given a set of XML schemas.
Definition: XSL.cs:134
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
The ClientEvents class allows applications to push information asynchronously to web clients connecte...
Definition: ClientEvents.cs:51
static Task< int > PushEvent(string[] TabIDs, string Type, object Data)
Puses an event to a set of Tabs, given their Tab IDs.
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:147
static HttpServer HttpServer
HTTP Server
Definition: Gateway.cs:4118
static IUser AssertUserAuthenticated(HttpRequest Request, string Privilege)
Makes sure a request is being made from a session with a successful user login.
Definition: Gateway.cs:3868
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:3132
Returns the time elapsed since the gateway was started.
Definition: Runtime.cs:13
DateTime Updated
When the object was updated.
void LogEnvironmentError(string EnvironmentVariable, object Value)
Logs an error to the event log, telling the operator an environment variable value contains an error.
virtual Task MakeCompleted()
Sets the configuration task as completed.
Abstract base class for multi-step system configurations.
override void SetStaticInstance(ISystemConfiguration Configuration)
Sets the static instance of the configuration.
override int Priority
Priority of the setting. Configurations are sorted in ascending order.
static ThemeDefinition[] GetDefinitions()
Gets available theme definitions.
override Task< string > Title(Runtime.Language.Language Language)
Gets a title for the system configuration.
string GetThemeId(IHostReference HostReference)
Gets the Theme ID that corresponds to a host.
override async Task InitSetup(HttpServer WebServer)
Initializes the setup object.
override Task< bool > EnvironmentConfiguration()
Environment configuration by configuring values available in environment variables.
static bool TryGetTheme(string ThemeId, out ThemeDefinition Definition)
Tries to get the theme definition, given its ID.
override string ConfigPrivilege
Minimum required privilege for a user to be allowed to change the configuration defined by the class.
const string GATEWAY_THEME_ID
ID of theme to use.
override Task UnregisterSetup(HttpServer WebServer)
Unregisters the setup object.
override Task ConfigureSystem()
Is called during startup to configure the system.
static ThemeConfiguration Instance
Current instance of configuration.
Contains properties for a theme.
string Title
A human readable title for the theme.
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
Represents an HTTP request.
Definition: HttpRequest.cs:22
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:182
bool HasData
If the request has data.
Definition: HttpRequest.cs:113
string Host
Host reference. (Value of Host header, without the port number)
Definition: HttpRequest.cs:263
async Task< ContentResponse > DecodeDataAsync()
Decodes data sent in request.
Definition: HttpRequest.cs:139
Base class for all HTTP resources.
Definition: HttpResource.cs:23
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:23
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
Implements an HTTP server.
Definition: HttpServer.cs:41
bool Unregister(HttpResource Resource)
Unregisters a resource from the server.
Definition: HttpServer.cs:1743
async Task SetETagSalt(string NewSalt)
Sets a new salt value used when calculating ETag values.
Definition: HttpServer.cs:818
The server has not found anything matching the Request-URI. No indication is given of whether the con...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
Contains information about a language.
Definition: Language.cs:17
Task< string > GetStringAsync(Type Type, int Id, string Default)
Gets the string value of a string ID. If no such string exists, a string is created with the default ...
Definition: Language.cs:209
Contains information about a namespace in a language.
Definition: Namespace.cs:17
Static class managing persistent host settings. Host settings default to runtime settings if host-spe...
Definition: HostSettings.cs:18
static Task< Dictionary< string, object > > GetHostValuesAsync(string Key)
Gets available settings for a given key, indexed by host.
static async Task< bool > SetAsync(string Host, string Key, string Value)
Sets a string-valued setting.
Definition: HostSettings.cs:77
Interface for objects that contain a reference to a host.
string Host
Host reference.
Interface for system configurations. The gateway will scan all module for system configuration classe...