Skip to content

Conference App: complete the application

Add Rooms, Presentations, and Settings to the client documented in Synchronize presenters. This phase adds the missing data dictionaries and views, then expands the existing application, database, dashboard, and Presenter behavior.

Before you begin: Complete Synchronize presenters. Keep all files from that page. This is the phase that changes the local database filename to /dev/idbfs/ConferenceDataV10.db and introduces version-aware synchronization.

Step 1 — Add Room, Presentation, and Settings data

Add these files before adding views or changing synchronization. All three entities use the same { Connection=conf_data } logical connection ID, so their Data Dictionaries use the SQLite driver already registered by the template. Entities define the persistent models; Relations and Constraints explains how Presentation relates Room and Presenter. Settings stores DataVersion and LastSync as name/value records.

AppSrc/RoomDataDictionary.pkg

Room is the parent entity for room selection and Presentation navigation. Its required Name and indexes supply the room list used by later views.

Use Db\cDataDictionary.pkg

{ Connection=conf_data}
Entity Room
    { PrimaryKey=True AutoIncrement=True }
    Integer RoomId
    String Name
    String Description
    String Directions
    Integer Capacity

    Add_Index RoomId ASC
    Add_Index Name ASC
End_Entity

{ Entity=Room }
Class cRoomDataDictionary is a cDataDictionary
    Procedure Construct_Object
        Forward Send Construct_Object

        Set phEntity to (RefEntity(Room))

        Set Field_Option (RefTable(Self.Name)) DD_Required to True
    End_Procedure
End_Class

AppSrc/PresentationDataDictionary.pkg

Presentation references both Room and Presenter through its relations, so its Data Dictionary can bind parent DDOs and validate required title, room, date, and time fields.

Use Db\cDataDictionary.pkg
Use RoomDataDictionary.pkg
Use PresenterDataDictionary.pkg

{ Connection=conf_data }
Entity Presentation
    { PrimaryKey=True AutoIncrement=True }
    Integer PresentationId
    Integer RoomId
    Integer PresenterId
    String Title
    String Description
    Date Date
    Time StartTime
    Time EndTime
    String Level
    Integer MaxAttendees

    Add_Index PresentationId ASC
    Add_Index RoomId ASC PresentationId ASC
    Add_Index Date ASC StartTime ASC Title ASC
    Add_Index Title ASC

    Add_Relation RoomId to Room.RoomId
    Add_Relation PresenterId to Presenter.PresenterId
End_Entity

{ Entity=Presentation }
Class cPresentationDataDictionary is a cDataDictionary
    Procedure Construct_Object
        Forward Send Construct_Object

        Set phEntity to (RefEntity(Presentation))

        Set Field_Option (RefTable(Self.Title)) DD_Required to True
        Set Field_Option (RefTable(Self.RoomId)) DD_Required to True
        Set Field_Option (RefTable(Self.Date)) DD_Required to True
        Set Field_Option (RefTable(Self.StartTime)) DD_Required to True
        Set Field_Option (RefTable(Self.EndTime)) DD_Required to True

    End_Procedure
End_Class

AppSrc/SettingsDataDictionary.pkg

Settings stores named values. The complete synchronization package uses DataVersion to compare local and remote data and LastSync to display the latest successful synchronization time.

Use Db\cDataDictionary.pkg

{ Connection=conf_data }
Entity Settings
    { PrimaryKey=True }
    String Name
    String Value

    Add_Index Name ASC
End_Entity

{ Entity=Settings }
Class cSettingsDataDictionary is a cDataDictionary
    Procedure Construct_Object
        Forward Send Construct_Object

        Set phEntity to (RefEntity(Settings))

        Set Field_Option (RefTable(Self.Name)) DD_Required to True
    End_Procedure

    Function LoadSetting String sSetting String sDefault Returns String
        Move sSetting to Self.Name
        Send FindByField EQ (RefTable(Self.Name))

        If (Found) ;
            Function_Return Self.Value
        Else ;
            Function_Return sDefault
    End_Function

    Procedure StoreSetting String sSetting String sValue
        Move sSetting to Self.Name
        Send FindByField EQ (RefTable(Self.Name))

        If (not(Found)) Begin
            Send Clear 
            Move sSetting to Self.Name
        End

        Move sValue to Self.Value
        Send Request_Save
    End_Procedure
End_Class

Step 2 — Add Room and Presentation drill-down views

Add each complete file in this phase. Select views bind their list DDOs and register zoom navigation; Data Binding explains these local-buffer bindings, while Relations and Constraints covers the Room and Presenter parent DDOs used by Presentation views. Set Main_DD identifies the DDO that owns the current record, Set Server supplies records to data-aware controls, and each Entry_Item line binds a concrete DDO field to a control.

AppSrc/SelectRoom.wo

The Room list binds Main_DD and Server to oRoomDD, then forwards a selected row to oZoomRoom. Its Entry_Item oRoomDD.Name column reads the current Room DDO buffer; the list does not access SQLite or a global file buffer directly.

Use WebUI\cWebView.pkg
Use WebUI\cWebPanel.pkg
Use WebUI\cWebButton.pkg
Use WebUI\cWebList.pkg
Use WebUI\cWebColumn.pkg
Use WebUI\cWebMenuGroup.pkg 
Use WebUI\cWebMenuItem.pkg
Use WebUI\cWebForm.pkg
Use WebUI\cWebColumnButton.pkg
Use RoomDataDictionary.pkg

