In Deutsch

The Contacts Sample

A complete object-oriented PHP Database Web-Application

© 2012- Gerald Zincke, Austria

Probably there is not any other place in the world-wide-web where you can find a:

·         Web Application based on a

·         strong object oriented Architecture with

·         complete free PHP source code providing a

·         GUI in standard HTML and CSS that is working

·         without relying on Javascript , executing in

·         all major Browsers from IE 6 using the

·         free MySQL Database executing on

·         standard Web Servers as available

·         from free Web Hosters, commercial Hosters and

·         locally on your PC of course.

Here I describe, how you can design, build and deploy such an application.

1         Introduction

The web is full of blogs, scripts and FAQs around PHP software development. But it is not easy to create a big picture and a meaningful application architecture from there. And there are not too many places where you can download complete applications.

Here you will find a complete, documented, real-life web-application with all the information and all the downloads you need to make it work on your own machine or make it available in the internet on the server of your favorite web hoster. I will describe the Elements of the general architecture that is used to build it.

1       Introduction.. 1

2       Sample Application - Requirements. 2

3       Elements. 3

4       An Object-Oriented Web-Application Architecture. 3

4.1         The simple Picture. 3

4.2         The basic Ideas. 4

4.3         Design Guidelines. 4

4.4         Information Flow.. 4

4.4.1      Start Main Window.. 5

4.4.2      User Actions on Windows. 6

4.4.3      Opening a Dialog from a Window.. 7

4.4.4      User Actions on Dialogs. 8

4.4.5      Closing a Dialog and returning to the Parent-Window.. 9

4.5         Class Responsibilities. 10

5       Sample Application Implementation.. 11

5.1         The logical Data Model. 11

5.2         The physical Database Design. 12

5.2.1      Company Table. 13

5.2.2      Person Table. 13

5.2.3      “Company employs Person” Relationship. 14

5.2.4      Technical Tables. 14

5.3         GUI 15

5.3.1      Description. 15

5.3.2      GUI Layouts. 16

5.4         Code. 22

5.4.1      Preparing the Framework Directory. 23

5.4.2      Preparing the Framework Icons Directory. 24

5.4.3      The Base Directory. 24

5.4.4      Preparing the Application Directory. 26

6       Deployment Procedures. 51

6.1         Execute the Contacts Application on your PC. 51

6.1.1      Setup Web Server and Database. 51

6.1.2      Put the Application on the Local Webserver 52

6.2         Execute the Application on a Web Server in the Internet. 53

6.2.1      Setup a Web Server in the Internet 53

6.2.2      Put the Application on the remote Web Server 55

7       Downloads. 60

2         Sample Application - Requirements

I will describe the architecture, using a sample application. I assume the following requirements:

User Requirements

1.      The application should manage customer contact data

2.      Data that must be stored contain: First/Last Name, Title, Company, Department, Position, Phone, Mobile, Street, City, ZIP and Country

3.      The data must be accessible for many people simultaneously

4.      It must be possible to scroll through the stored contacts and to select single entries to view/update them

5.      It must be possible to copy contact data to a mobile

6.      Access must be possible via the Internet

7.      Access must be restricted to authorized people only

Technical Requirements

1.      The application runs on a web server

2.      Programming is done in PHP

3.      Data is stored in a new MySQL database

4.      All data modifications are transaction-safe, implementing the ACID principles

5.      User authentication is done with userid and password

6.      The application is secured against SQL-injection, XSS-attacks and hyperlink modifications

7.      Data validation is done on the server

3         Elements

We need the following elements to design and implement the sample application.

·         Model – Describes the most important application-specific data structures we will need.

·         Database – Describes database tables and sample data needed to make the model persistent, to store and retrieve application data.

·         GUI – Design and implementation of the application’s user interface.

·         Code – Application logic to glue everything together

·         Deployment Procedures – Description how to setup the application on a web-server, either locally on your own machine (your LAN) or on a web-server in the internet. 

4         An Object-Oriented Web-Application Architecture

4.1      The simple Picture

The web originally was designed to host documents, not applications. Therefore the basic structure of web interactions is simple:

·         The client sends an URL (a request)  to the server

·         The server answers with a HTML document

The common web browsers offer the following ways to send URL requests to the server:

·         By entering it manually via the browser’s address line or by selecting a shortcut/favorite.

·         By clicking on a hyperlink in a HTML document that is already displayed in the browser

·         By clicking on a submit button. In that case the input data of a HTML form becomes part of the client request and is submitted to the server either as part of the URL string or as hidden data

·         Indirectly, by creating a client request string by a script in the browser (Javascript, Flash, MS-Silverlight …)

A web application then is a piece of code that interprets the URLs and generates (dynamic) HTML documents (with variable input).  Many PHP  applications are structured rather simple.  For every application page there are two or more scripts. The first one creates the page and sends it to the client and the second or additional ones  process the incoming URL requests. The problem with this approach is, that

·         the sending and receiving scripts share some common knowledge about the dialog context and if there is a change, it is not so easy to update all the files that are impacted and keep everything consistent.

·         It is not easy to embed requests and answers in an overall conversation flow. For instance: the application should allow to lookup some information and then the dialog flow should return to the input screen (with no input data lost, of course).

For sophisticated web applications we need more advanced design patterns to keep the server software structured and maintainable.

4.2      The basic Ideas

The basic ideas are:

·         Web Applications display their output in web pages, called windows.

·         All code that is needed to create the window contents and send the window to the client as well as the code needed to process  user input and user reactions on the window, is encapsulated in a single PHP class.

·         A window-object can open other windows (either in a new web-browser tab or a new web-browser window) simultaneously.

·         There are also special windows, called dialogs. When a window opens a dialog, the parent window is not accessible for the user until the dialog is closed.

·         Dialogs can open additional sub-dialogs and become inaccessible until the subdialog closes.

4.3      Design Guidelines

A good architecture provides a structure for a standardized information flow. The components of the architecture have clearly defined (non-overlapping) areas of responsibility. Therefore there are clear rules where which part of the application logic has to be implemented. The interfaces are narrow and easy to understand. Data is stored in the objects where it is needed most. Years ago Larry Constantine coined the Definitions of (low) Coupling and (high) Cohesion for this design approach.

4.4      Information Flow

The following pictures show the interaction and the information flow between of the most important elements of the architecture.

4.4.1     Start Main Window

The first one shows, what happens when an application is started and the main window of the application opens.

Fig.1: Start Application

In the top left you see that the client sends an http:// request (something like http://localhost/index.php  to the server. The user can do this by typing the URL into the address field in his browser or by clicking on a hyperlink our by using a browser shortcut.

This will invoke the application startup script (index.php). This first creates a new session object. The session object has an automated persistency mechanism and will survive the end of the transaction (is stored to a file automatically). So it can be used to store data that should be available when processing the next user interaction.

Next the startup script creates a new context object (not shown as a box). A context object is responsible to store data important in the context of an invocation of a window. (If the same window is open twice at the same time there are two different context objects). The context “knows” who opened the window (parent context) and stores the values of the model object. The model object are the data that are displayed and modified with the application.

The startup script then creates the window object and opens it.

The window object initializes its model and creates a structure of control objects. These define the structure of the window layout. They encapsulate things like output fields, menus, images etc. They store output values (number, text, url’s) and their most important capability is that they can render themselves to HTML.

How does a window object “know” how to initialize the model objects and which control objects it should create ? For each specific window in an application we create a derived class from the basic window class. The derived class, like a CTSMainWindow, overrides the functions initModel() and initWindow(). There it is specified how a CTSMainWindow will look like, which event handler functions will be called for response to a user action and which model data it will handle.

The window object will then fill the controls with (initial) data and will then use them to render them to a HTML document. It echoes the HTML to the client and then it saves the current status of the context object (containing the model data) in the session object, which in turn is saved to disk by PHP standard mechanisms.

The dashed line means the end of the server transaction. The user can now see the main window of the application in his web-browser.

4.4.2     User Actions on Windows

The next picture shows, what happens when the user clicks an active user interface control to update something shown in the window.

Fig. 2 User Interaction with a window

The user interaction always begins with a click on a hyperlink on the window (for some good reasons, that I will describe later, (main-)windows should not contain submit buttons)

All hyperlinks on a window are designed in a way that they will invoke the dispatcher script on the server. The URL provides the class name of the window to open, the id of the window context and the name of an event handler callback function.

Note: The dispatcher has a security mechanism that will reject all invalid URL-requests that are hyperlinks that were not part of the window structure sent in the previous step). This prevents hackers from manipulating Hyperlinks to invoke functions that are not allowed in the current context of the window (for instance because the user is not yet logged in).

The dispatcher fetches the context from the session, creates the window object and the model object data are initialized with the same values as at the end of the previous transaction.

The window object then again builds its control object layout structure. Then the event handler callback function is started. It may (depending on the user’s request)

·         compute or retrieve something and then modify the contents, the values  of the controls or

·         it may open another window or a sub-dialog (shown later).

In the use case above, we assume that the event function just does some local modifications, fills result values into the controls and we will stay in the same window. The window object as before therefore renders the controls to HTML, sends the HTML document to the client and saves its context to the session.

The user can now see an updated window on the screen.

4.4.3     Opening a Dialog from a Window

The next picture shows, what happens when the user clicks an active user interface control to open a data-entry dialog-window. To simulate the concept of a modal dialog – that needs to be closed first before the calling window will be active again (such as you are used to with file-open dialogs) – the dialog is opened in the current web-browser tab, therefore “hiding” the calling window by overwriting it.

Fig 3: Opening a Dialog from a Window

The hyperlink  from the previously opened window invokes the dispatcher. Again it fetches the window context and creates the window object. The window will then call the event function that is responsible to handle the user request and that is identified by the callback parameter in the user request.

This event function will prepare a new model object for the sub dialog. This will contain all the parameter-data, the sub dialog will need from the from the window. Then the new dialog object is created and opened. The parent’s context object is saved to the context stack in the persistent session object (this allows us later to return from the dialog with all window data still available).

The base class of the dialog class is derived from the base class of the window class. Therefore the dialog object does it like the window before: It will create the control-objects, will then fill the controls with data (these may be data it got from the calling window) and will then use them to render them to a HTML document. It echoes the HTML to the client and then it saves the current status of the context object (containing the model data) in the session object, which in turn is saved to disk by PHP standard mechanisms.

Note that the context storage in the session object is organized as a stack and the parent context will not be overwritten.

The user can now see the opened dialog on the screen.

4.4.4     User Actions on Dialogs

There may be user actions that redisplay the same dialog (with update data values) again, or cause to open a sub-dialog. This works just as described in the previous two sections. This is because – you guessed it – a dialog class derives all properties of the base class for  window classes and therefore can do everything a window can (and some additional things).

4.4.5     Closing a Dialog and returning to the Parent-Window

Quite often a user will enter data into a dialog, click OK, the dialog closes and the calling window appears again. The following picture shows how it works.

 

Fig. 4 Closing the Dialog and returning to the Parent Window

To close the dialog the user most likely will click on a submit button. This will cause the form data in the dialog will be submitted to the server. There again the dispatcher is invoked.

It first fetches the dialog context and creates the dialog object. The dialog object will fill all the input data into the entry-field controls and selection controls. The dialog will then call it’s event function that is responsible to handle the click on the submit button. The event function can now get the values of the input data and process it.

If everything is OK, it will store the dialog results in its model object, save its context and create and open the parent- window object.

This will create the control-objects, will then fill the controls with data (these are the data it had before calling the dialog) and then in most cases it will call a return event handler function to process the results from the dialog. This function can fetch the old sub-context and may write result data into the window controls

Then the window object uses the controls to render them to a HTML document. It echoes the HTML to the client and then it saves the current status of the context object (containing the model data) in the session object, which in turn is saved to disk by PHP standard mechanisms.

The user can now see the re-opened window on the screen.

4.5      Class Responsibilities

 

Class

Base Class

Responsibility (selected subset)

Window

-

Knows how to open itself at the client (renders itself to HTML)

Knows the user interface controls, it contains

Manages model data

Fills variable data (from model) into controls

Can handle all valid client requests from the window displayed at the client side

Tells the context object which client requests are valid in the current status of the window.

Identifies menu items and callback hyperlinks and triggers event handlers tied to a button.

Separates data , if the window is opened in different context simultaneously

Dialog

Window

Processes HTML form data and fills it into the user interface controls.

Takes care that all data from entry-fields is checked according to a specified input class (numeric, plain text without special characters. date …),

Identifies form submit buttons and triggers event handlers tied to the button.

Control

-

Holds a value

Can render the value to HTML

Entry Field

Control

Uses the value as default text contents. Knows a tooltip

Processes client input data for the control

Knows its input class (numeric, plain text without special characters. Date …), checks all input against that class

Image

Control

Knows the URL of the image

Pushbutton

Control

Uses the value as button text.  Knows a tooltip

Knows which function has to handle the click on the button

Menu Item

Control

Knows its menu text and a tooltip

Knows which function has to handle the click on the menu item

Control Pane

Control

Takes control objects and stores them.

Knows how a control object has to be positioned relatively to the previous control.

Renders the stored control objects to a <div> or <span> tag, observing their relative positioning.

Context

-

Has a unique id

Knows its window-class

Knows its model object

Takes care that unexpected client requests und unexpected parameter values are rejected

Can open a window or dialog on the model object

 

 

5         Sample Application Implementation

