update to better support translations, multiple tenants, and more

This commit is contained in:
Michael Becker 2024-08-04 14:29:26 -04:00
parent 77a4ca3f93
commit b39eabccc2
8 changed files with 278 additions and 68 deletions

View File

@ -15,13 +15,13 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using System.Net;
using MBS.Core;
using MBS.Web;
using MBS.Web.UI;
using MBS.Web.UI.WebControls;
using Mocha.Core;
using Mocha.Core.OmsImplementations;
using Mocha.Core.OmsImplementations.Mini;
namespace Mocha.ServerApplication;
@ -37,7 +37,7 @@ public abstract class MochaWebApplication : WebApplication
/// Gets the <see cref="Mocha.Core.Oms" /> responsible for hosting data on the Mocha advanced database infrastructure.
/// </summary>
/// <value></value>
public Oms Oms { get; }
public Oms? Oms { get; private set; }
/// <summary>
/// Gets or sets the name of the tenant which should be navigated to
@ -46,73 +46,75 @@ public abstract class MochaWebApplication : WebApplication
/// <value></value>
public string DefaultTenantName { get; set; } = "super";
public MochaWebApplication()
protected override void OnBeforeStartInternal(System.ComponentModel.CancelEventArgs e)
{
Oms = new MiniOms();
Oms.Initialize();
base.OnBeforeStartInternal(e);
Oms = CreateOms();
Oms.BasePath = VirtualBasePath;
Oms.TenantName = "super";
Oms.TenantName = DefaultTenantName;
}
protected virtual Oms CreateOms()
{
Oms oms = new MemoryOms();
TenantHandle super = oms.CreateTenant("super");
oms.SelectTenant(super);
oms.Initialize();
return oms;
}
class LoginWebPage : WebPage
{
protected override void InitializeInternal()
{
base.InitializeInternal();
protected override void InitializeInternal()
{
base.InitializeInternal();
MochaWebApplication app = ((MochaWebApplication)Application.Instance);
InstanceHandle loginHeaderImage = app.Oms.GetInstance(new Guid("{c4f31b1a-aede-4e91-9fa0-511537f098a5}"));
StyleSheets.Add(WebStyleSheet.FromContent("text/css", @"
table.uwt-formview td.uwt-formview-item-label { padding-top: 10px; }
table.uwt-formview td.uwt-formview-item-content { padding-bottom: 8px; }
body > form > div.uwt-panel
{
margin-right: auto;
margin-left: auto;
width: 600px;
margin-top: 64px;
}
InstanceHandle loginHeaderImage;
if (app.Oms.TryGetInstance(new Guid("{c4f31b1a-aede-4e91-9fa0-511537f098a5}"), out loginHeaderImage))
{
StyleSheets.Add(WebStyleSheet.FromContent("text/css", @"
body > form > div.uwt-panel > div.uwt-content > div:first-child
{
background-image: url('" + app.Oms.GetAttachmentUrl(loginHeaderImage) + @"');
background-repeat: no-repeat;
background-size: cover;
height: 256px;
width: 256px;
margin-left: auto;
margin-right: auto;
margin-top: 32px;
margin-bottom: 32px;
}
"));
}
protected override void CreateChildControls()
{
base.CreateChildControls();
}
}
protected override void CreateChildControls()
{
base.CreateChildControls();
Oms oms = (Oms)((MochaWebApplication)Application.Instance).Oms;
InstanceHandle c_Tenant = oms.GetInstance(KnownInstanceGuids.Classes.Tenant);
InstanceHandle i_Tenant = oms.GetInstancesOf(c_Tenant).First();
string loginHeaderText = oms.GetTranslationValue(i_Tenant, oms.GetInstance(KnownRelationshipGuids.Tenant__has_login_header__Translation));
Panel panel = new Panel();
panel.ContentControls.Add(new Container());
panel.ContentControls.Add(new Heading(1, loginHeaderText));
panel.ContentControls.Add(new FormView(new FormView.FormViewItem[]
{
new FormView.FormViewItem("User _name", new TextBox() { Name = "username" }),
new FormView.FormViewItem("_Password", new TextBox(TextBoxType.Password) { Name = "password" } )
}));
panel.FooterControls.Add(new Button("_Log In"));
panel.FooterControls.Add(new Button("_Log In") { UseSubmitBehavior = true });
Controls.Add(panel);
}
}
protected override void OnProcessRequest(WebServerProcessRequestEventArgs e)
{
base.OnProcessRequest(e);
Literal footer = new Literal("<div class=\"uwt-copyright-text\">Powered by Mocha<br />&#169; 2024 MBS Business Solutions</div>");
Controls.Add(footer);
}
}
protected override void OnProcessRequest(WebServerProcessRequestEventArgs e)
{
base.OnProcessRequest(e);
MochaWebApplication app = ((MochaWebApplication)Application.Instance);
@ -162,11 +164,11 @@ body > form > div.uwt-panel > div.uwt-content > div:first-child
e.Context.Response.Redirect("~/" + parts[1] + "/d/home.htmld");
e.Handled = true;
}
}
}
protected override void OnServerCreated(WebServerCreatedEventArgs e)
{
base.OnServerCreated(e);
protected override void OnServerCreated(WebServerCreatedEventArgs e)
{
base.OnServerCreated(e);
e.Server.UserAgent = "Mocha User Interface Service";
@ -181,6 +183,8 @@ body > form > div.uwt-panel > div.uwt-content > div:first-child
e.Server.Routes.Add(new WebRoute("/madi/authgwy/{tenant}/login.htmld", new LoginWebPage()));
e.Server.Routes.Add(new WebRoute("/madi/asset/{name}/{version}/{path}", new AssetWebHandler()));
e.Server.Routes.Add(new WebRoute("/{tenant}/attachment/{iid}/{accesskey}", new AttachmentWebHandler()));
}
Console.WriteLine("Mocha User Interface Service started - http://localhost:{0}", DefaultPort);
}
}

View File

@ -18,6 +18,8 @@
using System.Net;
using MBS.Core;
using MBS.Web;
using Mocha.Core;
using Mocha.Core.OmsImplementations.Mini;
namespace Mocha.ServerApplication;
@ -25,12 +27,42 @@ public class Program : MochaWebApplication
{
protected override int DefaultPort => 10020;
public Program()
public Program()
{
ShortName = "mocha-server";
}
public static int Main(string[] args)
protected override Oms CreateOms()
{
// we can override this here to provide a different default OMS
MiniOms oms = new MiniOms();
oms.Initialize();
TenantHandle t_super = oms.GetTenantByName("super");
{
oms.SelectTenant(t_super);
InstanceHandle c_Tenant = oms.GetInstance(KnownInstanceGuids.Classes.Tenant);
InstanceHandle i_Tenant = oms.GetInstancesOf(c_Tenant).First();
InstanceHandle i_English = oms.GetInstance(KnownInstanceGuids.Languages.English);
InstanceHandle r_Tenant__has_login_header__Translation = oms.GetInstance(KnownRelationshipGuids.Tenant__has_login_header__Translation);
oms.SetTranslationValue(i_Tenant, r_Tenant__has_login_header__Translation, i_English, "Welcome to your New Tenant");
}
TenantHandle t_wdoms = oms.CreateTenant("wdoms");
{
oms.SelectTenant(t_wdoms);
InstanceHandle c_Tenant = oms.GetInstance(KnownInstanceGuids.Classes.Tenant);
InstanceHandle i_Tenant = oms.GetInstancesOf(c_Tenant).First();
InstanceHandle i_English = oms.GetInstance(KnownInstanceGuids.Languages.English);
InstanceHandle r_Tenant__has_login_header__Translation = oms.GetInstance(KnownRelationshipGuids.Tenant__has_login_header__Translation);
oms.SetTranslationValue(i_Tenant, r_Tenant__has_login_header__Translation, i_English, "OMS Tenant Manager");
}
return oms;
}
public static int Main(string[] args)
{
return (new Program()).Start();
}

View File

@ -45,6 +45,7 @@ namespace Mocha.Core
public static Guid File { get; } = new Guid("{5D081079-E136-406A-A728-7472937A6E5E}");
public static Guid Language { get; } = new Guid("{61102B47-9B2F-4CF3-9840-D168B84CF1E5}");
public static Guid Translation { get; } = new Guid("{04A53CC8-3206-4A97-99C5-464DB8CAA6E6}");
public static Guid TranslationValue { get; } = new Guid("{6D38E757-EC18-43AD-9C35-D15BB446C0E1}");
@ -410,5 +411,16 @@ namespace Mocha.Core
public static Guid NotExactMatchWithSelectionList { get; } = new Guid("{f08a632a-1f3e-4979-8ec5-17fefc33b865}");
public static Guid ExactMatchWithSelectionList { get; } = new Guid("{93611da1-e21e-4021-9ddd-f7575e0c79ec}");
}
public static class Languages
{
public static Guid English { get; } = new Guid ("{68BB6038-A4B5-4EE1-AAE9-326494942062}");
public static Guid Spanish { get; } = new Guid ("{6dc357cb-37c3-43ed-ae13-6259fb109213}");
public static Guid French { get; } = new Guid ("{6bf0cf09-87c9-4e21-b360-7eb5a1c279de}");
public static Guid German { get; } = new Guid ("{c7c1d740-0d3c-493f-ab0b-fe1b42546d0a}");
public static Guid Italian { get; } = new Guid ("{cf165170-0680-4a41-8f88-88f34b2b1986}");
public static Guid Chinese { get; } = new Guid ("{6f908a9b-7464-4a16-aed9-7eccb8d39032}");
public static Guid Japanese { get; } = new Guid ("{1e16de9d-0e49-4a79-b690-4905c46a94cc}");
public static Guid Korean { get; } = new Guid ("{d03a795e-906b-49ee-87ea-c1bef4b8ee9a}");
}
}
}

