Overwriting AppGini functions, allowing admin group members to access the admin area

Heads-up! We’ve recently added an option to allow Admins group members to access the admin area (except the admin settings page). So, the code listed here for overriding getLoggedAdmin() is no longer necessary if you’re using AppGini 22.12 or above. The code for overriding generated functions is still applicable though if you need to override other functions.

You might already know that AppGini applications can be extended through hooks. Hooks allow you to intercept certain events, like when a user submits a new record to be inserted into a table, or when a record has been updated, .. etc. The great benefit of using hooks is that you write them once, and they persist even if you re-generate your AppGini application any number of times later. So, it’s a write once and forget way of extending your AppGini apps.

Psst! Want to learn more about using AppGini hooks, even if you’re not a programmer? Check our online video course.

Sometimes, however, the modification you want to make might have no relevant hook. In that case, you might find the only way to apply that modification is to manually edit the generated code. This is a painful process that I usually advise against unless it’s absolutely necessary. The problem with this method is that your changes would be overwritten if you re-generate your code later, and you’ll have to re-apply them. Moreover, there is a big chance those changes might not work correctly in future versions of AppGini. Using Git or a similar source code management tool might help, but it’s still a pain.

In this post, we’ll discuss a method that makes this process a little easier. You can use it to replace one or more PHP functions defined by AppGini with your own version. It works by looking for the function body and comparing it with our custom version. If they are not the same, our custom version replaces the one generated by AppGini. This is all performed in an automated manner and the only action we need to take is call a script once after we re-generate our AppGini app.

Let’s start by creating a new file inside the hooks folder, hooks/replace-appgini-functions.php, and adding the following code to it:

To make the code easier to follow, I’ve added comments above to mark the 3 steps we need to apply to change the above code.

Step 1: Specify the file containing the function we want to overwrite

In this step, we should provide the full path to the file to be modified. The above code can replace AppGini functions defined only in incCommon.php or admin/incFunctions.php. Functions defined in other generated files won’t be replaced using the above code, but you could make some modifications to the search algorithm if you wish in order to make this possible.

Step 2: Specify the file containing our version of the function

In this example, we’re going to modify the getLoggedAdmin() function. This function is responsible for authenticating the super admin user, controlling who can access the admin area of your AppGini application. AppGini allows only a single user to access that area. We’ll modify this function to allow any user in the ‘Admins’ group to access the admin area. Let’s define the customized code in a file that we’ll name hooks/mod.getLoggedAdmin.php:

The above code is our own version of the getLoggedAdmin() function, allowing any user that belongs to Admins group to access the admin area.

Step 3: Specify the name of the function we want to overwrite

Finally, we’ll specify that we want to change the function named getLoggedAdmin.

How do we apply the modification?

We’ve now created 2 files inside the hooks folder: replace-appgini-functions.php and mod.getLoggedAdmin.php. Whenever you want to regenerate your AppGini application, all you need to do is to call replace-appgini-functions.php once to apply your modified code. As a safety measure, the file to be modified will be backed up first. Also, our code above would check if modifications were already applied, and if so they won’t be re-applied. So, it’s actually safe to call this file multiple times. Only the first time will apply modifications, and others would do nothing and just notify you that changes were already applied.

Assuming our app is hosted on example.com/staff-db, to apply the modified getLoggedAdmin() function, we should visit this URL from a browser: example.com/staff-db/hooks/replace-appgini-functions.php

Changing multiple functions

The same method above could be used to modify multiple functions. Simply repeat steps 1 to 3 for each function. Make sure to store each modified function in a separate mod file. For example, let’s say we’re going to modify getLoggedAdmin() and sendmail(). We should store the new code of getLoggedAdmin() in hooks/mod.getLoggedAdmin.php, and the new code of sendmail() in hooks/mod.sendmail.php.

Next, we should modify the steps in hooks/replace-appgini-functions.php to the following:

After every time you regenerate your AppGini app, visit replace-appgini-functions.php once in your browser to apply all modifications. I hope you find this approach much more convenient than manually modifying generated files. Comments are welcome 🙂

Moving an AppGini application to a new server