5.1      The logical Data Model

The model describes the most important application-specific data structures we will need.

The Entity/Relationship diagram of the domain model looks like this:

The entity type “Person” has the following attributes:

·         FirstName

·         LastName

·         Title

·         Department

·         Position

·         Phone

·         Mobile

·         EMail

·         LastChange

The entity type “Company” has the following attributes:

·         CompanyName

·         Street

·         City

·         ZIPCode

·         Country

·         LastChange

The type of all attributes will be string.

There is a releationship type between the entity types. It can be named “Person works for Company” or "Company employs Person". It is a 1-to-many relationship. Each company can employ zero or more persons. Each person can be employed by a single company (in reality this is not true of course, but for our example we will keep things simple).

Please believe me that in a real application the model would look a bit more sophisticated. But for our sample this will do it.

5.2      The physical Database Design

The database design describes database tables and sample data needed to make the model persistent, to store and retrieve application data. We will use MySQL as database engine.

First we create a new database (For downloading the source code refer to chapter “Downloads”).

CREATE DATABASE Contacts;

USE Contacts;

DROP TABLE IF EXISTS Person;

DROP TABLE IF EXISTS Company;

 

SQL Script to create the Database

We need two database tables to make domain model objects persistent and two technical tables.

5.2.1     Company Table

We use SQL to define the table structure for the company table and add some initial contents,