Object oSelectRoom is a cWebView

    // Your DDO structure will go here
    Object oRoomDD is a cRoomDataDictionary
    End_Object

    Set Main_DD to oRoomDD
    Set Server to oRoomDD

    Set peWebViewStyle to wvsDrillDown
    Set peViewType to vtSelect

    Set psCaption to "Rooms"

    Set piMaxWidth to 1024
    Set piColumnCount to 6
    Set pbShowCaption to False
    Set psStateViewName to "Rooms"

    WebSetResponsive piColumnCount rmTabletPortrait to 3

    Object oList is a cWebList
        Set pbFillHeight to True
        Set piColumnSpan to 0
        Set pbServerOnRowClick to True
        Set psCSSClass to "MobileList"
        Set piSortColumn to 0 // set this to allow for searching
        Set pbShowHeader to False

        WebRegisterPath ntNavigateForward oSelectPresentation

        // place web column controls controls here.

        Object oRoomDD_Name is a cWebColumn
            Set psCaption to "Name"
            Set psCSSClass to "RowCaption"
            Set peAlign to alignLeft
            Entry_Item oRoomDD.Name
        End_Object 

        // sample of a right-aligned info column button
        Object oDetailButton is a cWebColumnButton
            Set piWidth to 45
            Set pbFixedWidth to True
            Set psCaption to "btn"
            Set pbResizable to False
            Set psBtnCssClass to "WebButtonIcon WebIcon_Info"
            Set peAlign to alignRight
            Set piListRowSpan to 2

            WebRegisterPath ntNavigateForward oZoomRoom

            Procedure OnClick
                Send NavigatePath
            End_Procedure

            Procedure OnGetNavigateForwardData tWebNavigateData ByRef NavigateData Handle hoToView
                Move True to NavigateData.bReadOnly
            End_Procedure

        End_Object 

        Object oRoomDD_Description is a cWebColumn
            Set psCaption to "Description"
            Set pbNewLine to True
            Set psCSSClass to "RowDetail"
            Entry_Item oRoomDD.Description
        End_Object

        // What to do when the row is selected.
        //
        // Depending on how you are using this view, you may wish to
        // 1. Close the view, which will update the invoking view:
        //      Send NavigateClose Self
        // 2. Navigate forward to another view (either a from-main Zoom or a from-parent select):
        //      Send NavigateForward of oZoomMainView Self
        //      Send NavigateForward of oSelectChildView Self
        //    Note: Do not forget to register the forward navigation paths using WebRegisterPath
        Procedure OnRowClick String sRowID

            // If this view is being used in multiple contexts, you may need a block of code
            // like this to handle different types of navigation
            tWebNavigateData NavigateData
            Get GetNavigateData to NavigateData
            Case Begin
                Case (NavigateData.eNavigateType=nfFromParent)
                    Send NavigatePath
                    Case Break
                Case (NavigateData.eNavigateType=nfFromChild)
                    // If from child, this is a probably a parent lookup from a Zoom,
                    // so you just want to close (See 1 above)
                    Send NavigateClose Self
                    Case Break
                Case (NavigateData.eNavigateType=nfFromMain)
                    // If from main, this is a propbably a main file lookup from a Zoom, 
                    // so you just want to close (see 1 above). 
                    // This is not used often with the drilldown style
                    Send NavigateClose Self
                    Case Break
                Case Else // must be nfUndefined
                    Send NavigatePath
            Case End

        End_Procedure

        // this makes the zoom view read-only or new or some other customization
        Procedure OnGetNavigateForwardData tWebNavigateData ByRef NavigateData Handle hoToView
        End_Procedure

    End_Object    


    // add action menu items here

    Object oActionGroup is a cWebMenuGroup
        Set psGroupName to "MainActions"

        Object oSearch is a cWebMenuItem
            Set psCaption to C_$Search
            Set psCSSClass to "WebPromptMenuItem"

            Procedure OnClick
                Send Search of oList
            End_Procedure            
        End_Object

        Object oNewButton is a cWebMenuItem
            Set psCaption to C_$New
            Set psCSSClass to "WebClearMenuItem"

            WebRegisterPath ntNavigateForward oZoomRoom

            Procedure OnClick
                Send NavigatePath
            End_Procedure

            Procedure OnGetNavigateForwardData tWebNavigateData ByRef NavigateData Handle hoToView
                Move True to NavigateData.bNewRecord
            End_Procedure

        End_Object 

//        // Sample Change sort order button 
//        Object oSearchOrderNumber is a cWebMenuItem
//            Set psCaption to "View by ????"
//            Set peActionDisplay to adMenu  
//
//            Procedure OnClick
//                Integer eType
//                WebSet piSortColumn of oList to 0 // set a column number
//                Send GridRefresh of oList
//            End_Procedure
//            
//        End_Object


        // Sample top/bottom button
        Object oFindTop is a cWebMenuItem
            Set psCaption to C_$Top
            Set peActionDisplay to adMenu
            Set pbBeginGroup to True             

            Procedure OnClick
                Send MoveToFirstRow of oList
            End_Procedure

        End_Object

        Object oFindLast is a cWebMenuItem
            Set psCaption to C_$Bottom
            Set peActionDisplay to adMenu             
            Set pbServerOnClick to True

            Procedure OnClick
                Send MoveToLastRow of oList
                    Send NavigatePath
            End_Procedure

        End_Object
    End_Object

    // Add code to customize your Select View based on how it was invoked.
    // Use NavigateData to determine the context this view will be used in.
    Procedure OnNavigateForward tWebNavigateData NavigateData Integer hoInvokingView Integer hoInvokingObject

        // if this view is being used in multiple contexts, you may need a block of code
        // like this to handle customizations. This would include hiding rows and buttons
        // (WebSet pbRender) and changing the values of various captions.
        Case Begin
            Case (NavigateData.eNavigateType=nfFromParent)
                // If from parent, this is a constrained drill down.
                // If needed you could check NavigateData.iTable to determine the constraining parent.
                Case Break

            Case (NavigateData.eNavigateType=nfFromChild)
                // If from child, this is a probably a parent lookup from a Zoom
                Case Break

            Case (NavigateData.eNavigateType=nfFromMain)
                // If from main, this is a propbably a main file lookup from a Zoom.
                // This is not used often with the drilldown style
                Case Break

            Case Else // must be nfUndefined
                // This may be the start of a drilldown query or some kind ofcustom query.
                // You may want to check NavigateData.NamedValues.

        Case End

    End_Procedure

End_Object

AppSrc/ZoomRoom.wo

The Room zoom binds editable room fields to oRoomDD and provides save, edit, delete, and cancel actions. Each Entry_Item line binds one oRoomDD field to a control; Data Dictionary actions validate and persist that DDO buffer through the configured SQLite driver.

Use WebUI\cWebView.pkg
Use WebUI\cWebPanel.pkg
Use WebUI\cWebForm.pkg 
Use WebUI\cWebGroup.pkg
Use WebUI\cWebMenuGroup.pkg 
Use WebUI\cWebMenuItem.pkg
Use WebUI\cWebEdit.pkg

