Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

SupB - A social network in 60 minutes with NetFluid

0.00/5 (No votes)
2 Jun 2013CPOL4 min read 12.2K  
A simple introduction to NetFluid Application Server 11

Introduction

The idea it's to make a simple social network to post text, pictures and generic files from pc and mobile about a generic topic, without registration and app to be downloaded.

The solution flows naturally: email, the most implemented comunication system of any device, including my old, not smart, telephone.  

 So the system will build in this way: an smtp server linked to a web interface, whenever a mail is recieved it will be posted on the corresponding  group.

Example: if we send an email to genova@supb.eu it will be shown with others in supb.eu/genova.

Background     

This project run upon NetFluid Application Server, the free version is downloadable here

Using the code 

Step 1: Setting up the project

First of we need to create a new NetFluid Web App, a Visual Studio template is available at link above.
Our web app is compilable as portable executable (*.exe) with embedded web interface and server or as library (*.dll) to be loaded by another instance of NetFluid.

In this case we opt for executable so we need to set up the AppConfig to set basics NetFluid settting:

XML
<NetFluidSettings MaxPostSize="268435456" SessionDuration="7200">
   <Interfaces>
      <Interface IP="127.0.0.1" Port="8080" />
   </Interfaces>
   <PublicFolders>
     <PublicFolder RealPath="./Public" UriPath="/" />
   </PublicFolders>
</NetFluidSettings>   

And to load the NetFluid Engine

C#
static void Main(string[] args)
{
    Engine.Load(Assembly.GetExecutingAssembly());
    Engine.Start();
}   

Step 2: Add the email system

After the experience with the huge-mangrovia code of LumiSoft of the previous article I opted  for an in-house solution NetFluid.Mail.

C#
namespace NetFluidApp.SupB
{
	public class Program : FluidPage
	{
		static SmtpServer smpt;
 
      		static void Main(string[] args)
        	{
            		Engine.Load(Assembly.GetExecutingAssembly());
            		Engine.Start();
			
			smpt = new SmtpServer();
            		smpt.Start();
		}
	}
} 

The smtp server can be also embedded in any web page (FluidPage) of our project without being garbage collected, just mark it as static.  

Step 3: Data model

We got just two entities: the users and the messages.

[BsonId]
public ObjectId Id { get; set; }
public string Name;
public string Address;
 
public override string ToString()
{
   return Name;
} 
public class Message
{
    [BsonId]
    public ObjectId Id { get; set; }
    public User User;
    public string Group;
    public string Subject;
    public string Body;
    public DateTime DateTime;
    public string[] Attachments;
} 

 

The  BsonId attribute and the ObjectId type are present because we use MongoDB if you don't intend to install it you can simply use the PersistentBag collection in NetFluid.Collection.

Step 4: The posting system

Now got all that is need, the web and smtp interface and the data model, it's time to program the core of our project: the posting system. 

If there isn't a user for the sender we create a new one and after that we simply copy the message in each group.

The group of a message it's specified simply the the Group field in Message. 

C#
smpt.MessageReceived +=(s,e) =>
{
    var body = e.Message.Body;
    User user;

    try
    {
        #region GET USER OR CREATE IT

// if we know the sender we retrieve him from the database
// otherwise we create a new user
        user = Database.User(e.Message.From.Address);

        if (user == null)
        {
var name = string.IsNullOrEmpty(e.Message.From.DisplayName)
                ? e.Message.From.User
                : e.Message.From.DisplayName;

           user = new User { Name = name, Address = e.Message.From.Address };
           Database.Store(user);
        }

        #endregion

        #region SAVE A COPY OF THE MESSAGE IN EACH GROUP
        foreach (var recipient in e.Message.To)
        {
            #region NEW MESSAGE

            if (recipient.Host == "supb.eu")
            {
                #region NEW MESSAGE
        // a basic check upon group characters is already made by the smtp server
                var group = recipient.User.ToLower();

                var msg = new Message
                {
                    Group = @group,
                    Subject = e.Message.Subject,
                    DateTime = DateTime.Now,
                    User = user,
                    Body = body,
                    Attachments = e.Message.Attachments.Select(x => x.SaveIn("./Public/attachments")).ToArray(),
                };
                Database.Store(msg);

                #endregion
            }

            #endregion
        }
        #endregion
    }
    catch (Exception exception)
    {
        Engine.Logger.Log(LogLevel.PageException, exception);
    }
};

Step 5: Read the posts

Now we got our database with all the messages and relatives users, it's time to show them.

To achieve this we need to define a web page of our web app, the base type for this is FluidPage. 

NOTE: NetFluid use a huge amount of run-time generated code so FluidPages defined in the code must be signed as PUBLIC

C#
namespace SupB
{
    public class MessageManager : FluidPage
    {
        [Route("/",true)]
        public void Index(string group,string postId)
        {
	}
    }
}   

With the Route Attribute we tell to the NetFluid engine to call this page on the main uri.