View File

@ -74,7 +74,9 @@ namespace Mocha.Core
public static Guid Relationship__has_sibling__Relationship { get; } = new Guid("{656110FF-4502-48B8-A7F3-D07F017AEA3F}");
public static Guid Translation__has__Translation_Value { get; } = new Guid("{F9B60C00-FF1D-438F-AC74-6EDFA8DD7324}");
public static Guid Translation_Value__for__Translation { get; } = new Guid("{458c218a-9965-44ba-884b-deda5bf2931d}");
public static Guid Translation_Value__has__Language { get; } = new Guid("{3655AEC2-E2C9-4DDE-8D98-0C4D3CE1E569}");
public static Guid Language__for__Translation_Value { get; } = new Guid("{bf885bb1-da84-447c-95dc-143ce9deac65}");
public static Guid String__has__String_Component { get; } = new Guid("{3B6C4C25-B7BC-4242-8ED1-BA6D01B834BA}");
public static Guid Extract_Single_Instance_String_Component__has__Relationship { get; } = new Guid("{5E499753-F50F-4A9E-BF53-DC013820499C}");
@ -241,8 +243,8 @@ namespace Mocha.Core
public static Guid Tenant__has_logo_image__File { get; } = new Guid("{4C399E80-ECA2-4A68-BFB4-26A5E6E97047}");
public static Guid Tenant__has_background_image__File { get; } = new Guid("{39B0D963-4BE0-49C8-BFA2-607051CB0101}");
public static Guid Tenant__has_icon_image__File { get; } = new Guid("{CC4E65BD-7AAA-40DA-AECA-C607D7042CE3}");
public static Guid Tenant__has_login_header__Translatable_Text_Constant { get; } = new Guid("{41D66ACB-AFDE-4B6F-892D-E66255F10DEB}");
public static Guid Tenant__has_login_footer__Translatable_Text_Constant { get; } = new Guid("{A6203B6B-5BEB-4008-AE49-DB5E7DDBA45B}");
public static Guid Tenant__has_login_header__Translation { get; } = new Guid("{41D66ACB-AFDE-4B6F-892D-E66255F10DEB}");
public static Guid Tenant__has_login_footer__Translation { get; } = new Guid("{A6203B6B-5BEB-4008-AE49-DB5E7DDBA45B}");
public static Guid Tenant__has_application_title__Translation { get; } = new Guid("{76683437-67ba-46d9-a5e7-2945be635345}");
public static Guid Tenant__has_mega__Menu { get; } = new Guid("{cdd743cb-c74a-4671-9922-652c7db9f2d8}");

