Data Aquarium Framework

Labels
AJAX(112) App Studio(9) Apple(1) Application Builder(245) Application Factory(207) ASP.NET(95) ASP.NET 3.5(45) ASP.NET Code Generator(72) ASP.NET Membership(28) Azure(18) Barcode(2) Barcodes(3) BLOB(18) Business Rules(1) Business Rules/Logic(140) BYOD(13) Caching(2) Calendar(5) Charts(29) Cloud(14) Cloud On Time(2) Cloud On Time for Windows 7(2) Code Generator(54) Collaboration(11) command line(1) Conflict Detection(1) Content Management System(12) COT Tools for Excel(26) CRUD(1) Custom Actions(1) Data Aquarium Framework(122) Data Sheet(9) Data Sources(22) Database Lookups(50) Deployment(22) Designer(178) Device(1) DotNetNuke(12) EASE(20) Email(6) Features(101) Firebird(1) Form Builder(14) Globalization and Localization(6) How To(1) Hypermedia(2) Inline Editing(1) Installation(5) JavaScript(20) Kiosk(1) Low Code(3) Mac(1) Many-To-Many(4) Maps(6) Master/Detail(36) Microservices(4) Mobile(63) Mode Builder(3) Model Builder(3) MySQL(10) Native Apps(5) News(18) OAuth(9) OAuth Scopes(1) OAuth2(13) Offline(20) Offline Apps(4) Offline Sync(5) Oracle(11) PKCE(2) Postgre SQL(1) PostgreSQL(2) PWA(2) QR codes(2) Rapid Application Development(5) Reading Pane(2) Release Notes(184) Reports(48) REST(29) RESTful(29) RESTful Workshop(15) RFID tags(1) SaaS(7) Security(81) SharePoint(12) SPA(6) SQL Anywhere(3) SQL Server(26) SSO(1) Stored Procedure(4) Teamwork(15) Tips and Tricks(87) Tools for Excel(3) Touch UI(93) Transactions(5) Tutorials(183) Universal Windows Platform(3) User Interface(338) Video Tutorial(37) Web 2.0(100) Web App Generator(101) Web Application Generator(607) Web Form Builder(40) Web.Config(9) Workflow(28)
Archive
Blog
Data Aquarium Framework
Saturday, April 25, 2009PrintSubscribe
In-Place Creation of Lookup Items

Data Aquarium Framework features on-demand creation of lookup items.

See it in Action

On the screen shot below a user has selected Edit command in context menu of a grid row that lists products from Northwind sample database.

Context-Sensitive Popup Menu

The row is now displayed in edit mode. You can see that supplier company name field has an icon  right next to the lookup box.

New Lookup Item Icon

Row With Lookups That Allow In-Place Item Creation

A click on this icon will bring up New Suppliers modal dialog that allows entering a supplier in-place. The supplier is automatically selected in the lookup when user clicks OK button.

In-Place Lookup Item Creation

You can try this online at http://dev.codeontime.com/demo/nwblob.

Controlling Access to This Feature

The feature is extremely useful but shall not be left uncontrolled. Typically only certain categories of users are allowed to create new lookup items.

This is how the creation of new lookup items is turned on in the data controller definition files.

<field name="SupplierID" type="Int32" label="Supplier#">
  <items style="Lookup" dataController="Suppliers" newDataView="createForm1" />
</field>

Attribute newDataView of items element specifies the view defined in data controller identified by dataController attribute. The attribute value is automatically assigned by Code OnTime Generator. You can define a custom view in Suppliers data controller to provide an alternative form to create new suppliers.

If you don’t want in-place lookup item creation to to be enabled then simply delete the attribute.

Controlling in-place lookups With Roles

A better solution is to allow only certain user roles to create new lookup items.

Open data controller ~/Controllers/Suppliers.xml and modify New Suppliers action as follows:

<actionGroup scope="ActionBar" headerText="New">
  <action commandName="New" commandArgument="createForm1" 
        headerText="New Suppliers" 
description="Create a new Suppliers record." roles="Administrators"/> </actionGroup>

Attribute roles will enable this action to be executed by users with Administrators role only. The framework will make sure that there is an action with New command in Suppliers data controller with an argument matched to the view specified by newDataView attribute of items element. If such action is not available then in-place creation of lookup items is automatically disabled.

