Demo Gnoga – AdaBlog

Description

Adablog uses an MVC style model of programming similar for Ruby on Rails.

It demonstrates how flexible Gnoga can be and that it is just as capable in the area of web sites, as web apps or application GUIs.

An effort was made to also make more use of attaching to the DOM of existing pages and avoiding app data types.

This applications uses SQLite.

Source code

adablog.ads

with Gnoga.Server.Database.SQLite;

package AdaBlog is
   use Gnoga;
   use all type Gnoga.String;

   Connection : Gnoga.Server.Database.Connection_Access := Gnoga.Server.Database.SQLite.Connect ("adablog.db");
end AdaBlog;

adablog-controller.ads

with Gnoga.Gui.Window;
with Gnoga.Application.Multi_Connect;

package AdaBlog.Controller is
   procedure Index
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Connection  :        access Gnoga.Application.Multi_Connect.Connection_Holder_Type);

   procedure New_Entry
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Connection  :        access Gnoga.Application.Multi_Connect.Connection_Holder_Type);

   procedure Log_Out
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Connection  :        access Gnoga.Application.Multi_Connect.Connection_Holder_Type);
end AdaBlog.Controller;

adablog-controller.adb

with Ada.Calendar;

with Gnoga.Types;
with Gnoga.Gui.Base;
with Gnoga.Gui.Element;
with Gnoga.Gui.Element.Common;
with Gnoga.Gui.Element.Form;

with AdaBlog.View;
with AdaBlog.Model;