View File

@ -57,7 +57,13 @@ public abstract class Oms
MethodImplementation[] methodImplementations = MBS.Core.Reflection.TypeLoader.GetAvailableTypes<MethodImplementation>(new System.Reflection.Assembly[] { Assembly.GetExecutingAssembly() });
foreach (MethodImplementation impl in methodImplementations)
{
RegisterMethodImplementation(GetInstance(impl.MethodClassGuid), impl);
if (TryGetInstance(impl.MethodClassGuid, out InstanceHandle methodClassInstance))
{
RegisterMethodImplementation(methodClassInstance, impl);
}
else{
Console.WriteLine("failed to load method type '{0}' from instance {1} (inst not found)", impl.GetType().FullName, impl.MethodClassGuid);
}
}
}
@ -70,7 +76,15 @@ public abstract class Oms
protected abstract TenantHandle CreateTenantInternal(string tenantName);
public TenantHandle CreateTenant(string tenantName)
{
return CreateTenantInternal(tenantName);
Console.WriteLine("oms: creating tenant '{0}'", tenantName);
TenantHandle tenant = CreateTenantInternal(tenantName);
RegisterTenant(tenantName, tenant);
SelectTenant(tenant);
InitializeTenant();
return tenant;
}
public TenantHandle GetTenantByName(string tenantName)
@ -185,7 +199,7 @@ public abstract class Oms
{
return ih;
}
throw new KeyNotFoundException();
throw new KeyNotFoundException(String.Format("inst not found: {0}", globalIdentifier));
}
public InstanceHandle GetInstance(InstanceKey ik)
{
@ -193,7 +207,7 @@ public abstract class Oms
{
return ih;
}
throw new KeyNotFoundException();
throw new KeyNotFoundException(String.Format("inst not found: {0}", ik));
}
public bool TryGetInstance<T>(Guid globalIdentifier, out T instance) where T : ConcreteInstanceWrapper
@ -914,4 +928,94 @@ public abstract class Oms
}
return c_Enum;
}
public string GetTranslationValue(InstanceHandle sourceInstance, InstanceHandle relationshipInstance, string defaultValue = "")
{
//FIXME: do not hardcode hat is supposed to be a result of a call to `User@get preferred display Language`
InstanceHandle languageInstance = GetInstance(KnownInstanceGuids.Languages.English);
return GetTranslationValue(sourceInstance, relationshipInstance, languageInstance, defaultValue);
}
public string GetTranslationValue(InstanceHandle sourceInstance, InstanceHandle relationshipInstance, InstanceHandle languageInstance, string defaultValue = "")
{
InstanceHandle c_Translation = GetInstance(KnownInstanceGuids.Classes.Translation);
InstanceHandle c_TranslationValue = GetInstance(KnownInstanceGuids.Classes.TranslationValue);
InstanceHandle a_Value = GetInstance(KnownAttributeGuids.Text.Value);
InstanceHandle r_Translation__has__Translation_Value = GetInstance(KnownRelationshipGuids.Translation__has__Translation_Value);
InstanceHandle r_Translation_Value__has__Language = GetInstance(KnownRelationshipGuids.Translation_Value__has__Language);
InstanceHandle translation = GetRelatedInstance(sourceInstance, relationshipInstance);
if (translation != InstanceHandle.Empty)
{
if (!IsInstanceOf(translation, c_Translation))
{
// the relationship was found, but it is not a Translation, so fail hard - something's wrong
throw new InvalidOperationException();
}
// find the translation value for the given language and update it if found
IEnumerable<InstanceHandle> translationValues = GetRelatedInstances(translation, r_Translation__has__Translation_Value);
foreach (InstanceHandle translationValue in translationValues)
{
InstanceHandle language = GetRelatedInstance(translationValue, r_Translation_Value__has__Language);
if (language == languageInstance)
{
string value = GetAttributeValue<string>(translationValue, a_Value);
return value;
}
}
}
return defaultValue;
}
public void SetTranslationValue(InstanceHandle sourceInstance, InstanceHandle relationshipInstance, InstanceHandle languageInstance, string value)
{
InstanceHandle c_Translation = GetInstance(KnownInstanceGuids.Classes.Translation);
InstanceHandle c_TranslationValue = GetInstance(KnownInstanceGuids.Classes.TranslationValue);
InstanceHandle a_Value = GetInstance(KnownAttributeGuids.Text.Value);
InstanceHandle r_Translation__has__Translation_Value = GetInstance(KnownRelationshipGuids.Translation__has__Translation_Value);
InstanceHandle r_Translation_Value__has__Language = GetInstance(KnownRelationshipGuids.Translation_Value__has__Language);
InstanceHandle translation = GetRelatedInstance(sourceInstance, relationshipInstance);
if (translation != InstanceHandle.Empty)
{
if (!IsInstanceOf(translation, c_Translation))
{
// the relationship was found, but it is not a Translation, so fail hard - something's wrong
throw new InvalidOperationException();
}
// find the translation value for the given language and update it if found
IEnumerable<InstanceHandle> translationValues = GetRelatedInstances(translation, r_Translation__has__Translation_Value);
foreach (InstanceHandle translationValue in translationValues)
{
InstanceHandle language = GetRelatedInstance(translationValue, r_Translation_Value__has__Language);
if (language == languageInstance)
{
SetAttributeValue(translationValue, a_Value, value);
return;
}
}
}
else
{
translation = CreateInstanceOf(c_Translation);
AssignRelationship(sourceInstance, relationshipInstance, translation);
}
// if we reach here, we didn't find a `Translation Value` for the given language, so create it
InstanceHandle newTranslationValue = CreateInstanceOf(c_TranslationValue);
AssignRelationship(newTranslationValue, r_Translation_Value__has__Language, languageInstance);
AssignRelationship(translation, r_Translation__has__Translation_Value, newTranslationValue);
SetAttributeValue(newTranslationValue, a_Value, value);
}
public void InitializeTenant()
{
ValidateConstraints = false;
InitializeTenantInternal();
ValidateConstraints = true;
}
protected virtual void InitializeTenantInternal()
{
}
}