Object oZoomRoom is a cWebView

    // Your DDO structure will go here
    Object oRoomDD is a cRoomDataDictionary
    End_Object

    Set Main_DD to oRoomDD
    Set Server to oRoomDD


    Set peWebViewStyle to wvsDrillDown
    Set peViewType to vtZoom
    Set pbShowCaption to False
    Set Verify_Save_msg to 0 // don't confirm saves

    Set psCaption to "ZoomRoom"

    Set piMaxWidth to 1024
    Set piColumnCount to 12

    Object oWebMainPanel is a cWebPanel
        Set piColumnCount to 12

        Object oRoomDD_Name is a cWebForm
            Set piColumnSpan to 0
            Set psLabel to "Name"
            Entry_Item oRoomDD.Name
        End_Object

        Object oRoomDD_Description is a cWebForm
            Set piColumnSpan to 0
            Set psLabel to "Description"
            Entry_Item oRoomDD.Description
        End_Object

        Object oRoomDD_Directions is a cWebEdit
            Set piColumnSpan to 0
            Set psLabel to "Directions"
            Entry_Item oRoomDD.Directions
        End_Object
        WebSetResponsive piColumnCount rmMobile to 6

        // place controls here
        // Your view will grow as controls are added

        // This shows how using groups can be an effective to create a responsive view.

    End_Object 

    // add action menu items here
    // we've included some common buttons 
    Object oActionGroup is a cWebMenuGroup
        Set psGroupName to "MainActions"

        Object oSaveBtn is a cWebMenuItem
            Set psCaption to C_$Save
            Set psCSSClass to "WebSaveMenuItem"

            Procedure OnClick
                Send Request_Save
            End_Procedure
        End_Object

        Object oEditBtn is a cWebMenuItem
            Set psCaption to C_$CategoryEdit
            Set psCSSClass to "WebEditMenuItem"
            Procedure OnClick
                Send ChangeEditMode True
                Send SetActionButtons
            End_Procedure
        End_Object 

        Object oDeleteBtn is a cWebMenuItem
            Set psCaption to C_$Delete
            Set psCSSClass to "WebDeleteMenuItem"
            Set peActionDisplay to adMenu            

            Procedure OnClick
                Send Request_Delete
            End_Procedure
        End_Object

        Object oCancelChangesBtn is a cWebMenuItem
            Set psCaption to C_$ToolTipClear
            Set peActionDisplay to adMenu            

            Procedure OnClick
                // this will undo any unchanged saves and show the latest
                Send RefreshRecord
            End_Procedure
        End_Object 

    End_Object

    // This can be used to show and hide buttons based on context.
    // This can be called any time the view is active.
    Procedure SetActionButtons
        tWebNavigateData NavigateData
        Boolean bHasRecord
        Handle hoDD

        Get Server to hoDD
        Get GetNavigateData to NavigateData

        If (hoDD) Begin
            Get HasRecord of hoDD to bHasRecord
        End
        Else Begin
            Move False to bHasRecord
        End

        // let's hide all buttons and then show the ones we want
        WebSet pbRender of oEditBtn to False 
        WebSet pbRender of oSaveBtn to False
        WebSet pbRender of oCancelChangesBtn to False
        WebSet pbRender of oDeleteBtn to False

        If (NavigateData.bReadOnly) Begin 
            WebSet pbRender of oEditBtn to True 
        End
        Else Begin
            WebSet pbRender of oSaveBtn to True
            WebSet pbRender of oCancelChangesBtn to True
            WebSet pbRender of oDeleteBtn to bHasRecord
        End
    End_Procedure

    // this will close the view after a save
    Procedure OnViewSaved Handle hoServer Boolean bChanged
        Send NavigateClose Self
    End_Procedure

    // this will close the view after a delete
    Procedure OnViewDeleted Handle hoDDO
        Send NavigateClose Self
    End_Procedure

    // Add code to customize your Zoom View based on how it was invoked.
    // Use NavigateData to determine the context this view will be used in.
    Procedure OnNavigateForward tWebNavigateData NavigateData Integer hoInvokingView Integer hoInvokingObject

        // if this view is being used in multiple contexts, you may need a block of code
        // like this to handle customizations. This would include hiding rows and buttons
        // (WebSet pbRender) and changing the values of various captions.
        Case Begin
            Case (NavigateData.eNavigateType = nfFromMain)
                // If from main, this is a probably a main file Select to Zoom.
                // This is the most typical way to navigate to a zoom.
                Case Break

            Case (NavigateData.eNavigateType = nfFromParent)
                // If from parent, this is a constrained drill down.
                // If needed, you could check NavigateData.iTable to determine the constraining parent.
                // This would be unusual - normally you drill down from a parent to a list (Select view).
                Case Break

            Case (NavigateData.eNavigateType = nfFromChild)
                // If from child, this is a probably a parent Zoom from a Zoom. 
                // This is unusual, but it might be used for adding a new child record.
                Case Break

            Case Else // must be nfUndefined
                // This may be the start of a query or this may be used for some kind of custom operation.
                // You may want to check NavigateData.NamedValues.

        Case End

        // Action buttons can be changed dynamically during a Zoom's display lifetime.
        // It's best to set these buttons in their own procedure so you can change later.
        Send SetActionButtons

    End_Procedure

End_Object

AppSrc/SelectPresentation.wo

The Presentation list uses oPresentationDD as its main/server DDO and adds oRoomDD and oPresenterDD as related DDOs. Its row navigation reaches oZoomPresentation. Direct Entry_Item oPresentationDD.Title and Entry_Item oRoomDD.Name bindings read fields from those DDOs.

The presenter column shows the expression form of Entry_Item: Entry_Item @SQL"CONCAT(Presenter.FirstName, ' ', Presenter.LastName)" from oPresenterDD. This is not a standalone SELECT statement and does not bind to a nonexistent combined field. It sends a native expression for the oPresenterDD source to the backend, where the configured SQLite adapter evaluates it as a display-only value; native-expression syntax is backend-specific. See Data Binding for the distinction between DD-field and expression bindings.

