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.
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.
2 Sample Application - Requirements
4 An Object-Oriented Web-Application Architecture
4.4.3 Opening a Dialog from a Window
4.4.5 Closing a Dialog and returning to the Parent-Window
5 Sample Application Implementation
5.2 The physical Database Design
5.2.3 “Company employs Person” Relationship
5.4.1 Preparing the Framework Directory
5.4.2 Preparing the Framework Icons Directory
5.4.4 Preparing the Application Directory
6.1 Execute the Contacts Application on your PC
6.1.1 Setup Web Server and Database
6.1.2 Put the Application on the Local Webserver
6.2 Execute the Application on a Web Server in the Internet
6.2.1 Setup a Web Server in the Internet
6.2.2 Put the Application on the remote Web Server
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
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.
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.
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.
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.
The following pictures show the interaction and
the information flow between of the most important elements of the
architecture.
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.
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.
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.
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).
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.
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 |
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.
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.
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 |
+------------+-------------+--------+-----------+---------+---------+---------------------+
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
+-----------+-----------+----------+------------+----------+---------------+------------------
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.
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
Here we
describe the design and implementation of the application’s graphical user
interface.
The user
starts the application by entering
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.
We create
the GUI layouts with the ReinHTML Dialog Designer (http://www.ReinHTML.eu )
To keep
things simple it consists just of a menu bar and a greeting message.
Main Window
The login
dialog offers entry fields for user id and password.
Login Dialog
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.
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.
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.
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
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.
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).
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.
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
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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
.
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.
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.
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.
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 ;
If you have
already a web server in the Internet skip the next chapter.
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.
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.
Download
all the source code for the contacts application here.
For the
most recent versions of the GGF Framework see here.