package body AdaBlog.Controller is

   procedure On_Create_User (Object : in out Gnoga.Gui.Base.Base_Type'Class);
   --  The create user button was clicked

   procedure On_User_Login (Object : in out Gnoga.Gui.Base.Base_Type'Class);
   --  User clicked the submit button for his user name and password

   procedure On_Submit_Entry (Object : in out Gnoga.Gui.Base.Base_Type'Class);
   --  User clicked the submit button to an a blog entry.

   procedure On_Create_User (Object : in out Gnoga.Gui.Base.Base_Type'Class) is
      Main_Window  : aliased Gui.Window.Window_Type;
      Message_Area : Gui.Element.Common.DIV_Type;
      User_Login   : Gui.Element.Common.Button_Type;

      username : Gui.Element.Form.Text_Type;
      password : Gui.Element.Form.Text_Type;
      pass2    : Gui.Element.Form.Text_Type;
      verify   : Gui.Element.Common.DIV_Type;

      User : AdaBlog.Model.Users.Active_Record;
   begin
      Main_Window.Attach (Object.Connection_ID);
      Message_Area.Attach_Using_Parent (Main_Window, "message");

      User_Login.Attach_Using_Parent (Main_Window, "login-button");
      User_Login.Disabled;

      username.Attach_Using_Parent (Main_Window, "username");
      password.Attach_Using_Parent (Main_Window, "pass");
      pass2.Attach_Using_Parent (Main_Window, "pass2");

      verify.Attach_Using_Parent (Main_Window, "verify-pass");
      verify.Display ("inline");

      if Gnoga.String'(password.Value) /= Gnoga.String'(pass2.Value) or pass2.Value = "" then
         Message_Area.Text ("Please verify password.");
         return;
      end if;

      if username.Value = "" then
         Message_Area.Text ("Username is required.");
         return;
      end if;

      User.Find_Where ("username='" & username.Value & "'");

      if User.Value ("id") /= "" then
         Message_Area.Text ("Username is already taken.");
         return;
      end if;

      User.Value ("username", Value => username.Value);
      User.Value ("pass", Value => password.Value);
      User.Value ("last_session", Value => Main_Window.Gnoga_Session_ID);
      User.Save;

      Main_Window.Location.URL ("/");
   end On_Create_User;

   procedure On_User_Login (Object : in out Gnoga.Gui.Base.Base_Type'Class) is
      Main_Window  : aliased Gui.Window.Window_Type;
      Message_Area : Gui.Element.Common.DIV_Type;

      username : Gui.Element.Form.Text_Type;
      password : Gui.Element.Form.Text_Type;

      User : AdaBlog.Model.Users.Active_Record;
   begin
      Main_Window.Attach (Object.Connection_ID);
      Message_Area.Attach_Using_Parent (Main_Window, "message");

      username.Attach_Using_Parent (Main_Window, "username");
      password.Attach_Using_Parent (Main_Window, "pass");

      User.Find_Where
        (Where      => "username ='" & username.Value & "'" & " AND " & "pass ='" & password.Value & "'",
         Create_New => False);
      User.Value ("last_session", Value => Main_Window.Gnoga_Session_ID);
      User.Save;

      Main_Window.Location.URL ("/");
   exception
      when others =>
         Message_Area.Text ("Invalid Login");
   end On_User_Login;

   procedure On_Submit_Entry (Object : in out Gnoga.Gui.Base.Base_Type'Class) is
      User    : AdaBlog.Model.Users.Active_Record;
      Entries : AdaBlog.Model.Blog_Entries.Active_Record;

      Main_Window : aliased Gui.Window.Window_Type;
      Text        : Gnoga.Gui.Element.Form.Text_Area_Type;
   begin
      Main_Window.Attach (Object.Connection_ID);

      Text.Attach_Using_Parent (Main_Window, "entry_text");

      User.Find_Where ("last_session ='" & Main_Window.Gnoga_Session_ID & "'");
      Entries.Value ("user_id", Value => User.Value ("id"));
      Entries.Value ("entry_date", Date_Value => Ada.Calendar.Clock);
      Entries.Value ("entry_text", Value => Text.Value);
      Entries.Save;

      Main_Window.Location.URL ("/");
   end On_Submit_Entry;

   procedure Index
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Connection  :        access Gnoga.Application.Multi_Connect.Connection_Holder_Type)
   is
      Content_Area : Gui.Element.Common.DIV_Type;
      Left_Area    : Gui.Element.Common.DIV_Type;
      User_Login   : Gui.Element.Common.Button_Type;
      Create_User  : Gui.Element.Common.Button_Type;

      User : AdaBlog.Model.Users.Active_Record;

      procedure Display_Row (Row : Gnoga.Types.Data_Map_Type);

      procedure Display_Row (Row : Gnoga.Types.Data_Map_Type) is
      begin
         AdaBlog.View.Display_Blog_Entry (Content_Area, Row);
      end Display_Row;
   begin
      User.Find_Where ("last_session ='" & Main_Window.Gnoga_Session_ID & "'");

      Main_Window.Disable_Auto_Set_View;

      Content_Area.Create (Parent => Main_Window, ID => "main-body");

      AdaBlog.Connection.Iterate
        (SQL =>
           "select username, entry_date, entry_text " & "from users, blog_entries " &
           "where users.id=blog_entries.user_id",
         Process => Display_Row'Unrestricted_Access);

      View.User_Panel (Main_Window => Main_Window, Panel => Left_Area, User_Record => User.Values);

      View.Template (Main_Window => Main_Window, Content => Content_Area, Left_Panel => Left_Area);

      User_Login.Attach_Using_Parent (Main_Window, "login-button");
      User_Login.On_Click_Handler (On_User_Login'Access);

      Create_User.Attach_Using_Parent (Main_Window, "create-button");
      Create_User.On_Click_Handler (On_Create_User'Access);

      Connection.Hold;
   end Index;

   procedure New_Entry
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Connection  :        access Gnoga.Application.Multi_Connect.Connection_Holder_Type)
   is
      User : AdaBlog.Model.Users.Active_Record;

      Content_Area : Gui.Element.Common.DIV_Type;
      Left_Area    : Gui.Element.Common.DIV_Type;

      Submit : Gui.Element.Common.Button_Type;
   begin
      User.Find_Where ("last_session ='" & Main_Window.Gnoga_Session_ID & "'");

      if User.Value ("id") = "" then
         Main_Window.Location.URL ("/");
         return;
      end if;

      Main_Window.Disable_Auto_Set_View;

      View.New_Entry_Form (Main_Window => Main_Window, Content => Content_Area);

      View.User_Panel (Main_Window => Main_Window, Panel => Left_Area, User_Record => User.Values);

      View.Template (Main_Window => Main_Window, Content => Content_Area, Left_Panel => Left_Area);

      Submit.Attach_Using_Parent (Main_Window, "submit_entry");
      Submit.On_Click_Handler (On_Submit_Entry'Access);

      Connection.Hold;
   end New_Entry;

   procedure Log_Out
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Connection  :        access Gnoga.Application.Multi_Connect.Connection_Holder_Type)
   is
      pragma Unreferenced (Connection);
      User : AdaBlog.Model.Users.Active_Record;
   begin
      User.Find_Where ("last_session ='" & Main_Window.Gnoga_Session_ID & "'");

      if User.Value ("id") /= "" then
         User.Value ("last_session", "");
         User.Save;
      end if;

      Main_Window.Location.URL ("/");
   end Log_Out;
begin
   Application.Multi_Connect.On_Connect_Handler (Controller.Index'Unrestricted_Access, "default");
   Application.Multi_Connect.On_Connect_Handler (Controller.Index'Unrestricted_Access, "main");
   Application.Multi_Connect.On_Connect_Handler (New_Entry'Unrestricted_Access, "new_entry");
   Application.Multi_Connect.On_Connect_Handler (Log_Out'Unrestricted_Access, "logout");
end AdaBlog.Controller;

adablog-view.ads

with Gnoga.Types;
with Gnoga.Gui.Window;
with Gnoga.Gui.Element.Common;

package AdaBlog.View is

   procedure Display_Blog_Entry
     (Parent : in out Gnoga.Gui.Element.Common.DIV_Type'Class;
      Data   : in     Gnoga.Types.Data_Map_Type);
   --  Adds blog entries to content

   procedure New_Entry_Form
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Content     : in out Gnoga.Gui.Element.Common.DIV_Type'Class);
   --  Creates new entry form for content

   procedure User_Panel
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Panel       : in out Gnoga.Gui.Element.Common.DIV_Type'Class;
      User_Record : in     Gnoga.Types.Data_Map_Type);
   --  Setup Panel with User_Record information or Login if User_Record empty

   procedure Template
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Content     : in out Gnoga.Gui.Element.Common.DIV_Type'Class;
      Left_Panel  : in out Gnoga.Gui.Element.Common.DIV_Type'Class);
   --  Content will be placed in to Content area of Template
end AdaBlog.View;

adablog-view.adb

package body AdaBlog.View is

   procedure Display_Blog_Entry
     (Parent : in out Gnoga.Gui.Element.Common.DIV_Type'Class;
      Data   : in     Gnoga.Types.Data_Map_Type)
   is
      Entry_Div : Gnoga.Gui.Element.Common.DIV_Type;
   begin
      Entry_Div.Create
        (Parent,
         "<div class='blog_entry'>" & "<div class='blog_user'>" & Data.Element ("username") & "</div>" &
         "<div class='blog_date'>&nbsp;said on " & Data.Element ("entry_date") & ":</div><br />" &
         "<div class='blog_text'>" & Data.Element ("entry_text") & "</div></div>");
   end Display_Blog_Entry;

   procedure New_Entry_Form
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Content     : in out Gnoga.Gui.Element.Common.DIV_Type'Class)
   is
   begin
      Content.Create
        (Parent  => Main_Window,
         Content =>
           "<div><form>" & "Blog Entry: <br />" & "<textarea cols=60 rows=10 id='entry_text' " &
           "name='entry_text' autofocus></textarea>" & "<input type='button' id='submit_entry' " &
           "value='Submit'></form></div>",
         ID => "main-body");
   end New_Entry_Form;

   procedure User_Panel
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Panel       : in out Gnoga.Gui.Element.Common.DIV_Type'Class;
      User_Record : in     Gnoga.Types.Data_Map_Type)
   is
   begin
      if not User_Record.Contains ("username") then
         Panel.Create
           (Parent  => Main_Window,
            Content =>
              "<form>" & "Username: <input type=text name='username' " & "id='username' size='20' autofocus><br />" &
              "Password : <input type=password name='pass' " & "id='pass' size='20'><br />" &
              "<div id='verify-pass' style='display:none'> " & "Verify Password : <input type=password name='pass2' " &
              "id='pass2' size='20'><br /></div>" & "<input id='login-button' type='button'" & " value='Submit'> " &
              "<input id='create-button' type='button'" & " value='Create'> <br/>" & "</form>",
            ID => "left-panel");
      else
         Panel.Create
           (Parent  => Main_Window,
            Content =>
              " Username: <b>" & User_Record.Element ("username") & "</b><br /><br />" & "<a href='/new_entry'>" &
              "Add Entry</a><br /><br />" & "<a href='/logout'>Logout</a ><br/>",
            ID => "left-panel");
      end if;
   end User_Panel;

   procedure Template
     (Main_Window : in out Gnoga.Gui.Window.Window_Type'Class;
      Content     : in out Gnoga.Gui.Element.Common.DIV_Type'Class;
      Left_Panel  : in out Gnoga.Gui.Element.Common.DIV_Type'Class)
   is
      Title_Div    : Gnoga.Gui.Element.Common.DIV_Type;
      Message_Area : Gnoga.Gui.Element.Common.DIV_Type;
   begin
      Main_Window.Document.Load_CSS ("/css/adablog.css");
      Main_Window.Disable_Auto_Set_View;

      Title_Div.Create (Parent => Main_Window, Content => "AdaBlog", ID => "title");
      Title_Div.Place_Inside_Top_Of (Main_Window.Document.Body_Element.all);

      Left_Panel.Place_After (Title_Div);

      Content.Place_After (Left_Panel);

      Message_Area.Create (Main_Window, ID => "message");
      Message_Area.Place_Inside_Top_Of (Content);
   end Template;
end AdaBlog.View;

adablog-migrations.adb

with Gnoga.Server.Migration;

procedure AdaBlog.Migrations (M : in out Gnoga.Server.Migration.Migration_Collection) is
begin
   --  Add migrations here
   M.Add_Migration_Up
     ("CREATE TABLE `users`" & " (" & AdaBlog.Connection.ID_Field_String & "," & "  username VARCHAR(80)," &
      "  pass VARCHAR(80)," & "  last_session VARCHAR(80))");
   M.Add_Migration_Down ("DROP TABLE `users`");

   M.Add_Migration_Up
     ("CREATE TABLE `blog_entries`" & " (" & AdaBlog.Connection.ID_Field_String & "," & "  user_id INTEGER," &
      "  entry_date DATE," & "  entry_text TEXT)");
   M.Add_Migration_Down ("DROP TABLE `blog_entries`");
end AdaBlog.Migrations;

adablog-model.ads

with Gnoga.Server.Model.Table;

package AdaBlog.Model is

   package Users is new Gnoga.Server.Model.Table ("users", Connection);

   package Blog_Entries is new Gnoga.Server.Model.Table ("blog_entries", Connection);

end AdaBlog.Model;

adablog-main.adb

with GNAT.OS_Lib;

with Gnoga.Application.Multi_Connect;
with Gnoga.Server.Migration;

with AdaBlog.Migrations;
with AdaBlog.Controller;

procedure AdaBlog.Main is
   pragma Linker_Options ("-lsqlite3");
begin
   if Gnoga.Server.Migration.Migrations_Handled_Command_Line (Connection, Migrations'Unrestricted_Access) then
      GNAT.OS_Lib.OS_Exit (0);
   end if;

   Application.Title ("AdaBlog - Gnoga Demo");
   Application.HTML_On_Close ("<b>Connection to Application has been terminated</b>");
   Application.Multi_Connect.Initialize (Boot => "debug.html");

   Application.Multi_Connect.Message_Loop;
exception
   when E : others =>
      Log (E);
end AdaBlog.Main;

Project file

with "settings.gpr";
with "gnoga.gpr";

project AdaBlog is

   for Object_Dir use Settings.Obj_Dir;
   for Exec_Dir use Settings.Exe_Dir;
   for Main use ("adablog-main.adb");
   for Create_Missing_Dirs use Settings'Create_Missing_Dirs;

   package Builder is
      for Executable ("adablog-main.adb") use "adablog";
   end Builder;

   package Compiler is
      for Default_Switches ("Ada") use Settings.Compiler'Default_Switches ("Ada") & "-gnatyN";
   end Compiler;

   package Binder renames Settings.Binder;
   package Linker renames Settings.Linker;
   package Pretty_Printer renames Settings.Pretty_Printer;

end AdaBlog;