Use WebUI\cWebView.pkg
Use WebUI\cWebPanel.pkg
Use WebUI\cWebButton.pkg
Use WebUI\cWebList.pkg
Use WebUI\cWebColumn.pkg
Use WebUI\cWebMenuGroup.pkg
Use WebUI\cWebMenuItem.pkg
Use WebUI\cWebForm.pkg
Use WebUI\cWebColumnButton.pkg
Use WebUI\cWebColumnHighlight.pkg
Use PresentationDataDictionary.pkg
Use RoomDataDictionary.pkg
Use PresenterDataDictionary.pkg

Object oSelectPresentation is a cWebView
    Object oPresenterDD is a cPresenterDataDictionary
    End_Object

    Object oRoomDD is a cRoomDataDictionary
    End_Object

    Object oPresentationDD is a cPresentationDataDictionary
        Set DDO_Server to oPresenterDD
        Set DDO_Server to oRoomDD
    End_Object

    Set Main_DD to oPresentationDD
    Set Server to oPresentationDD

    Set peWebViewStyle to wvsDrillDown
    Set peViewType to vtSelect

    Set psCaption to "Presentations"

    Set piMaxWidth to 1024
    Set piColumnCount to 6
    Set pbShowCaption to False
    Set psStateViewName to "Presentations"

    WebSetResponsive piColumnCount rmTabletPortrait to 3

    Object oList is a cWebList
        Set pbFillHeight to True
        Set piColumnSpan to 0
        Set pbServerOnRowClick to True
        Set psCSSClass to "MobileList"
        Set pbShowHeader to False
        Set peGrouping to grpAutomatic
        Set peDbGridType to gtAllData

        Object oPresentationDD_Title is a cWebColumnHighlight
            Set psCaption to "Title"
            Set psCSSClass to "RowCaption"
            Set peAlign to alignLeft
            Set piListColSpan to 4

            Entry_Item oPresentationDD.Title
        End_Object


        Object oDetailButton is a cWebColumnButton
            Set piWidth to 45
            Set pbFixedWidth to True
            Set psCaption to "btn"
            Set pbResizable to False
            Set psBtnCssClass to "WebButtonIcon WebIcon_Info"
            Set peAlign to alignRight
            Set piListRowSpan to 3

            WebRegisterPath ntNavigateForward oZoomPresentation

            Procedure OnClick
                Send NavigatePath
            End_Procedure

            Procedure OnGetNavigateForwardData tWebNavigateData ByRef NavigateData Handle hoToView
                Move True to NavigateData.bReadOnly
            End_Procedure

        End_Object

        Object oPresenterDD_Name is a cWebColumn
            Set psCaption to "Presenter"
            Entry_Item @SQL"CONCAT(Presenter.FirstName, ' ', Presenter.LastName)" from oPresenterDD

            Set pbNewLine to True
            Set piListColSpan to 4
        End_Object

        Object oPresentationDD_Date is a cWebColumn
            Set psCaption to "Day"
            Set pbNewLine to True
            Set psCSSClass to "RowDetail"
            Entry_Item oPresentationDD.Date
            Set psMask to "Ddd d Mmmm yyyy"
            Set pbFixedWidth to True
            Set piWidth to 120
        End_Object

        Object oPresentationDD_StartTime is a cWebColumn
            Set psCaption to "Start"
            Set psCSSClass to "RowDetail"
            Entry_Item oPresentationDD.StartTime
            Set pbFixedWidth to True
            Set piWidth to 80
        End_Object

        Object oPresentationDD_EndTime is a cWebColumn
            Set psCaption to "End"
            Set psCSSClass to "RowDetail"
            Entry_Item oPresentationDD.EndTime

            Set pbFixedWidth to True
            Set piWidth to 80
        End_Object

        Object oRoom_Name is a cWebColumn
            Set psCaption to "Room"
            Set psCSSClass to "RowDetail"

            Entry_Item oRoomDD.Name
        End_Object

        WebRegisterPath ntNavigateForward oZoomPresentation

        Procedure OnRowClick String sRowID

            tWebNavigateData NavigateData
            Get GetNavigateData to NavigateData
            Case Begin
                Case (NavigateData.eNavigateType=nfFromParent)
                    Send NavigatePath
                    Case Break
                Case (NavigateData.eNavigateType=nfFromChild)
                    Send NavigateClose Self
                    Case Break
                Case (NavigateData.eNavigateType=nfFromMain)
                    Send NavigateClose Self
                    Case Break
                Case Else // must be nfUndefined
                    Send NavigatePath
            Case End

        End_Procedure

        Procedure OnGetNavigateForwardData tWebNavigateData ByRef NavigateData Handle hoToView
        End_Procedure

        Procedure OnLoad
            tWebGroupConfig[] aGroups

            Get piColumnId of oPresentationDD_Date to aGroups[0].iColumnId
            Get psCaption of oPresentationDD_Date to aGroups[0].sLabel
            Move False to aGroups[0].bReverse

            Send ApplyGroupConfig aGroups True
        End_Procedure

        Set piSortColumn to (piColumnId(oPresentationDD_StartTime))

    End_Object


    Object oActionGroup is a cWebMenuGroup
        Set psGroupName to "MainActions"

        Object oSearch is a cWebMenuItem
            Set psCaption to C_$Search
            Set psCSSClass to "WebPromptMenuItem"

            Procedure OnClick
                Send Search of oList
            End_Procedure
        End_Object

        Object oNewButton is a cWebMenuItem
            Set psCaption to C_$New
            Set psCSSClass to "WebClearMenuItem"

            WebRegisterPath ntNavigateForward oZoomPresentation

            Procedure OnClick
                Send NavigatePath
            End_Procedure

            Procedure OnGetNavigateForwardData tWebNavigateData ByRef NavigateData Handle hoToView
                Move True to NavigateData.bNewRecord
            End_Procedure

        End_Object

        Object oFindTop is a cWebMenuItem
            Set psCaption to C_$Top
            Set peActionDisplay to adMenu
            Set pbBeginGroup to True

            Procedure OnClick
                Send MoveToFirstRow of oList
            End_Procedure

        End_Object

        Object oFindLast is a cWebMenuItem
            Set psCaption to C_$Bottom
            Set peActionDisplay to adMenu

            Procedure OnClick
                Send MoveToLastRow of oList
            End_Procedure

        End_Object
    End_Object

    Procedure OnNavigateForward tWebNavigateData NavigateData Integer hoInvokingView Integer hoInvokingObject

        Case Begin
            Case (NavigateData.eNavigateType=nfFromParent)
                If (NavigateData.iTable = RefEntity(Presenter)) Begin
                    Send SetBreadcrumbCaption (SFormat("Presentations from %1", oPresenterDD.FirstName * oPresenterDD.LastName))
                End
                Else If (NavigateData.iTable = RefEntity(Room)) Begin
                   Send SetBreadcrumbCaption (SFormat("Presentations in %1", oRoomDD.Name)) 
                End
                Case Break

            Case (NavigateData.eNavigateType=nfFromChild)
                Case Break

            Case (NavigateData.eNavigateType=nfFromMain)
                Case Break

            Case Else // must be nfUndefined

        Case End

    End_Procedure