Our sample application has been generated with ASP.NET Membership enabled. Here is how the row will look if we sign in a user with the standard name user. This user belongs to the role Users and is not authorized to create new suppliers.

Affect Of Action Roles on Lookups

You can see that the icon that allows creating new suppliers is gone. The user still can create new categories.

The centralized business logic and definitions of Data Aquarium Framework ensure that any other references to the Suppliers lookup in the application are affected as well.

This is how the supplier screen will look when displayed to the same user. Notice that New option is not available on the action bar anymore.

Global Effect Of Roles

Conclusion

ASP.NET declarative security if fully integrated into Data Aquarium Framework and allows easy control over AJAX web applications of any complexity.

Tuesday, April 14, 2009PrintSubscribe
Tracking User Actions

Tracking of user activities is a common requirement for many business applications. Data Aquarium Framework support Microsoft ASP.NET Membership via an advanced user management and login/logout user interface components. You can quickly create business rules to track user actions.

Sample Application

Generate a Data Aquarium project with the membership option enabled. Here is a typical screen shot of a Northwind database sample after the user with the name user has signed in.

image

Task 1

You want to keep a journal of user activities. The built-in .NET diagnostics facility will play a role of a journal where we will be keeping all activity records.

Solution

Create a business rules class Class1 and create method OrdersAfterUpdate as shown below. Link the business rules class to ~/Controllers/Orders.xml data controller as explained here.

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MyCompany.Data;

public class Class1 : BusinessRules
{
    [ControllerAction("Orders", "Update", ActionPhase.After)]
    protected void OrdersAfterUpdate(int orderId, FieldValue shipAddress)
    {
        System.Diagnostics.Debug.WriteLine(String.Format(
            "Order #{0} has been updated by '{1}' on {2}", 
            orderId, Context.User.Identity.Name, DateTime.Now));
        if (shipAddress.Modified) 
            System.Diagnostics.Debug.WriteLine(String.Format(
                 "Address has changed from '{0}' to '{1}.",
                 shipAddress.OldValue, shipAddress.NewValue));
    }
}

VB

Imports Microsoft.VisualBasic
Imports MyCompany.Data

Public Class Class1
    Inherits BusinessRules

    <ControllerAction("Orders", "Update", ActionPhase.After)> _
    Protected Sub OrdersAfterUpdate(ByVal orderId As Integer, _
                                    ByVal shipAddress As FieldValue)
        System.Diagnostics.Debug.WriteLine(String.Format( _
            "Order #{0} has been updated by '{1}' on {2}", _
            orderId, Context.User.Identity.Name, DateTime.Now))
        If (shipAddress.Modified) Then
            System.Diagnostics.Debug.WriteLine(String.Format( _
                 "Address has changed from '{0}' to '{1}.", _
                 shipAddress.OldValue, shipAddress.NewValue))
        End If
    End Sub

End Class

Open application in a web browser and select Orders data controller from the drop down in the top left corner. Start editing any order in the grid or form view and make sure to change Ship Address field. This field is the last visible field in the screen shot.

image

Hit OK button and the business method rule will intercept the action as soon as a successful database update has been completed. The first line of code will report the order ID and the user’s identity. The second line of code will detect the change in the address field.

image

Property Context provides business rules developers with the same Request, Response, User, Application, Session, and Server properties that are available  to web form developers.

The first two properties shall not be used since they provide information related to a current web service request and cannot be used to influence the user interface presentation.

Use the other properties as you if you were writing a typical web form.

Replace System.Diagnostics.Debug with a business object that is designed to keep track of user activities in a permanent data store such as a database table.

Task 2

All records in a database of orders must be marked with a reference to a user. User information will be utilized to filter data and for data analysis and reporting purposes.

Solution

Alter table [Northwind][.dbo].[Orders] to include new field UserName by executing the following SQL statement.

alter table Orders
add UserName varchar(50)
go

Modify command command1 in the data controller ~/Controllers/Orders.xml to select the new field twice. Once the field is selected under its own name and the other time we are selecting this very field under alias UserNameReadOnly. The reason for that is explained later.

        <command id="command1" type="Text">
            <text>
                <![CDATA[
select
    "Orders"."OrderID" "OrderID"
    ....................
    ,"Orders"."UserName" "UserName"
    ,"Orders"."UserName" "UserNameReadOnly"
from "dbo"."Orders" "Orders"
  ................
]]>
            </text>
        </command>