So, you have an AppGini-generated application that is up and running on your server. You then decided you want to move it to a new server. May be because you found a better hosting service, or you’re upgrading the server, or for any other reason. This is a simple checklist of the steps you should follow to make sure your app and data are moved successfully without any data losses, and with minimum downtime.

  1. On the old server you’re moving from, sign in to your AppGini app as admin, and go to the admin area.
  2. Open the Utilities menu > Database backups.
  3. Create a new backup.
  4. Go to the admin homepage by clicking the link to the top left of the page.
  5. Switch to maintenance mode. This would prevent users form modifying the database during migration. You might wish to edit the maintenance mode message displayed to users to tell them when the application is expected to be up again. To do so, open the Utilities menu > Admin settings, and scroll down to the “Maintenance mode message”, where you can edit the default message.
  6. Copy the entire application folder to your new server (to a folder under your web document root). Depending on your server configuration, this could be done using FTP, or SSH, or through the file manager under your server hosting control panel.
  7. On the new server, delete the config.php and setup.md5 files from the copied app folder. Also, under the ‘admin’ folder, delete the file ‘.maintenance’ (yes, it begins with a dot!)
  8. Open the URL of the application on your new server in a web browser. You should see the setup page.
  9. Enter your database login info and your admin user info (admin info should be the same as the old app: same admin username, password and email)
  10. After setup is finished, go to the admin area on the newly set-up app and open the Utilities menu > Database backups.
  11. You should see the backup file you made on your old server. Restore it to retrieve your data to the new server.

Following the above steps, your app should be ready on the new server, with all the data and uploaded files. Depending on your setup, you might also need to adjust your DNS settings to point your domain to the new server.

Trying to detect your application URI using DOCUMENT_ROOT? Read this first!

The application URI is the part of the URL after the host name (domain name) and before the home of your application. For example, for this blog, the URI is /blog/. If you are writing a PHP application to be distributed and installed in many environments that you have no idea how they are configured, you need to write your application to be as generic as possible when it comes to handling environment parameters. And one of the things you should not assume, is the path in which the application is going to be installed, and accordingly, the application URI.

I used to do the following in my applications to detect the application URI, and I thought that was generic enough to work on any environment:

I’d place the above function into a helper file and include it in all application pages. calling app_uri() should then return the application URI and it should, presumably, work on all environments, right? Wrong! Sorry!

First of all, let’s see how the above function works, and then see where it would fail. Line 2 above uses DOCUMENT_ROOT to retrieve the path to the main directory for serving pages from our web server. This would, in most Debian systems, for example, return /var/www/html.

Line 3 would return the path to the file containing the app_uri() function. This might be something like /var/www/html/blog/lib, assuming the helper file is stored inside a sub directory named ‘lib’. Now, retrieving the application URI would be a simple matter of removing the DOCUMENT_ROOT from that path, and removing the ‘lib’ sub directory at the end, which is what line 7 does, returning the precious /blog/ URI.

This used to work beautifully, until it didn’t! Like I mentioned above, we have no idea whatsoever about the environments in which our app is going to be deployed. Making the assumption that the above works everywhere was, as I found painfully, an arrogant claim!

Some environments are configured so that the document root is a symbolic link. Maybe, to make things easier for users of a server, the server administrator decided to define a symbolic link in each user’s home directory named public_html that links to a directory in their name under /var/www … For example, let’s say our user is johndoe, and his home directory on the server is /home/johndoe

When johndoe lists his home directory contents, he sees public_html and so he installs our app into that directory. So, is our app installed into /home/johndoe/public_html/blog? Well, not really. Since public_html is actually a symbolic link, the real path to our app is /var/www/johndoe/blog.

The problem with DOCUMENT_ROOT is that it doesn’t resolve real paths .. so it would return /home/johndoe/public_html … OTOH, __FILE__ does resolve the real path, thus it would return /var/www/johndoe/blog/lib … Suddenly, our beautiful little function above that used to work elegantly no longer works!

I tried fixing this by applying realpath() to DOCUMENT_ROOT .. Unfortunately, some server configurations would fail that as well.

So, after researching the above for hours, I came to the conclusion that the ONLY accurate way of detecting the application URI is not to try! Simply avoid DOCUMENT_ROOT and __FILE__ altogether, and write the URI into a config file:

And the above line would be added to the config file by the setup script that runs the first time the app is installed to the server, and can be obtained from dirname($_SERVER['REQUEST_URI']). It seems this is indeed the way popular open source PHP apps/frameworks retrieve the application URI .. they don’t try to detect it from DOCUMENT_ROOT .. they just save it as a fixed predefined config value. I didn’t find this explicitly mentioned anywhere I searched. I guess this is because people usually write about how to do things rather than how not to do them! So, I thought I’d write a post about this.