End_Object

AppSrc/ZoomPresentation.wo

The Presentation zoom binds oPresentationDD to Room and Presenter parent DDOs, exposing related room/presenter fields alongside editable presentation data and navigation actions. oPresentationDD owns the current row; Set DDO_Server connects its RoomId and PresenterId relations to the parent DDOs so Entry_Item oRoom_DD.Name, Entry_Item oPresenter_DD.FirstName, and Entry_Item oPresenter_DD.LastName can resolve related values. Entry_Item oPresentation_DD.Title and the other presentation-field controls edit the presentation DDO, whose save actions validate and persist through the same relationship-aware graph.

Use WebUI\cWebView.pkg
Use WebUI\cWebPanel.pkg
Use WebUI\cWebForm.pkg
Use WebUI\cWebGroup.pkg
Use WebUI\cWebMenuGroup.pkg
Use WebUI\cWebMenuItem.pkg
Use WebUI\cWebEdit.pkg
Use WebUI\cWebDateForm.pkg
Use WebUI\cWebCombo.pkg
Use PresentationDataDictionary.pkg
Use PresenterDataDictionary.pkg
Use RoomDataDictionary.pkg

Object oZoomPresentation is a cWebView
    Set peWebViewStyle to wvsDrillDown
    Set peViewType to vtZoom
    Set pbShowCaption to False
    Set Verify_Save_msg to 0 // don't confirm saves
    Set psCaption to "Details of Presentation"
    Set psStateViewName to "Presentation"
    Set piMaxWidth to 1024
    Set piColumnCount to 12
    Set peLayoutType to ltFlow

    Object oRoom_DD is a cRoomDataDictionary
    End_Object

    Object oPresenter_DD is a cPresenterDataDictionary
    End_Object

    Object oPresentation_DD is a cPresentationDataDictionary
        Set DDO_Server to oRoom_DD
        Set DDO_Server to oPresenter_DD
    End_Object

    Set Main_DD to oPresentation_DD
    Set Server to oPresentation_DD

    Object oWebMainPanel is a cWebPanel
        Set piColumnCount to 12

        Object oPresentation_Title is a cWebForm
            Entry_Item oPresentation_DD.Title
            Set psLabel to "Title"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 13
        End_Object

        Object oPresentation_Description is a cWebEdit
            Entry_Item oPresentation_DD.Description
            Set psLabel to "Description"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 13
            Set piMinHeight to 200
            Set piHeight to 200
        End_Object

        Object oPresentation_Date is a cWebDateForm
            Entry_Item oPresentation_DD.Date
            Set psLabel to "Date"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 2
        End_Object

        Object oPresentation_StartTime is a cWebForm
            Entry_Item oPresentation_DD.StartTime
            Set psLabel to "StartTime"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 2
            Set peDataType to typeTime
            Set piColumnIndex to 2
        End_Object

        Object oPresentation_EndTime is a cWebForm
            Entry_Item oPresentation_DD.EndTime
            Set psLabel to "EndTime"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 2
            Set peDataType to typeTime
            Set piColumnIndex to 4
        End_Object

        Object oPresentation_Level is a cWebForm
            Entry_Item oPresentation_DD.Level
            Set psLabel to "Level"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 4
        End_Object

        Object oPresentation_MaxAttendees is a cWebForm
            Entry_Item oPresentation_DD.MaxAttendees
            Set psLabel to "MaxAttendees"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 2
            Set piColumnIndex to 4
        End_Object

        Object oRoom_Name is a cWebForm
            Entry_Item oRoom_DD.Name
            Set psLabel to "Room"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 6
            Set piColumnIndex to 6
        End_Object

        Object oPresenter_FirstName is a cWebForm
            Entry_Item oPresenter_DD.FirstName
            Set psLabel to "Presenter (firstname):"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 3
            Set pbServerOnPrompt to True

            WebRegisterPath ntNavigateForward oSelectPresenter

            Set pbPromptButton to True

            Procedure OnPrompt
                Send NavigatePath
            End_Procedure
        End_Object

        Object oPresenter_LastName is a cWebForm
            Entry_Item oPresenter_DD.LastName
            Set psLabel to "Presenter (lastname):"
            Set peLabelPosition to lpTop
            Set piColumnSpan to 3
            Set pbServerOnPrompt to True
            Set piColumnIndex to 3

            WebRegisterPath ntNavigateForward oSelectPresenter

            Set pbPromptButton to True

            Procedure OnPrompt
                Send NavigatePath
            End_Procedure
        End_Object

        WebSetResponsive piColumnCount rmMobile to 6
    End_Object

    // add action menu items here
    // we've included some common buttons
    Object oActionGroup is a cWebMenuGroup
        Set psGroupName to "MainActions"

        Object oSaveBtn is a cWebMenuItem
            Set psCaption to "Save"
            Set psCSSClass to "WebSaveMenuItem"

            Procedure OnClick
                Send Request_Save
            End_Procedure
        End_Object

        Object oEditBtn is a cWebMenuItem
            Set psCaption to "Edit"
            Set psCSSClass to "WebEditMenuItem"

            Procedure OnClick
                Send ChangeEditMode True
                Send SetActionButtons
            End_Procedure
        End_Object

        Object oDeleteBtn is a cWebMenuItem
            Set psCaption to "Delete"
            Set psCSSClass to "WebDeleteMenuItem"
            Set peActionDisplay to adMenu

            Procedure OnClick
                Send Request_Delete
            End_Procedure
        End_Object

        Object oCancelChangesBtn is a cWebMenuItem
            Set psCaption to "Clear Changes"
            Set psCSSClass to "WebIcon_Refresh"
            Set peActionDisplay to adMenu
            Set pbServerOnClick to True

            Procedure OnClick
                // this will undo any unchanged saves and show the latest
                Send RefreshRecord
                    Send NavigatePath
            End_Procedure
        End_Object
    End_Object

    // This can be used to show and hide buttons based on context.
    // This can be called any time the view is active.
    Procedure SetActionButtons
        tWebNavigateData NavigateData
        Boolean bHasRecord
        Handle hoDD

        Get Server to hoDD
        Get GetNavigateData to NavigateData

        If (hoDD) Begin
            Get HasRecord of hoDD to bHasRecord
        End
        Else Begin
            Move False to bHasRecord
        End

        // let's hide all buttons and then show the ones we want
        WebSet pbRender of oEditBtn to False
        WebSet pbRender of oSaveBtn to False
        WebSet pbRender of oCancelChangesBtn to False
        WebSet pbRender of oDeleteBtn to False

        If (NavigateData.bReadOnly) Begin
            WebSet pbRender of oEditBtn to True
        End
        Else Begin
            WebSet pbRender of oSaveBtn to True
            WebSet pbRender of oCancelChangesBtn to True
            WebSet pbRender of oDeleteBtn to bHasRecord
        End
    End_Procedure

    // this will close the view after a save
    Procedure OnViewSaved Handle hoServer Boolean bChanged
        Send NavigateClose Self
    End_Procedure

    // this will close the view after a delete
    Procedure OnViewDeleted Handle hoDDO
        Send NavigateClose Self
    End_Procedure

    // Add code to customize your Zoom View based on how it was invoked.
    // Use NavigateData to determine the context this view will be used in.
    Procedure OnNavigateForward tWebNavigateData NavigateData Handle hoInvokingView Handle hoInvokingObject
        If (HasRecord(oPresentation_DD)) Begin
            Send SetBreadcrumbCaption oPresentation_DD.Title
        End
        Else Begin
            Send SetBreadcrumbCaption "New presentation"
        End

        // if this view is being used in multiple contexts, you may need a block of code
        // like this to handle customizations. This would include hiding rows and buttons
        // (WebSet pbRender) and changing the values of various captions.
        Case Begin
            Case (NavigateData.eNavigateType = nfFromMain)
                // If from main, this is a probably a main file Select to Zoom.
                // This is the most typical way to navigate to a zoom.
                Case Break
            Case (NavigateData.eNavigateType = nfFromParent)
                // If from parent, this is a constrained drill down.
                // If needed, you could check NavigateData.iTable to determine the constraining parent.
                // This would be unusual - normally you drill down from a parent to a list (Select view).
                Case Break
            Case (NavigateData.eNavigateType = nfFromChild)
                // If from child, this is a probably a parent Zoom from a Zoom.
                // This is unusual, but it might be used for adding a new child record.
                Case Break
            Case Else // must be nfUndefined
                // This may be the start of a query or this may be used for some kind of custom operation.
                // You may want to check NavigateData.NamedValues.
        Case End

        // Action buttons can be changed dynamically during a Zoom's display lifetime.
        // It's best to set these buttons in their own procedure so you can change later.
        Send SetActionButtons
    End_Procedure

    Procedure MyDeleteConfirmation Handle hmCallBack
        Send ShowYesNo (Self) hmCallBack "Delete Presentation?" "Confirm"
    End_Procedure
    Set Verify_Delete_msg to (RefProc(MyDeleteConfirmation))