CREATE TABLE Company (

  Company_ID integer NOT NULL AUTO_INCREMENT ,

  CompanyName   varchar(64) NOT NULL,

  Street      varchar(32) NOT NULL,

  City        varchar(32) ,

  ZIPCode     varchar(16) ,

  Country     varchar(16) ,

  LastChange timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  PRIMARY KEY (Company_ID)

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

 

INSERT INTO Company SET Company_ID=1, CompanyName='Microsoft', City='Redmond', Country='USA';

INSERT INTO Company SET Company_ID=2, CompanyName='Apple', City='Cupertino', Country='USA';

 

select * from Company;

 

 

SQL Script to create the Company Table

Result of the script:

+------------+-------------+--------+-----------+---------+---------+---------------------+

| Company_ID | CompanyName | Street | City      | ZIPCode | Country | LastChange          |

+------------+-------------+--------+-----------+---------+---------+---------------------+

|          1 | Microsoft   |        | Redmond   | NULL    | USA     | 2012-01-13 19:27:35 |

|          2 | Apple       |        | Cupertino | NULL    | USA     | 2012-01-13 19:27:35 |

+------------+-------------+--------+-----------+---------+---------+---------------------+

 

5.2.2     Person Table

The person table will store data of contact persons. The foreign key reference implements the relationship ‘person works for (at least one and only one) company’. It will prevent creating person records that have no or an invalid relationship to a company record. It will also prevent that company records will be deleted where person records exist, that reference it (referential integrity).

DROP TABLE IF EXISTS Person;

 

CREATE TABLE Person (

  Person_ID   integer NOT NULL AUTO_INCREMENT,

  FirstName   varchar(32) ,

  LastName      varchar(32) NOT NULL,

  Title       varchar(32) ,

  Company_ID integer NOT NULL,

  Department varchar(32) ,

  Position      varchar(32) ,

  Phone       varchar(16) ,

  Mobile      varchar(16) ,

  EMail       varchar(255),

  FOREIGN KEY (Company_ID) REFERENCES Company(Company_ID),

  LastChange timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  PRIMARY KEY (Person_ID)

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

 

INSERT INTO Person SET

Person_ID=1,

FirstName='Bill',

LastName='Gates',

Position='Chairman',

Phone='001 123456789',

Company_ID=1;

 

INSERT INTO Person SET

Person_ID=2,

FirstName='Steve',

LastName='Ballmer',

Position='CEO',

Phone='001 234567891',

Company_ID=1;

 

INSERT INTO Person SET

Person_ID=3,

FirstName='Tim',

LastName='Cook',

Position='CEO',

Phone='001 345678912',

Company_ID=2;

 

select Person_ID, FirstName, LastName, Company_ID, Position, Phone, LastChange from Person;

 

SQL Script to create the Person Table

Result of the script:

 

+-----------+-----------+----------+------------+----------+---------------+------------------

| Person_ID | FirstName | LastName | Company_ID | Position | Phone         | LastChange      

+-----------+-----------+----------+------------+----------+---------------+------------------

|         1 | Bill      | Gates    |          1 | Chairman | 001 123456789 | 2012-01-14 12:11

|         2 | Steve     | Ballmer  |          1 | CEO      | 001 234567891 | 2012-01-13 19:44

|         3 | Tim       | Cook     |          2 | CEO      | 001 345678912 | 2012-01-13 19:44

+-----------+-----------+----------+------------+----------+---------------+------------------

 

5.2.3     “Company employs Person” Relationship

How to implement the relationship?  Note the Company_ID field in the person table. This is used to store the primary key of the company the person works for. The FOREIGN KEY specification takes care that a Company_ID value can be only the id if an existing company record.

5.2.4        Technical Tables

We also need a table to store and check valid user id’s and a table to store queries .

USE Contacts;

 

DROP TABLE IF EXISTS user;

 

CREATE TABLE `user` (

  `userid`         varchar(16) NOT NULL,

  `password_enc`   varchar(32) NOT NULL,

  `lastname`    varchar(32) ,

  `firstname`      varchar(32),

  `preferences` text DEFAULT NULL,

  `email`       varchar(255),

  `LastChange`     timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 

PRIMARY KEY (`userid`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

 

INSERT INTO user SET userid='test', password_enc=MD5('test');

INSERT INTO user SET userid='demo', password_enc=MD5('demo');

 

select userid, password_enc, lastchange from user;

 

SQL Script to create the User Table

Result of the script:

+--------+----------------------------------+---------------------+

| userid | password_enc                     | lastchange          |

+--------+----------------------------------+---------------------+

| demo   | fe01ce2a7fbac8fafaed7c982a04e229 | 2012-01-14 19:49:43 |

| test   | 098f6bcd4621d373cade4e832627b4f6 | 2012-01-14 19:49:43 |

+--------+----------------------------------+---------------------+

 

DROP TABLE IF EXISTS GGFQuery;

 

CREATE TABLE GGFQuery

(

   id   integer AUTO_INCREMENT,

   owner      VARCHAR(16) NOT NULL,

   ETypeName     VARCHAR(64) NOT NULL,

   browser         VARCHAR(64) NOT NULL,

   qname   VARCHAR(128) NOT NULL,

   filter     TEXT,

   sorter     TEXT,

   thecolumns TEXT,

   columnsize TEXT,

   thechecksum   TEXT,

   PRIMARY KEY (id)

) ENGINE = InnoDB;

 

SQL Script to create the Query Table

5.3       GUI

Here we describe the design and implementation of the application’s graphical user interface.

5.3.1     Description

The user starts the application by entering

http://localhost/index.php

Into the address field of a web browser. The server will reply displaying the main window. It offers menus to

·         Login

·         Logout

·         Browse contacts

·         Browse company

·         View About Dialog

·         Close the window

The login dialog allows to enter user id and password.

The browse contacts window allows to scroll through the stored contacts. It offers menus to insert new contacts and to close the browser window. Hyperlinks allow to view/update contacts and to delete a contact.

The update contacts dialog allows to view a single contact, update a contact, or enter data for a new contact. The company of a contact person can be selected from a list. If a company does not exist yet. It can be inserted from here.

The browse company window allows to scroll through the stored companies. It offers a menu to close the browser window. Hyperlinks allow to view/update companies and to delete a company.

The update company dialog allows to view a single company, update a company, or enter data for a new company.

5.3.2     GUI Layouts

We create the GUI layouts with the ReinHTML Dialog Designer (http://www.ReinHTML.eu )

5.3.2.1     CTSMainWindow

To keep things simple it consists just of a menu bar and a greeting message.

Main Window

5.3.2.2       CTSLoginDialog

The login dialog offers entry fields for user id and password.

 

Login Dialog

5.3.2.3       CTSCompanyBrowserWindow

The company browser window displays the list of stored companies. If there are more entries in the database than fit on a screen, the user can scroll through the list page by page, using the buttons at the right.

 

Company Browser Window

Clicking a record will open a dialog with the company’s data. The icons on the left are used to delete and copy records. To create a new record click on “Insert” . To close the window click on “Close”. The rest are convenience functions derived from the base class, that you will be able to discover as soon as you got the application running.

5.3.2.4     CTSCompanyInsertDialog

In many cases it is required to provide some initial data when you want to create a new database record. There may be NOT NULL fields or it may just not be meaningful to store a record with a lot of empty fields.

For that the architecture uses an Insert Dialog. It will open when you click on “Insert” in the Company Browser Window.

 

Company Insert-Dialog

The Company Insert Dialog contains input fields for the company record. Note that it does not contain a field for the Company_ID because this is created automatically.

5.3.2.5     CTSCompanyUpdateDialog

This dialog allows us to view and update company records. It is opened by clicking on a company record in the browser window.

Company Update-Dialog

Note: The Update Dialog shows also the Company_ID . It is a read only field.

5.3.2.6     CTSContactsBrowserWindow

The Contacts Browser Window shows the list of stored contacts. Note that the list contains data from the person table and from the company table (column „Employer“).

Contacts Browser Window

5.3.2.7       CTSContactInsertDialog

The insert dialog opens when the user clicks “Insert” in the contacts browser window.

Contact Insert-Dialog

It allows to enter the data of a new contact person. And by selecting a company from the drop-down at the bottom, you can establish a relationship to the employer - a company.

5.3.2.8     CTSContactUpdateDialog

The contact update dialog appears when the user clicks on an entry in the contact browser window.

Contact Update-Dialog

Note: The update dialog displays the Person_ID. This is a read-only field. The relationship to a company can be changed by selecting a different company in the drop down field.

Note: The yellow button between OK and Cancel. If pressed, it will create a VCard for that person, that can be downloaded and processed by phonebook applications in smart phones and by most e-mail clients.

On a Symbian phone this looks like that:

Downloading a Contact to a Symbian Phone

In iOS (iPhone, iPad) it is not possible to download VCards and store them into the phonebook. But as always: there is an App for that! (Qrafter or VCard Getter for instance).

5.4      Code

Here we describe the application logic to glue everything together. The code of a web application must be put in the documents directory of the web server (For downloading the source code refer to chapter “Downloads”). Typical installations of the Apache web server (as it is created for instance by the XAMPP package) have a directory structure like this

·         Programdir

o   apache

o   htdocs

o   mysql

o   php

o  

“Programdir” is the installation directory choosen during installation of the web server. The directory Programdir/htdocs then is the place where we have to put our code. We call it the base directory. There we need three subdirectories Programdir/htdocs/GGF , the framework directory,  Programdir/htdocs/GGFIcons , the directory for framework icons and Programdir/htdocs/CTS , the application directory. This gives the complete directory structure:

·         Programdir

o   apache

o   htdocs

§  GGF

§  GGFIcons

§  CTS

o   mysql

o   php

o  

For security reasons the /CTS and /GGF  subdirectory must not be accessible from the internet. This is especially important for the directory /htdocs/GGF because it contains setup information (see below).  For Apache web servers access rights are specified by placing a .htaccess file into each of these directories. Some web hosters alternatively offer interactive user interfaces to specify access rights.

The directories /htdocs/GGFIcons – and  /htdocs – however must allow read-access for users accessing the web site. It makes sense to deny directory listings.

Note: the access rights that are specified by a .htaccess file do not affect PHP scripts executing on the server. That is why the  index.php script can load GGF/GGF.php . But users will not be able to open it via an URL like http://yourserver/GGF/GGF.php or similar.

 

5.4.1     Preparing the Framework Directory

The directory /htdocs/GGF contains  the GGF Framework (For downloading the source code refer to chapter “Downloads”). For security reasons this directory must not be accessible from the internet. We can set the access rights with .htacess file.

deny from all


file: /htdocs/GGF/.htaccess

There is only one file that has to be adapted to our application. The file /htdocs/GGF/GGFSetup.php is used to setup the framework.

 

/**

 * settings for Contacts application

 *

 * @todo

 * @package GGF

 * @version 4.0

 * @since 1.0

 * @author Gerald Zincke, Austria

 * @copyright 2005, 2011 Gerald Zincke

 */

ini_set("session.use_trans_sid", 0); // Do not add PHP Session ID automatically to relative links anhängen (this does not work for frames)

ini_set("session.use_cookies", 0);        // do not use local cookies

//ini_set("session.save_path","/home/vhosts/contacts.freetzi.com/mySessions");  // on a shared server for security reasons it makes sense redirect your session data to your private data area

                                                                            // you need to supply the absolute server path to one of your directories

ini_set("session.use_only_cookies", 0);   // allow submission of Session ID via URL

 

 

 

$traceLevel = 2;       // 0: no error logging, 1: errors, 2: information

$largeListThreshold = 50; // if a rowset is larger than this, the browserwindow will show scroll buttons to allow a page-wise stepping through the row-set

                         // in a meaningful configuration this should always be bigger than $smallListPageSize

$smallListPageSize = 17; // this number is used by GGFControlSortDialog for the height of its listboxes

                         // and for GGFControlBrowserWindow for the initial page size (page-wise scrolling; more than $largeListTreshold lines to display)

                         // and if cookies are not supported

                         // for rowsets larger than $largeListTreshold this is the number of lines in a page. In a meaningful configuration this

                         // number of lines should fit on a 1024x768 screen, in a maxcimized browser window (without showing an elevator-scrollbar)

                         // be sure to leave room for button bars in the browser and large start- lines

$maxColumnSize = 132;    // maximum number of characters shown in a column of a GGFListArea

$HMItemHeight = 1.6;   // height of menu menu item. Used fror 2nd level flyout menus in CSS and GGFControls

 

// application specific values

$appPath = "CTS/";                // relative path to application classes

$APPIconsPath = "CTSIcons/"; // relative path to application specific icons, shapes and graphics

$http_type = "http";          // may also be set to "https", if web server supports that

$historyTable = "GGFHistory"; // this is the name of a db-table will be used to write history (see GGFDatabase)

$dbhost = "localhost";            // See GGFDatabase. May be of the form www.server.domain:port

$dbuser = "root";             // See GGFDatabase, you should change this for production

$dbpw = "";                   // See GGFDatabase, you should change this for production

$dbname = "Contacts";         // See GGFDatabase

$appname ="Contacts";         // application name, used in window title etc.

$appversion = "1.0";          // application version, used in GGFAboutDialog

$mainwindow = "CTSMainwindow.php";

$goodbyefile = "GGFGoodbye.php";

$invalidContextFile = "GGFInvalidContext.php";

$webmasterMail = "info@ReinHTML.eu"; // this is used for feedback mails in the system tray

 

// application specific variables

$accountlimit = 250;

$maxOnlineFiles = 100;

$googleAnalyticsCode ='

';

$smtp = "email.aon.at";       // smtp server to send e-mails

 

?>


Script: /htdocs/GGF/GGFSetup.php

The text in bold face has to be adapted to the application.

·         appPath – the path to the application specific files; relative from the base directory

·         APPIconsPath – a path relative to the base directory to a directory for application specific icons. This is not needed in the contacts application

·         dbhost – the server hosting the database. If the database is on the same machine as the webserver this must be “localhost”. 

·         dbuser, dbpw - Be sure to provide a valid database user (“theContactsApp” in the sample above) and a database password that is different from the standard user “root”.  Don’t forget: The default root user, as it is provided by default MySQL installations, always must be protected with a password too when a MySQL database is accessible from the internet.

·         mainwindow – the name of the script implementing the main window

·         webmasterMail – this by default is offered for user support

5.4.2     Preparing the Framework Icons Directory

The directory /htdocs/GGFIcons has to contain  the icon files as provided by the GGF Framework. There is nothing else to do.

Make sure that this directory is accessible from the internet, because the icon files are accessed from the user’s web browser.

5.4.3     The Base Directory

First we need the code for the entry-point of the application:

<?PHP

/** 

* Contacts main startup-file 

*  

* @package CTS 

* @version 1.0 

* @since 1.0 

* @author Gerald Zincke, Austria 

* @copyright 2012 Gerald Zincke 

*/

$GGFPath = "GGF/";       // relative path to framework classes

require $GGFPath."GGF.php";

$mainWindow = new CTSMainWindow(windowContext(0,"CTSMainWindow")->contextID);

$mainWindow->open();

?>

Script: /htdocs/index.php

This script is stored as index.php or alternatively as contacts.php in the document directory (usually named /htdocs in typical Apache web server installations) of the web server. We assume that this will be the base directory for the contacts application.

Some additional files are needed in the base directory of the contacts application:

·         GGFFormats.css – The CSS style sheet. This is part of the GGF Framework and can be taken as is.

·         GGFDispatch.php – The request dispatcher. This is also part of the GGF Framework and can be taken as is.

·         GGFGoodbye.php – The script to be executed after closing a window. This is part of the GGF Framework and can be taken as is. But it makes sense to modify it a bit.

·         GGFInvalidContext.php – The script that is executed after a session timeout. This is part of the GGF Framework and can be taken as is. But it makes sense to modify it a bit.

You can adapt the script GGFGoodbye.php as follows:

<html>

   <head>

     <title>Closed Window</title>

   </head>

   <body onload="javascript:self.close()">

     <br>You can close this browser window/tab now.

   </body>

</html>

 

Script: /htdocs/GGFGoodbye.php

You can adapt the script GGFInvalidContext.php as follows:

<html>

   <head>

     <title>Invalid Context</title>

     <meta http-equiv="refresh" content="10; URL=index.php">

   </head>

   <body>

   The context of this window has expired or has become invalid.

   Please select <a href="index.php" target="_top">contacts application</a> to logon again.

   </body>

</html>

 

Script: /htdocs/GGFInvalidContext.php

The rest of the code and some additional files is distributed to three sub-directories:

·         /htdocs/GGF – the framework files

·         /htdocs/GGFIcons – some GIF files  needed by the framework

·         /htdocs/CTS – the application specific files

For security reasons the /CTS and /GGF  subdirectory must not be accessible from the internet. This is especially important for the directory /htdocs/GGF because it contains setup information (see below).  For Apache web servers access rights are specified by placing a .htaccess file into each of these directories. Some web hosters alternatively offer interactive user interfaces to specify access rights.

The directories /htdocs/GGFIcons – and  /htdocs – however must allow read-access for users accessing the web site. It makes sense to deny directory listings.

Note: the access rights that are specified by a .htaccess file do not affect PHP scripts executing on the server. That is why the  index.php script can load GGF/GGF.php . But users will not be able to open it via an URL like http://yourserver/GGF/GGF.php or similar.

5.4.4     Preparing the Application Directory

The application directory contains one script for each window and a script to define the application model. For security reasons this directory must not be accessible from the internet. We can set the access rights with .htacess file.

deny from all


file: /htdocs/CTS/.htaccess

5.4.4.1       The Application Model Definition

The application model describes the structure of the data objects that are used by the application code. At a first glance it may look complicated to create such a description. But this has a big advantage: It allows the framework to generate all SQL code! You will not need to create any SELECT, INSERT and UPDATE statement with their WHERE and ORDER BY clauses and - by the way - the GGF Framework will generate BEGIN TRANSACTION, COMMIT and ROLLBACK statements too: exactly where needed to form a secure and transaction safe application.

<?PHP

/**

 * Class describing the ERmodel of the Contacts application

 *

 * It describes entitytypes "user", "company" and "person",

 * a relationshop type "Company employs Person" and an expanded

 * entitytype "Person_expanded" . It inherits the definition of

 * the entitytype "query".

 *

 * @package CTS

 * @version 1.0

 * @since 1.0

 * @author Gerald Zincke, Austria

 * @copyright 2012 Gerald Zincke

 */

class ContactsModel extends GGFERModel {

   protected function initialize() {

     global $ERModel;

     parent::initialize();

    

     /* create the model for the user table   */

     $user = new GGFEntityType("user");

     $user->addPrimaryKey(new GGFTextAttribute("userid"));

     $user->add(new GGFTextAttribute("userid"));

     $user->add(new GGFTextAttribute("password_enc"));

     $user->add(new GGFTextAttribute("lastname"));

     $user->add(new GGFTextAttribute("firstname","title/first name"));

     $user->add(new GGFTextAttribute("preferences"));

     $user->add(new GGFTextAttribute("email"));

     $user->add(new GGFTextAttribute("LastChange"));

     $user->setDefaultAttributeNames(array('userid','firstname','lastname','last_change'));

     $this->add($user);

    

     /* create the model for the company table*/

     $company = new GGFEntityType("Company");

     $company->addPrimaryKey(new GGFNumAttribute("Company_ID"));

     $company->add(new GGFNumAttribute("Company_ID"));

     $company->add(new GGFTextAttribute("CompanyName"));

     $company->add(new GGFTextAttribute("Street"));

     $company->add(new GGFTextAttribute("City"));

     $company->add(new GGFTextAttribute("ZIPCode"));

     $company->add(new GGFTextAttribute("Country"));

     $company->add(new GGFTextAttribute("LastChange"));

     $company->setDefaultAttributeNames(array('Company_ID','CompanyName','City','Country'));

     $this->add($company);

    

     /* create the model for the person table*/

     $person = new GGFEntityType("Person");

     $person->addPrimaryKey(new GGFNumAttribute("Person_ID"));

     $person->add(new GGFNumAttribute("Person_ID"));

     $person->add(new GGFTextAttribute("FirstName"));

     $person->add(new GGFTextAttribute("LastName"));

     $person->add(new GGFTextAttribute("Title"));

     $person->add(new GGFNumAttribute("Company_ID"));

     $person->add(new GGFTextAttribute("Department"));

     $person->add(new GGFTextAttribute("Position"));

     $person->add(new GGFTextAttribute("Phone"));

     $person->add(new GGFTextAttribute("Mobile"));

     $person->add(new GGFTextAttribute("EMail"));

     $person->add(new GGFTextAttribute("LastChange"));

     $person->setDefaultAttributeNames(

        array('Person_ID','FirstName','LastName','Phone','EMail')

     );  

     $this->add($person);

    

     /* create relationshiptype "Company employs Person"  */

     $company_person = new GGFRelationshipType(

           "Company employs Person",

           $company,array("Company_ID"),

           array(1,1,0,999999999),

           $person,array("Company_ID"));

     $this->addRel($company_person);

    

     /* create expanded entitytype "Person_expanded"  */

     $personExpanded = $person->createExpandedType();

     $personExpanded->

     getAttributeNamed('Company_ID.CompanyName')->setExternalName('Employer');

     $personExpanded->getAttributeNamed('Person.LastName')->setExternalName('Last Name');

     $personExpanded->

     setDefaultAttributeNames(

        array('Person.LastName', 'Person.FirstName', 'Person.Title', 'Person.EMail',

        'Company_ID.CompanyName')

     );

     $this->add($personExpanded);

   }

}

?>

 

Script: /htdocs/CTS/contactsModel.php

For each of the database table a model definition is required. Additionally the script describes the relationship between person and company. Furthermore an “expanded entity type” is created. This solves two typical problems for a database application:

·         Normalized tables usually contain foreign key fields. But in many cases the user will not want to see these (internal) key values, but more often he will be interested in the attribute values of the referenced object. For instance in a listing of persons, the user does not want to see an internal  Company_ID of the employer of a person but the company name, address etc.

 

·         If a user wants to filter a list of database records, in many cases filtering on the columns of the table itself is not sufficient. For instance: “I will go to Cupertino next week. Give me all my contact-persons who work for a company located in Cupertino”. You cannot create such a list by filtering with attributes of the person table. The filter criteria have to include values of referenced objects too. In our contacts application a user can find persons by specifying properties of their employer.

With the model description the GGF Framework is able to satisfy these requirements. It can create listings of normalized tables that show user-friendly values instead of internal keys or codes. And it provides a filter function that generates WHERE clauses to filter on properties of related entities.   

To keep things simple there is just one file for each window or dialog. The ReinHTML Dialog Designer at http://www.ReinHTML.eu  is used to generate the window or dialog layouts for the application. The code generated by the tool utilizes the GGF Framework, especially the base classes for windows.

5.4.4.2     Main Window Implementation: CTSMainWindow.php

We derive the main window of the application from the GGFControlMainWindow class in the  GGF Framework and the layout is created with the ReinHTML Dialog Designer. That saves a lot of work. The Dialog Designer generates the skeleton of the class and especially the initWindow function.

<?PHP

   /**

    * Main window class for the Contacts application

    * (goto http://www.ReinHTML.eu/RD to update the panel layout)

    *

    * @package CTS

    * @version 1.0

    * @since 1.0

    * @author Gerald Zincke, Austria

    * @copyright 2012 Gerald Zincke

    */

   class CTSMainWindow extends GGFControlMainWindow {

  

     function __construct($contextID) {

        parent::__construct($contextID);

     }

     function __destruct() {

    parent::__destruct();

     }

       

     /**

     * RD generated function initWindow

     * create the layout definition of the window

     */

     protected function initWindow($mc) {

     //*H1--generated code, do not touch. Use RD to update ---

     $this->CSSURL='GGFFormats.css';

     $this->windowTitle='Contacts';

     $cont0 = new GGFControlPane("_main","",""); $cont0->initP(array( "name" => "_main")); $cont0->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->setWindow($this);

     $this->pane = $cont0;

        $cont1 = new GGFHMenu("Mainmenu","",""); $cont1->initP(array( "name" => "Mainmenu")); $cont1->resetP(array( "value","tiptext","myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont0->add($cont1);

           $cont2 = new GGFHMenu("DatabaseMenu","Database",""); $cont2->initP(array( "value" => "Database","name" => "DatabaseMenu")); $cont2->resetP(array( "tiptext","myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->add($cont2);

              $cont3 = new GGFHMItem("Login","Login",""); $cont3->initP(array( "validator" => "isLoggedOff","callback" => "eventLogin","name" => "Login","value" => "Login","isDisabled" => "","readonly" => "","mandatory" => "")); $cont3->resetP(array( "target","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont2->add($cont3);

              $cont4 = new GGFHMItem("Logoff","Logoff",""); $cont4->initP(array( "validator" => "isLoggedOn","callback" => "eventLogoff","name" => "Logoff","value" => "Logoff","isDisabled" => "","readonly" => "","mandatory" => "")); $cont4->resetP(array( "target","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont2->add($cont4);

              $cont5 = new GGFHMItem("Close","Close",""); $cont5->initP(array( "callback" => "eventClose","name" => "Close","value" => "Close","isDisabled" => "","readonly" => "","mandatory" => "")); $cont5->resetP(array( "target","validator","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont2->add($cont5);

           $cont6 = new GGFEndPane("end:DatabaseMenu","",""); $cont6->initP(array( "name" => "end:DatabaseMenu")); $cont6->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont2->add($cont6);

           $cont7 = new GGFHMenu("Browse","Browse",""); $cont7->initP(array( "value" => "Browse","name" => "Browse")); $cont7->resetP(array( "tiptext","myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->add($cont7);

              $cont8 = new GGFHMItem("Contacts","Contacts",""); $cont8->initP(array( "target" => "_blank","validator" => "isLoggedOn","callback" => "eventContacts","name" => "Contacts","value" => "Contacts","isDisabled" => "","readonly" => "","mandatory" => "")); $cont8->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont7->add($cont8);

              $cont9 = new GGFHMItem("Companies","Companies",""); $cont9->initP(array( "target" => "_blank","validator" => "isLoggedOn","callback" => "eventCompanies","name" => "Companies","value" => "Companies","isDisabled" => "","readonly" => "","mandatory" => "")); $cont9->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont7->add($cont9);

           $cont10 = new GGFEndPane("end:Browse","",""); $cont10->initP(array( "name" => "end:Browse")); $cont10->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont7->add($cont10);

           $cont11 = new GGFHMItem("About","About",""); $cont11->initP(array( "callback" => "eventAbout","name" => "About","value" => "About","tiptext" => "open the about dialog","isDisabled" => "","readonly" => "","mandatory" => "")); $cont11->resetP(array( "target","validator","size","extraHTML","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->add($cont11);

        $cont12 = new GGFEndPane("end:Mainmenu","",""); $cont12->initP(array( "name" => "end:Mainmenu")); $cont12->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont1->add($cont12);

        $cont0->newP();

        $cont14 = new GGFStaticfield("s1","Welcome to the Contacts Application !",""); $cont14->initP(array( "name" => "s1","value" => "Welcome to the Contacts Application !","isDisabled" => "","readonly" => "","mandatory" => "","fontFamily" => "Arial,sans-serif","fontSize" => "larger","fontStyle" =>  array(bold => "", italic => "", underline => ""), "lineHeight" => "")); $cont14->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background"));

        $cont0->add($cont14);

        $cont0->newP();

        $cont16 = new GGFNewLinePane("Notebox","",""); $cont16->initP(array( "name" => "Notebox","style" => "border:1px;")); $cont16->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont0->add($cont16);

           $cont17 = new GGFStaticfield("","Note:",""); $cont17->initP(array( "value" => "Note:","extraHTML" => "font-weight:bolder;","isDisabled" => "","readonly" => "","mandatory" => "")); $cont17->resetP(array( "name","size","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont16->add($cont17);

           $cont18 = new GGFStaticfield("","This is a demo installation. Data are restored to the initial state at every login.",""); $cont18->initP(array( "value" => "This is a demo installation. Data are restored to the initial state at every login.","isDisabled" => "","readonly" => "","mandatory" => "")); $cont18->resetP(array( "name","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont16->add($cont18);

        $cont19 = new GGFEndPane("end:Notebox","",""); $cont19->initP(array( "name" => "end:Notebox")); $cont19->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont16->add($cont19);

        $cont0->newP();

     $cont21 = new GGFEndPane("end:_main","",""); $cont21->initP(array( "name" => "end:_main")); $cont21->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->add($cont21);

     //****RD generated code, do not touch**********

     ….

     //*H2--end of generated code, do not touch code above. Use RD to update ----

   } // end initWindow    

     //*H3--RD-generated event handlers below. do not remove this line.         

             

   /**

   * RD generated function

   * Validator for control MenuItem(Login, , , ) 

   *

   */

   public function isLoggedOff() {

      return parent::isLoggedOff();   

   }

             

   /**

   * RD generated function

   * Validator for control MenuItem(Logoff, , , ) 

   *

   */

   public function isLoggedOn() {

      return parent::isLoggedOn();    

   }         

             

   /**

   * RD generated function

   * event handler for control MenuItem(Login, , , ) 

   *

   */

   protected function eventLogin() {

      // open the login window

     $myContext = &windowContext($this->myContextID,0);

     $myContext->openDialogOn("CTSLoginDialog", array(0,array('userid'=>"",'password'=>"")));

     exit;

   } 

  

             

   /**

   * RD generated function

   * event handler for control MenuItem(Logoff, , , ) 

   *

   */

   protected function eventLogoff() {

      $_SESSION["userid"] = "";

     unset($this->myModel["percentFull"]);

     $mc = &windowContext($this->myContextID,0);

     $mc->model = $this->myModel;

     $mc->save();

     $this->initWindow($mc);

   }

             

   /**

   * RD generated function

   * event handler for control MenuItem(Browse Contacts, , , ) 

   *

   */

   protected function eventContacts() {

      $myContext = &windowContext($this->myContextID, 0);

     $myContext->openWindowOn("CTSContactBrowserWindow", array());

   }

             

   /**

   * RD generated function

   * event handler for control MenuItem(Browse Companies, , , ) 

   *

   */

   protected function eventCompanies() {

      $myContext = &windowContext($this->myContextID, 0);

     $myContext->openWindowOn("CTSCompanyBrowserWindow", array());

   }

       

   /**

   * RD generated function

   * event handler for control MenuItem(About, , , ) 

   *

   */

   protected function eventAbout() {

     // display the about dialog

     $myContext = &windowContext($this->myContextID,0);

     $myContext->openDialogOn("GGFControlAboutDialog", 0);

     exit;

   }

             

   /**

   * RD generated function

   * event handler for control MenuItem(Close, , , ) 

   *

   */

   protected function eventClose() {

      parent::eventClose();

   }

  

} //end RD generated class CTSMainWindow

?>


file: /htdocs/CTS/CTSMainWindow.php

As you can see, a lot of functionality (like isLoggedOn, isLoggedOff, eventClose) is inherited from the base class.

5.4.4.3     Login Dialog Implementation: CTSLoginDialog.php

The login dialog offers entry fields for user id and password. It will be opened from the main window by clicking the login menu-item, which calls the eventLogin function in the CTSMainWindow class. The CTSLoginDialog class is derived from the GGFControlDialog class in the  GGF Framework and the layout has again been created with the ReinHTML Dialog Designer.  The Dialog Designer generates the skeleton of the class and especially the initWindow function.

<?PHP

   /**

    * RD generated window class CTSLoginDialog

    * (goto http://www.ReinHTML.eu/RD to update the panel layout)

    *

    * @package CTS

    * @version 1.0

    * @since 1.0

    * @author Gerald Zincke, Austria

    * @copyright 2012 Gerald Zincke

    */

   class CTSLoginDialog extends GGFControlDialog {

  

     function __construct($contextID) {

        parent::__construct($contextID);

        $this->ETypeName = 'user';

        $this->extraAttributesToUpdate = array();

     }

     function __destruct() {

        parent::__destruct();

     }

       

     /**

     * RD generated function initWindow

     * create the layout definition of the window

     */

     protected function initWindow($mc) {

    

     //*H1--generated code, do not touch. Use RD to update ---

     $this->CSSURL='GGFFormats.css';

     $this->windowTitle='Login to Contacts Application';

     $cont0 = new GGFControlPane("_main","",""); $cont0->initP(array( "name" => "_main")); $cont0->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","isFrameset","style","Foreground","Background"));

     $cont0->setWindow($this);

     $this->pane = $cont0;

        $cont1 = new GGFDialogFormPane("_mainform","",""); $cont1->initP(array( "name" => "_mainform")); $cont1->resetP(array( "demohtml","demohtml2","myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","isFrameset","style","Foreground","Background"));

        $cont0->add($cont1);

           $cont1->newLine();

           $cont3 = new GGFStaticfield("s0","Please enter your User-ID and Password. Then click OK.to login.",""); $cont3->initP(array( "name" => "s0","value" => "Please enter your User-ID and Password. Then click OK.to login.","isDisabled" => "","readonly" => "","mandatory" => "")); $cont3->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background"));

           $cont1->add($cont3);

           $cont1->newP();

           $cont1->newP();

           $cont6 = new GGFStaticfield("s2","User-ID",""); $cont6->initP(array( "name" => "s2","value" => "User-ID","isDisabled" => "","readonly" => "","mandatory" => "")); $cont6->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background"));

           $cont1->newTable($cont6, '', '', '');

           $cont7 = new GGFEntryfield("userid","",""); $cont7->initP(array( "maxlength" => "32","name" => "userid","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont7->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background"));

           $cont1->newCell($cont7, '', '', '');

           $cont7->setAutofocus();

           $cont8 = new GGFStaticfield("s3","Password",""); $cont8->initP(array( "name" => "s3","value" => "Password","isDisabled" => "","readonly" => "","mandatory" => "")); $cont8->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background"));

           $cont1->newRow($cont8, '', '', '');

           $cont9 = new GGFPasswordEntryfield("password","",""); $cont9->initP(array( "maxlength" => "32","name" => "password","tiptext" => "Enter your Password here. ","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont9->resetP(array( "value","size","extraHTML","myContainer","tabindex","Foreground","Background"));

           $cont1->newCell($cont9, '', '', '');

           $cont10 = new GGFStaticfield("placeholder","",""); $cont10->initP(array( "name" => "placeholder","isDisabled" => "","readonly" => "","mandatory" => "")); $cont10->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont10, '', '', '');

           $cont11 = new GGFStaticfield("s5","* input mandatory",""); $cont11->initP(array( "name" => "s5","value" => "* input mandatory","isDisabled" => "","readonly" => "","mandatory" => "")); $cont11->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont11, '', '', '');

           $cont1->newP();

           $cont1->newP();

           $cont14 = new GGFControlPane("_buttonarea","",""); $cont14->initP(array( "name" => "_buttonarea")); $cont14->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","isFrameset","style","Foreground","Background"));

           $cont1->newTable($cont14, '', '', '');

              $cont15 = new GGFPushbutton("_OK","OK","60"); $cont15->initP(array( "callback" => "eventOK","validator" => "dataPlausible","name" => "_OK","value" => "OK","size" => "60","extraHTML" => "width:80px; overflow:hidden; ","tiptext" => "click to login","isDisabled" => "","readonly" => "","mandatory" => "")); $cont15->resetP(array( "myContainer","tabindex","Foreground","Background"));

              $cont14->add($cont15);

              $cont14->newSpace();

              $cont17 = new GGFPushbutton("_Cancel","Cancel","60"); $cont17->initP(array( "callback" => "eventClose","name" => "_Cancel","value" => "Cancel","size" => "60","tiptext" => "no login; close dialog","isDisabled" => "","readonly" => "","mandatory" => "")); $cont17->resetP(array( "validator","extraHTML","myContainer","tabindex","Foreground","Background"));

              $cont14->add($cont17);

           $cont18 = new GGFEndPane("end:_buttonarea","",""); $cont18->initP(array( "name" => "end:_buttonarea")); $cont18->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background"));

           $cont14->add($cont18);

        $cont19 = new GGFEndPane("end:_mainform","",""); $cont19->initP(array( "name" => "end:_mainform")); $cont19->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background"));

        $cont1->add($cont19);

     $cont20 = new GGFEndPane("end:_main","",""); $cont20->initP(array( "name" => "end:_main")); $cont20->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background"));

     $cont0->add($cont20);

     //****RD generated code, do not touch**********

    

     //*H2--end of generated code, do not touch code above. Use RD to update ---

   } // end initWindow    

     //*H3--RD-generated event handlers below. do not remove this line.         

   /**

   * RD generated function

   * Validator for control Pushbutton(_OK, OK, 60, width:80px; overflow:hidden; ) 

   *

  * @todo replace RD-generated template-code by desired functionality

   *

  */

   public function dataPlausible() {

      // ---- begin template code ----

     return TRUE;

     // ---- end template code ------

    

   }

             

   /**

   * RD generated function

   * event handler for control Pushbutton(_OK, OK, 60) 

   *

   * This checks the userid and password against the user table.

   * It will also restore the contents of the company and person table

   * (this is for security in the demo installation).

   *

   */

   protected function eventOK() {

      // handles OK event

     global $db;

     global $errorStack;

     if ($this->validateData()) {

          

        $userid = mysql_real_escape_string(strtolower($_POST["userid"]));

        $select_statement = "SELECT * FROM user WHERE userid='".$userid."' ";

        $result = $db->execSQL($select_statement);

        $err = mysql_errno();

       

        if (!$err==0) {

           $errmsg = $this->appname.": Error checking UserID, Password ";

           $errorStack->pushUsrMsg(1,$errmsg);

        } else {     

           $num_rows = mysql_num_rows($result);

           if($num_rows >0 ) {          

              $row = mysql_fetch_array($result);

              if (

                strcmp($row["password_enc"],

                substr(md5(mysql_real_escape_string($_POST["password"])),0,32)

              )==0) {

                $this->ok = TRUE;

                $_SESSION["userid"] = $userid;

                $this->eventClose();            

              } else {

                $errorStack->pushUsrMsg(1,"UserID/Password is wrong. ");

                $this->ok = FALSE;

              }

           } else {

              $errorStack->pushUsrMsg(1,"Login failed. ");

           }

        }

     }

   }         

   /**

   * RD generated function

   * event handler for control Pushbutton(_Cancel, Cancel, 60) 

   *

   */

   protected function eventClose() {

      // handles Cancel event

     parent::eventClose();

   }            

} //end RD generated class LoginDialog

?>


file: /htdocs/CTS/CTSLoginDialog.php

When the user clicks the OK button the eventOK function is called. The implementation uses the database interface of the GGF Framework to check user-ID and password. If something goes wrong, error messages are just pushed to the error stack provided by the framework. The framework takes care that error messages are displayed in a message-box.

5.4.4.4     CTSCompanyBrowserWindow

The company browser window displays the list of stored companies. If there are more entries in the database than fit on a screen, the user can scoll through the list page by page, using the buttons at the right. As you can see there is really not much to do. Almost everything is inherited from the GGFControlBrowserWindow class in the GGF Framework.

<?PHP

/**

 * Company browsing window

 *

 * @package CTS

 * @version 1.0

 * @since 1.0

 * @author Gerald Zincke, Austria

 * @copyright 2012 Gerald Zincke

 */

class CTSCompanyBrowserWindow extends GGFControlBrowserWindow {

 

   function __construct($contextID) {

     parent::__construct($contextID);

     $this->ETypeName = "Company";    // initialize the name of the main model object class of the window

     $this->enableCopy = true; // enables copy feature in browser window

   }

 

   /**

    * define event dialog-classes

    */

   protected function initModel($mc) {

     // prepare the model object

     parent::initModel($mc);

  

     $this->myModel["_updateDialogClass"] = "CTSCompanyUpdateDialog";

     $this->myModel["_insertDialogClass"] = "CTSCompanyInsertDialog";

     $this->myModel["_copyDialogClass"]   = "CTSCompanyInsertDialog";

     $this->myModel["_updateAfterInsert"] = FALSE;

     $mc->model = $this->myModel;

     $mc->save();

   }

 

}

?>


file: /htdocs/CTS/CTSCompanyBrowserWindow.php

We have to tell the object the name of the entity type (“Company”) that will be shown in the window. The structure of the “Company” entity type has already been defined in the model of our application. (See chapter "The logical Data Model" and "The Application Model Description".) And we have to tell the model object which sub-dialog classes should be used for standard actions like update, insert or copy of a company record.

5.4.4.5     CTSCompanyInsertDialog

In many cases it is required to provide some initial data when you want to create a new database record. There may be NOT NULL fields or it may just not be meaningful to store a record with a lot of empty fields. For that the architecture needs an insert dialog. It will open when you click the “Insert” menu in the Company Browser Window.

The CTSCompanyInsertDialog class is derived from the GGFControlInsertDialog class in the  GGF Framework and the layout has again been created with the ReinHTML Dialog Designer.  The Dialog Designer generates the skeleton of the class and especially the initWindow function.

<?PHP

   /**

    * RD generated window class CTSCompanyInsertDialog

    * (goto http://www.ReinHTML.eu/RD to update the panel layout)

    *

    * @package CTS

    * @version 1.0

    * @since 1.0

    * @author Gerald Zincke, Austria

    * @copyright 2012 Gerald Zincke

    */

   class CTSCompanyInsertDialog extends GGFControlInsertDialog {

  

   function __construct($contextID) {

    parent::__construct($contextID);

    $this->ETypeName = 'Company';

    $this->extraAttributesToUpdate = array("CompanyName", "Street", "ZIPCode", "City", "Country" );

   }

   function __destruct() {

    parent::__destruct();

   }

       

   /**

   * RD generated function initWindow

   * create the layout definition of the window

   */

   protected function initWindow($mc) {

    

     //*H1--generated code, do not touch. Use RD to update ---

     $this->CSSURL='GGFFormats.css';

     $this->windowTitle='Update Company';

     $cont0 = new GGFControlPane("_main","",""); $cont0->initP(array( "name" => "_main")); $cont0->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->setWindow($this);

     $this->pane = $cont0;

        $cont1 = new GGFDialogFormPane("_mainform","",""); $cont1->initP(array( "name" => "_mainform")); $cont1->resetP(array( "demohtml","demohtml2","formScriptName","myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont0->add($cont1);

           $cont1->newLine();

           $cont1->newP();

           $cont4 = new GGFStaticfield("s1","Enter data for new Company and click OK.",""); $cont4->initP(array( "name" => "s1","value" => "Enter data for new Company and click OK.","isDisabled" => "","readonly" => "","mandatory" => "")); $cont4->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont4, '', '', '');

           $cont1->newP();

           $cont6 = new GGFStaticfield("s3","Company Name",""); $cont6->initP(array( "name" => "s3","value" => "Company Name","isDisabled" => "","readonly" => "","mandatory" => "")); $cont6->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont6, '', '', '');

           $cont7 = new GGFEntryfield("CompanyName","","32"); $cont7->initP(array( "maxlength" => "64","inputClass" => "1","name" => "CompanyName","size" => "32","tiptext" => "The Name of the company","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont7->resetP(array( "autofocus","value","extraHTML","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont7, '', '', '');

           $cont7->setAutofocus();

           $cont8 = new GGFStaticfield("s4","Street",""); $cont8->initP(array( "name" => "s4","value" => "Street","isDisabled" => "","readonly" => "","mandatory" => "")); $cont8->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont8, '', '', '');

           $cont9 = new GGFEntryfield("Street","","32"); $cont9->initP(array( "maxlength" => "32","inputClass" => "1","name" => "Street","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont9->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont9, '', '', '');

           $cont10 = new GGFStaticfield("s5","Zipcode",""); $cont10->initP(array( "name" => "s5","value" => "Zipcode","isDisabled" => "","readonly" => "","mandatory" => "")); $cont10->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont10, '', '', '');

           $cont11 = new GGFEntryfield("ZIPCode","","8"); $cont11->initP(array( "maxlength" => "16","inputClass" => "5","name" => "ZIPCode","size" => "8","tiptext" => "postal code for address","isDisabled" => "","readonly" => "","mandatory" => "")); $cont11->resetP(array( "autofocus","value","extraHTML","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont11, '', '', '');

            $cont12 = new GGFStaticfield("s6","City",""); $cont12->initP(array( "name" => "s6","value" => "City","isDisabled" => "","readonly" => "","mandatory" => "")); $cont12->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->add($cont12);

           $cont13 = new GGFEntryfield("City","","32"); $cont13->initP(array( "maxlength" => "32","inputClass" => "1","name" => "City","size" => "32","tiptext" => "name of city in address","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont13->resetP(array( "autofocus","value","extraHTML","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->add($cont13);

           $cont14 = new GGFStaticfield("s7","Country",""); $cont14->initP(array( "name" => "s7","value" => "Country","isDisabled" => "","readonly" => "","mandatory" => "")); $cont14->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont14, '', '', '');

           $cont15 = new GGFDropDown("Country","Austria|Germany|Switzerland|UK|USA",""); $cont15->initP(array( "selections" => "Austria","returnIndex" => "","name" => "Country","value" => "Austria|Germany|Switzerland|UK|USA","isDisabled" => "","readonly" => "","mandatory" => "")); $cont15->resetP(array( "vsize","keys","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont15, '', '', '');

           $cont16 = new GGFStaticfield("placeholder","",""); $cont16->initP(array( "name" => "placeholder","isDisabled" => "","readonly" => "","mandatory" => "")); $cont16->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont16, '', '', '');

           $cont17 = new GGFStaticfield("s8","* input is mandatory",""); $cont17->initP(array( "name" => "s8","value" => "* input is mandatory","isDisabled" => "","readonly" => "","mandatory" => "")); $cont17->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont17, '', '', '');

           $cont1->newP();

           $cont19 = new GGFControlPane("_buttonarea","",""); $cont19->initP(array( "name" => "_buttonarea")); $cont19->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont19, '', '', '');

              $cont20 = new GGFPushbutton("_OK","OK","60"); $cont20->initP(array( "callback" => "eventOK","validator" => "dataPlausible","name" => "_OK","value" => "OK","size" => "60","extraHTML" => " width:80px; overflow:hidden ","tiptext" => "save data in database")); $cont20->resetP(array( "myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont19->add($cont20);

              $cont21 = new GGFPushbutton("_Cancel","Cancel","60"); $cont21->initP(array( "callback" => "eventClose","name" => "_Cancel","value" => "Cancel","size" => "60","tiptext" => "do not save data in database")); $cont21->resetP(array( "validator","extraHTML","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont19->add($cont21);

           $cont22 = new GGFEndPane("end:_buttonarea","",""); $cont22->initP(array( "name" => "end:_buttonarea")); $cont22->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont19->add($cont22);

        $cont23 = new GGFEndPane("end:_mainform","",""); $cont23->initP(array( "name" => "end:_mainform")); $cont23->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont1->add($cont23);

     $cont24 = new GGFEndPane("end:_main","",""); $cont24->initP(array( "name" => "end:_main")); $cont24->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->add($cont24);

     //****RD generated code, do not touch**********

    

     //*H2--end of generated code, do not touch code above. Use RD to update ---

   } // end initWindow    

     //*H3--RD-generated event handlers below. do not remove this line.         

   /**

   * RD generated function

   * Validator for control Pushbutton(_OK, OK, 60,  width:80px; overflow:hidden ) 

   *

  * @todo replace RD-generated template-code by desired functionality

  * @return boolean

   *

  */

   public function dataPlausible() {

      // ---- begin template code ----

     return TRUE;

     // ---- end template code ------

   }

             

   /**

   * RD generated function

   * event handler for control Pushbutton(_OK, OK, 60) 

   *

  */

   protected function eventOK() {

      parent::eventOK();

   }

             

   /**

   * RD generated function

   * event handler for control Pushbutton(_Cancel, Cancel, 60) 

   *

   */

   protected function eventClose() {

     parent::eventClose();

   }

             

  

} //end RD generated class CTSCompanyInsertDialog

?>


file: /htdocs/CTS/CTSCompanyInsertDialog.php

There is not really much to do. The eventClose and the eventOK function are inherited from the base class.

5.4.4.6     CTSCompanyUpdateDialog

This dialog allows us to view and update company records. It is opened by clicking on a company record in the Company Browser Window.

The CTSCompanyUpdateDialog class is derived from the GGFControlUpdateDialog class in the  GGF Framework and the layout has again been created with the ReinHTML Dialog Designer.  The Dialog Designer generates the skeleton of the class and especially the initWindow function.

For this dialog a little manual programming was done to get the listbox filled with the data of the people working for the selected company. This is done in the fillControls function. The standard entry fields of the form are filled by calling the parent function. Then a “filter” object for persons is defined, because we want only those persons in the listbox that belong to the company shown. Then we can use the inherited function fillSelectionControlER to fill the listbox with the data selected by the filter object.

<?PHP

   /**

    * RD generated window class CTSCompanyUpdateDialog

    * (goto http://www.ReinHTML.eu/RD to update the panel layout)

    *

    * @package CTS

    * @version 1.0

    * @since 1.0

    * @author Gerald Zincke, Austria

    * @copyright 2012 Gerald Zincke

    */

   class CTSCompanyUpdateDialog extends GGFControlUpdateDialog {

  

   function __construct($contextID) {

    parent::__construct($contextID);

    $this->ETypeName = 'Company';

    $this->extraAttributesToUpdate = array();

   }

   function __destruct() {

    parent::__destruct();

   }

       

   protected function fillControls($mc) {

     parent::fillControls($mc);

    

     $filter = new GGFERFilter($this->ERModel->entityTypeNamed('Person'));

     $filter->add(new GGFSqlFilterClause('AND', 'Company_ID', '=', $this->myModel[1]['Company_ID'])); 

 

     $this->fillSelectionControlER("employees", "Person",

     array('FirstName', 'LastName', 'Department', 'Position'),

     array(12,16,4,4),$filter);

   }

  

/**

   * RD generated function initWindow

   * create the layout definition of the window

   */

   protected function initWindow($mc) {

    

     //*H1--generated code, do not touch. Use RD to update ---

     $this->CSSURL='GGFFormats.css';

     $this->windowTitle='Update Company';

     $cont0 = new GGFControlPane("_main","",""); $cont0->initP(array( "name" => "_main")); $cont0->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->setWindow($this);

     $this->pane = $cont0;

        $cont1 = new GGFDialogFormPane("_mainform","",""); $cont1->initP(array( "name" => "_mainform")); $cont1->resetP(array( "demohtml","demohtml2","formScriptName","myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont0->add($cont1);

           $cont1->newLine();

           $cont1->newP();

           $cont4 = new GGFStaticfield("s1","Enter data and click OK.",""); $cont4->initP(array( "name" => "s1","value" => "Enter data and click OK.","isDisabled" => "","readonly" => "","mandatory" => "")); $cont4->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont4, '', '', '');

           $cont1->newP();

           $cont6 = new GGFStaticfield("s2","ID",""); $cont6->initP(array( "name" => "s2","value" => "ID","isDisabled" => "","readonly" => "","mandatory" => "")); $cont6->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont6, '', '', '');

           $cont7 = new GGFEntryfield("Company_ID","123","9"); $cont7->initP(array( "maxlength" => "10","inputClass" => "5","name" => "Company_ID","value" => "123","size" => "9","isDisabled" => "1","readonly" => "1","mandatory" => "","Background" => "#E6E6E6")); $cont7->resetP(array( "autofocus","extraHTML","tiptext","myContainer","tabindex","Foreground","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont7, '', '', '');

           $cont8 = new GGFStaticfield("s3","Company Name",""); $cont8->initP(array( "name" => "s3","value" => "Company Name","isDisabled" => "","readonly" => "","mandatory" => "")); $cont8->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont8, '', '', '');

           $cont9 = new GGFEntryfield("CompanyName","","32"); $cont9->initP(array( "maxlength" => "64","inputClass" => "1","name" => "CompanyName","size" => "32","tiptext" => "The Name of the company","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont9->resetP(array( "autofocus","value","extraHTML","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont9, '', '', '');

            $cont9->setAutofocus();

           $cont10 = new GGFStaticfield("s4","Street",""); $cont10->initP(array( "name" => "s4","value" => "Street","isDisabled" => "","readonly" => "","mandatory" => "")); $cont10->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont10, '', '', '');

           $cont11 = new GGFEntryfield("Street","","32"); $cont11->initP(array( "maxlength" => "32","inputClass" => "1","name" => "Street","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont11->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont11, '', '', '');

           $cont12 = new GGFStaticfield("s5","Zipcode",""); $cont12->initP(array( "name" => "s5","value" => "Zipcode","isDisabled" => "","readonly" => "","mandatory" => "")); $cont12->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont12, '', '', '');

           $cont13 = new GGFEntryfield("ZIPCode","","8"); $cont13->initP(array( "maxlength" => "16","inputClass" => "5","name" => "ZIPCode","size" => "8","tiptext" => "postal code for address","isDisabled" => "","readonly" => "","mandatory" => "")); $cont13->resetP(array( "autofocus","value","extraHTML","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont13, '', '', '');

           $cont14 = new GGFStaticfield("s6","City",""); $cont14->initP(array( "name" => "s6","value" => "City","isDisabled" => "","readonly" => "","mandatory" => "")); $cont14->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->add($cont14);

           $cont15 = new GGFEntryfield("City","","32"); $cont15->initP(array( "maxlength" => "32","inputClass" => "1","name" => "City","size" => "32","tiptext" => "name of city in address","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont15->resetP(array( "autofocus","value","extraHTML","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->add($cont15);

           $cont16 = new GGFStaticfield("s7","Country",""); $cont16->initP(array( "name" => "s7","value" => "Country","isDisabled" => "","readonly" => "","mandatory" => "")); $cont16->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont16, '', '', '');

           $cont17 = new GGFDropDown("Country","Austria|Germany|Switzerland|UK|USA",""); $cont17->initP(array( "selections" => "Austria","returnIndex" => "","name" => "Country","value" => "Austria|Germany|Switzerland|UK|USA","isDisabled" => "","readonly" => "","mandatory" => "")); $cont17->resetP(array( "vsize","keys","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont17, '', '', '');

           $cont18 = new GGFStaticfield("placeholder","",""); $cont18->initP(array( "name" => "placeholder","isDisabled" => "","readonly" => "","mandatory" => "")); $cont18->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont18, '', '', '');

           $cont19 = new GGFStaticfield("s8","* input is mandatory",""); $cont19->initP(array( "name" => "s8","value" => "* input is mandatory","isDisabled" => "","readonly" => "","mandatory" => "")); $cont19->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont19, '', '', '');

           $cont20 = new GGFStaticfield("s7","Employees",""); $cont20->initP(array( "name" => "s7","value" => "Employees","isDisabled" => "","readonly" => "","mandatory" => "")); $cont20->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont20, '', '', '');

           $cont1->newLine();

           $cont22 = new GGFPseudoPushbutton("browse"," open Browser ",""); $cont22->initP(array( "callback" => "eventContacts","target" => "_blank","name" => "browse","value" => " open Browser ","isDisabled" => "","readonly" => "","mandatory" => "")); $cont22->resetP(array( "validator","url","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->add($cont22);

           $cont1->newSpace();

           $cont24 = new GGFListbox("employees","","360"); $cont24->initP(array( "vsize" => "5","returnIndex" => "","name" => "employees","size" => "360","extraHTML" => "overflow:scroll;","isDisabled" => "","readonly" => "1","mandatory" => "")); $cont24->resetP(array( "selections","keys","value","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont24, '', '', '');

           $cont1->newP();

           $cont26 = new GGFControlPane("_buttonarea","",""); $cont26->initP(array( "name" => "_buttonarea")); $cont26->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont26, '', '', '');

              $cont27 = new GGFPushbutton("_OK","OK","60"); $cont27->initP(array( "callback" => "eventOK","validator" => "dataPlausible","name" => "_OK","value" => "OK","size" => "60","extraHTML" => " width:80px; overflow:hidden ","tiptext" => "save data in database")); $cont27->resetP(array( "myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont26->add($cont27);

              $cont28 = new GGFPushbutton("_Cancel","Cancel","60"); $cont28->initP(array( "callback" => "eventClose","name" => "_Cancel","value" => "Cancel","size" => "60","tiptext" => "do not save data in database")); $cont28->resetP(array( "validator","extraHTML","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont26->add($cont28);

           $cont29 = new GGFEndPane("end:_buttonarea","",""); $cont29->initP(array( "name" => "end:_buttonarea")); $cont29->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont26->add($cont29);

        $cont30 = new GGFEndPane("end:_mainform","",""); $cont30->initP(array( "name" => "end:_mainform")); $cont30->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont1->add($cont30);

     $cont31 = new GGFEndPane("end:_main","",""); $cont31->initP(array( "name" => "end:_main")); $cont31->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->add($cont31);

     //****RD generated code, do not touch**********

  

   //*H2--end of generated code, do not touch code above. Use RD to update ---

   } // end initWindow    

     //*H3--RD-generated event handlers below. do not remove this line.         

   /**

   * RD generated function

   * Validator for control Pushbutton(_OK, OK, 60,  width:80px; overflow:hidden ) 

   *

   * @return boolean

   *

   */

   public function dataPlausible() {

      // ---- begin template code ----

     return TRUE;

     // ---- end template code ------

   }

  

   /**

   * RD generated function

   * event handler for control PseudoPushbutton(browse,open Browser) 

   *

   */

   protected function eventContacts() {

      $et = $this->ERModel->entityTypeNamed($this->ETypeName);

      $f = $et->foreignKeyFilter($this->myModel[1],"Company employs Person");

 

      $myContext = &windowContext($this->myContextID, 0);

     $myContext->openWindowOn("CTSContactBrowserWindow", array("_where" => $f));

   }

             

   /**

   * RD generated function

   * event handler for control Pushbutton(_OK, OK, 60) 

   *

   */

   protected function eventOK() {

      // handles OK event

     $this->ok = $this->validateData();

     $this->saveModifications();

     if ($this->ok) {

        $this->eventClose();

     }

   }

             

   /**

   * RD generated function

   * event handler for control Pushbutton(_Cancel, Cancel, 60) 

   *

   */

   protected function eventClose() {

      // handles Cancel event

     parent::eventClose();

   }

             

 

} //end RD generated class CTSCompanyUpdateDialog

?>


file: /htdocs/CTS/CTSCompanyUpdateDialog.php

The event handler for the cancel button is inherited. Have a look at the eventClose function. It calls the inherited saveModifications function. That does all the SQL stuff for us. It will – by the way - not only do the update, but it will also take care for preventing double update problems.

The eventContacts function will open a browser window for persons. This browser window will not show every person in the database, but only those persons working for the company in the company update dialog. For that we create a filter object by asking our entitytype for a foreignKeyFilter via the "Company employs Person" relationship. The filter is provided in the call to openWindowOn as part of the model object argument.

5.4.4.7     CTSContactsBrowserWindow

The Contacts Browser Window shows a table of the persons in the database. It is not really the person table, but a so called expanded entity ("Person_expanded") that contains not only person data but data of that person’s employer too. See chapter “The Application Model Description” for a description of expanded entities.

<?PHP

/**

 * Contacts browsing window

 *

 * @package CTS

 * @version 1.0

 * @since 1.0

 * @author Gerald Zincke, Austria

 * @copyright 2012 Gerald Zincke

 */

class CTSContactBrowserWindow extends GGFControlBrowserWindow {

 

   function __construct($contextID) {

       parent::__construct($contextID);

       $this->ETypeName = "Person_expanded"; // initialize the name of the main model object class of the window

       $this->enableCopy = true;   // enables copy feature in browser window

   }

 

   /**

    * define event dialog-classes

    */

   protected function initModel($mc) {

        // prepare the model object

     global $appname;

     parent::initModel($mc);

  

     $this->myModel["_updateDialogClass"] = "CTSContactUpdateDialog";

     $this->myModel["_insertDialogClass"] = "CTSContactInsertDialog";

     $this->myModel["_copyDialogClass"]   = "";

     $this->myModel["_updateAfterInsert"] = true;

     $this->windowTitle = "Contacts - Browser ";

     $mc->model = $this->myModel;

     $mc->save();

   }

 

}

?>


file: /htdocs/CTS/CTSContactBrowserWindow.php

Again there is not much to implement for the contact browser window. In the initModel function we specify which sub dialog classes are used.

5.4.4.8     CTSContactInsertDialog

The insert dialog opens when the user clicks the Insert menu in the contacts browser window. The CTSContactInsertDialog class is derived from the GGFControlInsertDialog class in the  GGF Framework and the layout has again been created with the ReinHTML Dialog Designer.  The Dialog Designer generates the skeleton of the class and especially the initWindow function.

<?PHP

   /**

    * RD generated window class CTSContactInsertDialog

    * (goto http://www.ReinHTML.eu/RD to update the panel layout)

    *

    * @todo comment, cleanup, version, test

    * @package CTS

    * @version 1.0

    * @since 1.0

    * @author Gerald Zincke, Austria

    * @copyright 2012 Gerald Zincke

    */

   class CTSContactInsertDialog extends GGFControlInsertDialog {

  

   function __construct($contextID) {

    parent::__construct($contextID);

    $this->ETypeName = 'Person';

    $this->extraAttributesToUpdate =

    array("FirstName","LastName","Title","Company_ID",

    "Department","Position","Phone","Mobile","EMail");

   }

   function __destruct() {

    parent::__destruct();

   }

  

   protected function fillControls($mc) {

     parent::fillControls($mc);

  

     $this->fillSelectionControlER("Company_ID", "Company", array('CompanyName',

     'Street', 'City', 'Country'), array(24,16,16,16));

   }

  

   /**

   * RD generated function initWindow

   * create the layout definition of the window

   */

   protected function initWindow($mc) {

    

     //*H1--generated code, do not touch. Use RD to update ---

     $this->CSSURL='GGFFormats.css';

     $this->windowTitle='Create new Contact';

     $cont0 = new GGFControlPane("_main","",""); $cont0->initP(array( "name" => "_main")); $cont0->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->setWindow($this);

     $this->pane = $cont0;

        $cont1 = new GGFDialogFormPane("_mainform","",""); $cont1->initP(array( "name" => "_mainform")); $cont1->resetP(array( "demohtml","demohtml2","formScriptName","myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont0->add($cont1);

           $cont1->newLine();

           $cont1->newP();

           $cont4 = new GGFStaticfield("s1","Enter data for new contact and click OK.",""); $cont4->initP(array( "name" => "s1","value" => "Enter data for new contact and click OK.","isDisabled" => "","readonly" => "","mandatory" => "")); $cont4->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont4, '', '', '');

           $cont1->newP();

           $cont6 = new GGFStaticfield("s2","First Name",""); $cont6->initP(array( "name" => "s2","value" => "First Name","isDisabled" => "","readonly" => "","mandatory" => "")); $cont6->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont6, '', '', '');

           $cont7 = new GGFEntryfield("FirstName","","32"); $cont7->initP(array( "maxlength" => "32","inputClass" => "1","name" => "FirstName","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "")); $cont7->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont7, '', '', '');

           $cont7->setAutofocus();

           $cont8 = new GGFStaticfield("s3","Last Name",""); $cont8->initP(array( "name" => "s3","value" => "Last Name","isDisabled" => "","readonly" => "","mandatory" => "")); $cont8->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont8, '', '', '');

           $cont9 = new GGFEntryfield("LastName","","32"); $cont9->initP(array( "maxlength" => "32","inputClass" => "1","name" => "LastName","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont9->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont9, '', '', '');

           $cont10 = new GGFStaticfield("s4","Title",""); $cont10->initP(array( "name" => "s4","value" => "Title","isDisabled" => "","readonly" => "","mandatory" => "")); $cont10->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont10, '', '', '');

           $cont11 = new GGFEntryfield("Title","","16"); $cont11->initP(array( "maxlength" => "64","inputClass" => "1","name" => "Title","size" => "16","isDisabled" => "","readonly" => "","mandatory" => "")); $cont11->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont11, '', '', '');

           $cont12 = new GGFStaticfield("s5","Position",""); $cont12->initP(array( "name" => "s5","value" => "Position","isDisabled" => "","readonly" => "","mandatory" => "")); $cont12->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont12, '', '', '');

           $cont13 = new GGFEntryfield("Position","","32"); $cont13->initP(array( "maxlength" => "32","inputClass" => "1","name" => "Position","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "")); $cont13->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont13, '', '', '');

           $cont14 = new GGFStaticfield("s6","Department",""); $cont14->initP(array( "name" => "s6","value" => "Department","isDisabled" => "","readonly" => "","mandatory" => "")); $cont14->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont14, '', '', '');

           $cont15 = new GGFEntryfield("Department","","32"); $cont15->initP(array( "maxlength" => "64","inputClass" => "1","name" => "Department","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "")); $cont15->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont15, '', '', '');

           $cont16 = new GGFStaticfield("s6","Mobile",""); $cont16->initP(array( "name" => "s6","value" => "Mobile","isDisabled" => "","readonly" => "","mandatory" => "")); $cont16->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont16, '', '', '');

           $cont17 = new GGFEntryfield("Mobile","","16"); $cont17->initP(array( "maxlength" => "32","inputClass" => "5","name" => "Mobile","size" => "16","isDisabled" => "","readonly" => "","mandatory" => "")); $cont17->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

            $cont1->newCell($cont17, '', '', '');

           $cont18 = new GGFStaticfield("s6","Phone",""); $cont18->initP(array( "name" => "s6","value" => "Phone","isDisabled" => "","readonly" => "","mandatory" => "")); $cont18->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont18, '', '', '');

           $cont19 = new GGFEntryfield("Phone","","16"); $cont19->initP(array( "maxlength" => "32","inputClass" => "5","name" => "Phone","size" => "16","isDisabled" => "","readonly" => "","mandatory" => "")); $cont19->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont19, '', '', '');

           $cont20 = new GGFStaticfield("s6","E-Mail",""); $cont20->initP(array( "name" => "s6","value" => "E-Mail","isDisabled" => "","readonly" => "","mandatory" => "")); $cont20->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont20, '', '', '');

           $cont21 = new GGFEntryfield("EMail","","32"); $cont21->initP(array( "maxlength" => "64","inputClass" => "14","name" => "EMail","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "")); $cont21->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont21, '', '', '');

           $cont22 = new GGFStaticfield("placeholder","",""); $cont22->initP(array( "name" => "placeholder","isDisabled" => "","readonly" => "","mandatory" => "")); $cont22->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont22, '', '', '');

           $cont23 = new GGFStaticfield("s8","* input is mandatory",""); $cont23->initP(array( "name" => "s8","value" => "* input is mandatory","isDisabled" => "","readonly" => "","mandatory" => "")); $cont23->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont23, '', '', '');

           $cont24 = new GGFFieldsetPane("Employer","",""); $cont24->initP(array( "name" => "Employer")); $cont24->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont24, '', '', '');

              $cont25 = new GGFStaticfield("s6","Company",""); $cont25->initP(array( "name" => "s6","value" => "Company","isDisabled" => "","readonly" => "","mandatory" => "")); $cont25->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont24->newRow($cont25, '', '', '');

              $cont24->newSpace();

              $cont27 = new GGFDropDown("Company_ID","",""); $cont27->initP(array( "returnIndex" => "","name" => "Company_ID","isDisabled" => "","readonly" => "","mandatory" => "")); $cont27->resetP(array( "vsize","selections","keys","value","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont24->newCell($cont27, '', '', '');

           $cont28 = new GGFEndPane("end:Employer","",""); $cont28->initP(array( "name" => "end:Employer")); $cont28->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont24->add($cont28);

           $cont1->newP();

           $cont30 = new GGFControlPane("_buttonarea","",""); $cont30->initP(array( "name" => "_buttonarea")); $cont30->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont30, '', '', '');

              $cont31 = new GGFPushbutton("_OK","OK","60"); $cont31->initP(array( "callback" => "eventOK","validator" => "dataPlausible","name" => "_OK","value" => "OK","size" => "60","extraHTML" => " width:80px; overflow:hidden ","tiptext" => "save data in database")); $cont31->resetP(array( "myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont30->add($cont31);

              $cont32 = new GGFPushbutton("_Cancel","Cancel","60"); $cont32->initP(array( "callback" => "eventClose","name" => "_Cancel","value" => "Cancel","size" => "60","tiptext" => "do not save data in database")); $cont32->resetP(array( "validator","extraHTML","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont30->add($cont32);

           $cont33 = new GGFEndPane("end:_buttonarea","",""); $cont33->initP(array( "name" => "end:_buttonarea")); $cont33->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont30->add($cont33);

        $cont34 = new GGFEndPane("end:_mainform","",""); $cont34->initP(array( "name" => "end:_mainform")); $cont34->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont1->add($cont34);

     $cont35 = new GGFEndPane("end:_main","",""); $cont35->initP(array( "name" => "end:_main")); $cont35->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->add($cont35);

     //****RD generated code, do not touch**********

    

     //*H2--end of generated code, do not touch code above. Use RD to update ---

   } // end initWindow    

     //*H3--RD-generated event handlers below. do not remove this line.         

   /**

   * RD generated function

   * event handler for control Pushbutton(_OK, OK, 60,  width:80px; overflow:hidden ) 

   *

  */

   protected function eventOK() {

      parent::eventOK();

   }

             

   /**

   * RD generated function

   * Validator for control Pushbutton(_OK, OK, 60,  width:80px; overflow:hidden ) 

   *

  * @todo replace RD-generated template-code by desired functionality

  * @return boolean

   *

  */

   public function dataPlausible() {

      // ---- begin template code ----

     return TRUE;

     // ---- end template code ------

    

   }

             

   protected function validateData() {

     return true;

   }

               

   /**

   * RD generated function

   * event handler for control Pushbutton(_Cancel, Cancel, 60) 

   *

  */

   protected function eventClose() {

     parent::eventClose();

   }

  

} //end RD generated class CTSContactInsertDialog

?>


file: /htdocs/CTS/CTSContactInsertDialog.php

It allows to enter the data of a new contact person. And by selecting a company from the drop-down at the bottom, you can establish a relationship to the employer - a company.

5.4.4.9     CTSContactUpdateDialog

The contact update dialog appears when the user clicks on an entry in the contact browser window. The CTSContactUpdateDialog class is derived from the GGFControlUpdateDialog class in the  GGF Framework and the layout has again been created with the ReinHTML Dialog Designer.  The Dialog Designer generates the skeleton of the class and especially the initWindow function.

<?PHP

   /**

    * RD generated window class CTSContactUpdateDialog

    * (goto http://www.ReinHTML.eu/RD to update the panel layout)

    *

    * @package CTS

    * @version 1.0

    * @since 1.0

    * @author Gerald Zincke, Austria

    * @copyright 2012 Gerald Zincke

    */

   class CTSContactUpdateDialog extends GGFControlUpdateDialog {

  

   function __construct($contextID) {

    parent::__construct($contextID);

    $this->ETypeName = 'Person';

    $this->extraAttributesToUpdate = array();

   }

   function __destruct() {

    parent::__destruct();

   }

  

   protected function fillControls($mc) {

     parent::fillControls($mc);

  

     $this->fillSelectionControlER("Company_ID", "Company", array('CompanyName',

     'Street', 'City', 'Country'), array(24,16,16,16));

   }

  

   /**

   * RD generated function initWindow

   * create the layout definition of the window

   */

   protected function initWindow($mc) {

    

     //*H1--generated code, do not touch. Use RD to update ---

     $this->CSSURL='GGFFormats.css';

     $this->windowTitle='Update Contact';

     $cont0 = new GGFControlPane("_main","",""); $cont0->initP(array( "name" => "_main")); $cont0->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->setWindow($this);

     $this->pane = $cont0;

        $cont1 = new GGFDialogFormPane("_mainform","",""); $cont1->initP(array( "name" => "_mainform")); $cont1->resetP(array( "demohtml","demohtml2","formScriptName","myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont0->add($cont1);

           $cont1->newLine();

           $cont1->newP();

           $cont4 = new GGFStaticfield("s1","Enter data and click OK.",""); $cont4->initP(array( "name" => "s1","value" => "Enter data and click OK.","isDisabled" => "","readonly" => "","mandatory" => "")); $cont4->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont4, '', '', '');

           $cont1->newP();

           $cont6 = new GGFStaticfield("s2","ID",""); $cont6->initP(array( "name" => "s2","value" => "ID","isDisabled" => "","readonly" => "","mandatory" => "")); $cont6->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont6, '', '', '');

           $cont7 = new GGFEntryfield("Person_ID","","8"); $cont7->initP(array( "maxlength" => "12","inputClass" => "5","name" => "Person_ID","size" => "8","isDisabled" => "","readonly" => "1","mandatory" => "","Background" => "#D8D8D8")); $cont7->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont7, '', '', '');

           $cont8 = new GGFStaticfield("s2","First Name",""); $cont8->initP(array( "name" => "s2","value" => "First Name","isDisabled" => "","readonly" => "","mandatory" => "")); $cont8->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont8, '', '', '');

           $cont9 = new GGFEntryfield("FirstName","","32"); $cont9->initP(array( "maxlength" => "32","inputClass" => "1","name" => "FirstName","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "")); $cont9->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont9, '', '', '');

           $cont10 = new GGFStaticfield("s3","Last Name",""); $cont10->initP(array( "name" => "s3","value" => "Last Name","isDisabled" => "","readonly" => "","mandatory" => "")); $cont10->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont10, '', '', '');

           $cont11 = new GGFEntryfield("LastName","","32"); $cont11->initP(array( "maxlength" => "32","inputClass" => "1","name" => "LastName","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "1")); $cont11->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont11, '', '', '');

           $cont12 = new GGFStaticfield("s4","Title",""); $cont12->initP(array( "name" => "s4","value" => "Title","isDisabled" => "","readonly" => "","mandatory" => "")); $cont12->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont12, '', '', '');

           $cont13 = new GGFEntryfield("Title","","16"); $cont13->initP(array( "maxlength" => "64","inputClass" => "1","name" => "Title","size" => "16","isDisabled" => "","readonly" => "","mandatory" => "")); $cont13->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont13, '', '', '');

           $cont14 = new GGFStaticfield("s5","Position",""); $cont14->initP(array( "name" => "s5","value" => "Position","isDisabled" => "","readonly" => "","mandatory" => "")); $cont14->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont14, '', '', '');

           $cont15 = new GGFEntryfield("Position","","32"); $cont15->initP(array( "maxlength" => "32","inputClass" => "1","name" => "Position","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "")); $cont15->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont15, '', '', '');

           $cont16 = new GGFStaticfield("s6","Department",""); $cont16->initP(array( "name" => "s6","value" => "Department","isDisabled" => "","readonly" => "","mandatory" => "")); $cont16->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont16, '', '', '');

           $cont17 = new GGFEntryfield("Department","","32"); $cont17->initP(array( "maxlength" => "64","inputClass" => "1","name" => "Department","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "")); $cont17->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont17, '', '', '');

           $cont18 = new GGFStaticfield("s6","Mobile",""); $cont18->initP(array( "name" => "s6","value" => "Mobile","isDisabled" => "","readonly" => "","mandatory" => "")); $cont18->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont18, '', '', '');

           $cont19 = new GGFEntryfield("Mobile","","16"); $cont19->initP(array( "maxlength" => "32","inputClass" => "18","name" => "Mobile","size" => "16","isDisabled" => "","readonly" => "","mandatory" => "")); $cont19->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont19, '', '', '');

           $cont20 = new GGFStaticfield("s6","Phone",""); $cont20->initP(array( "name" => "s6","value" => "Phone","isDisabled" => "","readonly" => "","mandatory" => "")); $cont20->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont20, '', '', '');

           $cont21 = new GGFEntryfield("Phone","","16"); $cont21->initP(array( "maxlength" => "32","inputClass" => "18","name" => "Phone","size" => "16","isDisabled" => "","readonly" => "","mandatory" => "")); $cont21->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont21, '', '', '');

           $cont22 = new GGFStaticfield("s6","E-Mail",""); $cont22->initP(array( "name" => "s6","value" => "E-Mail","isDisabled" => "","readonly" => "","mandatory" => "")); $cont22->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont22, '', '', '');

           $cont23 = new GGFEntryfield("EMail","","32"); $cont23->initP(array( "maxlength" => "64","inputClass" => "14","name" => "EMail","size" => "32","isDisabled" => "","readonly" => "","mandatory" => "")); $cont23->resetP(array( "autofocus","value","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont23, '', '', '');

           $cont24 = new GGFStaticfield("placeholder","",""); $cont24->initP(array( "name" => "placeholder","isDisabled" => "","readonly" => "","mandatory" => "")); $cont24->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newRow($cont24, '', '', '');

           $cont25 = new GGFStaticfield("s8","* input is mandatory",""); $cont25->initP(array( "name" => "s8","value" => "* input is mandatory","isDisabled" => "","readonly" => "","mandatory" => "")); $cont25->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newCell($cont25, '', '', '');

           $cont26 = new GGFFieldsetPane("Employer","",""); $cont26->initP(array( "name" => "Employer")); $cont26->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont26, '', '', '');

              $cont27 = new GGFStaticfield("s6","Company",""); $cont27->initP(array( "name" => "s6","value" => "Company","isDisabled" => "","readonly" => "","mandatory" => "")); $cont27->resetP(array( "size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont26->newRow($cont27, '', '', '');

              $cont26->newSpace();

              $cont29 = new GGFDropDown("Company_ID","",""); $cont29->initP(array( "returnIndex" => "","name" => "Company_ID","isDisabled" => "","readonly" => "","mandatory" => "")); $cont29->resetP(array( "vsize","selections","keys","value","size","extraHTML","tiptext","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont26->newCell($cont29, '', '', '');

           $cont30 = new GGFEndPane("end:Employer","",""); $cont30->initP(array( "name" => "end:Employer")); $cont30->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont26->add($cont30);

           $cont1->newP();

           $cont32 = new GGFControlPane("_buttonarea","",""); $cont32->initP(array( "name" => "_buttonarea")); $cont32->resetP(array( "myContainer","controls","disabled","inTable","tabindex","bodyExtraHTML","frameID","paneType","isFrameset","style","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont1->newTable($cont32, '', '', '');

              $cont33 = new GGFPushbutton("_OK","OK","60"); $cont33->initP(array( "callback" => "eventOK","validator" => "dataPlausible","name" => "_OK","value" => "OK","size" => "60","extraHTML" => " width:80px; overflow:hidden ","tiptext" => "save data in database")); $cont33->resetP(array( "myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont32->add($cont33);

              $cont32->newSpace();

              $cont35 = new GGFImagePushbutton("getVCF","get VCard",""); $cont35->initP(array( "imageURL" => "GGFIcons/GGFmail.jpg","callback" => "eventVCFDownload","name" => "getVCF","value" => "get VCard","extraHTML" => "vertical-align:middle;","tiptext" => "download a VCF file to import into your local phonebook or contact list","isDisabled" => "","readonly" => "","mandatory" => "")); $cont35->resetP(array( "valuex","valuey","validator","size","myContainer","tabindex","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont32->add($cont35);

              $cont32->newSpace();

              $cont37 = new GGFPushbutton("_Cancel","Cancel","60"); $cont37->initP(array( "callback" => "eventClose","name" => "_Cancel","value" => "Cancel","size" => "60","tiptext" => "do not save data in database")); $cont37->resetP(array( "validator","extraHTML","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

              $cont32->add($cont37);

           $cont38 = new GGFEndPane("end:_buttonarea","",""); $cont38->initP(array( "name" => "end:_buttonarea")); $cont38->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

           $cont32->add($cont38);

         $cont39 = new GGFEndPane("end:_mainform","",""); $cont39->initP(array( "name" => "end:_mainform")); $cont39->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

        $cont1->add($cont39);

     $cont40 = new GGFEndPane("end:_main","",""); $cont40->initP(array( "name" => "end:_main")); $cont40->resetP(array( "value","size","extraHTML","tiptext","myContainer","tabindex","isDisabled","readonly","mandatory","Foreground","Background","fontFamily","fontSize","fontStyle","lineHeight"));

     $cont0->add($cont40);

     //****RD generated code, do not touch**********

      

     //*H2--end of generated code, do not touch code above. Use RD to update ---

   } // end initWindow    

     //*H3--RD-generated event handlers below. do not remove this line.         

               

   /**

   * RD generated function

   * Validator for control Pushbutton(_OK, OK, 60,  width:80px; overflow:hidden ) 

   *

   * @todo replace RD-generated template-code by desired functionality

   * @return boolean

   *

   */

   public function dataPlausible() {

      // ---- begin template code ----

     return TRUE;

     // ---- end template code ------

   }

             

   /**

   * RD generated function

   * event handler for control Pushbutton(_OK, OK, 60) 

   *

   */

   protected function eventOK() {

      // handles OK event

     $this->ok = $this->validateData();

     $this->saveModifications();

     if ($this->ok) {

        $this->eventClose();

     }

   }

             

   /**

   * RD generated function

   * event handler for control Pushbutton(_Cancel, Cancel, 60) 

   *

   */

   protected function eventClose() {

      // handles Cancel event

     parent::eventClose();

   }

  

   /**

   * create a VCard for the person and download it

   *

   * @global GGFDatabase $db

   */

   protected function eventVCFDownload(){

     global $db;

     $contact = $this->myModel[1];

    

     $et = $this->ERModel->entityTypeNamed('Person');

     $res = $et->relatedEntitiesVia($contact, "Company employs Person");

       

     if ($db->OK()) {

        $company = $res[0];

        header("Content-disposition: attachment; filename=".$contact["LastName"].'_'.$contact["FirstName"].".vcf");

        header("Content-type: application/octet-stream");

       

        echo sprintf(

"BEGIN:VCARD

VERSION:2.1

N;LANGUAGE=de:%s;%s

FN:%1

ORG:%s;%s

TITLE:%s

TEL;WORK;VOICE:%s

TEL;CELL;VOICE:%s

ADR;WORK;CHARSET=Windows-1252:%s

EMAIL;PREF;INTERNET:%s

REV:%s

END:VCARD",

           $contact["LastName"],$contact["FirstName"],

           $contact["Title"].' '.$contact["FirstName"].' '.$contact["LastName"],

           $company["CompanyName"], $contact["Department"],

           $contact["Title"],

           $contact["Phone"],

           $contact["Mobile"],

           ";;".$company["Street"].";".$company["City"].";;".$company["ZIPCode"].";".$company["Country"],

           $contact["EMail"],

           $contact["Person_ID"]

        );

     }

     exit(0);

  }

} //end RD generated class CTSContactUpdateDialog

?>


file: /htdocs/CTS/CTSContactUpdateDialog.php

The event handler functions are similar to the classes above. The function eventVCFDownload will create a VCard file from the contact data and will offer it for download to the client. See chapter CTSContactUpdateDialog .

6         Deployment Procedures

Assuming, you just downloaded the contacts application. So how to you get it running? There are several options.

·         Execute it locally on your PC

·         Execute it on a web server via the Internet

Refer to the following sub-chapters.

6.1      Execute the Contacts Application on your PC

This describes how to get the contacts application running on your PC. If you have already installed a web server and a MySQL database engine locally, skip the next chapter.

6.1.1     Setup Web Server and Database

The most easy way to install a web server and a database is to get a predefined WAMP (or LAMP for Linux) package from the Internet. I prefer XAMPP. You find it at http://www.apachefriends.org/en/xampp-windows.html   and http://www.apachefriends.org/en/xampp-windows.html#646  (or refer to http://www.apachefriends.org/en/xampp-linux.html for Linux).

Check the Installation instructions at http://www.apachefriends.org/en/xampp-windows.html#522 . Installation is a matter of less than a minute.

Note: Starting XAMPP will not work, if you already have a webserver installed on your machine. Some apps come with a built-in webserver. In this case you must assure that this is shut-down before starting XAMPP.

6.1.2     Put the Application on the Local Webserver

Your web server will be accessible from a web browser via the URL http://localhost this per default will show the contents of the “documents”-directory of the web server. In an XAMPP installation you can find that directory at \XAMPP\htdocs.

Expand the downloaded application files to that directory. It expands into the directories

o   htdocs

§  GGF

§  GGFIcons

§  CTS

Refer to chapter “5.4 Code” for more details on how to put the files for the contacts web application there. The web application then can be started via the URL http://localhost/index.php .

However you need to create the database before the web application can start. Some database tables have to be created. For that you can use the interactive PHPmyAdmin tool that comes with XAMPP. It can be invoked via http://localhost/phpmyadmin/ . There select the SQL Tab.

PHPMyAdmin: The SQL Tab

Paste the SQL script contacts_database.SQL from the download file here. It will create all database structures that are needed.

One last step is to adapt the application setup file GGF/GGFSetup.php. Locate the following 4 lines:

$dbhost = "localhost";        // See GGFDatabase. May be of the form www.server.domain:port

$dbuser = "root";          // See GGFDatabase, you should change this for production

$dbpw = "";                // See GGFDatabase, you should change this for production

$dbname = "Contacts";         // See GGFDatabase

File GGFSetup.php: Default Parameters for Database Access to be replaced

In most cases the database server will be the same machine as the web server and the database engine can be accessed via localhost. But that depends on your server setup. You need to change the database user entry dbuser. For security reasons this should never be the MySQL default user, named root. And of course it should not be an empty password.

You can use SQL to create a new database user

CREATE USER dbuser IDENTIFIED BY PASSWORD 'password';

The password of the root user is changed with:

SET PASSWORD FOR root = PASSWORD('some password');

The access rights for this database user are defined with:

GRANT ALL ON contacts.* TO dbuser ;

6.2      Execute the Application on a Web Server in the Internet

If you have already a web server in the Internet skip the next chapter.

6.2.1     Setup a Web Server in the Internet

There are a lot of providers. The procedure will be slightly different but similar as shown in the sample below. If you want to use the offer by www.freewebhosting.com you have to do the following:

Set up sample Web Site:  Create a sub-domain on a sample free Web Hoster Site

Set up sample Web Site:  Create your Account

 

Set up sample Web Site: Receive User IDs and Passwords

Finally you got a

·         www server ( in our sample http://contacts.freetzi.com ) with userid and password for

·         access to the maintenance application (here “Account Manager” at http://freetzi.freewebhostingarea.com ) and access to an

·         FTP server (in our sample FTP://freetzi.com) with userid and password.

·         Access to a database management application (in our sample  http://freetzi.freewebhostingarea.com/pma/) with a userid and password provided via the account manager.

The FTP server gives you read/write access to upload scripts and other files to your www server .

The  database management application allows to create and initialize tables that your application will use.

6.2.2     Put the Application on the remote Web Server

It depends on the provider how the documents directory on the web server can be reached. In most cases it can be done via FTP. You can use FTP tools like Filezilla to transfer files to it. Some providers also have some web based file transfer solutions. But for Windows users it is probably more convenient to use the Windows file explorer.

Just enter the URL of your FTP server (in our sample FTP://freetzi.com) in the address line of the Windows file explorer.

FTP Window: Access to Web Server with the File Explorer

You will be prompted for your FTP userid and password for your FTP server account (see above).

As soon as you are connected you can create directories and copy files there just as you would do it in a local directory. Locate the “documents”-root directory of the web server.

Then expand the downloaded application files to that directory. Copy them into the directories

o   Documents-root

§  GGF

§  GGFIcons

§  CTS

Note: Do not forget to set the access rights for the subdirectories. Web-users must not have read, write or listing rights to the GGF folder and the CTS folder. This is done by uploading a .htaccess file to the folder.

deny from all


file: /htdocs/GGF/.htaccess

deny from all


file: /htdocs/CTS/.htaccess

Refer to chapter “5.4 Code” for more details on how to put the files for the contacts web application there.

FTP Window: Web Server with installed Files

The web application then can be started via the URL of your server . For instance: http://contacts.freetzi.com/index.php .

However you need to create the database before the web application can start. Some database tables have to be created. Most web space providers offer the interactive PHPmyAdmin tool. Check the documentation how to invoke it. In our sample it can be invoked via http://freetzi.freewebhostingarea.com/pma/ .

Database Management with PHPMyAdmin

Now you could select the SQL tab and paste the SQL script contacts_database.SQL from the download archive here. But many web hosters restrict the database name. In this case you cannot use the database name “CONTACTS” and  it is not possible to execute the first three commands of the SQL script:

Drop database if exists contacts;

CREATE DATABASE CONTACTS;

USE CONTACTS;

SQL Script: contacts_database.SQL – the first 3 lines

You have to do the following. First delete the first three lines from the SQL script.

Then activate the database. In our sample the database 407421 already exists and it is listed on the left side of the window. Just double click it to use it. Then click the SQL tab.

PHPMyAdmin: The SQL Tab

Now you can paste the SQL script contacts_database.SQL here.

PHPMyAdmin Window: Database Creation

Then click “Go”. It will create all database structures that are needed.

One last step is to adapt the application setup file GGF/GGFSetup.php. Locate the following 4 lines:

$dbhost = "localhost";        // See GGFDatabase. May be of the form www.server.domain:port

$dbuser = "root";          // See GGFDatabase, you should change this for production

$dbpw = "";                // See GGFDatabase, you should change this for production

$dbname = "Contacts";         // See GGFDatabase

File GGFSetup.php: Default Parameters for Database Access to be replaced

In most cases the database server will be the same machine as the web server and the database engine can be accessed via localhost. But that depends on your web hoster. You need to change the database user entry dbuser. For security reasons this should never be the MySQL default user, named root. The web hoster should provide you the database user and password (dbpw).

$dbhost = "localhost";     // See GGFDatabase. May be of the form www.server.domain:port

$dbuser = "407421";           // See GGFDatabase, you should change this for production

$dbpw = "XYZ123";          // See GGFDatabase, you should change this for production

$dbname = "407421";           // See GGFDatabase

File GGFSetup.php: Sample Parameters for Database Access

Now the web application then can be started via the URL of your server . For instance: http://contacts.freetzi.com  .

If you get error messages like this:

Fatal error: require(): Failed opening required 'GGF/GGF.php' (include_path='.:/usr/share/pear:/usr/share/php') in /home/vhosts/contacts.freetzi.com/index.php on line 13

Error Message: Access Rights for Web Server/PHP Task missing

and you are sure that the file exists on the server this is usually an access permission problem. Probably the host FTP server that was used to create your files on the server ran with a user permission different from the web server task. You can fix that easily. In the FTP window open the context menu of the file or directory and enable read access for all.

NOTE: The file read access permission  is only for server tasks, not for internet access. Your .htaccess file (see above) will prevent users from reading your source code and setup data.

7         Downloads

Download all the source code for the contacts application here.

For the most recent versions of the GGF Framework see here.