I started designing Intranets many (and many 😅) years ago and, despite new technological stuffs, these sites are hard to die.
An intranet is a computer network for sharing information, easier communication, collaboration tools, operational systems, and other computing services within an organization, usually to the exclusion of access by outsiders. An organization-wide intranet can constitute an important focal point of internal communication and collaboration, and provide a single starting point to access internal and external resources. Many modern intranets have search engines, user profiles, blogs, mobile apps with notifications, and events planning within their infrastructure.
Commercial products (like Sharepoint) seems the perfect replacement for Intranets, but:
- Very frequently, behind the pages there is a lot of code to do complex jobs and writing code, with the constraints of commercial frameworks, is not so easy.
- This code is often written inside the Enterprise by the IT staff and it’s expensive to follow framework updates.
- Low-Code approach may not meet legacy requirements (for example old ERP integrations).
I have seen dozen of replacement projects never completed for legacy constraints even though the generous budget of the customer. So, in that environments, there was two link: “old intranet” and “new intranet” 😅 hoping that a day no one will click the first link!
Also the licensing model of the new software can be an obstacle, for example:
- The expense of buying license for thousands of employees.
- Respect license with “anonymous” users such customers or vendors.
- Arrange license with overlapped user roles or device roles (stores, warehouses, production equipments…).
Real cases
#1 Retail industry with hundreds of stores and thousands of employees, Intranet used as ERP “companion” for a better integration between stores and HQ:
- Payments reconciliation
- List and search various types of contact
- Catalogues
- Shift management
- Timesheet
- …
#2 Distribution industry with thousands of customers and multiple deliveries per day:
- Web EDI for document exchange
- Sales return requests
- Various accounting information
- Item tracking search by serial no. or lot no.
- …
My approach
Because I’m lazy 😅 in these years I written several frameworks to speed up Intranet development. The first was called “Todo” (PHP), then “Todo.NET” (C#), then “Active Bricks” (C#), then “Oxygn” (C#), then “Portal” (Python) and now “Shaper” (C#).
As you can see, C# is my favourite choice 😅 but the first “Todo”, written in 1998, has my personal largest installation with more than 10.000 different PHP pages accessed every day! 🥶
But why so many projects? Due to my lazyness I always searched for something that automatically creates UI, focusing myself only on the backend. Moreover I’m not very good doing beautiful frontends!
Let’s start learning Shaper with the following example (more complete documentation here: https://kb.brayns.it/books/shaper-quick-start)
📌 Install Visual Studio (the big one, not Visual Studio Code) with support for Web and ASP.NET development
📌 Create a new empty ASP.NET core project (yeah, this framework works also on Linux or can be deployed within a Container or in the Cloud)
📌 Clone these repositories and add the dependencies to your new ASP.NET core project:
- Main backend library
https://github.com/brayns-it/shaper - Frontend library based on the great AdminLTE work
https://github.com/brayns-it/shaper-web - System APP that provides authentication, log, email…
https://github.com/brayns-it/shaper-system
📌 Customize “Program.cs” to start Shaper:
using Brayns.Shaper;
var builder = WebApplication.CreateBuilder(args);
builder.InitializeShaper();
var app = builder.Build();
app.MapShaperApi();
app.MapShaperClient();
app.MapShaperDefault();
app.UseWebSockets();
app.UseStaticFiles(new StaticFileOptions
{
ServeUnknownFileTypes = true,
DefaultContentType = "application/other"
});
app.UseShaperMonitor();
app.Run();
📌 Mark your project assembly as container of Shaper APPs, adding a new “AssemblyInfo.cs”:
[assembly: Brayns.Shaper.Classes.AppCollection]
📌 Almost done! I’m very lazy 😅 so just now you can run the project in debug mode and create the database! For this demo purpose select the embedded SQLite database.
📌 The database is automatically created and you can login with “admin” / “admin” credentials:
📌 Do not forget to change “admin” password 😅
Example: Contact Management
Let’s continue the example doing a simple Contact Management:
- Contact database table
- Contact page list
- Contact page card
📌 Add a new folder “code” to your project that will contain custom classes
📌 Create a new C# file “Contact.Table.cs”
namespace DemoShaper
{
public class Contact : Table<Contact>
{
public Fields.Code No { get; } = new("No.", Label("No."), 20);
public Fields.Text Name { get; } = new("Name", Label("Name"), 100);
public Fields.Text Address { get; } = new("Address", Label("Address"), 100);
public Fields.Text City { get; } = new("City", Label("City"), 100);
public Fields.Text EMail { get; } = new("E-Mail", Label("E-Mail"), 100);
protected override void Initialize()
{
UnitCaption = Label("Contact");
TableName = "Contact";
TablePrimaryKey.Set(No);
}
}
}
📌 If you start again the project, the table is automatically compiled and added to database (in this example SQLite but it works with all supported databases such as SQL Server).
📌 Create “ContactList.Page.cs” to define the list
namespace DemoShaper
{
public class ContactList : Page<ContactList,Contact>
{
protected override void Initialize()
{
UnitCaption = Label("Contact list");
var content = Controls.ContentArea.Create(this);
{
var grid = new Controls.Grid(content);
{
new Controls.Field(grid, Rec.No);
new Controls.Field(grid, Rec.Name);
new Controls.Field(grid, Rec.EMail);
}
}
}
}
}
📌 Create the “ContactCard.Page.cs” to define the card
namespace DemoShaper
{
public class ContactCard : Page<ContactCard, Contact>
{
protected override void Initialize()
{
UnitCaption = Label("Contact card");
var content = Controls.ContentArea.Create(this);
{
var general = new Controls.Group(content, Label("General"));
{
new Controls.Field(general, Rec.No);
new Controls.Field(general, Rec.Name);
new Controls.Field(general, Rec.Address);
new Controls.Field(general, Rec.City);
new Controls.Field(general, Rec.EMail);
}
}
}
}
}
📌 Link the list to the card setting the property of “ContactList” class in the “Initialize” method
Card = typeof(ContactCard);
📌 Add the list to the right menu, extending the start page. Create a new file “Start.PageExt.cs”
namespace Brayns.System
{
public partial class Start : Page<Start>
{
[Extended]
protected void DMO_Extend()
{
var navigationPane = Control<Controls.NavigationPane>();
{
var demo = new Controls.ActionGroup(navigationPane!, Label("Demo"));
{
var contacts = new Controls.Action(demo, Label("Contacts"), Icon.FromName("fas fa-address-book"))
{
Run = typeof(DemoShaper.ContactList)
};
}
}
}
}
}
📌 Well done! Start and enjoy!
Conclusion
We are in the “custom” domain so there are a lot of frameworks one more beautiful than the other! I think that my approach is easy, with “batteries included” and very powerful through C#. I deployed with success +10 environments during the last year. 😊
In a future post I will explain how to integrate Shaper and Business Central! 😎