End_Object

Step 3 — Expand the existing application

Keep the generated code around these focused edits. Add only the behavior needed for the complete application: the ConferenceData database path, API constants, application routes, force-aware initialization, version-first synchronization, relationship navigation, and one automatic dashboard synchronization.

Change the local database path and add the API constants near the existing database definitions. The new path starts a separate complete-application database while the API constants are shared by the database package. The logical conf_data connection ID remains unchanged; the generated SQLite connection now resolves it to this new path.

Define C_SQLiteDb for "/dev/idbfs/ConferenceDataV10.db"
Define C_API_Path for "/ConferenceData/API/v1/"
Define C_API_Endpoint for "https://abs01.daelab.net/"

Change the generated client title:

Set psApplicationTitle to "Conference App"

Add these menu routes in the existing menu objects. Add the Dashboard item under the main menu button, and add Rooms, Presenters, and Presentations under oViewMenu. Each item gives the application a named route and navigates to its select view.

Object oDashboard_itm is a cWebMenuItem
    Set psCaption to C_$Dashboard

    WebRegisterPath ntNavigateBegin oDashboard

    Procedure OnClick
        Send NavigatePath
    End_Procedure
End_Object

Object oSelectRoomItem1 is a cWebMenuItem
    Set psCaption to "SelectRoom"

    WebRegisterPath ntNavigateBegin oSelectRoom

    Procedure OnClick
        Forward Send OnClick
            Send NavigatePath
    End_Procedure
End_Object

Object oSelectPresenterItem is a cWebMenuItem
    Set psCaption to "Presenters"

    WebRegisterPath ntNavigateBegin oSelectPresenter

    Procedure OnClick
        Forward Send OnClick
            Send NavigatePath
    End_Procedure
End_Object

Object oSelectPresentationItem is a cWebMenuItem
    Set psCaption to "Presentations"

    WebRegisterPath ntNavigateBegin oSelectPresentation

    Procedure OnClick
        Forward Send OnClick
            Send NavigatePath
    End_Procedure
End_Object

Change initialization to allow a forced database recreation, then include every view added by this phase:

Use CreateAndSyncDB.pkg
Send InitializeDB of oCreateAndSyncDB False

Use Dashboard.wo
Use SelectRoom.wo
Use ZoomRoom.wo
Use SelectPresenter.wo
Use ZoomPresenter.wo
Use SelectPresentation.wo
Use ZoomPresentation.wo

Keep the generated source-level Dashboard default view.

AppSrc/CreateAndSyncDB.pkg