Add two field definitions for UserName instances in SQL statement to the list of data controller fields.

<fields>
    ...........
    <field name="UserName" type="String" label="User Name"/>
    <field name="UserNameReadOnly" type="String" label="User Name" readOnly="true"/>
</fields>

Let’s add the new read-only version of the field and a hidden version of the field to the list of data fields of views grid1 and editForm1.

<dataField fieldName="UserNameReadOnly"/>
<dataField fieldName="UserName" hidden="true"/>

Modify view createForm1 to include field UserName as a single hidden field.

<dataField fieldName="UserName" hidden="true"/>

We will silently assign a user name when a new record is created to the data controller field UserName. The captured value will be displayed when users review existing records but will be drawn from UserNameReadOnly for display purposes instead.

Add the following business rule to class Class1.

C#

[ControllerAction("Orders", "Update", ActionPhase.Before)]
[ControllerAction("Orders", "Insert", ActionPhase.Before)]
protected void OrdersBeforeUpdate(FieldValue userName)
{
    userName.NewValue = Context.User.Identity.Name;
    userName.Modified = true;
}

VB

    <ControllerAction("Orders", "Update", ActionPhase.Before)> _
    <ControllerAction("Orders", "After", ActionPhase.Before)> _
    Protected Sub OrdersBeforeUpdate(ByVal userName As FieldValue)
        userName.NewValue = Context.User.Identity.Name
        userName.Modified = True
    End Sub

This method will be automatically invoked whenever an order is about to be updated or inserted into database.

The first line will assign name of the currently logged-in user to the UserName field.

The second line will indicate that the field has actually changed. The framework is using Modified property of FieldValue instances to determine if a field shall be included in automatically generated SQL statement to update or insert a record.

You can also do an update on your own without relying on the framework. The best place for that sort of updates is in business rules methods with ActionPhase.After specifies as a parameter of ControllerAction attribute.

Here is how the grid of orders will look if you update a few records. The right-most column is displaying the name of the user.

image

The two fields UserName and UserNameReadOnly are required since read-only fields are never transferred to the server from the client web page. Hidden fields are never displayed but always travel from the client to the server and back. By introducing two versions of the same field we overcome this limitation imposed by the framework’s optimization logic.

Conclusion

Business rules in Data Aquarium Framework provide an excellent place to universally track user activities.

Sunday, April 5, 2009PrintSubscribe
Present What You Want

Data Aquarium Framework offers users of your applications impressive data filtering and reporting capabilities. Nevertheless there will always be a situation when you need to present a specific database record or a group of records on demand.

Filtering Via URL Parameter

The simplest method is to redirect your user to a specific page while supplying a record ID in the URL. For example, navigate to http://dev.codeontime.com/demo/nwblob/?EmployeeID=5 and you will see an employee with EmployeeID equal to 5 presented in the list.

image

Remove EmployeeID from the URL and all employees are displayed.

image

You are not limited to the record IDs only. For example, the following URL will select all employees with job title “Sales Representative” working in London office:

http://dev.codeontime.com/demo/nwblob?Title=Sales%20Representative&City=London

image

If the filtered field is visible by default then Data Aquarium Framework will automatically hide the corresponding field column in the grid view since the field value is presumed to be known to the user. There is little value repeating “Sales Representative” and “London” in each row in the picture above. You may want to verify that each employee is indeed working in London and has the title that we have specified.

Filtering Via an Element on The Page

You can also point your DataViewExtender components to read the filter value form any element on the page.

Add ~/Products.aspx page to the Data Aquarium project generated from Northwind database. Modify this page as shown below.

