Blog: Posts from March, 2012

Labels
AJAX(112) App Studio(7) 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(177) 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(183) 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(2) 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
Posts from March, 2012
Monday, March 5, 2012PrintSubscribe
Orders: Configuring Lookup Fields

When creating a new order, the first item that will be entered will probably be the customer. Due to the denormalized nature of Northwind database, the customer information is stored in the table Customers, and the Orders table references a customer via a foreign key field CustomerID. Upon creating a new order, it would be convenient if the user were prompted to either select an existing customer or create a new one. When the customer is selected, the shipping information should be copied from the customer to the order.

Switch back to the Designer, open grid1 view under Order Form page, expand CustomerID data field, and double click on the CustomerID field.

CustomerID field in Code On Time Designer

Enter the following settings for CustomerID:

Property Text
Data Value Field CustomerID
Data Text Field CompanyName
Copy

ShipName=ContactName
ShipAddress=Address
ShipCity=City
ShipRegion=Region
ShipPostalCode=PostalCode
ShipCountry=Country

Search on Start True
Activate If Blank True
Lookup window description Select a customer.

CustomerID Lookup configuration in Code On Time Designer

Press OK to save the field.

When the customer has been selected, we’d want the application to prompt the user to select the employee handling the order. To make this part easier, let’s add photos of each employee in the lookup.

Open EmployeeID data field, and double click on EmployeeID field.

EmployeeID field in Code On Time Designer

Change the following settings for EmployeeID:

Property Text
Activate If Blank True
Lookup window description Select an employee.

EmployeeID Lookup configuration in Code On Time Designer

Press OK to save the record.

Switch to the Controllers tab at the bottom of the Explorer, open Employees controller, open Views, and right click on grid1. Press New Data Field.

New Data Field for Employees controller

Select a Field Name of “Photo”, and save the data field.

New Photo Field in Employees Controller

In the upper left corner, press Generate, and wait for the application to load. Navigate to the Order Form page, and press New Orders on the action bar. You will be prompted to select a customer.

CustomerID Lookup showing filtering and advanced search in Code On Time Preview

When you search for and select a customer, you will then be prompted to select an employee. You can see the photo for each employee as well.

Employee ID Lookup for Order Form

Select the employee, and you will see that the shipping fields have been populated from the selected customer.

New Order Form in Code On Time Preview

Monday, March 5, 2012PrintSubscribe
Business Rules and Baseline Processing Enhancements

Code On Time release 6.0.0.22 includes the following bug fixes and enhancements:

  • Application baseline will not be deleted upon project refresh if the application generator detects that the baseline has been changed.
     
  • Selection of text in the input fields of the application generator via a mouse double-click will no longer lead to lost input focus.
     
  • Fields requiring formatting on the server via System.String.Format method are correctly formatting the native value of the field instead of its 'string' representation. This applied to fields with Format On Client set to false.
     
  • Adaptive filtering adjusts the upper range for DateTimeOffset fields by 1 day.
     
  • Microsoft Sql Server "tinyint" type is treated as "Byte" by the code generator.
     
  • Application framework is using ConvertToType method to convert values passed from the client to the server compatible data types.
     
  • The data sheet view will not allow editing data if user-selectable Edit or New actions are not available in the UI.
     
  • Various cosmetic improvements in the Social theme.
     
  • Static property DataControllerBase.DefaultDataControllerStream is now available to support data controller virtualization.
      
  • Method BusinessRules.UpdateFieldValue has been enhanced to update field values without the need to use SelectFieldValeObject method.
  • using System;
    using System.Data;
    using System.Collections.Generic;
    using System.Linq;
    using MyCompany.Data;

    namespace MyCompany.Rules
    {
        public partial class SharedBusinessRules : MyCompany.Data.BusinessRules
        {

            [ControllerAction("Orders", "Update", ActionPhase.Before)]
            public void DoIt(decimal orderId, string ShipAddress)
            {
                UpdateFieldValue("ShipAddress", ShipAddress + "*");
            }
        }
    }
     

  • Incorrect type conversion in business rules processor is fixed.
     
  • Method ViewPage.ToList correctly processes fields that exist in the data controller only and do not have a matching property in data access objects.
Saturday, March 3, 2012PrintSubscribe
Read-Only Database Applications

Data controllers of Code On Time web applications are equipped with a flexible set of data manipulation commands. Users can insert, duplicate, update, and delete data served by any data controller. The exceptionally simple and powerful action state machine makes very complex action sequences possible.

No doubt that the application administrator will be quite happy! What if you have a requirement to allow read-only access to data for a certain group of users or even a certain user account?

Let’s inventory the scope of work that needs to be performed.

Consider the following application built on top of the Northwind sample. Zero effort is required to enable full data editing in the Products list.

'Products'  list with all data manipulation capabilities enabled requires zero configuration

The context menu of data rows in the grid view offers Edit, Delete, Duplicate, and New commands.

Context menu in a grid view

The action bar offers New Products, Edit, Delete, and Import options.

Action bar offers New, Edit, Delete, and Import options.

