appgini-documentation
appgini-documentation
Contents
AppGini projects 16
What is an AppGini project? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
How do I start a new project? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
The project name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
The project window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
How do I save a project? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Auto save of projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Opening an existing project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Advanced: The project file format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1
Toolbar > Generate PHP Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Toolbar > Preferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Toolbar > Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Toolbar > About AppGini . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Toolbar > Ideas and tips on our Twitter page . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Toolbar > Learn to use Appgini through our YouTube playlist . . . . . . . . . . . . . . . . . . 27
Toolbar > Exit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Project Browser Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Table properties > Table view title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Table properties > Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Table properties > Records per page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Table properties > Show quick search box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Table properties > Default sort by,Descendingly . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Table properties > Allow sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Table properties > Allow filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Table properties > Allow saving data to CSV files . . . . . . . . . . . . . . . . . . . . . . . . . 28
Table properties > Allow print-view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Table properties > Allow users to save filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Table properties > Hide link in homepage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Table properties > Allow mass delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Table properties > Filter before showing table view . . . . . . . . . . . . . . . . . . . . . . . . 29
Table properties > Hide link in navigation menu . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Table properties > Show record count in Homepage . . . . . . . . . . . . . . . . . . . . . . . . 29
Table properties > Table group . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Table properties > Table template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Table properties > Detail view title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Table properties > Redirect after insert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Table properties > Display a link to children records from . . . . . . . . . . . . . . . . . . . . . 29
Table properties > Default focus field . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Table properties > Enable detail view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Table properties > Allow detail print-view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Table properties > Hide ‘Save As Copy’ when editing . . . . . . . . . . . . . . . . . . . . . . . 30
Table properties > Allow adding new records from Homepage . . . . . . . . . . . . . . . . . . . 30
Table properties > Delete records even if they have children records . . . . . . . . . . . . . . . 30
Table properties > Display detail view in a separate page . . . . . . . . . . . . . . . . . . . . . 30
Table properties > Keep action buttons visible while scrolling down . . . . . . . . . . . . . . . 31
Table properties > Table technical documentation . . . . . . . . . . . . . . . . . . . . . . . . . 31
Table properties > Edit Technical Documentation . . . . . . . . . . . . . . . . . . . . . . . . . 31
Table properties > Technical documentation preview . . . . . . . . . . . . . . . . . . . . . . . . 31
Table properties > Parent/Children settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Table properties > Table icon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Search box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
File menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Tables menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Project menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Add-ons menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Help menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Copy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Paste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Toggle Highlight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Activate/deactivate help for toolbar icons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2
What about field properties? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3
Understanding lookup fields 62
How will a lookup field appear in the generated application? . . . . . . . . . . . . . . . . . . . 62
Displaying lookup fields as an options list (radio buttons) . . . . . . . . . . . . . . . . . . . . . 64
Related screencasts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
AppGini lookup fields and master detail pages . . . . . . . . . . . . . . . . . . . . . . . . 65
Using auto-fill look-up fields to automatically populate fields from another table . . . . . 65
Creating cascading drop downs with AppGini . . . . . . . . . . . . . . . . . . . . . . . . . 65
Displaying child info (count + add new) in the table view . . . . . . . . . . . . . . . . . . 65
Calculated fields 68
What are calculated fields? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Conditions for a field to become a calculated field . . . . . . . . . . . . . . . . . . . . . . . . . . 68
How to configure a calculated field . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Special variables for use in calculated field queries . . . . . . . . . . . . . . . . . . . . . . . . . 70
The query helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Debugging your query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Batch-updating calculated fields via command line . . . . . . . . . . . . . . . . . . . . . . . . . 72
Basic examples of calculated fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Calculate subtotal for an invoice line by multiplying unit price and quantity . . . . . . . . 73
Automatic code by concatenating 2 or more fields . . . . . . . . . . . . . . . . . . . . . . . 73
More advanced examples of calculated fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Updating batch status to ‘Consumable’, ‘Warning’ or ‘Expired’ based on expiry date . . . 73
Invoice subtotal by summing subtotals of invoice items . . . . . . . . . . . . . . . . . . . . 74
Looking for more help with queries? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Known issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Setting the child record owner to match the owner of its parent record 89
About this feature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Updating the owner of existing child records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
A practical example: Set the country sales manager as the owner of all orders and order items
of that country . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
4
The admin interface 104
Member groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Sample scenario: A content publishing application . . . . . . . . . . . . . . . . . . . . . . 104
Accessing the admin homepage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Managing groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Managing members . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Managing records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
Other features of the admin interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
5
Parameters: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Return value: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
child_records_config() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Parameters: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Return value: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
6
Order Items table joined with Products table . . . . . . . . . . . . . . . . . . . . . . . . . 140
7
Third party resources used by AppGini applications 163
8
AppGini Online Guide
Introduction to AppGini
AppGini is a powerful tool designed to simplify the creation of web-based database applications. Even
without any programming skills, you can quickly design and generate fully functional web interfaces for
your business applications. Simply define your data structure, configure a few settings, and with a click
of the ‘Generate’ button, your application is ready!
9
• Customization: Offers extensive customization options for the application’s appearance and
behavior, adapting to specific needs.
• Media Handling: Supports the uploading of images and files, as well as Google maps, Youtube
videos, and rich text fields, enriching the data records with multimedia content.
Supported Technologies
AppGini applications are generated in PHP and are designed to connect with MySQL/MariaDB databases.
This selection leverages the widespread support and compatibility of PHP and MySQL/MariaDB across
different hosting environments, ensuring that the applications you create are robust and deployable on
virtually any server without requiring prior configuration.
Getting Started
Dive into the world of AppGini without needing to learn the intricacies of PHP or database management.
The intuitive interface and comprehensive feature set allow you to start creating immediately. Whether
you choose to explore the documentation further or jump straight into using the software, AppGini
promises a smooth and productive experience.
Explore the potential of AppGini and start building your web database applications today. Join us as we
uncover the powerful capabilities and ease of use that AppGini offers!
10
Install AppGini on Windows
System requirements
AppGini runs on Windows 7 through Windows 11. It’s compatible with both 32-bit and 64-bit versions
of Windows. It’s also compatible with Windows Server editions.
It’s also possible to run AppGini on MacOS or Linux using a virtual machine or a compatibility layer like
Wine or Crossover. You can find more information on this in the related topics section below.
Installing AppGini
After downloading AppGini (the trial or pro version), you can install it by following the steps below:
1. Double-click the downloaded setup file.
11
Figure 3: Click ‘Yes’ when prompted by User Account Control
12
Figure 5: Setup progress
13
If you can’t see the shortcut in the start menu, you can type AppGini in the search box to find it.
6. The first time you run AppGini, you’ll be asked to choose how you want to begin your work. You
can choose to create a new project, open an existing project, among other options.
Uninstalling AppGini
To uninstall AppGini, you can use the ‘Add or remove programs’ feature in Windows. Here’s how:
1. Open the ‘Add or remove programs’ window by typing ‘Add or remove programs’ in the start menu
search box and clicking the result.
14
Figure 8: How do you want to begin your work?
2. Find ‘AppGini’ in the list of installed programs, click it, and then click ‘Uninstall’.
3. Follow the steps in the uninstall wizard to remove AppGini from your computer.
After uninstalling AppGini, you can delete the installation folder to remove any remaining files.
Related topics
• Installing AppGini on MacOS or Linux
• Installing AppGini on a remotely-hosted Windows VM
15
AppGini projects
16
Figure 10: New project window
17
Advanced: The project file format
AppGini project files have the extension .axp. They are XML files that store all the information about
your project, including the tables, fields, and their properties, as well as the project settings. You can
open an .axp file in a text/code editor to see its contents. You can also make modifications to the file,
but be careful not to corrupt it, as this may prevent you from opening the project in AppGini.
18
Getting help while you work
In addition to this online help file, there are several additional help resources: there is the continuously
expanding tips and tutorials section, AppGini FAQs, the context help inside AppGini, and the AppGini
community forums. Context help is a handy tool for obtaining help while you work with your projects
without having to be online.
Figure 11: F1
To activate context help, press F1. This would open the help section at the right side of the AppGini
window. This section displays help on whatever element has the focus at the moment. You can use the
mouse or the Tab key to move the focus between all the elements of the project window. To hide the
help section, press F1 again.
Pressing F1 key opens the help section in the right-side panel. This panel displays context-sensitive help
based on the currently focused element.
19
Figure 13: AppGini context help based on the currently focused element
20
Project properties pane
21
Working with tables
22
What about table properties?
Click with the mouse on any property and press F1 to obtain help about its function.
23
Table properties pane
24
<area shape="rect" href="#table-template" coords="270,454,699,491" />
<area shape="rect" href="#detail-view-settings" coords="226,508,1085,532" />
<area shape="rect" href="#detail-view-title" coords="253,536,611,560" />
<area shape="rect" href="#redirect-after-insert" coords="227,562,611,587" />
<area shape="rect" href="#display-a-link-to-children-records-from" coords="263,590,611,661" />
<area shape="rect" href="#default-focus-field" coords="621,536,1062,561" />
<area shape="rect" href="#parent-and-children-setting" coords="621,562,841,660" />
<area shape="rect" href="#enable-detail-view" coords="349,666,611,682" />
<area shape="rect" href="#allow-print-view" coords="349,685,611,702" />
<area shape="rect" href="#hide-save-as-copy-when-editing-records" coords="349,706,611,723" />
<area shape="rect" href="#allow-adding-new-records-from-homepage" coords="349,727,611,744" />
<area shape="rect" href="#delete-records-even-if-they-have-children-records" coords="620,666,915,
<area shape="rect" href="#display-detail-view-in-a-separate-page" coords="620,685,915,702" />
<area shape="rect" href="#keep-action-buttons-visible-while-scrolling-down" coords="620,706,915,7
<area shape="rect" href="#appgini-search-box" coords="12,962,210,990" />
<area shape="rect" href="#table-technical-documentation" coords="226,758,1085,784" />
<area shape="rect" href="#edit-technical-documentation" coords="236,791,347,819" />
<area shape="rect" href="#technical-documentation-preview" coords="236,822,1077,931" />
</map>
25
Toolbar > New Table
Click on this icon to add a new table to your database.
back to top
26
Toolbar > Ideas and tips on our Twitter page
Click on this icon to visit (and optionally subscribe to) our Twitter feed, which includes a lot of helpful
resources and tips to get the most out of AppGini.
back to top
27
Table properties > Default sort by,Descendingly
If you’d like records in the table view sorted by default, select the field that you want to sort by from the
‘Default sort by’ drop down. The default sorting direction is ascendingly (A-Z, 0-9) unless you check the
‘Descendingly’ option. If you don’t want any default sorting, select ‘None’ from the ‘Default sort by’ drop
down.
back to top
28
back to top
29
an artists table, and a songs table. The songs table has an ‘Artist’ field whose parent table is the artists
table. Using this feature, users who select an artist from the artists table will see a ‘songs’ link in the
detail view of the selected artist. Clicking on that songs link, users will see all the songs that belong to
the selected artist.
back to top
30
Table properties > Keep action buttons visible while scrolling
down
For long detail view forms, we recommend checking this option so that the action buttons (Save changes,
Back, Print preview, Delete, .. etc) are always visible to the user, without having to scroll up/down to
find them.
back to top
Search box
You can use this box to search for a specific table/field by typing its name or part of it then clicking the
lens icon. Click it again to move to the next matching table/field.
back to top
File menu
Includes commands for starting a new project, opening an existing one, importing from an existing
MySQL database or a CSV file, and saving current project.
back to top
31
Tables menu
Includes commands for adding and deleting tables and fields from the current project.
back to top
Project menu
Includes commands for generating the web application, and for changing the application theme.
back to top
Add-ons menu
Open this menu to see a list of available AppGini add-ons and plugins that can enhance your project and
add more functions to it.
back to top
Help menu
Includes various help resources
back to top
Copy
Copies the selected table or field to the clipboard
back to top
Paste
Pastes a copy of the field or table in the clipboard to the project
back to top
Toggle Highlight
Marks/unmarks the current table or field with a yellow background for easily returning to it later on.
back to top
32
Working with table fields
33
Can I clone/copy a field?
You can copy a field from another AppGini project or from the same project using the Copy and Paste
icons from the top toolbar.
34
Field properties pane
Caption
The field caption is the title of the field as it would appear in the table view and the detail view of its
table in the generated web application.
35
Description
This section allows you to provide an optional description for the field.
Data Type
(Dropdown menu) This menu allows you to choose the type of data the field will store in your application’s
database.
Length
Specifies the maximum size of data that can be stored in this field. The way Length property works
depends on the data type you choose for the field. Please read the data type guidelines displayed at the
bottom of the Properties window for more information.
Precision
This property works with decimal data types only (e.g. Float, Double and Decimal). It specifies how
many digits to display after the decimal point.
Default
If you specify a default value, it will be placed in any records that the user creates while leaving this field
empty (if the user does want an empty field, she can delete the default value after the record has been
added and then update the record). If you click the ‘Automatic Value’ button at the right of the ‘Default’
box, you can select an automatic value to be inserted into the field when the record is created or changed.
This works only if the field is set as read-only.
36
Automatic value
You can set a field to contain one or more automatic values by specifying them from the drop-down menu.
The field will be read-only and its value set automatically by the app.
Read Only
If checked, this field will be displayed in the detail view but not editable. This is useful in many scenarios,
including forcing a default value (or automatic value), calculated fields, fields that are populated using
custom code or a third party app, .. etc.
Primary key
If you check this option, the current field will be the primary key of the current table. In AppGini, Every
table must have a single primary key field in order to uniquely identify records of the table (multi-field
primary keys are not supported by AppGini).
Zero Fill
Check this property to left-pad numeric data types with zeros when they are displayed, so that the total
number of digits matches the field’s ‘Length’ property. For example, if the field length is 5 and is set as
zero-fill, a field value of 22 would be displayed as 00022.
37
Figure 21: AppGini_bI8MW1vvBU
Required
If you check this property, users must provide a value for this field before submitting the form. If a user
attempts to submit a record while leaving a required field empty, they’d see an error message similar to
the one below. If you set the field as read-only, auto-increment or hidden in detail view, this property
will be ignored.
Auto increment
While defining the properties for the ID field (likely named “id”), look for an option labeled “Auto
Increment”. This option appear as a checkbox with an “Auto Increment” selection.
Unique
The data values in this field cannot be repeated if you check this property. This is similar to the Primary
key property except that unique fields can be empty (null). If a user tries to enter a value that already
exists into a unique field, they’ll see an error message like the one below.
38
Figure 23: AppGini_BNj6i82r3r
Text area
If checked, this field will be displayed as a text area in the detail view. This is useful if you want to allow
multiple lines in a field, for example to enter addresses or notes.
39
Figure 26: AppGini_NVMbCU6ZpJ
Unsigned
This property works for numerical data types only. If you check it, the field will not accept negative data
values.
Do not filter
If checked, the current field is not displayed in the filters page. Thus, users will not be able to use this
field to filter data.
Binary
This property works only with Char and VarChar data types. It forces the field to behave like a binary
value rather than the default text behavior. For example, the list (a,G,x,E,A,g) is sorted as (a,A,E,g,G,x)
if it is treated as text values, and (A,E,G,a,g,x) if it is treated as binary values.
40
Check box
If you check this property, the field will be displayed as a check box, where users can either check or
uncheck it.
41
Figure 30: AppGini_o49PVvFmMq
Alignment
Specifies the alignment of the field in the table view.
42
Figure 31: AppGini_lJl6g3oWof
43
Figure 34: AppGini_gG1TJkJGDp
to the storage of the server running your AppGini app. In the configuration window, you can specify the
maximum file size in KB that users are allowed to upload. Also, you can specify whether you want to
delete the file from the server if the record is deleted. You can also configure how the field is displayed to
users.
44
Figure 35: AppGini_TNckzO2MHt
45
Figure 37: AppGini_3ff5I6qgqx
46
Lookup field > Parent caption field 1
If this is a lookup field (foreign key), select the field to be used as the first part of the caption from this
menu. A parent caption is the caption or alias that appears to the user in the lookup drop-down. For
more information about lookup fields, refer to the section ‘Understanding lookup fields’ in the online help.
47
Figure 40: AppGini_jLCg6vx72k
48
Figure 41: AppGini_2VmbYdPyMx
49
Figure 42: AppGini_WiL0WHggir
50
Figure 44: AppGini_tkO9m89sRq
51
Figure 47: AppGini_Cc7Gs5XGGu
this option, the field data type must be a TEXT or BLOB type. The screenshot below shows an example
where the user has selected 2 values form the drop-down.
Calculated field > Automatically calculate the value of this field using the
following SQL query
This is an advanced feature that requires some SQL knowledge. Instead of receiving user input, or a
default value, calculated fields are populated by running the specified SQL query, and storing the returned
value. The SQL query should return a single value: single row and single column. Use the ‘Query
helper’ button to open an advanced query editor that makes it much easier to build queries. The list of
variables at the right allows you to insert special placeholders in the SQL query, to be replaced by their
corresponding values before running the query. For more details and examples, please refer to the online
help.
52
Figure 48: AppGini_3IrB4EUtGC
53
Figure 50: AppGini_8lyktlLxci
54
The Media Tab
This tab allows you to configure your field to be displayed as a web-link, an image, a file, a google map or
even a YouTube video.
Link option
Configure the way your field behaves when clicked. It can be configured to open the detail view of the
current record, a URL, an email link, or not be clickable at all.
If you configure the field to display as a web-link and the user clicks that field, the link is opened in a
new window.
55
Figure 52: Link as displayed in the table view
56
Figure 55: Image thumbnail in the table view
57
Figure 58: File upload field in the table view
Google Maps
You can add a google map to your records, simply by creating a new field having any textual data type
and setting the field length to 200 or more.
Google Maps require a Google API key to work correctly. You can add one by simply clicking the settings
dialog to get a key. This allows you to insert interactive maps into your application.
You can configure how to display the Google map in the detail view as well as in the table view. Choose
the size that meets your requirements.
In the table view, the map is displayed as a thumbnail image.
YouTube video
This field accepts a YouTube URL and displays it as a movie in the detail view.
You can configure how to display the YouTube video in the detail view and the table view. Here is an
example of how the YouTube video is displayed in the detail view.
And this is how the YouTube video is displayed in the table view.
58
Figure 60: Google Maps API key in settings
59
Figure 62: Google Map in the table view
60
Figure 64: YouTube video in the detail view
61
Understanding lookup fields
A lookup field (also known as a foreign key) is how AppGini links 2 fields from 2 tables together. For
example, let’s say that our database contains a products table, a suppliers table, and a product categories
table.
Tip: The list of video tours to the left of this page contains some helpful videos explaining several features
of lookup fields. It takes less than 20 minutes to watch them all. So, please do.
The products table stores data about each product, including the supplier of the product, and the product
category. Since suppliers and categories are stored in their own tables, the products table should look up
those two tables when storing supplier and category data for each product.
The products table is thus a child table that has 2 parent tables: suppliers and categories. To achieve
this, we should create a field in the products table to hold supplier data, and another one to hold category
data. Each of these two fields is called a lookup field. We can define its properties in the Lookup field
tab of the field properties pane, which is shown above. Lookup fields are also known as foreign key fields.
62
Figure 67: Lookup field in the detail view of the generated application
63
primary key yet, you should change the data type of the lookup field manually to match the primary key
once you create one.
Figure 69: A lookup field displayed as radio buttons in the detail view
AppGini makes it possible to display the lookup field as an options list (radio buttons list) rather than a
drop-down menu, as shown above. To do so, simply check the “Show as radio buttons” option in AppGini,
as shown below.
## Parent/Children settings
When you configure a field as a lookup field, the parent table you specify for that field can, in turn, be
configured to show some special behavior. In AppGini, if you click the parent table, you should see a
button labeled Parent/Children settings, like the one to the right.
Clicking that button displays the Parent/Children settings window - as shown below, which allows you to
enable displaying child records below the detail view of the parent record.
This window lists all child tables of the current table (that is, tables that contains a lookup field where
the parent table is set to the current table). Select a child table from the grid at the left to configure its
related behavior in the parent table. An example of parent and child tables is the orders and order_details
tables. The orders table is a parent table of order_details. Every order saved in the orders table would
have one or more items saved in the order_details table.
Show tab below detail view would display a list of child records below the detail view when you select
a parent record. For example, this is how an order looks like in the detail view, where the order items are
listed at the bottom.
Copy child records when copying parent would copy child records if the user copies the parent
record by clicking the Save As Copy button. The lookup field in the copied child records would be
automatically set to the new parent record. This is a very handy feature for scenarios like duplicating an
64
Figure 71: Parent/Children settings window in AppGini
order and all its order items, a product, and all the items in its bill of materials, . . . etc. It saves users
the time to manually add child records from scratch.
Copying child records requires cURL PHP extension to be installed and enabled on your server.
As of AppGini 5.81, a new configuration parameter, $host, was added in config.php. The value of this
parameter is set by default to the host name of your server as automatically detected by PHP. However,
if copying of child records is not working (and curl PHP extension is enabled), you might need to check
and change this value manually by editing the config file. PHP might not detect the internal host name
correctly in cases where servers are behind NAT or load balancers, Docker containers, or similar network
configurations.
Display child info in the table view. Starting with AppGini 23.15, the Parent/Children settings
dialog includes the option Show count of children in table view. Enabling this option displays the
count of child records in the table view. You can also add new child records directly from the table view
by enabling the option Add new children from table view. The screenshot below shows how both
options would be displayed in the table view of the orders table, showing the count of order items in each
order, and allowing users to add new items directly from the table view of orders.
See also the related video tutorial
Related screencasts
AppGini lookup fields and master detail pages
Using auto-fill look-up fields to automatically populate fields from another
table
Creating cascading drop downs with AppGini
Displaying child info (count + add new) in the table view
Your browser does not support the video tag.
65
Figure 72: Child records displayed below the detail view of a parent record
66
Figure 73: Displaying child info in the table view
67
Calculated fields
68
Figure 74: Calculated field error in AppGini.
69
3. Navidate to the Calculated field tab and check the option Automatically calculate the value of this
field using the following SQL query
4. Type the SQL query for calculating the field value.
Important note:
Valid SQL queries for calculated fields must be SELECT queries that return a single value. The returned
value should be of the same data type as the calculated field. For example, this is a valid query to
calculate the subtotal of an invoice line by multiplying the unit price by the quantity:
SELECT quantity * unit_price FROM invoice_items WHERE id='%ID%'
70
The above special variables make it easy to write flexible queries that depend on the current user, group,
or record. For example, you can use %USERNAME% to calculate the total sales made by the currently
logged user, or use %GROUP% to calculate the total sales made by the group of the currently logged user.
%TABLENAME% and %PKFIELD% can be used to write generic queries that can be copied and pasted to other
tables without modification.
The query helper window allows you to quickly insert various special code pieces into your SQL query.
Just place the cursor at the position where you want to insert the piece of code, then choose the code you
want to insert from the boxes at the right or the bottom of the query box, then click the Insert button.
You can insert special variables (as explained above), field names, SQL functions, or JOIN statements
that join the table of the calculated field with one or more of its parent or child tables. This not only
saves you time for manually typing these snippets, but also reduces typos and syntax errors.
Of course, using calculated fields requires some knowledge of SQL language, specifically SQL SELECT
statement. There are many great SQL tutorials available online, as well as the official MySQL reference.
We’ll also list a few examples below that cover some widely-used scenarios. You can also ask for help
from other users on our forum.
In the screencast below, we create a new ‘Sales’ field in the clients table, and configure it as a calculated
field that displays the total of sales made to each client, by retrieving the sum of her paid invoices total.
71
We use the Query helper window to quickly and precisely write the query, including the join between the
clients and invoices tables.
Examples:
php cli-update-calculated-fields.php
Updates all records of all tables. Not recommended for large databases.
72
php cli-update-calculated-fields.php -x clients
Updates all records of all tables excluding clients table.
Calculate subtotal for an invoice line by multiplying unit price and quantity
Let’s assume you have an app for managing invoices. The invoice header (invoice number, due date,
customer info, . . . etc) is stored in the invoices table. Invoice lines (item, quantity, unit price, subtotal)
are stored in the invoice_items table. You’d like to have the subtotal field automatically calculated
when adding or editing an invoice line.
To set up this calculation, we’d check the read-only option for the subtotal field, then set it as a
calculated field, and use this SQL query for calculating its value:
SELECT
`invoice_items`.`unit_price` * `invoice_items`.`quantity`
FROM `invoice_items`
WHERE `invoice_items`.`id` = '%ID%'
73
To do so, we should configure the status field as read-only, calculated field, and use a query like this for
the calculation:
SELECT IF(
DATEDIFF(`expiry_date`, NOW()) > 30,
'Consumable',
IF(
DATEDIFF(`expiry_date`, NOW()) > 0,
'Warning',
'Expired'
)
) FROM `batches`
WHERE `id` = '%ID%'
Here is a brief explanation of the above query: The SQL IF() function accepts 3 parameters: a condition
to check, and a value to return if the condition is true, and a value to return if false. For example, IF(10
> 1, 'yes', 'no') checks if 10 is greater than 1, and returns either ‘yes’ if true or ‘no’ if false. Of
course, this should return ‘yes’. In the above query, we nested 2 IF expressions to evaluate 3 cases rather
than just 2. DATE_DIFF() accepts 2 dates and returns the difference between them in days. NOW() returns
the current date/time.
Known issues
The following limitations apply to calculated fields:
• Calculated fields are re-evaluated every time the record or its child records are accessed in the table
view, the detail view, the print preview or the child table view. This could cause some performance
issues for complex queries. This can be resolved using MySQL query caching.
• Calculated fields are evaluated only when their records/child records are accessed. If data that
affects the calculation is changed, and you then retrieve the value stored in the calculated field
74
through a third-party app, it won’t reflect the changes until it’s accessed through your AppGini
app itself.
• Similarly, if the calculated field is used as a parent caption field for a lookup field in another table,
the lookup drop-down might not display the most up-to-date calculated values until the records of
the calculated field are accessed in your AppGini app.
The simple work-around for the second and third issues above is to access the record(s) containing the
calculated field in the table view in your AppGini app to update them.
75
Working with styles
AppGini offers you the flexibility to control the look of the generated application using CSS (Cascading
Style Sheets). Click on the Application theme icon on the tool bar or from the Project menu select
Themes . The window below will appear.
You can select a theme from the drop-down menu at the bottom.
76
Generating the PHP application
After you have finished working with your project (defining tables, fields and styles) the only thing
remaining now is firing your application. Click the ‘Generate App’ icon. You’ll be asked to select (or
create) an output folder.
If you’ve generated code for the open project before, this dialog will, by default, display the same output
folder used before. You can select the output folder using the Browse . . . button to navigate to any other
folder.
By clicking the yellow folder button, you can change the output folder to the parent folder of the one
currently specified. Same as project file would set the output folder to the same as the folder containing
the AXP project file, and Last used folder would set the output folder to the one you used last (beware!
this might contain an app generated by a different project file).
If you choose a folder that already contains previously-generated code, you’ll see a window that lists all
the files that will be generated. You can specify in this window (shown below) which files to overwrite
and which to skip.
Finally, a log window (shown below) reports events that happened during file generation: error checking,
files overwritten, files skipped, failed files, and instructions for deploying the generated application. You
can save the log for future reference if you click the “Save log” button. At this point you are finished
with AppGini. The next step is to upload and set up your PHP application.
Tip: If you want to customize some of the generated files and don’t want AppGini to overwrite them
if you regenerate your project later, set them as read-only. This is a very easy way of retaining your
customized code. AppGini will just report that it couldn’t overwrite that file, and will continue generating
the other files normally.
For more advanced code management, you should consider using hooks. Hooks allow you to add more
77
Figure 79: ‘Select files to overwrite’ dialog
78
Figure 80: Log of generated files
79
functionality and customize your application behavior without loosing your customizations whenever you
regenerate the application later.
80
Keyboard shortcuts in AppGini
There are several keyboard shortcuts in AppGini that will help you work even faster with projects. Here
is a list of them.
• Ctrl + N: Create a new project.
• Ctrl + O: Open an existing project.
• Ctrl + S: Save the current project.
• Ctrl + Q: Quit AppGini.
• Ctrl + T: Create a new table.
• Ctrl + F: Create a new field.
• F2: Rename the selected table or field.
• F5: Generate the application.
> Tip: Hold Ctrl while clicking the “Generate AppGini app” icon to generate your application
using the most recent options you selected before (last output folder and file overwriting settings)
without showing the options dialogs.
• F3: Show the project properties pane.
• Shift + F3: Show the application theme selector window.
• F1: Show/hide the context help pane.
• F4: Navigate between the project browser (left pane), the properties pane (right pane), and the
project search box.
See also: Shortcut keys in generated applications
81
Automatic application uploader
As of AppGini 23.10, we introduced a new feature that makes it much easier to deploy (upload) your
AppGini apps to your server. By clicking a single ‘Upload’ button, AppGini checks the changed files in
your app and uploads them to your server. You no longer need to use external FTP, SSH or other upload
tools, and you don’t have to worry about uploading the right files to the right folders.
82
2. Manually upload the generated file-uploader.php file to your server. This only needs
to be done once. You can do it by using any FTP or SSH tool. Once you upload it, you can use
automatic uploading for all future changes. The file-uploader.php file is located in the home
folder of your app. And it should be uploaded to the matching folder on your server. So, for the
example application URL above, that file should be accessible at:
https://example.com/catalog/file-uploader.php
You can also find the ‘Upload’ button after generating your app in the status window:
After clicking the ‘Upload’ button, you’ll see a list of checks that AppGini performs before uploading.
83
Figure 84: Checks performed before uploading apps in AppGini
Click the ‘Begin checks and upload the app’ button to start checks. If any checks fail, you’ll see an error
message:
If all checks pass, AppGini will scan for file changes. This would take a couple of minutes or so, depending
on the size of your app.
After that, AppGini will begin uploading only the changed files to your server. The upload progress
window will show you the progress of the upload.
After the upload is complete, AppGini will show you how many files were uploaded, how many were
skipped and how many failed, if any, along with a full list of files processed.
Troubleshooting
If you’re having issues with automatic file uploading, please check the following:
84
Figure 86: Scanning for changed files
85
• Have you set the application URL? If you haven’t set the application URL, the ‘Upload’
button will display an error. Please refer to the How to enable automatic file uploading section
above for more information.
• Have you uploaded file-uploader.php to your server? If you haven’t uploaded
file-uploader.php to your server, the checks performed before uploading will fail. You can
upload file-uploader.php to your server using any FTP or SSH tool. It should be uploaded to
the home folder of your app on your server.
• Is your application accessible over HTTPS?. Automatic file uploading only works over
HTTPS. This is to prevent anyone from intercepting the upload key and using it to upload files to
your server. Make sure your server has a valid, non-self-signed SSL certificate, and make sure it’s
not expired.
• Make sure the secret upload key is correct. The secret upload key can be retrieved from
the AppGini preferences window. If it doesn’t match the key in the file-uploader.php on your
server, you can regenerate your app, then manually re-upload the new file-uploader.php file,
overwriting the old one on the server.
Tip: You can view the secret upload key stored in the file-uploader.php file by opening it in a
text editor. Line 2 contains the key, like so:
<?php
define('UPLOAD_KEY', '2DF5367D046FFE742277D04B107CF46B');
• Is curl installed on your PC?. The automatic file uploader uses curl to upload files to your
server. Curl is installed by default on modern Windows machines, Linux and MacOS. On older
Windows PCs, you can download curl from the official curl website.
• Do you have modsecurity or a similar web application firewall (WAF) installed on
your server? This might prevent the automatic file uploader from working. If you have a
WAF installed on your server, you can try adding an exception for the file-uploader.php file
to the WAF configuration. For modsecurity, you can try adding this code to a new file inside
/etc/apache/mods-enabled/ (maybe name it appgini.conf) or similar, then restart apache:
<IfModule mod_security2.c>
SecRule REQUEST_URI "/file-uploader.php$" id:300001,allow
</IfModule>
Hint: Check your server error logs to see if modsecurity is blocking requests to file-uploader.php
or not.
• Are you using Cloudflare? Cloudflare is a great service for securing your website, but since it
also acts as a web application firewall, it might block the automatic file uploader from working.
You’ll need to add an exception for the file-uploader.php file to Cloudflare’s firewall rules.
• Are folder permissions/ownership set correctly? Make sure that the folder to which you’re
uploading your app and any subfolder are writable by the web server software (apache, nginx, ..
etc) you’re using. For example, on most apache setups on linux, the user that owns the app folders
should be www-data
Security considerations
Automatic file uploading is a great feature, but it’s important to understand the security implications of
it. Here are some things to keep in mind:
• The automatic file uploader uses HTTPS. This is to prevent anyone from intercepting the
upload key and using it to upload files to your server. Make sure your server has a valid, non-self-
signed SSL certificate, and make sure it’s not expired.
• The automatic file uploader uses a secret upload key. The secret upload key can be retrieved
from the AppGini preferences window, under the ‘App uploader’ tab.
Make sure to keep this key secret. Anyone with access to this key can upload executable files to
your server and compromise it. If you think your key has been compromised, you must immediately:
86
Figure 89: App uploader tab in AppGini preferences window
87
1. Remove the file-uploader.php file from your server.
2. Generate a new key from the AppGini preferences window.
3. Regenerate your app and upload the new file-uploader.php to your server.
We also recommend that you remove all app files from the server and use the automatic file uploader
to re-upload them.
• During the upload process, the application is set to maintenance mode. This means that no one
can access the app while it’s being uploaded. After the upload is complete, the app is set back to
normal mode.
• For tighter security, you can add a rule to your server firewall or to Cloudflare (if you’re using it) to
block access to the file-uploader.php file from all IP addresses except the one you’re using to
upload your app.
If you’re using Apache, you can add this rule to your .htaccess file or your site’s Apache configu-
ration file:
<Files "file-uploader.php">
Order allow,deny
Deny from all
Allow from 124.233.112.210
</Files>
Replace 124.233.112.210 with the external IP address of the PC you’re using to upload your app.
For nginx, you can use this rule instead:
location ~* ^/file-uploader\.php$ {
allow 124.233.112.210;
deny all;
}
You could also specify a range of IP addresses in the above rules by using CIDR notation instead of
a single IP address.
88
Setting the child record owner to
match the owner of its parent record
89
Updating the owner of existing child records
If you already have existing child records, the above setting will not apply to them unless you update
each record manually. This can be a pain if you have many records. That’s why we’ve also added a utility
to mass update the owner of existing child records.
To use this utility, follow these steps:
1. Sign in as an administrator to your AppGini application.
2. Go to the admin area and open the Utilities menu.
3. Click on Fix record owners.
4. This would open a page that lists all tables where the owner of child records can be updated. Click
the Start button above the list of tables.
5. Leave the page open until the process is complete. This might take a while if you have many records.
6. Once the process is complete, the button would display the message Done. You can now close the
page or navigate to another page.
90
Figure 92: Fix record owners page
91
Figure 93: Fix record owners page done
92
If a sales person creates an order for a German customer, the sales manager of Germany would become
the owner of that order and all order items belonging to that order.
To apply this to existing German orders and order items, we use the Fix record owners utility to
update the owner of all existing orders and order items.
93
Setup of AppGini-generated
applications
94
Figure 94: Setup captcha
95
Figure 96: Setup checklist
96
• MySQL server (host): This is usually localhost, but it might be different depending on your
hosting provider.
• Database name, MySQL Username, and MySQL Password: These are the database name,
username, and password you created earlier.
• MySQL port: This is usually 3306, but it might be different depending on your hosting provider.
After typing in the above information, a connection test will be performed to ensure that the application
can connect to the database. If the connection test fails, you’d see an error message as below:
In that case, double-check the information you entered and make sure it’s correct. Then click the ‘Retry’
button to the right of the error message to re-test the connection.
If the connection test succeeds, you’ll see a success message as below:
Next, provide the desired admin username, email, and password for your application. This will be the
username and password you’ll use to log in to your application’s admin area, where you can manage your
application’s settings, users, groups, and more.
After filling in the admin information, click ‘Submit’ to proceed to the final step.
97
Figure 99: Database connection success
98
Working with tables and records
99
Figure 102: The table view page
100
The Detail View
The detail view page can be used to enter data of a new record. To do so, click the "ADD NEW" button.
In addition, when you click on any record in the table, its data is displayed in the Detail View for viewing,
editing, deleting, printing, and/or copying to a new record. The exact functions enabled depend on the
permissions of the logged user.
To sum up, the table view allows you to control and manage data of the table by allowing you to insert
new records, edit and delete records; sort and navigate data, filter data or move to any other table. All
this can be done in one page in an easy to understand manner.
Remember that you can control the appearance of the table view through Project Styles in AppGini.
You can also control table view and detail view layouts by editing the generated template files .
101
Working with filters
Filters allow users to have advanced control over data display. The filters button brings a filters page as
shown below.
For example, if we wish to find all customers from France, Germany or Mexico, whose contact names
begin with A, M or P, the filters would look like this:
If a filter begins with ‘And’ it means the condition must be fulfilled, and if it begins with ‘Or’ then the
condition is optional. You can use % (percentage sign) and _ (underscore) in comparison values when
the comparison operator is ‘Like’ or ‘Not Like’. % means any number of characters and _ means any
single character.
There are several comparison operators available for filters, the following drop-down from the filters page
shows them all.
102
Figure 106: Filters for customers from France, Germany or Mexico, whose contact names begin with A,
M or P
To apply filters to the table view after specifying them, simply click the "Apply filters" button.
Related screencasts
Saving filters for use later
Performing advanced search queries in AppGini using filters
103
The admin interface
AppGini allows you to create member accounts and control the privileges of members. For each table in
your application, you can control whether members can add new records, edit existing ones, and/or delete
records. Moreover, you can control which records a member can edit and/or delete: only his own records
(records added by the member himself), or his group’s records (records added by any member of the
group to which our member belongs), or all records entered by him and any other member of any group.
Member groups
To make administration of members easier, AppGini allows you to create groups and assign each member
to a group. Thus, instead of assigning privileges to each individual member separately, you assign
privileges to a group. All members of the group are then automatically assigned these privileges.
Managing groups
To view available groups, click the ‘View Groups’ link on the top of the admin homepage. This would
display a page similar to this one below.
If you click the "Edit" icon to the left of a group, you can edit the group’s details and permissions
(privileges). This will open a page similar to this one below.
104
Figure 107: “The admin homepage of an AppGini application”
105
Scrolling down the group editing page, you’ll see the group’s permissions for each table. If you pass your
mouse pointer over any item in the permissions section, you’ll see a detailed description of what it means.
In AppGini 5.80 and above, the buttons with checkmarks to the right of each permission allow you to
apply the same permission value to all tables in one step. Clicking that button opens a menu, as shown
at the right. If you click on Set all to Group, for example, all tables will have the ‘View’ permission set
to ‘Group’.
106
To define a new group, open the Groups menu at the top of any admin page, and click the Add Group
command. This will open a page similar to the group editing page but with empty fields for you to fill.
Managing members
To view available members, click the ‘View Members’ link on the top of the admin homepage. This would
display a page similar to this one below.
If you click the "Edit" icon to the left of a member, you can edit the member’s details. This will open a
page similar to this one below.
107
Note that AppGini allows you as an admin to ban (suspend) members temporarily. A banned member
will not be able to sign in. You can unban him at any time later.
Managing records
The admin interface allows you to view all records entered by any member or group. Click the ‘View
Members’ Records’ link on the top of the admin homepage. This will display a page similar to the one
below.
If you click the ‘Edit’ icon to the left of any record, you can view all the data in that record, and you can
also edit the record ownership. This will open a page similar to the one below.
108
If you want to change the ownership of multiple records at once, you should use the ‘Batch Transfer
Wizard’ instead of the above page. Click on the ‘Batch Transfer Wizard’ link in the admin homepage
and follow the wizard instructions. The ‘Batch Transfer Wizard’ allows you also to move members of a
group to another group if you want to.
109
Shortcut keys in AppGini apps
Shortcut keys are a great way to speed up your work in AppGini-generated applications. They allow you
to quickly navigate between different sections of your app, perform common actions, and more. This
page provides a reference for the available shortcut keys in AppGini apps.
Shortcut keys can be used in AppGini apps as of AppGini 5.90.
2. Alternatively, you can enable or disable shortcut keys by opening the profile menu (by clicking your
username at the top right corner of the page), then clicking the Keyboard shortcuts link. This
will open the same popup as above.
110
Figure 110: Enable/disable shortcut keys
Homepage
• SHIFT + F2 : Highlight first table group.
• CTRL + F2 : Highlight the first table link.
Table view
• ALT + Q : Quick search (type your search then press ENTER ).
• ALT + SHIFT + Q: Clear quick search (instant action).
• F2 : same as ALT + Q.
111
• SHIFT + F2 : First button in the buttons bar above the table.
• ALT + F2 : First element in the bottom navigation bar.
• CTRL + F2 : First record selector checkbox.
– You can then navigate to other records using CTRL + ↑ and CTRL + ↓.
– Tip: to open the currently highlighted record in the detail view, press TAB then ENTER.
Detail view
• F2 : First field in form.
• SHIFT + F2 : First button in the action buttons at the right of the form.
• CTRL + F2 : First child record.
• F8 : First child link (from the child links above the detail view form).
• SHIFT + F8 : First child tab.
• ALT + F8 : First navigation button in child tab.
Filters page:
• F2 : First filter.
• SHIFT + F2 : Apply filters button.
• CTRL + ENTER : Apply filters (instant action).
• CTRL + SHIFT + ENTER : Save and apply filters (instant action).
• ALT + X : Cancel and go back to table view (instant action).
112
(AppGini 5.91+) Hide ‘keyboard shortcuts reference’ link
In AppGini 5.91 and above, the shortcuts window includes a link at the bottom that points to this page.
This makes it easy for your app users to see a full reference. You can remove this link if desired by adding
this line to hooks/footer-extras.php:
<script>_noShortcutsReference = true;</script>
113
LDAP Authentication
AppGini applications now support LDAP integration starting from version 24.10, providing a more
streamlined login process for users who are already part of an LDAP directory. Here’s how you can set
up LDAP integration within your AppGini application.
Kindly note that LDAP authentication is available only in AppGini Pro.
114
Figure 112: AppGini LDAP settings
LDAP Version
Select the LDAP protocol version that corresponds to your server’s configuration. Most servers will work
with version 3, which is recommended for optimal compatibility.
115
Note that the list of groups doesn’t include the Admins group to prevent unintended privilege escalation
of normal users. If you need to assign admin rights to an LDAP user, you must do so manually from the
admin area.
Important Considerations
Before enabling LDAP authentication, confirm that your AppGini admin username exists in the LDAP
directory. Otherwise, you’ll be unable to log in to AppGini with admin privileges. If this does occur, you
will need to edit the config.php file manually to revert to the default login method. This can be done
by changing the line:
'loginMethod' => "ldap",
to:
'loginMethod' => "default",
Troubleshooting
If you’re unable to sign in after enabling LDAP, and you get locked out of your admin account, you can
manually disable LDAP. To do so, you will need to edit the config.php file to revert to the default login
method. This can be done by changing the line:
'loginMethod' => "ldap",
to:
'loginMethod' => "default",
Conclusion
By following these steps, you can effectively integrate LDAP authentication into your AppGini application,
leveraging existing user accounts and streamlining the login process. Make sure to thoroughly test the
configuration with different user scenarios to ensure a smooth transition.
116
Enabling LDAP Extension in PHP
Before configuring LDAP settings in your AppGini app, you need to ensure that the LDAP extension in
PHP is enabled. This process varies depending on your operating system.
For Windows
Enabling the LDAP extension in Windows is done through the php.ini file. Follow these steps:
1. Locate your php.ini file, which is usually found in your PHP installation directory, e.g.,
C:\php\php.ini.
2. Open php.ini in a text editor with administrative privileges.
3. Search for the line ;extension=ldap. If the line starts with a semicolon (;), it’s commented out.
4. Remove the semicolon to enable the extension. It should look like this:
extension=ldap
5. Save the php.ini file and restart your web server for the changes to take effect.
For WAMP, XAMPP, or other integrated server packages, you might be able to enable the LDAP
extension through their respective control panels, usually by ticking a checkbox or switching a toggle
next to the PHP LDAP extension.
117
This outputs information about your PHP configuration. Look for a section titled ‘ldap’. If it’s present,
the LDAP extension is enabled and working.
After enabling the LDAP extension in PHP, a new ‘LDAP Settings’ tab will become available within the
admin settings page of your AppGini application. You can now proceed to configure the LDAP settings
as described in the sections above.
Remember to keep your PHP environment secure and up to date, as enabling extensions can expose new
vectors for potential vulnerabilities if not managed properly.
118
Detailed overview of AppGini and
generated application features
The following section provides a detailed overview of the features of AppGini and the applications it
generates. This is a work in progress and will be updated regularly.
Available topics
• Date and datetime offsets
• LDAP authentication
• Automated record ownership
• Calculated fields
119
• h: hours, e.g., +5h for 5 hours from now, or -5h for 5 hours ago.
• d: days, e.g., +5d for 5 days from now, or -5d for 5 days ago.
If you want to set the default value of a datetime field to now, you can enter +0s in the ‘Default value’
property.
120
Advanced topics
• Hooks
– The “hooks” folder
– Global hooks
∗ login_failed() hook
∗ login_ok() hook
∗ member_activity() hook
– Table-specific hooks
∗ tablename_before_insert() hook
∗ tablename_after_insert() hook
∗ tablename_before_update() hook
∗ tablename_after_update() hook
∗ tablename_before_delete() hook
∗ tablename_after_delete() hook
∗ tablename_dv() hook
∗ tablename_csv() hook
∗ tablename_init() hook
∗ tablename_header() hook
∗ tablename_footer() hook
∗ tablename_batch_actions() hook
– DataList object
– memberInfo array
– Magic files
– WindowMessages class
• Table and detail view classes
• Custom pages
• Third party libraries
• Command-line parameters
• URL parameters
• Troublesooting errors and blank pages
• AppGini files to ignore in git repositories
121
Hooks (AKA events)
AppGini Hooks (events) are means of advanced customization of AppGini-generated apps. They allow
you to customize your application behavior in a way that is separate from the generated code. This way,
your custom code doesn’t get overwritten if you regenerate your app later, and your project is ready for
use directly after code generation without any further modifications.
Hooks work by intercepting users’ actions (inserts, deletes, edits, selection of
records, . . . etc), and controlling what happens before and after these actions.
122
Global hooks
Global hook functions are defined in the generated hooks/__global.php file. This file contains hook
functions that get called when a new member signs up, when a member signs in successfully and when a
member fails to sign in. You could also define your own PHP functions here and they’ll be visible to all
your AppGini application pages.
The following hook functions are defined in this file:
• login_ok()
• login_failed()
• member_activity()
• sendmail_handler()
• child_records_config()
login_ok()
This hook function is called when a member successfully signs in. It can be used for example to redirect
members to specific pages rather than the home page, or to save a log of members’ activity, . . . etc. If
you open the generated hooks/__global.php file in a text editor, you can see this function defined as
follows:
function login_ok($memberInfo, &$args) {
return '';
}
Parameters:
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args is currently not implemented but is reserved for future use.
Return value:
A string containing the URL to redirect the member to. It can be a relative or absolute URL. If the return
string is empty, the member is redirected to the homepage (index.php), which is the default behavior.
Example:
Let’s add code to save a log of members’ login activity. Each time a member signs in, we’ll record his
username, IP address, login date and time into a log file. Here’s how the hook function looks like after
adding this code:
function login_ok($memberInfo, &$args) {
// the log file where we'll save member activity
$logFile = 'members.log';
123
$ip = $memberInfo['IP'];
$date = date('m/d/Y');
$time = date('h:i:s a');
return '';
}
login_failed()
This hook function is called when a login attempt fails. It can be used for example to log login errors. If
you open the generated hooks/__global.php file in a text editor, you can see this function defined as
follows:
function login_failed($attempt, &$args) {
Parameters:
• $attempt is an associative array containing details of the failed login attempt. It contains the
following keys:
– username: the username entered during the login attempt.
– password: the password entered during the login attempt.
– IP: the IP address of the client attempting to log in.
• $args is currently not implemented but is reserved for future use.
Return value:
None.
Example:
To notify the admin when a user fails to log in, we can add this code into the login_failed() function:
function login_failed($attempt, &$args){
// email of admin
$adminEmail = '[email protected]';
member_activity()
This hook function is called when a new member signs up. If you open the generated hooks/__global.php
file in a text editor, you can see this function defined as follows:
124
function member_activity($memberInfo, $activity, &$args){
switch($activity){
case 'pending':
break;
case 'automatic':
break;
case 'profile':
break;
case 'password':
break;
}
}
Parameters:
• $memberInfo is an associative array containing details of the member who signed up. Please refer
to memberInfo for more details.
• $activity is a string indicating the type of activity. It can be one of the following values:
– pending: the member signed up but his account is pending approval by the admin.
– automatic: the member signed up and his account is automatically approved.
– profile: the member updated his profile.
– password: the member changed his password.
• $args is currently not implemented but is reserved for future use.
Return value:
None.
Example:
This example sends a welcome email to new users who were automatically approved, and a ‘please wait’
email for new users pending approval.
function member_activity($memberInfo, $activity, &$args){
switch($activity){
case 'pending':
// send 'please wait' email to new user
@mail(
$memberInfo['email'], // email recipient
"Thank you for signing up at our website!", // subject
case 'automatic':
// send 'welcome' email to new user
@mail(
$memberInfo['email'], // email recipient
"Thank you for signing up at our website!", // subject
125
"Dear {$memberInfo['username']}, \n\n".
"You can now log into our website from this page:\n".
"http://www.domain.com/appgini\n\n".
"Thank you.", // message
case 'profile':
break;
case 'password':
break;
}
}
sendmail_handler()
This hook function is called when AppGini sends an email using the sendmail() function. It can be used
to modify the email before it’s sent. If you open the generated hooks/__global.php file in a text editor,
you can see this function defined as follows:
function sendmail_handler(&$pm) {
Parameters:
• $pm is a PHPMailer object, passed by reference. Please refer to PHPMailer project on Github for
more details.
Return value:
None.
child_records_config()
This hook function was added in AppGini 22.14, and can be used to modify the default configuration of
the child records section in the detail view.
If you open the generated hooks/__global.php file in a text editor, you can see this function defined as
follows:
function child_records_config($childTable, $childLookupField, &$config) {
Parameters:
• $childTable is the name of the child table.
• $childLookupField is the name of the lookup field in the child table.
• $config is an associative array containing the configuration for displaying child records for the
current user, passed by reference. The default configuration, is stored in the $pcConfig array
defined in the generated parent-children.php file.
126
Return value:
None.
127
Table-specific hooks
For each table in your project, AppGini generates a hook file named the same as the table name inside
the hooks folder. This file contains hook functions that get called when a new record is added, when a
record is edited, when a record is deleted, . . . etc. These hooks are table-specific. That’s why each table
in your project has its own hook file.
The following hook functions are defined in this file:
• tablename_before_insert()
• tablename_after_insert()
• tablename_before_update()
• tablename_after_update()
• tablename_before_delete()
• tablename_after_delete()
• tablename_dv()
• tablename_csv()
• tablename_init()
• tablename_header()
• tablename_footer()
• tablename_batch_actions()
tablename_init()
Called before rendering the page. This is a very powerful hook that allows you to control all aspects of
how the page is rendered. If you open the generated hooks/tablename.php file in a text editor (where
tablename is the name of the concerned table), you can see this function defined as follows:
function tablename_init(&$options, $memberInfo, &$args) {
return true;
}
Parameters
• $options (passed by reference so that it can be modified inside this hook function) a DataList
object that sets options for rendering the page. Please refer to DataList for more details.
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args is currently not used but is reserved for future uses.
Return value
true to render the page. false to cancel the operation (which could be useful for error handling to
display an error message to the user and stop displaying any data).
Example
The following example checks that the logged user belongs to the admin group and accordingly allows CSV
downloading of records. If the user is not a member of the admin group, CSV downloads are disabled.
128
function tablename_init(&$options, $memberInfo, &$args) {
if($memberInfo['group'] == 'Admins') {
$options->AllowCSV = 1;
} else {
$options->AllowCSV = 0;
}
return true;
}
There is another example in the Tips and tutorials section that uses the tablename_init
hook to modify part of the table view query. Another example uses the tablename_init
hook to apply a default filter to a table.
tablename_header()
Called before displaying page content. Can be used to return a customized header template for the table.
If you open the generated hooks/tablename.php file in a text editor (where tablename is the name of
the concerned table), you can see this function defined as follows:
function tablename_header($contentType, $memberInfo, &$args) {
$header='';
switch($contentType) {
case 'tableview':
$header='';
break;
case 'detailview':
$header='';
break;
case 'tableview+detailview':
$header='';
break;
case 'print-tableview':
$header='';
break;
case 'print-detailview':
$header='';
break;
case 'filters':
$header='';
break;
}
return $header;
}
Parameters
• $contentType specifies the type of view that will be displayed. Takes one of the following val-
ues: tableview, detailview, tableview+detailview, print-tableview, print-detailview or
filters.
129
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args is currently not used but is reserved for future uses.
Return value
String containing the HTML header code. If empty, the default header.php is used. If you want to
include the default header besides your customized header, include the <%%HEADER%%> placeholder in the
returned string. Note: If you have a customized header-extras.php file (see the contents of the hooks
folder for more info), it won’t be included in the page if you don’t include the <%%HEADER%%> placeholder
in the return string.
Example
The following example displays today’s date and current time above the print-preview pages, so that the
printed document shows this data. Notice that the placeholder <%%HEADER%%> is included so that
the original header is still output to users. The modified code is at lines 18 and 22.
function tablename_header($contentType, $memberInfo, &$args) {
$header='';
switch($contentType) {
case 'tableview':
$header='';
break;
case 'detailview':
$header='';
break;
case 'tableview+detailview':
$header='';
break;
case 'print-tableview':
$header='<%%HEADER%%><div align="right">'.date('r').'</div>';
break;
case 'print-detailview':
$header='<%%HEADER%%><div align="right">'.date('r').'</div>';
break;
case 'filters':
$header='';
break;
}
return $header;
}
tablename_footer()
Called after displaying page content. Can be used to return a customized footer template for the table. If
you open the generated hooks/tablename.php file in a text editor (where tablename is the name of the
concerned table), you can see this function defined as follows:
function tablename_footer($contentType, $memberInfo, &$args) {
$footer='';
130
switch($contentType) {
case 'tableview':
$footer='';
break;
case 'detailview':
$footer='';
break;
case 'tableview+detailview':
$footer='';
break;
case 'print-tableview':
$footer='';
break;
case 'print-detailview':
$footer='';
break;
case 'filters':
$footer='';
break;
}
return $footer;
}
Parameters
• $contentType specifies the type of view that will be displayed. Takes one of the following val-
ues: tableview, detailview, tableview+detailview, print-tableview, print-detailview or
filters.
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args is currently not used but is reserved for future uses.
Return value
String containing the HTML footer code. If empty, the default footer.php is used. If you want to
include the default footer besides your customized footer, include the <%%FOOTER%%> placeholder in the
returned string. Note: If you have a customized footer-extras.php file (see the contents of the hooks folder
for more info), it won’t be included in the page if you don’t include the <%%FOOTER%%> placeholder in the
return string.
Example
Please refer to the above example for tablename_header.
tablename_before_insert()
Called before executing the insert query. If you open the generated hooks/tablename.php file in a text
editor (where tablename is the name of the concerned table), you can see this function defined as follows:
function tablename_before_insert(&$data, $memberInfo, &$args) {
return true;
}
131
Parameters
• $data An associative array where the keys are field names and the values are the field data values
to be inserted into the new record. This array is passed by reference so that modifications to it
apply to the insert query.
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args was not in use prior to AppGini 5.90. As of AppGini 5.90, it’s used for exchanging further
data as follows:
– $args['error_message'] can be set inside the hook function to display an error message to
user in case of returning false .
Return value
A boolean true to perform the insert operation, or false to cancel it.
As of AppGini 5.90 , if returning false , an error message string (no HTML tags allowed) can be
displayed to users by passing it through $args['error_message'] .
Example 1
In this example, let’s assume that our table contains the fields: unit_price, quantity and total. We want
to automatically calculate the value of the total field by multiplying quantity and unit_price.
function tablename_before_insert(&$data, $memberInfo, &$args) {
return true;
}
See also: Using lookup fields in calculations.
if($data['duration'] <= 3) {
$args['error_message'] = 'Error: Duration must be higher than 3.';
return false;
}
return true;
}
tablename_after_insert()
Called after executing the insert query (but before executing the ownership insert query). If you open
the generated hooks/tablename.php file in a text editor (where tablename is the name of the concerned
table), you can see this function defined as follows:
function tablename_after_insert($data, $memberInfo, &$args) {
return true;
}
132
Parameters
• $data is an associative array where the keys are field names and the values are the field data values
that were inserted into the new record. It also includes the item $data['selectedID'] which
stores the value of the primary key for the new record.
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args is currently not used but is reserved for future uses.
Return value
A boolean true to perform the ownership insert operation or false to cancel it. Warning: if a false is
returned, the new record will have no ownership info.
Example 1
The following example sends a notification email to an employee when a user submits a new record. The
email contains the record data.
function tablename_after_insert($data, $memberInfo, &$args) {
sendmail([
'to' => '[email protected]',
'name' => 'Recipient Name',
'subject' => 'A new record needs your attention',
'message' => "The following new record was submitted by {$memberInfo['username']}: \n\n" . $m
]);
return true;
}
Example 2
The following example works with apps created by AppGini 23.17 or above. It uses the WindowMessages
class to display a custom message to the user after a record is inserted. In this example, we’re displaying
a sample instruction for the user to follow after he’s added a new record to the orders table reminding
him to add order items.
function orders_after_insert($data, $memberInfo, &$args) {
return true;
}
tablename_before_update()
Called before executing the update query. If you open the generated hooks/tablename.php file in a text
editor (where tablename is the name of the concerned table), you can see this function defined as follows:
function tablename_before_update(&$data, $memberInfo, &$args) {
return true;
}
133
Parameters
• $data An associative array where the keys are field names and the values are the new data values
to update the field with. This array is passed by reference so that modifications to it apply to the
update query. This array includes the item $data['selectedID'] which stores the value of the
primary key for the record to be updated.
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args was not in use prior to AppGini 5.90. As of AppGini 5.90, it’s used for exchanging further
data as follows:
– $args['error_message'] can be set inside the hook function to display an error message to
user in case of returning false .
– $args['old_data'] is an associative array containing existing record values. This is useful
for comparing the new values passed through the $data parameter to the stored values in the
record before the actual update operation is performed.
Return value
true to perform the update operation or false to cancel it.
As of AppGini 5.90 , if returning false , an error message string (no HTML tags allowed) can be displayed
to users by passing it through $args['error_message'] (See example 2 for tablename_before_insert ).
Example
Let’s say we have an orders table. When a user makes changes to a record and saves them, we want to
automatically calculate the value of the total field using the fields subtotal , discount and sales_tax , where
discount and sales_tax are stored as percentages (i.e. a discount value of 10 means 10% of subtotal):
function tablename_before_update(&$data, $memberInfo, &$args) {
return true;
}
Another example
Let’s say that we want to prevent updates to any records in a particular table that are older than 30
days. To do so, we would customize the tablename _before_update() hooks like this:
function tablename_before_update(&$data, $memberInfo, &$args) {
return true;
}
Don’t forget to replace tablename at line 5 above, with the actual name of your table.
134
tablename_after_update()
Called after executing the update query and before executing the ownership update query. If you open
the generated hooks/tablename.php file in a text editor (where tablename is the name of the concerned
table), you can see this function defined as follows:
function tablename_after_update($data, $memberInfo, &$args) {
return true;
}
Parameters
• $data is an associative array where the keys are field names and the values are the field data values
that were inserted into the new record. It also includes the item $data['selectedID'] which
stores the value of the primary key for the new record.
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args was not in use prior to AppGini 5.90. As of AppGini 5.90, it’s used for exchanging further
data as follows:
– $args['old_data'] is an associative array containing old record values that existed before
the update operation. This is useful for comparing the new values passed through the $data
parameter to the old values of the record that existed before the update operation. You could
use this for example for auditing purposes.
Return value
true to perform the ownership update operation or false to cancel it.
Example
Please refer to the example for tablename_after_insert hook above.
tablename_before_delete()
Called before deleting a record (and before performing child records check). If you open the generated
hooks/tablename.php file in a text editor (where tablename is the name of the concerned table), you
can see this function defined as follows:
function tablename_before_delete($selectedID, &$skipChecks, $memberInfo, &$args) {
return true;
}
Parameters
• $selectedID is the primary key value of the record to be deleted.
• $skipChecks is a flag passed by reference that determines whether child records check should be
performed or not. If you set $skipChecks to true inside this hook function, no child records check
will be made. If you set it to false , the check will be performed.
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args is currently not used but is reserved for future uses.
Return value
true to perform the delete operation or false to cancel it.
135
Example
In this example, we’ll assume that our table contains a checkbox field named approved . We want to
allow deleting of the record only if that field is not checked (set to 0). If the field is checked (set to 1), it
won’t be deleted unless the user is a member of the Admins group.
function tablename_before_delete($selectedID, &$skipChecks, $memberInfo, &$args) {
if($memberInfo['group']!='Admins') {
$id=makeSafe($SelectedID);
$approved=sqlValue("select `approved` from `tablename` where `id`='$id'");
return true;
}
We assumed in the above example that the primary key field of the table is named id. Also, notice in
line 7 the use of the makeSafe() function, which prepares variables to be used safely inside SQL queries.
In line 8, we used the sqlValue() function which performs a SQL query that we know returns a single
value. It’s a shortcut function that saves us the effort of processing a MySQL result set.
tablename_after_delete()
Called after deleting a record. If you open the generated hooks/tablename.php file in a text editor
(where tablename is the name of the concerned table), you can see this function defined as follows:
function tablename_after_delete($selectedID, $memberInfo, &$args) {
Parameters
• $selectedID is the primary key value of the deleted record.
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args is currently not used but is reserved for future uses.
Return value
None.
Example
This example logs the date and time a record was deleted and who deleted it.
function tablename_after_delete($selectedID, $memberInfo, &$args) {
// log file
$logFile='deletes.log';
136
fwrite($fp, "$datetime,{$memberInfo['username']},{$memberInfo['IP']},$selectedID\n");
fclose($fp);
}
tablename_dv()
Called when a user requests to view the detail view (before displaying the detail view). If you open the
generated hooks/tablename.php file in a text editor (where tablename is the name of the concerned
table), you can see this function defined as follows:
function tablename_dv($selectedID, $memberInfo, &$html, &$args) {
Parameters
• $selectedID The primary key value of the record selected. It’s set to false if no record is selected
(i.e. the detail view will be displayed to enter a new record).
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $html (passed by reference so that it can be modified inside this hook function) the HTML code of
the form ready to be displayed. This could be useful for manipulating the code before displaying it
using regular expressions, ... etc.
• $args is currently not used but is reserved for future uses.
Return value
None.
Example
The following example sets the price field as read-only for non-admin users. The example demonstrates
how to "inject" JavaScript code to the detail view to change its behavior. Please note that setting a field
as read-only via JavaScript is not sufficient to prevent modifying it. Power users can easily circumvent
this. So, you have to also force this server-side, for example using the before_update hook .
function tablename_dv($selectedID, $memberInfo, &$html, &$args) {
/* current user is not an admin? */
if($mi['group'] != 'Admins') {
ob_start();
?>
<script>
$j(function() {
$j('#price').prop('readonly', true);
})
</script>
<?php
$html .= ob_get_clean();
}
}
tablename_csv()
Called when a user requests to download table data as a CSV file (by clicking the SAVE CSV button). If
you open the generated hooks/tablename.php file in a text editor (where tablename is the name of the
concerned table), you can see this function defined as follows:
function tablename_csv($query, $memberInfo, $args) {
137
return $query;
}
Parameters
• $query contains the query that will be executed to return the data in the CSV file.
• $memberInfo is an array containing details of the member who signed in. Please refer to memberInfo
for more details.
• $args is currently not used but is reserved for future uses.
Return value
A string containing the query to use for fetching the CSV data. If false or empty is returned, the default
query is used.
Example
The following example modifies the SQL query used to limit records retrieved to 10 records only if the
user requesting the CSV file is not an admin.
function tablename_csv($query, $memberInfo, $args) {
return $query;
}
138
Using lookup fields in calculations
Products table
That was yummy! Each entry in the above table has a primary key ID value, which doesn’t tell much
about the item itself but is used as a reference to it. So, if we talk about product #18, we know we
are referring to Lindt EXCELLENCE Mint priced at $3.25. Primary key fields are usually (but not
necessarily) named ID.
Let’s now have a look at some data from the order_items table.
Similar to the products table, the ID column above is the primary key field of the order_items table, a
way of uniquely identifying each row. OrderID is a lookup field to the orders table (not shown here as it’s
irrelevant to our discussion). Product and ‘Unit Price’ are both lookup fields to the products table. To
understand this with an example, order item #2024 is an order for product #15, which is Lindt HELLO
139
Crunchy Nougat and its price is of course that of product #15 which is $2.05. And the quantity of Lindt
HELLO Crunchy Nougat ordered in this record is 1.
When your AppGini application displays the order_items table, it doesn’t display reference values like
the above. It automatically joins both tables and displays more human-readable results like the ones
below
If we later make any modifications to any product in the products table, like changing its name or unit
price, the changes are automatically reflected in the order_items table without having to perform any
manual data entry.
What remains now is to write code for calculating the subtotal column of the order_items table. We
want this calculation to be applied whenever we add a new order item and also whenever we make changes
to any existing order item. Therefore, we should perform the calculation in both the before_insert and
before_update hook functions.
The initial code I see many AppGini users write usually looks something like this:
$data['Subtotal'] = $data['UnitPrice'] * $data['Quantity'];
The problem with the above code is that $data['UnitPrice'] stores the primary key of the parent
product (the value of the ID field from the parent record in products). For example, if we’re calculating
the subtotal of order item #2025, the above code would display a subtotal of 18 x 3 = $54. This is of
course not correct, as the unit price for Lindt EXCELLENCE Mint is $3.25 and we have a quantity of 3
units. Therefore, the correct subtotal should be $3.25 x 3 = $9.75.
What’s wrong with the above code is that we didn’t take into consideration the fact that UnitPrice field
in order_items is actually a lookup field. The stored value is not the unit price but rather the primary
key value of the parent product. Accordingly, we should retrieve the actual unit price from the products
table using this code:
$UnitPrice = sqlValue(
"SELECT UnitPrice FROM products where ID='{$data['UnitPrice']}'"
);
The above code retrieves the unit price from the products table given the primary key value stored in the
child order_items table, $data['UnitPrice'] , and stores the actual unit price in $UnitPrice . We
can now perform the calculation as follows:
$data['Subtotal'] = $UnitPrice * $data['Quantity'];
Putting it all together, whenever we are performing calculations that involve lookup fields, we should first
retrieve the actual values from the parent table and use those retrieved values in the calculation formula.
It’s very easy to write once we understand how it works. To sum up, here is our subtotal code:
$UnitPrice = sqlValue(
"SELECT UnitPrice FROM products where ID='{$data['UnitPrice']}'"
);
$data['Subtotal'] = $UnitPrice * $data['Quantity'];
One final note . . . some tables contain non-numeric primary key values. For example, if the above
products table stores primary keys as LHCN01, LEM01 . . . etc rather than 18, 19 and so on, then we
should escape those primary keys first to avoid query errors and protect against SQL injection attacks:
140
/* Escape non-numeric lookup values before using them in SQL queries */
$SafeUnitPriceLookup = makeSafe($data['UnitPrice']);
/*
Now it's safe to use $SafeUnitPriceLookup to
retrieve our unit price
*/
$UnitPrice = sqlValue(
"SELECT UnitPrice FROM products where ID='{$SafeUnitPriceLookup}'"
);
141
Adding custom “batch actions” that
apply to multiple records
When you select one or more records in the table view, a “More” button is displayed above the table.
If you click that button, it opens the batch actions menu. This menu displays some actions that you
can perform on the records you selected – see the screenshot below. Which actions show up in the menu
depends on the permissions you have.
For example, if you are an admin, you can change the owner of the records. If you have delete permissions,
and you’ve enabled mass-delete in AppGini, you can delete the records.
142
moment. The tablename_batch_actions() hook works by returning an array of actions. Your AppGini
application receives this array and displays the actions in the “More” menu.
When a user chooses an action from the “More” menu, your AppGini application calls the javascript
function linked to that action. The name of this javascript function is part of the data in the array we
mentioned above (the array returned from the tablename_batch_actions hook).
You should define the javascript function in the file tablename-tv.js inside the hooks folder. This
function could do anything you want to apply to the selected records. It could open a new page, or make
an ajax request, or any other action you wish to do. There is no specific implementation that you have to
follow here. We’ll discuss an example action with all these details below so you can use it as a guideline.
This diagram explains how this all works.
143
Figure 115: Customers table
Let’s say you want to add a batch action to print mailing labels for the selected customers. Here is how
you can do it: the first step is to add the action into the customers_batch_actions() hook. To do so,
we’ll open the hooks/customers.php file, we should find our hook function:
function customers_batch_actions(&$args){
return array();
}
The function above is empty (we call this a skeleton function). We need to add our action to it. So, let’s
modify it to read:
function customers_batch_actions(&$args){
return [
[
'title' => 'Print mail labels',
'function' => 'print_mail_labels',
'icon' => 'th-list'
]
];
}
The code above tells our application to display an extra action in the “More” menu labeled “Print mail
labels”. If a user chooses that action, the application will pass the IDs (primary key values) of the selected
records to a javascript function named print_mail_labels(). We didn’t write this function yet. We’ll
do so in a moment. But before we do so, let’s take a look on the “More” menu after adding the code
above.
We’ve specified an icon name in the code above. So, the icon shows up to the left of the new action. For
a full list of supported icon names, please refer to the Bootstrap Glyphicons list. All icons there have a
name like “glyphicon-xyz” . . . just use the xyz part in our hook code to specify an icon.
TIP!
To display the batch action only to users from a specific group, you can add a conditional
144
Figure 116: More menu with print mail labels action
check in the hook function. For example, to display the action only to users in the ‘Admins’
group, you can add the following code:
$memberInfo = getMemberInfo();
// if the current user is not an admin, return an empty array
if($memberInfo['group'] != 'Admins') return [];
145
Figure 117: Alert showing selected IDs
window.open(url);
}
Finally, let’s write the server-side mail-labels.php script. Based on the code above, we assumed the
location of this script to be the main folder of our AppGini application. Here is how this script might
look like:
<?php
/*
Including the following files allows us to use many shortcut
functions provided by AppGini. Here, we'll be using the
following functions:
makeSafe()
protect against malicious SQL injection attacks
sql()
connect to the database and execute a SQL query
db_fetch_assoc()
same as PHP built-in mysqli_fetch_assoc() function
*/
include(__DIR__ . "/lib.php");
146
/* retrieve the records and display mail labels */
$eo = ['silentErrors' => true];
$res = sql("select * from customers " .
"where CustomerID in ({$cs_ids})", $eo);
while($row = db_fetch_assoc($res)){
?>
<b><?php echo $row['CompanyName']; ?></b><br>
<i>C/O <?php echo $row['ContactName']; ?></i><br>
<?php echo $row['Address']; ?><br>
<?php echo $row['City']; ?><br>
<?php echo $row['Region']; ?>
<?php echo $row['PostalCode']; ?><br>
<?php echo $row['Country']; ?><br>
<br>
<br>
<hr>
<?php
}
Here is a sample of the output from the above script.
147
We chose to implement the action handling using a javascript function to allow a lot of flexibility for
customizations. In the above example, we prepared some parameters and opened a new page. You might
instead wish to do something in the background by using an Ajax request without opening a new page.
It’s all up to you.
Note: The above example used the Northwind project, which is the same one used for our online demo.
You can download the Northwind project file, application files and the sample data to experiment on
your own.
TIP!
Don’t have the time or programming knowledge to write your own batch actions? We have a
plugin for that now! Check our Mass Update plugin. This plugin allows you to add as many
batch actions as you want in a very short time, without writing a single line of code.
148
DataList object
The DataList object exposes many options that you can control to affect the behavior and appearance
of each of the AppGini-generated table pages that users see.
DataList object is passed to the tablename_init hook function. This hook function is called before
displaying data to users. So, you can control the various appearance and behavior options by modifying
this object inside that hook function.
AllowDeleteOfParents
Setting this property to 1 allows users who have delete permissions to delete a record even if it has child
records in other tables. Setting it to 0 disables this.
AllowFilters
Setting this property to 1 allows users to access the filters page to view and modify filters. Setting it to 0
disables this.
AllowPrinting
Setting this property to 1 allows users to access the ‘Print preview’ page. Setting it to 0 disables this.
AllowSavingFilters
Setting this property to 1 allows users to save filters as HTML code to access them quickly later. Setting
it to 0 disables this.
AllowSorting
Setting this property to 1 allows users to sort table records. Setting it to 0 disables this.
CSVSeparator
Specifies the field separator to use when downloading data as a CSV file. The default is comma (,).
ColCaption
An array that specifies the titles of columns displayed in the table view.
149
ColNumber
An array that specifies which fields to use in the table view. It works by selecting some (or all) of the
fields listed in the QueryFieldsTV property explained below.
ColWidth
An array that specifies the width of each column in the table view. If the ShowTableHeader property
(explained below) is set to 1, the ColWidth property is overridden by the width values specified in the
table view template file (templates/tablename_templateTV.html).
DefaultSortDirection
A string that can be set to 'asc' or 'desc'. Please see the DefaultSortField property below.
DefaultSortField
Specifies the field to use for default sorting of the table view records. This property can be set to a
number to specify which field to sort by from the QueryFieldsTV property explained below. Alternatively,
it can be set to a string specifying an explicit field name or MySQL expression to use for default sorting.
DVClasses
Was added in AppGini 5.60. Additional CSS classes to apply to the detail view container (space-separated)
FilterPage
Specifies a custom search page to use when users click on the FILTERS button. If no value is provided,
the default filters page is used. You can use this feature to create advanced search forms for your tables.
Please see Creating customized search forms for a detailed example.
PrimaryKey
A string that specifies the name of the primary key field for the table. You shouldn’t change this value.
QueryFieldsCSV
An associative array specifying the fields used in the query that fetches data when users request to
download a CSV file. The array keys represent the field names or MySQL expressions used in the query.
The array values represent the column titles to display in the CSV file.
QueryFieldsFilters
An associative array specifying the fields used in the filters page. The array keys represent the field names.
The array values represent the field titles to display in the filters page.
QueryFieldsTV
An associative array specifying the fields that can be displayed in the table view. The array keys represent
the field names or MySQL expressions used in the query. The array values represent the column titles.
The fields actually displayed in the table view are specified in the ColNumber array explained above.
QueryFrom
A string that specifies the contents of the FROM part of the query used in the table view and the CSV
file.
150
QuickSearch
A number that specifies how to display the quick search box. It can take any of the following values:
• 0: no quick search box shown.
• 1: quick box shown on the top left of the table view.
• 2: quick box shown on the top center of the table view.
• 3: quick box shown on the top right of the table view.
Update: As of AppGini 5.20 and above, setting this property to 0 hides the quick search box, and
setting it to any non-zero value displays the quick search box. The position of the box is determined by
the screen size.
QuickSearchText
A string that specifies the title to display besides the quick search box.
RecordsPerPage
A number that specifies how many records to show per page in the table view.
RedirectAfterInsert
If users are allowed to add new records to the table, this property specifies the URL to which users will
be redirected after adding the new record.
SelectedTemplate
A string that specifies the path to the HTML template file to use for formatting a currently-selected
record in the table view.
SeparateDV
A number that is set to 1 to display the detail view in a separate page, or 0 to display it below the table
view.
ShowTableHeader
A number that is set to 1 (the default) to display column titles above the table view. Table ti-
tles are specified in the ColCaption property explained above. If set to 0, column titles are not
displayed (this is useful if you need to change the horizontal layout of fields in the template file
templates/tablename_templateTV.html to a different non-horizontal layout).
TableTitle
The title that will be displayed above the table view.
Template
A string that specifies the path to the HTML template file to use for formatting all records in the table
view except the currently-selected one.
TemplateDV
Was added in AppGini 5.61. A string that specifies the path (relative to the main directory of the
application) to the HTML template file to use for displaying the detail view.
TemplateDVP
Was added in AppGini 5.61. A string that specifies the path (relative to the main directory of the
application) to the HTML template file to use for displaying the print preview of the detail view.
151
TVClasses
Was added in AppGini 5.60. Additional CSS classes to apply to the table view container (space-separated)
return TRUE;
}
The above code will output the contents of the DataList object to the browser above the table view.
You can use this to inspect, debug and change the various properties. But you should use this carefully
in a protected environment for testing purposes only.
152
memberInfo array
$memberInfo is an associative array containing logged member’s info. The array contains the following
keys:
• username: the member username.
• groupID: the numeric ID of the member’s group.
• group: the name of the member’s group.
• admin: true for admin member, false for others.
• email: the email address of the member.
• IP: the IP address from where the member is currently logged.
• custom: a numeric array containing the values of custom fields for the member. Custom fields can
be defined via the admin settings page in the admin area of your AppGini application. Currently
up to 4 custom fields are supported. So, to access the value of the first custom field for the member,
you can use $memberInfo['custom'][0].
The $memberInfo array is passed to many hook functions, both global and table-specific. For ex-
ample, you can access the username of the currently logged member in a hook function by using
$memberInfo['username'].
Tip: You can retrieve this array in your own code by calling the function getMemberInfo(),
which returns this array.
153
Magic files in the hooks folder
You can create some files with specific names inside the hooks folder that your AppGini-generated
application would use to perform a specific task. These files are optional, meaning that if they exist, your
application will automatically use them to alter a default behavior. But if they don’t exist, the default
behavior will apply.
154
$j(() => {
if(!$j('.table_view').length) return;
In AppGini, you can define a field as an options list so that your application users can select the value of
the field from a set of options. For example, the Country field in the screenshot to the left is an options
list.
Now, what happens if you want to modify the contents of that options list, for example to limit the list
to some countries and remove the others? Normally, you would have to open your project in AppGini, go
to the Country field, modify the list contents, regenerate your application, and upload it. That’s a long
way to go.
So, we provide an easier method that doesn’t involve regenerating the application. Simply, create a
text file in the generated hooks folder and name it like this pattern: tablename.fieldname.csv .. For
example, for the Countries list in the screenshot, the file should be named customers.Country.csv.
Next, fill this file with all the options you want the user to be able to choose from, separated by double
semi-colons. Here are the file contents for a choice of just 3 countries as an example:
United States;;United Kingdom;;France
It’s now very easy to edit this file using any text editor to add/remove/modify options, without having
to regenerate your application. However, please beware that this file takes precedence over the options
provided in your AppGini project. So, if you decide later to modify the options in your project file and
regenerate your application, you should either delete the tablename.fieldname.csv hook file or update
it with the new options.
155
WindowMessages class
A class for displaying messages to the user on the next page load.
To add a message, use the WindowMessages::add() method:
WindowMessages::add('Hello world!', 'alert alert-success');
The first parameter is the message to display, and the second parameter is the CSS classes to apply to
the message <div> container. The second parameter is optional, and defaults to alert alert-info if
not specified.
The message(s) would be displayed on the next page load, and are specific to the current browser window.
That is, if you have multiple browser windows open, each window will have its own set of messages.
To add a dismissable message, use the WindowMessages::addDismissable() method:
WindowMessages::addDismissable('Hello world!', 'alert alert-success');
Parameters are the same as the WindowMessages::add() method.
156
ID as a hidden field in the form. You can do so using this code where you want the hidden field in the
form:
echo WindowMessages::includeWindowId();
157
Table and detail view classes
As of AppGini 5.60, a new advanced option was added to allow you to specify one or more space-separated
CSS class names to apply to the table view and the detail view of a table. This option can be found by
selecting a table in your project, then clicking the Template button in the table properties pane. This
would open a dialog as shown below.
Figure 120: How to open the template window in AppGini 5.60 and higher.
158
You can specify CSS classes to apply to the table view and the detail view. Most of the time, these would
be Bootstrap grid classes . You could also select classes form the drop-down at the right. For example, to
set the table view to display on the left part of the page, while the detail view on the right part, you
could use the class col-md-6 for each. The result would look something like this when you generate your
application.
TODO: Add a screenshot here.
Of course, to display the table and detail view on the same page, the option Display detail view in a
separate page must be unchecked for that table.
159
Adding custom limited-access pages
and reports
In most applications, you might need to create additional customized pages besides the ones generated by
AppGini. For example, you might want to add some reports, charts, switch boards, special forms, .. etc.
In this article, we’ll explain how you can create an additional page and limit access to it to authenticated
users. We’ll also explain how to integrate it as part of your AppGini application.
You probably want to achieve 3 goals while integrating new custom pages into your AppGini application:
1. Control access to the page. You want only authenticated users (or maybe only some authenti-
cated users) to be able to access the page, while others are redirected to the homepage or the login
form.
2. Integrate the page appearance into your application. That is, you want that custom page
to display the same top navigation menu shown in the other pages of your application, and to have
the same theme.
3. Link to the page from other pages so that your application users can easily find it. You might
want to link to it from the homepage and/or form the “Jump to” drop-down menu in the top
navigation bar.
We’ll cover all the above points in this article.
160
<?php
define('PREPEND_PATH', '../');
$hooks_dir = __DIR__;
include("$hooks_dir/../lib.php");
161
Integrate the page appearance into your AppGini application
After controlling access to your custom page, the next step is to customize its appearance so that it
matches the rest of the application pages. This can be very easily achieved by including the header and
footer files as follows.
<?php
define('PREPEND_PATH', '../');
$hooks_dir = __DIR__;
include("$hooks_dir/../lib.php");
include_once("$hooks_dir/../header.php");
include_once("$hooks_dir/../footer.php");
162
Third party resources used by
AppGini applications
This is a list of third-party open source libraries and resources used by AppGini applications.
• jQuery Website, Documentation
• Prototype Website, Documentation (will be removed in future releases)
• Scriptaculous Website, Documentation (will be removed in future releases)
• Lightbox2 Website and documentation (might be replaced or upgraded in future releases)
• Initializr Website
• Bootstrap Website, Documentation
• Select2 Website and documentation
• Datepicker Website, Documentation
• Bootstrap Timepicker Website and documentation
163
Command-line parameters
You can use command-line parameters to automate AppGini. For example, you can add a command to
generate an AppGini application as part of your deployment workflow in a batch/script file. Command-line
parameters work only with AppGini Pro. Currently, the following parameters are supported:
--generate "path\to\project\file"
(Added in AppGini 5.11). Generates application from provided AXP project file, using the output path
and file overwriting settings stored in the project file, then quits. If the path to the project file contains
spaces, use double-quotes around the project path.
--output "path\to\ouput\folder"
(Added in AppGini 5.73). Specifies output path to use when generating an application, overriding the
path stored in the project file. A project file must be specified using --generate. If the output path
contains spaces, use double-quotes around the output path.
--log "path\to\log\file"
(Added in AppGini 5.81). Specifies a path to a log file to save the output log to when generating an app.
If the log file path contains spaces, use double-quotes around it.
Examples
"C:\Program Files\AppGini\AppGini" --generate C:\projects\myapp.axp
This would launch AppGini and generate an application from myapp.axp to the folder stored in the
project file.
"C:\Program Files\AppGini\AppGini" --generate C:\projects\myapp.axp --output C:\xampp\htdocs\myapp2
This would launch AppGini and generate an application from myapp.axp to the folder C:\xampp\htdocs\myapp2
overriding the folder stored in the project file if different. If the specified folder doesn’t exist, AppGini
will attempt to create it.
"C:\Program Files\AppGini\AppGini" --generate C:\projects\myapp.axp --log C:\logs\myapp.log
This would launch AppGini and generate an application from myapp.axp, and save output log to
C:\logs\myapp.log.
164
Special links and URL parameters
By default, the links to various tables in your AppGini-generated application pass no URL parameters,
and thus display the default table view -- applying the default sorting and no filters. However, you can
add special parameters to the URL that allow you to control how the table view data is displayed.
The default way to link to a table is to link to tablename_view.php (where tablename is the name of
the concerned table). For example, if you have a table named customers , the default table view link
would be customers_view.php.
There are some special URL parameters that you can use as part of the link to change what you are
linking to. For example, if you want to link to the customers table, sorted by country, rather than not
sorted, the link would be customers_view.php?SortField=4 (assuming country is the fourth field in the
table as it appears in your AppGini project). You can add other URL parameters to the link, separating
them by & character (note that only the first parameter you add to the URL starts with a ? character).
You can add URL parameters in any order.
Here is a very easy way to obtain the table view link if you want to apply specific filters and sorting:
select the concerned table in AppGini, then check the option Allow users to save filters . This would
add a button down the filters page labeled Save and apply filters . You can then go to the filters page
of your table, define the filters and sorting you wish to link to, and then click that button. This will
display a permalink to the table view that you can copy and use to link to the filtered and sorted table.
See the illustration below.
You can then add this link to the homepage and/or the navigation menu for other users to access it without
having to redefine the filters and sorting. To do so, just edit the generated hooks/links-home.php (for
165
adding the link to the homepage), and hooks/links-navmenu.php (for adding the link to the navigation
menu).
SortDirection
• Default value: asc
• Details: If a SortField is provided, SortDirection determines the direction of sorting. Possible
values: asc, desc. Example:
– customers_view.php?SortField=4&SortDirection=desc sorts the table by the fourth field
in descending order.
FilterAnd[x]
• Default value: None
• Details: Please refer to working with filters programmatically
FilterField[x]
• Default value: None
• Details: Please refer to working with filters programmatically
FilterOperator[x]
• Default value: None
• Details: Please refer to working with filters programmatically
FilterValue[x]
• Default value: None
• Details: Please refer to working with filters programmatically
SearchString
• Default value: None
• Details: Displays records matching the provided search string. This is equivalent to typing a search
term in the quick search box above the table view. Example:
– customers_view.php?SearchString=germany
FirstRecord
• Default value: 1
• Details: This is equivalent to navigating the table view using the next/previous buttons or the Go
to page drop-down. Thus, you could use it to jump to a specific page instead of the default first
page of the table. Example:
– customers_view.php?FirstRecord=21 jumps to page 3 (assuming 10 records per page).
Print_x
• Default value: None
• Details: Used for displaying the print preview page. Example:
166
– customers_view.php?Print_x=1 displays the print preview of the table view.
addNew_x
• Default value: None
• Details: If set to any non-zero value, displays the detail view for entering a new record if the user
has insert permission to the table.
filterer_{fieldname}
• Default value: None
• Details: {fieldname} is the name of a non-autofill lookup field (that is, a lookup field displaying
a drop-down rather than being automatically filled). You should use this with addNew_x to set
a default value for the specified lookup field. Set the value of this parameter to the value of the
primary key of the parent record. For example, to start a new order, setting the customer to the
one whose ID is 203:
– orders_view.php?addNew_x=1&filterer_customer_id=203
SelectedID
• Default value: None
• Details: If provided, displays the detail view for editing the record identified by the given primary
key value. If the user doesn’t have edit permission for the given record, a read-only detail view is
displayed. Example:
– customers_view.php?SelectedID=203 displays the detail view for editing the customer whose
primary key is 203.
dvprint_x
• Default value: None
• Details: If set to any non-zero value, and a SelectedID value is also provided, displays the print
preview of the detail view of the record specified. Example:
– customers_view.php?SelectedID=203&dvprint_x=1
delete_x
• Default value: None
• Details: If set to any non-zero value, and a SelectedID value is also provided, the specified record
is deleted if the user has permission. > Use this with extreme caution!
noQuickSearchFocus
• Default value: None
• Details: AppGini 5.90 and above
– Passing noQuickSearchFocus=1 when linking to a table view prevents auto-focusing of quick
search button. This is useful for example when you embed a table view in another page and
don’t want the browser to “jump” down to the embedded page.
167
From Data to Dashboards, A Guide
to Redash Integration with AppGini
What is Redash?
Redash is an open-source data visualization and dashboarding tool that allows users to connect to various
data sources, create interactive visualizations, and build dynamic dashboards. It provides a user-friendly
interface for querying and exploring data, making it easier for non-technical users to access and analyze
data.
Redash supports a wide range of data sources, including relational databases (e.g. MySQL databases
used in AppGini applications), NoSQL databases, cloud storage services, and APIs. It allows users to
write queries using SQL or other query languages specific to the data source and visualize the results in
different chart types such as bar charts, line charts, pie charts, and more.
168
Installing Redash on your server
Redash is a stack of several components, including a Python web application, a PostgreSQL database, a
Redis server, and several other components. It can be installed on a Linux server using Docker, or on a
Windows server using a virtual machine. The installation process is documented in detail in the Redash
documentation.
Setting up Redash can be a bit challenging, especially if you’re not familiar with Docker. If you’re not
comfortable with the installation process, we can help you set up Redash for a small fee. Please contact
us for details.
169
4. In the “Query” field, enter your SQL query. You can use the “Schema” tree on the left to browse
the tables and fields in your database and click the » button next to a table or field to insert it into
your query.
5. Click on the “Execute” button to run the query and see the results.
6. If the query runs successfully, click on the “Save” button to save the query. You should give it a
name that describes what the query does.
7. You can optionally click on the “Visualize” button to create a visualization of the query results.
You can choose from a variety of chart types, including bar charts, line charts, pie charts, and more.
8. Once you’re done, click on the “Save” button to save the visualization.
9. In order to be able to display the query results or visualization in a dashboard, you should click the
“Publish” button. This will also make the query or visualization available to other users of Redash.
170
Dashboards in Redash are made up of widgets. Each widget can display the results of a query or a
visualization. To create a dashboard, follow these steps:
1. Click on the “Dashboards” link in the left sidebar.
2. Click on the “New Dashboard” button.
3. Give your dashboard a name and click on the “Save” button.
4. Click on the “Add Widget” button.
5. Select the query you want to display in the widget from the “Query” dropdown.
6. Select the visualization type you want to use from the “Visualization” dropdown.
7. Click on the “Add to Dashboard” button to add the widget to your dashboard.
8. You can move and resize the widget to control its position and size on the dashboard.
9. Repeat steps 4-8 to add more widgets to your dashboard.
10. Finally, click on the “Done Editing” button to save the dashboard.
11. If you want to share the dashboard with other Redash users, you can click on the “Publish” button.
171
Performance considerations when using Redash
Redash is a great tool for visualizing data from your AppGini application. However, users can create
complex queries that can put a heavy load on your database server. Moreover, Redash allows users to
create queries and dashboards that auto refresh periodically – sometimes as frequently as every minute or
so. This can put a heavy load on your database server, especially if you have a large number of users.
One way to avoid this, without limiting the functionality of Redash, is to set up a separate read replica
of your MySQL database and connect Redash to it instead of the main database. This way, Redash users
will be querying the read replica instead of the main database, and this will not affect the performance of
your AppGini application.
Conclusion
Throughout this tutorial, we’ve covered the integration of Redash with AppGini to unlock advanced data
visualization and dashboarding capabilities. You’ve learned how to set up Redash on your server, connect
it to your AppGini database with enhanced security through read-only access, and utilize the DataTalk
plugin to facilitate query creation without deep SQL knowledge. The step-by-step guidance provided
should now empower you to create insightful visualizations, build interactive dashboards, and share your
findings with ease.
As you apply these new skills, keep in mind the performance considerations vital for maintaining your
application’s responsiveness—especially the strategy of employing a read replica for your database to
mitigate the load from complex and frequent queries. With Redash as your tool of choice, you are
well-positioned to elevate the data experience for your team and stakeholders, ensuring your data is not
just informative but also actionable.
172
I see an error or a blank page in my
AppGini app - how to troubleshoot?
First of all: Have you made a customization to your AppGini app, either through hooks , or by directly
editing some of the generated files outside hooks? If yes:
1. Open your AXP project file in AppGini.
2. Generate the app to a new empty folder . This would create a clean app, without any customiza-
tions.
3. Run your new app in your browser, trying to reproduce the error. If the error doesn’t occur, then
we know there is something wrong in the customization you’ve made.
4. You can either undo the customization, or if you want to debug it further, keep reading below for
steps to discover the error and debug it.
The steps below are helpful to find the exact cause of the error, whether it’s in your
customized code, or in the generated code of your app.
• Please press F12 to open the browser’s inspector, then click on the ‘Console’ tab. Is there any error
reported there?
Figure 125: Inspector console tab showing an example error for an AppGini app.
173
Figure 126: View/Rebuild fields page showing a fix button
174
∗ you have one or more custom SQL queries for lookup fields,
if so, try revising your code to make sure the SQL queries are valid. You can use the ‘Interactive
SQL queries tool’ under the Utilties menu to test your query. Or you can use 3rd party tools
like phpMyAdmin.
• If you found no SQL errors, open your server error_log file and check to see if any errors related
to your AppGini app are reported in it.
– The location of that file varies based on your web server software and your OS. You should
consult your web server documentation to learn where to find the error log file. For example,
for Apache on linux, that file is typically at /var/log/apache2/error.log (but that might
vary based on your environment configuration).
If you find one or more errors reported in the error log file, and you’ve made a customization to the
generated code, try to revise your code and fix any syntax errors.
• If none of the above applies to your AppGini application, please send us your AXP project file along
with any error messages, screenshots and/or any other details about the error and how to reproduce
it. You can send these via the contact form at https://bigprof.com/appgini/support-request
175
Useful links
Hosting providers
Most hosting providers support hosting the applications generated by AppGini out-of-the-box, since
AppGini generates PHP applications that connect to MySQL databases. PHP and MySQL are very
widely available through almost any hosting provider.
If you’re looking for a very easy to use and highly reliable cloud hosting service , we’ve been using
Digital Ocean for many years and are quite content with their simplicity and reliability. This special link
provides you with $100 credit to try their service.
Digital Ocean however requires some experience managing your server updates and software installations
(they have many detailed guides for that though). If you’re looking for managed hosting (where the
176
provider takes care of installing, securing and upgrading your server software, as well as resolving the
majority of technical issues), Bluehost is a very good option.
A good source of information regarding hosting providers is the webhostingtalk forum .
Code management
If you plan to manually edit the generated code, you should be careful to avoid overwriting your
modifications in case you regenerate the code later. CVS software helps greatly with organizing and
versioning your code. It makes it very easy to undo (revert) harmful changes, and merge your modifications
into newly generated code. One such great, easy and free program is TortoiseGit . We have a tutorial on
how to use it with AppGini code.
Reference material
• HTML reference
• CSS reference
• Bootstrap 3 reference
• jQuery API reference
• PHP reference
• MySQL SQL reference
177
Contribute to AppGini
documentation
The content of this documentation is hosted on GitHub and is open-source. This means you can contribute
to it by submitting pull requests to the GitHub repository. This can be done very easily by clicking the
“Edit this page on GitHub” link at the top of each page.
The following video shows how to contribute to the documentation. Please note that the video was
recorded before updating the documentation theme, so the appearance of the documentation has changed
since then. However, the process of contributing is still the same:
By contributing to the documentation, you help other users and make AppGini better for everyone.
Thank you for your help!
178