To sum up, don’t try to detect the application URI from DOCUMENT_ROOT … you’re doing it wrong this way, even if it seems to work! Save yourself the trouble, detect it once during setup using REQUEST_URI, save that to a config file, and read it from there from now on!

Make it mandatory for new users to agree to terms and conditions before signing up

I got a question from an AppGini user on how to make it a requirement for users to check an ‘I agree’ box when they sign up to AppGini — possibly as part of GDPR compliance, or to agree to some other terms and conditions.

We do plan to add support for this as part of future versions of AppGini. However, for now it can be done through a quick JavaScript code customization in hooks/footer-extras.php. The steps in brief are: check if the current page is the signup page, and if so, change custom field #4 to a checkbox, and prevent form submission if the box is unchecked.

The steps above make use of one of the custom user fields and turn it into a checkbox. In AppGini, the user profile supports storing up to 4 customizable fields. If you follow the steps here, one of them will be used for the checkbox, so you’ll have only 3 left. It’s a caveat but can be used as a work-around for now till we implement it as a separate feature in AppGini. So, the code to be placed in hooks/footer-extras.php would look like this:

You could change the wording of the checkbox by editing line 12 above. Also, you could change the error message by changing line 17. You’ll need to add a file containing the terms and conditions in the hooks folder, naming it terms.php. It might look something like this:

Change the wording above as you see fit. Please let me know if you have any questions or comments.

Automatically be notified of any errors on your server

Sometimes a script on your website/server might be causing some errors that you are unaware of. Some users might be seeing these errors under certain circumstances and others not. If users don’t report these errors, you won’t know unless you’re regularly checking your server logs. But no one likes to check server logs as part of their daily routine 🙂 So, here is a one-liner you can add to your crontab file:

The above line would automatically check your server daily at 2:00 am for any error_log files modified within the last 1 day (this is what the option -mtime -1 above does). If any ones are found, you’ll get an email listing them — don’t forget to change the email address above to your email.

Displaying count of child records on the tab title

AppGini supports displaying a list of child records in the detail view of the parent record. For example, here is the detail view of an employee record from the online Northwind demo:

(Ignore the weird cartoons for now!) So … It would be nice if the count of subordinates is displayed on the ‘Subordinates’ tab, as well as the count of ‘Initiated orders’ on its tab title. One way of doing this is through JavaScript. Add the following code to the generated hooks/footer-extras.php file:

Afterwards, all child tabs for all tables would display a count of child records. Here is how this looks for our employee above:

The count would auto-update if you add or remove any child records. We might implement this functionality in future releases of AppGini. For now, I hope this helps 🙂

Retrieve a list of users who haven’t made any activity in the last x months

I just got an interesting question from an AppGini user:

Is there an SQL query that I can run manually in PhpMyAdmin that will show me a list of all users that haven’t logged into AppGini for the last 6 months please? Even if it is using one of the date fields in the membership_users table to show me a list of all users that haven’t added or changed a record for the last 6 months, that would suffice.

Well, AppGini doesn’t currently (until AppGini 5.72) store the login date/time of users (but we plan to add this in future releases). So, what we can do is run a query to retrieve non-active users (with no insert/edit activity in the last 6 months).

First, we need to create a new view in MySQL to list users’ last activity:

Now, you can use the view ‘members_activity’ that we defined above like you use any table (except it’s not actually a table, but it makes our life easier when performing complex queries). So, to retrieve all users who have no insert/update activity or whose last insert/update activity is more than 6 months old:

But the above query might retrieve new users who joined less than 6 months ago … to exclude those, we should add another condition:

Hiding the nicedit component from a page

AppGini uses nicedit Javascript component for displaying rich text areas, where users can enter formatted input. You might wish to hide/show the rich editor in an AppGini page, or in one of your custom pages. This can be done using JavaScript code.

To hide nicedit components in a page:

To show nicedit components:

 

How to hide a field in child table view?

In many cases, your application might have a child table, like this:

An example child table in an AppGini application. We want to hide some columns from that table.

Let’s say we want to hide the Company and Open columns from that child table. You can add a CSS rule to hide specific columns from the child table. In the generated hooks/footer-extras.php, add code similar to this:

In the above code, replace:

  • child-table with the name of the child table, “quotes” in the above example.
  • lookup-field with the name of the lookup field in the child table that points to the parent table, “company” in the above example.
  • field-to-hide name of the child table field to hide, “company” in the above example

You can repeat the above rule for every column you want to hide. So, to hide the company and open fields:

The result looks as follows:

Company and Open fields now hidden after applying the above CSS rules.