oqtane / oqtane.framework Goto Github PK
View Code? Open in Web Editor NEWCMS & Application Framework for Blazor & .NET MAUI
Home Page: http://www.oqtane.org
License: MIT License
CMS & Application Framework for Blazor & .NET MAUI
Home Page: http://www.oqtane.org
License: MIT License
See #24
To reproduce:
You will get the following error:
System.Text.Json.JsonException
HResult=0x80131500
Message=The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
Source=System.Text.Json
StackTrace:
at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& readStack, JsonReaderException ex)
at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack)
at System.Text.Json.JsonSerializer.ReadCore(Type returnType, JsonSerializerOptions options, Utf8JsonReader& reader)
at System.Text.Json.JsonSerializer.ParseCore(String json, Type returnType, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options)
at Oqtane.Repository.PermissionRepository.DecodePermissions(String PermissionStrings, Int32 SiteId, String EntityName, Int32 EntityId) in C:\Siliqon\oqtane\sbwalker.oqtane.framework\Oqtane.Server\Repository\PermissionRepository.cs:line 163
at Oqtane.Repository.PermissionRepository.UpdatePermissions(Int32 SiteId, String EntityName, Int32 EntityId, String Permissions) in C:\Siliqon\oqtane\sbwalker.oqtane.framework\Oqtane.Server\Repository\PermissionRepository.cs:line 71
at Oqtane.Repository.PageRepository.DeletePage(Int32 PageId) in C:\Siliqon\oqtane\sbwalker.oqtane.framework\Oqtane.Server\Repository\PageRepository.cs:line 65
at Oqtane.Controllers.PageController.Delete(Int32 id) in C:\Siliqon\oqtane\sbwalker.oqtane.framework\Oqtane.Server\Controllers\PageController.cs:line 89
at Microsoft.Extensions.Internal.ObjectMethodExecutor.<>c__DisplayClass33_0.<WrapVoidMethod>b__0(Object target, Object[] parameters)
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.VoidResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
Inner Exception 1:
JsonReaderException: The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. LineNumber: 0 | BytePositionInLine: 0.
Microsoft released .NET Core 3.0 Preview 5 on May 6. Currently Oqtane only runs on Preview 4 and will not compile on Preview 5. We should try to stay up to date with the latest Microsoft releases.
In addition to User, Roles and Permissions, are these the other core entities?
Something to add or something to remove?
The current admin user experience in Oqtane diplays a Delete option for each entity record in the summary grid view. When the user selects the Delete option it displays a distinct Delete component where all of the fields for the entity are displayed in readonly mode. The user can choose to Delete or Cancel - and if they choose Delete it removes the record ( either via soft delete or hard delete depending on the entity ).
The reason for this approach is that Delete is a serious/permanent action, so you want to provide the opportunity for a user to either confirm or cancel the action. Displaying a Delete component accomplishes this goal; however, the downside is that requires the development and ongoing maintenance of an additional component which contains more code/functionality than is truly necessary to accomplish the original goal.
Another common approach in product development is to simply display a Confirmation popup dialog when a user selects Delete from the summary grid view. This still provides the opportunity for a user to confirm/cancel the action - and it eliminates the need for creating a separate dedicated Delete component.
I would like to hear some feedback on whether Oqtane should transition to the Confirmation popup dialog approach.
Bootstrap is currently using a Beta version of 4.0.0. The CSS is currently loaded via a local minified CSS file. However, the JS is loaded via CDN. Bootstrap should be upgraded to 4.3.1 and fully loaded via CDN.
I noticed there's no documentation in the code. This was a big issue with DNN which had no documentation comments for many years before some users started adding them. Lack of proper documentation was a major pain for DNN. I hope this is not repeated again with Oqtane. Starting to add documentation comments early on before it becomes a daunting task will make it simpler and encourage developers to participate in advancing the framework. These comments help a lot also when using a documentation generator.
As we move more and more to the cloud, things like function apps make more sense when it comes to deploy ability and maintenance. Have you thought about examples where you decouple the server/ui and creating server less function endpoints?
Based on the terms used in other platforms, I would suggest a rename of the Skins concept to be Theme. This more closely aligns to other tools, which will help with understanding/adoptions.
I would gladly submit a PR for this, but would want to be sure it was desired before I do
There's no documentation and no tooltips to tell us what some of the vague options are.
For example, the Path field when creating a new page. I don't know what that this. A tooltip next to the label explaining what the field is for and an example value goes a long way.
One of the problems with a lot of frameworks like these is they are built w/out any 3rd party controls like DevExpress, Telerik, SynFusion, etc. So then when you start programming, some of the UI elements tend to look "off" because maybe some of the pages that are defied in the framework havent been updated to use the other control sets.
I havent looked at the framework fully yet, but it would be nice if you implemented helpers to render out controls that way they could be overridden easily if we wanted to use DevExpress editors and such.
Or is that like a "theme" instead?
To reproduce:
Microsoft.EntityFrameworkCore.DbUpdateException
HResult=0x80131500
Message=An error occurred while updating the entries. See the inner exception for details.
Source=Microsoft.EntityFrameworkCore.Relational
StackTrace:
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(DbContext _, ValueTuple`2 parameters)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList`1 entries)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
at Oqtane.Repository.DBContextBase.SaveChanges() in C:\Siliqon\oqtane\sbwalker.oqtane.framework\Oqtane.Server\Repository\Context\DBContextBase.cs:line 91
at Oqtane.Repository.PageModuleRepository.AddPageModule(PageModule PageModule) in C:\Siliqon\oqtane\sbwalker.oqtane.framework\Oqtane.Server\Repository\PageModuleRepository.cs:line 41
at Oqtane.Controllers.PageModuleController.Post(PageModule PageModule) in C:\Siliqon\oqtane\sbwalker.oqtane.framework\Oqtane.Server\Controllers\PageModuleController.cs:line 44
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
Inner Exception 1:
SqlException: Cannot insert the value NULL into column 'Title', table 'Oqtane.dbo.PageModule'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Currently the layout of Theme 1 is done using a container for the main content that adds some unnatural padding to the left & right, which when showcasing the power of things looks unusual. With a left-menu structure this should be updated to use a fluid container to properly manage the layout and improve look and feel.
What you think about move code into page.razor.cs file?
Visual Studio has better intellisense in .cs than .razor files
Ability to support connecting to Azure SQL using Managed Identities. We host our DBs in azure, we create a managed identity for the app, and give it auth to the DB it needs, this means we don't need to store usernames and passwords in the connection string thus its more secure.
I would think a toggle on the tenant creation.
REF:
https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi
public MyDatabaseContext(SqlConnection conn) : base(conn, true)
{
conn.ConnectionString = WebConfigurationManager.ConnectionStrings["MyDbConnection"].ConnectionString;
// DataSource != LocalDB means app is running in Azure with the SQLDB connection string you configured
if(conn.DataSource != "(localdb)\\MSSQLLocalDB")
conn.AccessToken = (new AzureServiceTokenProvider()).GetAccessTokenAsync("https://database.windows.net/").Result;
Database.SetInitializer<MyDatabaseContext>(null);
}
It would be great to be able to create some global users that have access into each "tenant" with a set of defined roles/permissions.
We use multi tenancy frameworks as a way to isolate our clients, but our internal people could work on multiple accounts, its not like their permissions change from tenant to tenant, so in most frameworks we have to create users in each tenant then assigned roles/permissions. Some of this can be automatic behind the scenes for new tenants when we add them, otherwise we have to run scripts that update 50+ DB with a new user or a new role. But we also need the ability to have external users from the client access things, which would be setup in the tenant db as normal.
Note: 1 DB with multiple tenants accessing the same DB is not a viable solution when you need to maintain tenancy data isolation. IE: Healthcare or competing companies.
From the "host" it would be great to be able to create the user and assign the roles/permissions, if behind the scene it "syncs" these accounts/roles/permissions its fine also, but not having to do it via sql scripts or migrations would be wonderful :).
When a Url is manually entered into the browser and the path does not match a page defined in the database for the site, the system should respond with a Page Not Found message
Is there a diagram / flow chart showing how the routing is handled for Oqtane? I am having a hard time trying to piece together in which order components are built buy the system to handle a routing request.
UI/UX standards indicate that deletion operations should be indicated with a red color. The control should support the designation of different types based on the theme. Such as btn-primary (the default) and btn-danger, or otherwise.
IMHO introducing a IRepository<TType>
will save time instead of creating I{Type}Repository
for every entity
For instance IJobRepository
will be:
public interface IJobRepository : IRepository<Job>
{
}
instead of
public interface IJobRepository
{
IEnumerable<Job> GetJobs();
Job AddJob(Job Job);
Job UpdateJob(Job Job);
Job GetJob(int JobId);
void DeleteJob(int JobId);
}
or we could implement the generic repository directly from within JobRepository
Sometimes we may need to create custom on such IUserRepository
:
public interface IUserRepository<User> : IRepository<User>
{
User GetUser(string Username);
}
There is no argument given that corresponds to the required formal parameter 'Restart' of 'IInstallationManager.InstallPackages(string, bool)' Oqtane.Server C:...\oqtane.framework\Oqtane.Server\Startup.cs 368
Steps to reproduce issue:
The screen will say "Error Loading Module Container"
Expected Result:
Note: The only way to clear this is to go to the database and remove the latest record in the PageModule table and then the latest record in the Module table.
Discussion
Rather than prevent a person from adding a module without a container, I suggest we allow a person to add a module without a container but not have it throw an error?
I would be willing to work on this, but did not want to do so without guidance
Should Oqtane be update to 3.1 preview 1 to support partial class for razor component and use code behind files?
Whether you are running the application in a client-side or server-side hosting model, it is a SPA and relies on client-side routing. In ASP.NET Core 3 Preview6 the client-side routing is broken. Currently in the server-side hosting model, every time you click a navigation link from the menu, it displays the message โAttempting to reconnect to the serverโ and it reloads the entire page ( and never retains any application state ). In the client-side model, when you click a navigation link it displays the initial splash page image for Oqtane and then renders the entire page ( and never retains any application state ).
In some controller you are using only AdminRole, why you don't use a framework built-in policy?
With policy even another role with specific policy (permission) can delete, add, edit, etc...
As example in UserController something like:
// DELETE api/<controller>/5?siteid=x
[HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole, Policy = Constants.DeleteUser)]
public void Delete(int id, string siteid)
instead of
// DELETE api/<controller>/5?siteid=x
[HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole)]
public void Delete(int id, string siteid)
Using:
VS Community 2019 Preview 16.3.0,
.NET Core 3.0 Preview 8 SDK (3.0.0-preview8-013656). Also tried updating to preview9-014004 with same result. These are the latest versions available as of today (14 Sep).
Build fails with error:
The type or namespace name 'RenderTreeBuilder' does not exist in the namespace 'Microsoft.AspNetCore.Components.Rendering'
in ~50 generated code files (e.g. Oqtane.Client\obj\Debug\netstandard2.0\RazorDeclaration\Modules\Counter\Index.razor.g.cs)
I did note the compatibility requirements mentioned on https://github.com/oqtane/oqtane.framework however, it does not seem possible to download 3.0.0-preview8.19405.7 now.
I've been reading through some of the code and I have a interest in NoSQL solutions (MongoDB is my preferred NoSQL of choice). As I was looking around how the code interacts with the SQL database as it sits now I see potential areas of improvement:
Create a IDatabase interface that the modules can then use for CRUD operations. This would allow the creation of different database providers that can be switched out at startup. I understand this would be a quiet large task to undertake, but feel it would allow a much more flexible system to be built.
If Oqtane was deployed to a system that auto-scales (like Kubernetes in my instance and I'm sure something like Azure) doing a replace on the app settings file would only change the one pod instance that did the install and all other pods wouldn't get this change. There are ways around this with persistent volume claims but its not a very straight forward process. Could thought be given to how this could be handled in a scalable environment?
But other then that this seems like an amazing framework and should get people up and running with Blazor and a website very easily. I cant wait to get my hands into creating some modules and themes for this.
seems like low activity for a while - just curious if it's still alive
There is a reason why you don't use Blazor built-in EditForm in Add and Edit pages?
I tried the following:
When running on server-side Blazor ( SignalR ) if you select the Login button and enter any valid user credentials ( ie. host/host ) the user does not appear to be immediately logged in - you need to navigate to another page in the site ( ie. Page2, Page3 ) and then the UI will be refreshed to reflect that you are logged in. This is because the JSInterop call in the SiteRouter to get the cookie for the user throws an exception. This problem does not exist when running on client-side Blazor ( Wasm ).
Hello
I'm running VS 16.3 preview 1 - but still on Blazor preview 6. But I've uninstalled the old Blazor extension
Just cloned Oqtane repo but can't build the client project due to 57 errors coming from the auto-generated *.razor.g.cs files (where * = index, installer, controlpanel, add, delete, edit, components)
From my (limited) experience with Blazor this usually comes from the html part of these components, but I can't see anything illegal there...
Any ideas?
After talking with @sbwalker (always talk to Shaun Walker before starting any feature), I will start working on a web based installer similar to what I did with ADefHelpDesk (http://www.adefhelpdesk.com/ADefHelpDeskDocumentation/InstallAzure.html)
To reproduce:
curl -X GET "http://localhost:44357/~/api/User" -H "accept: text/plain" return list of users !
Authorization is missing on api controllers.
What's the roadmap for Oqtane? I would like to see what features are intended to be released in every quarter or even better every month.
Personally, I would like to see this framework evolve into one that supports modules/plugins/extensions and themes like DNN. Modules should be able to use third party UI controls without affecting the framework or other installed modules.
An admin module is needed for managing Profile properties. This module should be developed in the standard manner ( with Index, Add, Edit, Delete capabilities ) and be exposed on the Admin Dashboard.
Why not have the classes for audit/soft deletes etc inherit from some base classes instead of implementing the properties in every class?
Also should probably use UTC times or be configurable. I dont remember if EFCore3 now allows to use the SQL datatime value instead which would be preferred. (EX: If you have a default value set in SQL currently EF doesnt honor it with EF.)
EX of classes:
[JsonObject(NamingStrategyType = typeof(DefaultNamingStrategy))]
public partial class Address: AuditBase<int>
{
#region Keys
/// <inheritdoc />
///<summary>
/// Identifier for an address.
///</summary>
[Column("AddressId")]
[Display(Name = "Address Id", Description = "Identifier for an address.", Prompt = "Address Id")]
public override int Id { get; set; }
#endregion
#region Scalars
///<summary>
/// Line 1 of the address.
///</summary>
[Required]
[StringLength(50, ErrorMessage="Address 1 max length is 50.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Address 1", Description = "Line 1 of the address.", Prompt = "Address 1")]
public string Address1 { get; set; }
///<summary>
/// Line 2 of the address.
///</summary>
[StringLength(50, ErrorMessage="Address 2 max length is 50.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Address 2", Description = "Line 2 of the address.", Prompt = "Address 2")]
public string Address2 { get; set; }
///<summary>
/// 3rd line of the address. Used in European addresses.
///</summary>
[StringLength(50, ErrorMessage="Address 3 max length is 50.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Address 3", Description = "3rd line of the address. Used in European addresses.", Prompt = "Address 3")]
public string Address3 { get; set; }
///<summary>
/// City of the location.
///</summary>
[Required]
[StringLength(35, ErrorMessage="City max length is 35.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="City", Description = "City of the location.", Prompt = "City")]
public string City { get; set; }
///<summary>
/// State of the address.
///</summary>
[StringLength(2, ErrorMessage="State max length is 2.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="State", Description = "State of the address.", Prompt = "State")]
public string State { get; set; }
///<summary>
/// Zip 5 of the address.
///</summary>
[StringLength(5, ErrorMessage="Zip max length is 5.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Zip", Description = "Zip 5 of the address.", Prompt = "Zip")]
public string Zip { get; set; }
///<summary>
/// Zip plus 4 of the address.
///</summary>
[StringLength(4, ErrorMessage="Zip 4 max length is 4.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Zip 4", Description = "Zip plus 4 of the address.", Prompt = "Zip 4")]
public string Zip4 { get; set; }
///<summary>
/// The postal code of the address. If a zip+4 is available for a US address this will be formatted as xxxxx-xxxx. Otherwise it will contain the 5-digit zip or the Canadian postal code.
///</summary>
[Required]
[StringLength(15, ErrorMessage="Postal Code max length is 15.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Postal Code", Description = "The postal code of the address. If a zip+4 is available for a US address this will be formatted as xxxxx-xxxx. Otherwise it will contain the 5-digit zip or the Canadian postal code.", Prompt = "Postal Code")]
public string PostalCode { get; set; }
///<summary>
/// Code of the country.
///</summary>
[StringLength(3, ErrorMessage="Country Code max length is 3.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Country Code", Description = "Code of the country.", Prompt = "Country Code")]
public string CountryCode { get; set; }
///<summary>
/// Federal Information Processing Standard Code that uniquely identifies counties and county equivalents in ht eUnited States, certain US possession, and certain freely associated
///</summary>
[StringLength(5, ErrorMessage="FIPS Code max length is 5.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="FIPS Code", Description = "Federal Information Processing Standard Code that uniquely identifies counties and county equivalents in ht eUnited States, certain US possession, and certain freely associated", Prompt = "FIPS Code")]
public string FIPSCode { get; set; }
///<summary>
/// Reutrn code provided by the address hygiene process.
///</summary>
[StringLength(1, ErrorMessage="Address Return Code max length is 1.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Address Return Code", Description = "Reutrn code provided by the address hygiene process.", Prompt = "Address Return Code")]
public string AddressReturnCode { get; set; }
///<summary>
/// Latitude of the address.
///</summary>
[Display(Name ="Latitude", Description = "Latitude of the address.", Prompt = "Latitude")]
public decimal? Latitude { get; set; }
///<summary>
/// Longitude based on the address.
///</summary>
[Display(Name ="Longitude", Description = "Longitude based on the address.", Prompt = "Longitude")]
public decimal? Longitude { get; set; }
///<summary>
/// Indicator as to the level that the longitude and latitude were calculated to.
///</summary>
[StringLength(1, ErrorMessage="Geo Coding Accuracy Code max length is 1.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Geo Coding Accuracy Code", Description = "Indicator as to the level that the longitude and latitude were calculated to.", Prompt = "Geo Coding Accuracy Code")]
public string GeoCodingAccuracyCode { get; set; }
///<summary>
/// Date that the reco
///</summary>
[Display(Name ="Last Hygiene Date", Description = "Date that the reco", Prompt = "Last Hygiene Date")]
public DateTime? LastHygieneDate { get; set; }
///<summary>
/// Matchkey used for a quick match of the address.
///</summary>
[Required]
[StringLength(250, ErrorMessage="Address Match Key max length is 250.")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Display(Name ="Address Match Key", Description = "Matchkey used for a quick match of the address.", Prompt = "Address Match Key")]
public string AddressMatchKey { get; set; }
#endregion
#region Reverse Navigation
///<summary>
/// Parent Address pointed to by AddressHygieneQueue.AddressId
///</summary>
[JsonIgnore]
public List<AddressHygieneQueue> AddressAddressHygieneQueue_Reverse { get; set; }
///<summary>
/// Parent Address pointed to by CompanyAddress.AddressId
///</summary>
[JsonIgnore]
public List<CompanyAddress> AddressCompanyAddress_Reverse { get; set; }
///<summary>
/// Parent Address pointed to by ContactAddress.AddressId
///</summary>
[JsonIgnore]
public List<ContactAddress> AddressContactAddress_Reverse { get; set; }
///<summary>
/// Parent Address pointed to by ContactChannelInfo.AddressId
///</summary>
[JsonIgnore]
public List<ContactChannelInfo> AddressContactChannelInfo_Reverse { get; set; }
///<summary>
/// Parent Address pointed to by Subscriber.AddressId
///</summary>
[JsonIgnore]
public List<Subscriber> AddressSubscriber_Reverse { get; set; }
#endregion
public Address()
{
OnCreated();
}
partial void OnCreated();
}
[JsonObject(NamingStrategyType = typeof(DefaultNamingStrategy))]
public class AuditBase<T> : IEntity<T>
{
[Column("Inserted", TypeName = "datetime")]
[Required(ErrorMessage = "Inserted is required")]
[Display(Name = "Inserted", Order = 9000)]
[ReadOnly(true)]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy hh:mm tt}")]
[DefaultValue("Getdate()")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual DateTime Inserted { get; set; }
[Column("InsertedBy")]
[MaxLength(128)]
[StringLength(128)]
[Required(ErrorMessage = "Inserted By is required")]
[Display(Name = "Inserted By", Order = 9001)]
public virtual string InsertedBy { get; set; }
[Column("Updated", TypeName = "datetime")]
[Required(ErrorMessage = "Updated is required")]
[Display(Name = "Updated", Order = 9002)]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy hh:mm tt}")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[DefaultValue("Getdate()")]
public virtual DateTime Updated { get; set; }
[Column("UpdatedBy")]
[MaxLength(128)]
[StringLength(128)]
[Required(ErrorMessage = "Updated By is required")]
[Display(Name = "Updated By", Order = 9003)]
public virtual string UpdatedBy { get; set; }
[Column("EditCount", TypeName = "int")]
[Required(ErrorMessage = "Edit Count is required")]
[Display(Name = "Edit Count", Order = 904)]
[ConcurrencyCheck]
public virtual int EditCount { get; set; }
public bool IsTransient()
{
if (EqualityComparer<T>.Default.Equals(Id, default))
{
return true;
}
//Workaround for EF Core since it sets int/long to min value when attaching to dbcontext
if (typeof(T) == typeof(int))
{
return Convert.ToInt32(Id) <= 0;
}
if (typeof(T) == typeof(long))
{
return Convert.ToInt64(Id) <= 0;
}
return false;
}
public virtual T Id { get; set; }
}
public interface IEntity : IEntity<int>
{
}
Looking at the SQL Scripts, what do you think about prefixing all the tables with "oqt" or something, if applying this to an existing DB there could be conflicts, plus when running code generation it would be nice to say ignore tables starting with "xxx".
Also not sure what Alias is. Can a Tenant have multiple sites? This table seems to be a junction between Tenant and Site, so shouldnt the name be TenantSites?
I assume the Master DB is installed to its own db?
Create an azuredeploy.json that can be placed in the README.md file of the oqtane/oqtane.framework GitHub site to allow users to easily deploy Oqtane to Azure and install it.
Did something on the Oqtane site change? I can't access the community/forums page. It says I don't have the access to view the page.
Add new page doesn't reload ContentPanel pages available.
I have to move to following code into OnParameterSet or there is another solution to reload it?
protected override void OnParametersSet()
{
pages = new List<Page>();
foreach(Page p in PageState.Pages)
{
if (UserSecurity.IsAuthorized(PageState.User, "View", p.Permissions))
{
pages.Add(p);
}
}
}
To reproduce:
One of the major advantages of .NET Core is that it is cross-platform. We need to ensure that we support databases that are more commonly used on Linux and Mac, and avoid a specific dependency on SQL Server (even though it is now available on Linux)
Create new site
Log in as host
Try to add a new page to the site
Get the following error:
[2019-11-12T02:51:47.988Z] Error: System.Text.Json.JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 0.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected(Int32 maxDepth)
at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteCore(Utf8JsonWriter writer, PooledByteBufferWriter output, Object value, Type type, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.WriteCore(PooledByteBufferWriter output, Object value, Type type, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.WriteCoreString(Object value, Type type, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonSerializerOptions options)
at Oqtane.Services.LogService.Log(Nullable1 PageId, Nullable
1 ModuleId, Nullable`1 UserId, String category, String feature, LogFunction function, LogLevel level, Exception exception, String message, Object[] args) in C:\Users\Michael\Source\Repos\ADefWebserver\oqtane.framework\Oqtane.Client\Services\LogService.cs:line 54
at Oqtane.Modules.ModuleBase.Log(LogLevel level, Exception exception, String message, Object[] args) in C:\Users\Michael\Source\Repos\ADefWebserver\oqtane.framework\Oqtane.Client\Modules\ModuleBase.cs:line 160
at Oqtane.Modules.ModuleBase.Logger.LogError(Exception exception, String message, Object[] args) in C:\Users\Michael\Source\Repos\ADefWebserver\oqtane.framework\Oqtane.Client\Modules\ModuleBase.cs:line 219
at Oqtane.Modules.Admin.Pages.Add.SavePage() in C:\Users\Michael\Source\Repos\ADefWebserver\oqtane.framework\Oqtane.Client\Modules\Admin\Pages\Add.razor:line 323
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle)
Are there any plans to try and make a dynamic form designer? Kinda like ninja forms for WordPress. If not any ideas on where to start to build it?
Currently, in the Client library, multiple .cshtml files are performing the same actions with the results from the IThemeService (Formerly ISkinService).
These operations should be condensed and moved to a method implementation inside of the IThemeService to prevent fragility in management of the code-base going forward.
Items to consider moving to a method implementation:
This will substantially reduce the on-view code levels greatly.
I'll submit a PR for this, after my other PR is processed, but wanted to get this documented.
Setup initial .gitignore
file for this repo.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.