<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.master" 
    AutoEventWireup="true" CodeFile="Products.aspx.cs" 
    Inherits="Products" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
    <style type="text/css">
        div.Caption
        {
            font-weight: bold;
            padding: 4px 4px 4px 4px;
            background-color: #FDEAB1;
            color: #60302A;
        }
    </style>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Header1Placeholder" 
        runat="Server">
    Products
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="Header2Placeholder" 
        runat="Server">
    Northwind
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="BodyPlaceholder" 
        runat="Server">
    <asp:HiddenField ID="ProductID" runat="server" Value="5" />
    <!-- "edit" mode -->
    <div class="Caption">
        "Edit" Mode</div>
    <div id="EditProduct" runat="server">
    </div>
    <aquarium:DataViewExtender ID="DataViewExtender1" runat="server" 
        TargetControlID="EditProduct"
        Controller="Products" 
        FilterSource="ProductID" FilterFields="ProductID" 
        ShowActionBar="false"
        StartCommandName="Edit" StartCommandArgument="editForm1" />
    <!-- "new" mode -->
    <div class="Caption">
        "New" Mode</div>
    <div id="NewProduct" runat="server">
    </div>
    <aquarium:DataViewExtender ID="DataViewExtender2" runat="server" 
    TargetControlID="NewProduct"
        Controller="Products" ShowActionBar="false" 
        StartCommandName="New" StartCommandArgument="createForm1"
        PageSize="1" />
    <!-- "read" mode -->
    <div class="Caption">
        "Read" Mode</div>
    <div id="ViewProduct" runat="server">
    </div>
    <aquarium:DataViewExtender ID="ProductListExtender" runat="server" 
        TargetControlID="ViewProduct"
        Controller="Products" 
        FilterSource="ProductID" FilterFields="ProductID" 
        ShowActionBar="false"
        StartCommandName="Select" StartCommandArgument="editForm1" />
</asp:Content>

You can see this page live at http://dev.codeontime.com/demo/filtering

At the top of the page there is HiddenField server control ProductID with its value set to 5.

    <asp:HiddenField ID="ProductID" runat="server" Value="5" />

Three different views are presented on the page. Let’s take a look at each of them.

The first view presents data in edit mode.

    <!-- "edit" mode -->
    <div class="Caption">
        "Edit" Mode</div>
    <div id="EditProduct" runat="server">
    </div>
    <aquarium:DataViewExtender ID="DataViewExtender1" runat="server" 
        TargetControlID="EditProduct"
        Controller="Products" 
        FilterSource="ProductID" FilterFields="ProductID" 
        ShowActionBar="false"
        StartCommandName="Edit" StartCommandArgument="editForm1" />

It uses hidden field as a filter source and filters by the field ProductID.  The startup command displays view editForm1 in edit mode.

image

The second view displays an empty record and does not specify any filter.

    <!-- "new" mode -->
    <div class="Caption">
        "New" Mode</div>
    <div id="NewProduct" runat="server">
    </div>
    <aquarium:DataViewExtender ID="DataViewExtender2" runat="server" 
        TargetControlID="NewProduct"
        Controller="Products" ShowActionBar="false" 
        StartCommandName="New" StartCommandArgument="createForm1"
        PageSize="1" />

The startup command is executed only after the data controller data is transferred to the client browser. This will cause the controller to read the first page of records from the database. We are reducing the number of returned records to one by specifying the corresponding page size. You can eliminate retrieval of any records if you link a business rules filter to the Products data controller.

Here is the screen shot of the view.

image

The third view uses the hidden field as a filter source to display a view of a single record in “read” mode in editForm1.

    <!-- "read" mode -->
    <div class="Caption">
        "Read" Mode</div>
    <div id="ViewProduct" runat="server">
    </div>
    <aquarium:DataViewExtender ID="ProductListExtender" runat="server" 
        TargetControlID="ViewProduct"
        Controller="Products" 
        FilterSource="ProductID" FilterFields="ProductID" 
        ShowActionBar="false"
        StartCommandName="Select" StartCommandArgument="editForm1" />

Here is how the view looks when rendered in a web browser.

image

Generated data controllers are configured by default to switch to grid1 view when user successfully executes Insert, Update, Delete, or Cancel command. You may want to play with configuration of actions in ~/Controls/Products.xml to make do what you see fit for your purposes. Read about it here.

Preventing Filtering

You may also want to prohibit any filtering at all. This is easy to accomplish if you assign None to the FilterSource property of the DataViewExtender component.

<aquarium:DataViewExtender ID="ProductExtender" runat="server" 
    TargetControlID="ProductList" Controller="Products" 
    FilterSource="None" />

Conclusion

It is very easy to filter and display records that you want to be presented. URL parameters and elements on the page can be automatically consumed as sources of filter values.

Next we will take a look at server-side filtering via code in business rules.