With the parametrized flag set on true, we tell to engine to take arguments the of method from the uri, instead from post or get parameters. 

Now we got four possibilities: 

 



http://supb.eu/ The main page of our site group=null and postId=null
http://supb.eu/style.css And other public files group="style.css" and postId=null
http://supb.eu/group The user requested a group group="group" and postId=null
http://supb.eu/group/message   The user requested a single post  group="group" and postId="message"

The first and the fourth case are obvious, we can distinguish the second and the third case with the IsPublicFile function, or resolve the problem at his root moving contents (images, styles, javascript, etc.) on another domain.

 

C#
    //no group specified, let's show the index
    if (group==null)
    {
        Render("SupB.UI.Master");
        return;
    }
 
    //because actually we can't know if /style.css mean the css of the page
    //or the group style.css@supb.eu
    //the problem it's solved by moving resources on other domain
    //ex: content.netfluid.org/style.css
    if (IsPublicFile("/"+group))
    {
        //Disabling the Blocking value we tell to the engine to go on after the execution
        //of this page, to public files check and send
        Blocking = false;
        return;
    }
 
    //normalizing the input
    group = group.ToLowerInvariant();
 
    //Fecthing the messages for this group
    //http://supb.ue/<group>
    var messages = Database.Group(@group);
 
    if (!messages.Any())
    {
        Render("SupB.UI.Error", new { Message = "Empty group" });
        return;
    }
 
    #region SHOW A SINGLE POST IF REQUESTED
    // http://supb.ue/<group>/<post>
    // show the specified post inside this group

    if (postId!=null)
    {
        //take the messages and pass them to the template
        var post = Database.Message(postId);
 
        if (post != null)
            Render("SupB.UI.Post", new { Post = post });
        else
            Render("SupB.UI.Error", new { Message = "Post not found" });
        return;
    }
    #endregion
 
    Render("SupB.UI.Group", new {Name = @group, Post = messages}); 

 

Step 6: assemble the bodywork 

Has we seen above, after taking the messages posted we pass them to a function called Render that display an *.html page to the output, passing an object translated into a dictionary.

NOTE: HTML page are runtime compiled by the NetFluid engine, so to use them remember to mark them as EMBEDDED RESOURCES

In  this project we got four pages:

Master: The main page and index of the web app, others page will inherit this one

Error: To show error messages

Post: To show a single post

Group: To show post inside a group

Here we can see the structure of Master, with the two NetFluid instruction define with these  we can define two field of the page overridable by derived types.

In this  case we defined two fields Column and Center

Any other C# function can be inserted into the html via the % symbol for single line instruction and <% %> for multiline instruction. 

HTML
<!DOCTYPE html>
 
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link href="http://www.codeproject.com/bootstrap-responsive.css" rel="stylesheet" />
    <link href="http://www.codeproject.com/bootstrap.css" rel="stylesheet" />
</head>
<body>
    <div class="row-fluid">
        <div class="span4">
            <div class="well">
                %define Column
 
                %end define
            </div>
        </div>
        <div class="span8">
            <h1>
                <a href="http://www.codeproject.com/">
                    <img src="http://www.codeproject.com/logo.png" alt="Sup /b!"/>
                </a>
            </h1>
            %define Center
                <div>
                     <img src="http://www.codeproject.com/explanation.png" alt="to post on Sup B, send a mail to any address at supb.eu"/>
                </div>
            %end define
        </div>
    </div>
    <footer style="text-align: center">
        <p>SupB - Mail based social network for pc and mobile</p>
    </footer>
</body>
</html>   

Now, with the NetFluid specific template instruction, we can tell to Group page to inherit the Master page and redefine the two fields.  

The {%  %} is used to print a value inside the html.

% page inherit Master
 
% using System;
% using System.IO;
% using System.Linq;
 
%redefine Column
     <h2>{% Args["Name"] %}@supb.eu</h2>
    % base.Column();
%end redefine
 
% redefine Center
    % foreach(Message post in Args["Post"])
        <article>
            <div class="row-fluid">
                <h3 style="margin:0">
                    <a href="http://www.codeproject.com/{% post.Group %}/{% post.Id %}">{% post.Subject %}</a>
                </h3>
            </div>
            <div class="data">
                <span>
                    <i class="icon-user"></i>{% post.User %}
                </span>
                <span>
                    <i class="icon-time"></i>{% post.DateTime %}
                </span>
            </div>
            % var notimg = post.Attachments.Where(x=>!x.EndsWith(".png") && !x.EndsWith(".jpg") && !x.EndsWith(".gif"));
 
            % if(notimg.Count()>

NOTE: Single line cycle and condition are closed by end instead of  }

Multiline:

%
	for(int i=0;i < count; i++)
	{
		//do something
	}
%>

Single line:
% for(int i=0;i < count; i++)
	% //do something
% end for 

Points of Interest

There isn't any good free .net smtp component in the whole internet.

See the result on http://supb.eu/ 

History  

First version


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)