View File

@ -200,8 +200,6 @@ public class MemoryOms : Oms
{
TenantHandle th = TenantHandle.Create();
_tenantData[th] = new _Tenant();
RegisterTenant(tenantName, th);
return th;
}
protected override void SelectTenantInternal(TenantHandle tenant)

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,55 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.OmsImplementations.Mini.Modules;
public class TranslationModule : MiniOmsModule
{
private InstanceHandle c_Tenant, c_Translation, c_TranslationValue, c_Language;
private InstanceHandle a_Name, a_Value;
private InstanceHandle r_Translation__has__Translation_Value, r_Translation_Value__has__Language;
private InstanceHandle r_Tenant__has_login_header__Translation, r_Tenant__has_login_footer__Translation;
protected override void BuildInternal(Oms oms)
{
a_Name = oms.GetInstance(KnownAttributeGuids.Text.Name);
a_Value = oms.GetInstance(KnownAttributeGuids.Text.Value);
c_Language = oms.CreateClass("Language", KnownInstanceGuids.Classes.Language);
oms.AddAttribute(c_Language, a_Name);
oms.CreateInstanceOf(c_Language, KnownInstanceGuids.Languages.English);
oms.CreateInstanceOf(c_Language, KnownInstanceGuids.Languages.Spanish);
oms.CreateInstanceOf(c_Language, KnownInstanceGuids.Languages.French);
oms.CreateInstanceOf(c_Language, KnownInstanceGuids.Languages.German);
oms.CreateInstanceOf(c_Language, KnownInstanceGuids.Languages.Italian);
oms.CreateInstanceOf(c_Language, KnownInstanceGuids.Languages.Chinese);
oms.CreateInstanceOf(c_Language, KnownInstanceGuids.Languages.Japanese);
oms.CreateInstanceOf(c_Language, KnownInstanceGuids.Languages.Korean);
c_Translation = oms.CreateClass("Translation", KnownInstanceGuids.Classes.Translation);
c_TranslationValue = oms.CreateClass("Translation Value", KnownInstanceGuids.Classes.TranslationValue);
oms.AddAttribute(c_TranslationValue, a_Value);
r_Translation__has__Translation_Value = oms.CreateRelationship(c_Translation, "has", c_TranslationValue, KnownRelationshipGuids.Translation__has__Translation_Value, false, "for", KnownRelationshipGuids.Translation_Value__for__Translation);
r_Translation_Value__has__Language = oms.CreateRelationship(c_TranslationValue, "has", c_Language, KnownRelationshipGuids.Translation_Value__has__Language, true, "for", KnownRelationshipGuids.Language__for__Translation_Value);
c_Tenant = oms.GetInstance(KnownInstanceGuids.Classes.Tenant);
r_Tenant__has_login_header__Translation = oms.CreateRelationship(c_Tenant, "has login header", c_Translation, KnownRelationshipGuids.Tenant__has_login_header__Translation, true);
r_Tenant__has_login_footer__Translation = oms.CreateRelationship(c_Tenant, "has login footer", c_Translation, KnownRelationshipGuids.Tenant__has_login_footer__Translation, true);
}
}