If a user clicks on the link in the first column of any product row or chooses Select option from the context menu then the form view will be activated. Users are able to start editing or delete the row. Action bar also offers New Products and Import actions.

Form view allows  to start editing, delete, create new, or import a batch of records.

Code On Time web applications allow specifying a list of roles that are authorized to execute an action. The partial action state machine of the data controller Products is presented below.

<actions>
  <actionGroup id="ag1" scope="Grid">
    <action id="a1" commandName="Select" commandArgument="editForm1" />
    <action id="a2" commandName="Edit" />
    <action id="a3" commandName="Delete" />
    <action id="a6" />
    <action id="a7" commandName="Duplicate" commandArgument="createForm1" />
    <action id="a8" commandName="New" commandArgument="grid1" />
  </actionGroup>
  <actionGroup id="ag2" scope="Form">
    <action id="a1" commandName="Edit" />
    <action id="a2" commandName="Delete" />
    . . . . .
  </actionGroup>
  <actionGroup id="ag3" scope="ActionBar" headerText="New" flat="true">
    <action id="a1" commandName="New" commandArgument="createForm1" 
            cssClass="NewIcon" />
  </actionGroup>
  <actionGroup id="ag4" scope="ActionBar" headerText="Edit/Delete" flat="true">
    <action id="a1" whenKeySelected="true" commandName="Edit" 
            commandArgument="editForm1" cssClass="EditIcon" whenView="grid1" />
    <action id="a2" whenKeySelected="true" commandName="Delete" 
            cssClass="DeleteIcon" whenView="grid1" />
  </actionGroup>
  <actionGroup id="ag5" scope="ActionBar" headerText="Actions">
    . . . . .
    <action id="a6" commandName="Import" commandArgument="createForm1" />
    . . . . .
  </actionGroup>
  . . . . .
</actions>

You can modify the data controller baseline and specify the “roles” attribute for each of the ten actions above.

<action id="a2" commandName="Edit" roles="Administrators" />

You can also use the  Project Designer and edit the the Roles property for each action there.

Action 'Roles' property in Project Designer

That may not be  a huge challenge in a small project but if you do need to configure a few dozen data controllers then you are probably thinking that there must be a better way.

It is possible to customize each data controller at runtime with the help of data controller virtualization.

Enable shared business rules in the Business Logic Layer settings of your web application and implement the SharedBusinessRules class as shown below.

C#:

using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using MyCompany.Data;
using System.Xml.XPath;
using System.Xml;

namespace MyCompany.Rules
{
    public partial class SharedBusinessRules : MyCompany.Data.BusinessRules
    {
        public override bool SupportsVirtualization(string controllerName)
        {
            return !UserIsInRole("Administrators");
        }

        public override void VirtualizeController(string controllerName, 
            XPathNavigator navigator, XmlNamespaceManager resolver)
        {
            string[] restrictedCommands = { "Edit", "New", "Delete", "Duplicate", "Import" };
            XPathNodeIterator actionIterator = navigator.Select("//c:action", resolver);
            while (actionIterator.MoveNext())
            {
                string commandName = actionIterator.Current.GetAttribute(
                    "commandName", String.Empty);
                if (restrictedCommands.Contains(commandName))
                    actionIterator.Current.CreateAttribute(
                        String.Empty, "roles", String.Empty, "Administrators");
            }
        }
    }
}

Visual Basic:

Imports MyCompany.Data
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Linq
Imports System.Xml
Imports System.Xml.XPath

Namespace MyCompany.Rules

    Partial Public Class SharedBusinessRules
        Inherits MyCompany.Data.BusinessRules

        Public Overrides Function SupportsVirtualization(controllerName As String) As Boolean
            Return Not UserIsInRole("Administrators")
        End Function

        Public Overrides Sub VirtualizeController(controllerName As String,
            navigator As XPathNavigator, resolver As XmlNamespaceManager)
            Dim restrictedCommands() As String =
                {"Edit", "New", "Delete", "Duplicate", "Import"}
            Dim actionIterator As XPathNodeIterator = navigator.Select("//c:action", resolver)
            While (actionIterator.MoveNext())
                Dim commandName As String = actionIterator.Current.GetAttribute(
                    "commandName", String.Empty)
                If (restrictedCommands.Contains(commandName)) Then
                    actionIterator.Current.CreateAttribute(
                        String.Empty, "roles", String.Empty, "Administrators")
                End If
            End While

        End Sub
    End Class
End Namespace

The method SupportsVirtualization returns True if the user performing the current web request is not an administrator.

Method VirtualizeController scans the list of actions and assigns “roles” attribute to actions that initiate commands Edit, New, Delete, Duplicate, and Import. The actual data controller file is not changed. The changes are discarded as soon as the current request is processed.

Notice that we “hide” the actions from the user account by assigning Administrators value to the “roles” attribute. You can assign any value instead of Administrators as long as the value does not represent the role assigned to the non-administrative user account. You can also delete the action from the data controller. The outcome will be exactly the same.

Run the application and login with the user account user/user123%. Notice that the ability to edit data is not available anymore.

Data controller virtualization allows implementing a single method that assignes a 'roles' attribute to each action that must have a restricted access.