Add the Room, Presentation, and Settings Data Dictionaries to oCreateAndSyncDB. oPresentationDD is the main DDO for Presentation records; its Set DDO_Server lines connect the Room and Presenter parent DDOs required by the entity relations. pbSynchronized records whether the current client session has completed a successful synchronization.

Use RoomDataDictionary.pkg
Use PresentationDataDictionary.pkg
Use PresenterDataDictionary.pkg
Use SettingsDataDictionary.pkg

Object oRoomDD is a cRoomDataDictionary
End_Object

Object oPresenterDD is a cPresenterDataDictionary
End_Object

Object oPresentationDD is a cPresentationDataDictionary
    Set DDO_Server to oRoomDD
    Set DDO_Server to oPresenterDD
End_Object

Object oSettingsDD is a cSettingsDataDictionary
End_Object

Property Integer pbSynchronized False

Change InitializeDB to accept a force flag. ExistsInDb opens the configured SQLite file and checks whether the schema exists; a forced call or a missing schema invokes CreateDatabase for the Presentation and Settings tables.

Procedure InitializeDB Boolean bForceRecreate
    Boolean bExists

    Get ExistsInDb of oPresentationDD True True to bExists
    If (bForceRecreate or not(bExists)) Begin
        Send CreateDatabase of oPresentationDD True True True
        Send CreateDatabase of oSettingsDD False False True
    End
End_Procedure

Change the existing HTTP client to use the shared endpoint constant:

Object oHttpReq is a cHttpClient
    Set psEndpoint to C_API_Endpoint
End_Object

Change SynchronizeData to accept bForce and use cHttpClient to fetch the version before downloading entity data. If the local version matches and the call is not forced, leave local data unchanged. Keep every existing non-200 return before the transaction so an HTTP failure cannot set pbSynchronized.

Procedure SynchronizeData Boolean bForce
    Integer iLocalDataVersion iRemoteDataVersion iItem iItemTo
    HttpStatus eStatus
    Room[] aRooms
    Presenter[] aPresenters
    Presentation[] aPresentations
    Boolean bErr

    Get LoadSetting of oSettingsDD "DataVersion" "0" to iLocalDataVersion

    Get HttpGet of oHttpReq (C_API_Path + "Version") to eStatus
    If (eStatus <> 200) Begin
        Send UserError (SFormat("Could not connect to server (HTTP status %1)", eStatus))
        Procedure_Return
    End

    Get HttpResponseToDataType of oHttpReq True "Version" to iRemoteDataVersion

    If (iLocalDataVersion <> iRemoteDataVersion or bForce) Begin
        Push_locale
        Set_Attribute DF_LOCALE to DF_LOCALE_ISO8601

        Get HttpGet of oHttpReq (C_API_Path + "Room") to eStatus
        If (eStatus <> 200) Begin
            Send UserError (SFormat("Could not connect to server (HTTP status %1)", eStatus))
            Pop_locale
            Procedure_Return
        End
        Get HttpResponseToDataType of oHttpReq False "" to aRooms

        Get HttpGet of oHttpReq (C_API_Path + "Presenter") to eStatus
        If (eStatus <> 200) Begin
            Send UserError (SFormat("Could not connect to server (HTTP status %1)", eStatus))
            Pop_locale
            Procedure_Return
        End
        Get HttpResponseToDataType of oHttpReq False "" to aPresenters

        Get HttpGet of oHttpReq (C_API_Path + "Presentation") to eStatus
        If (eStatus <> 200) Begin
            Send UserError (SFormat("Could not connect to server (HTTP status %1)", eStatus))
            Pop_locale
            Procedure_Return
        End
        Get HttpResponseToDataType of oHttpReq False "" to aPresentations

        Pop_locale

        Begin_Transaction
            ZeroFile oRoomDD
            Move (SizeOfArray(aRooms) - 1) to iItemTo
            For iItem from 0 to iItemTo
                Clear oRoomDD
                Send UpdateAllFields of oRoomDD aRooms[iItem]
                Get Request_Validate of oRoomDD to bErr
                If (not(bErr)) Begin
                    Send Request_Save of oRoomDD
                End
            Loop

            ZeroFile oPresenterDD
            Move (SizeOfArray(aPresenters) - 1) to iItemTo
            For iItem from 0 to iItemTo
                Clear oPresenterDD
                Send UpdateAllFields of oPresenterDD aPresenters[iItem]
                Get Request_Validate of oPresenterDD to bErr
                If (not(bErr)) Begin
                    Send Request_Save of oPresenterDD
                End
            Loop

            ZeroFile oPresentationDD
            Move (SizeOfArray(aPresentations) - 1) to iItemTo
            For iItem from 0 to iItemTo
                Clear oPresentationDD
                Send UpdateAllFields of oPresentationDD aPresentations[iItem]

                If (oRoomDD.RoomId <> aPresentations[iItem].RoomId) Begin
                    Move aPresentations[iItem].RoomId to oRoomDD.RoomId
                    Find EQ oRoomDD.RoomId
                    If (not(Found)) Begin
                        Error DFERR_PROGRAM "Received invalid data from the server"
                    End
                End

                If (oPresenterDD.PresenterId <> aPresentations[iItem].PresenterId) Begin
                    Move aPresentations[iItem].PresenterId to oPresenterDD.PresenterId
                    Find EQ oPresenterDD.PresenterId
                    If (not(Found)) Begin
                        Error DFERR_PROGRAM "Received invalid data from the server"
                    End
                End

                Get Request_Validate of oPresentationDD to bErr
                If (not(bErr)) Begin
                    Send Request_Save of oPresentationDD
                End
            Loop

            Send StoreSetting of oSettingsDD "DataVersion" iRemoteDataVersion
            Send StoreSetting of oSettingsDD "LastSync" (CurrentDateTime())
        End_Transaction
    End

    If (not(Err)) ;
        Set pbSynchronized to True
End_Procedure

The three entity downloads occur before one transaction. UpdateAllFields copies each typed response item into a DDO buffer; Request_Validate checks entity and relation rules; Request_Save sends valid records through the Data Dictionary adapter to the SQLite driver. Room and Presenter records are available as parent records before Presentation rows are saved. Invalid foreign IDs raise an error inside the transaction, and DataVersion/LastSync are stored only with the downloaded data. pbSynchronized is set only after the procedure has completed without an error.

Add the status accessor used by the Dashboard:

Function LastSync Returns String
    String sResult

    Get LoadSetting of oSettingsDD "LastSync" "--" to sResult
    Function_Return sResult
End_Function

AppSrc/Dashboard.wo

Add server-side show handling:

Set pbServerOnShow to True

Keep the existing synchronization tile object name. Add status rendering and a forced refresh action to that tile. The Dashboard calls DoSync False for its one-time automatic synchronization and DoSync True when the user clicks Synchronize.

Object oWelcomeTile is a cWebHtmlBox
    Set pbServerOnClick to True
    Set psHtml to '<div Class="WebCon_Sizer" data-ServerOnClick="doSync"><div class="Tile_Icon WebIcon_Refresh"></div><div Class="Tile_Subtitle">Initializing..</div></div>'

    Procedure DisplayStatus String sStatus
        Send UpdateHtml (SFormat('<div Class="WebCon_Sizer" data-ServerOnClick="doSync"><div class="Tile_Icon WebIcon_Refresh"></div><div Class="Tile_Subtitle">%1</div></div>', sStatus))
        Send UpdateWebUI of (Host(Self))
    End_Procedure

    Procedure DisplayLatest
        String sLastSync
        Get LastSync of oCreateAndSyncDB to sLastSync
        Send DisplayStatus sLastSync
    End_Procedure

    Procedure DoSync Boolean bForce
        Send DisplayStatus "Synchronizing.."
        Send SynchronizeData of oCreateAndSyncDB bForce
        Send DisplayLatest
    End_Procedure

    Procedure OnClick String sId String sParam
        Send DoSync True
    End_Procedure
End_Object

Add the one-time synchronization guard:

Procedure OnShow
    If (not(pbSynchronized(oCreateAndSyncDB))) Begin
        Send DoSync of oWelcomeTile False
    End
    Else Begin
        Send DisplayLatest of oWelcomeTile
    End
End_Procedure

Change the remaining dashboard tiles to route to the new select views:

Object oTile2 is a cWebHtmlBox
    Set psHtml to '<div class="WebCon_Sizer" data-ServerOnClick="openview"><div Class="Tile_Title">Rooms</div><div class="Tile_Subtitle"></div></div>'
    WebRegisterPath ntNavigateForwardCustom oSelectRoom
End_Object

Object oTile3 is a cWebHtmlBox
    Set psHtml to '<div class="WebCon_Sizer" data-ServerOnClick="openview"><div Class="Tile_Title">Presenters</div><div class="Tile_Subtitle"></div></div>'
    WebRegisterPath ntNavigateForwardCustom oSelectPresenter
End_Object

Object oTile4 is a cWebHtmlBox
    Set psHtml to '<div class="WebCon_Sizer" data-ServerOnClick="openview"><div Class="Tile_Title">Presentations</div><div class="Tile_Subtitle"></div></div>'
    WebRegisterPath ntNavigateForwardCustom oSelectPresentation
End_Object

Keep each tile's generated OnClick procedure that sends NavigatePath.

AppHtml/CssStyle/application.css

Add the application's custom stylesheet for the synchronization tile. The generated WebAssembly template provides the WebIcon_Refresh icon; this rule supplies the sample-specific tile icon sizing and position.

.Tile .Tile_Icon {
    font-size: 40px;
    text-align: center;
    position: relative;
    top: 30px;
}

AppSrc/SelectPresenter.wo

Add Presentation navigation to the Presenter list and change its visible identity to Presenters:

Set psCaption to "Presenters"
Set pbShowCaption to False
Set psStateViewName to "Presenters"

WebRegisterPath ntNavigateForward oSelectPresentation

Change the list columns to show last name, first name, and company while retaining the existing zoom button:

Object oPresenterDD_LastName is a cWebColumn
    Set psCaption to "Last Name"
    Set psCSSClass to "RowCaption"
    Set peAlign to alignLeft
    Entry_Item oPresenterDD.LastName
    Set piListColSpan to 2
End_Object

Object oPresenterDD_FirstName is a cWebColumn
    Set psCaption to "First Name"
    Set pbNewLine to True
    Entry_Item oPresenterDD.FirstName
End_Object

Object oPresenterDD_Company is a cWebColumn
    Set psCaption to "Company"
    Set psCSSClass to "RowDetail"
    Entry_Item oPresenterDD.Company
End_Object

Keep the existing oDetailButton route to oZoomPresenter. Change OnRowClick so a child or main lookup closes, a parent drill-down navigates forward, and the list keeps its existing context behavior:

Procedure OnRowClick String sRowID
    tWebNavigateData NavigateData
    Get GetNavigateData to NavigateData

    Case Begin
        Case (NavigateData.eNavigateType=nfFromParent)
            Send NavigatePath
            Case Break
        Case (NavigateData.eNavigateType=nfFromChild)
            Send NavigateClose Self
            Case Break
        Case (NavigateData.eNavigateType=nfFromMain)
            Send NavigateClose Self
            Case Break
        Case Else
            Case Break
    Case End
End_Procedure

AppSrc/ZoomPresenter.wo

Change the existing Presenter field controls to the complete application's binding names and captions. The controls still bind to oPresenterDD; only the view integration changes.

Object oPresenterDD_FirstName is a cWebForm
    Set piColumnSpan to 6
    Set psLabel to "First Name"
    Entry_Item oPresenterDD.FirstName
End_Object

Object oPresenterDD_LastName is a cWebForm
    Set piColumnSpan to 6
    Set psLabel to "Last Name"
    Entry_Item oPresenterDD.LastName
End_Object

Object oPresenterDD_Company is a cWebForm
    Set piColumnSpan to 0
    Set psLabel to "Company"
    Entry_Item oPresenterDD.Company
End_Object

Object oPresenterDD_Bio is a cWebEdit
    Set piColumnSpan to 0
    Set psLabel to "Bio"
    Entry_Item oPresenterDD.Bio
End_Object

Keep the existing save, edit, delete, cancel, OnViewSaved, and OnViewDeleted procedures. They preserve Presenter editing while navigation from the select view exposes the Presentation relationship.

Checkpoint: Press F5. The dashboard performs one automatic synchronization, then Rooms → Presentations → Presenter drill-down follows the relationship bindings.

Next

Add live Presentation search.