1 (edited by apmuthu 09/26/2015 12:15:25 pm)

Topic: deactivate_extension not dropping tables

Even if $check_only = false;, the $ok fails the test in function update_databases() in includes/hooks.inc as the function check_table() called in it and defined in admin/db/maintenance_db.inc as:

function check_table($pref, $table, $field=null, $properties=null)
{
    $tables = @db_query("SHOW TABLES LIKE '".$pref.$table."'");
    if (!db_num_rows($tables))
        return 1;        // no such table or error

    $fields = @db_query("SHOW COLUMNS FROM ".$pref.$table);
    if (!isset($field)) 
        return 0;        // table exists

    while( $row = db_fetch_assoc($fields)) 
    {
        if ($row['Field'] == $field) 
        {
            if (!isset($properties)) 
                return 0;
            foreach($properties as $property => $value) 
            {
                if ($row[$property] != $value) 
                    return 3;    // failed type/length check
            }
            return 0; // property check ok.
        }
    }
    return 2; // field not found
}

attempts to evaluate $fields = SHOW COLUMNS FROM... even when there is no $field argument available when dropping tables during deactivate_extensions! Hence the said function should be corrected to be:

function check_table($pref, $table, $field=null, $properties=null)
{
    $tables = @db_query("SHOW TABLES LIKE '".$pref.$table."'");
    if (!db_num_rows($tables))
        return 1;        // no such table or error

    if (!isset($field)) 
        return 0;        // table exists
    $fields = @db_query("SHOW COLUMNS FROM ".$pref.$table);

    while( $row = db_fetch_assoc($fields)) 
    {
        if ($row['Field'] == $field) 
        {
            if (!isset($properties)) 
                return 0;
            foreach($properties as $property => $value) 
            {
                if ($row[$property] != $value) 
                    return 3;    // failed type/length check
            }
            return 0; // property check ok.
        }
    }
    return 2; // field not found
}

Please confirm it being a fix.

2 (edited by apmuthu 09/26/2015 12:16:55 pm)

Re: deactivate_extension not dropping tables

The $hooks->deactivate_extension() is not being called anywhere in FA and hence the drop.sql is not being executed when an extension is deactivated. The Ajax on the Web UI just looks after the tick mark in the checkbox and removes the entry in the $installed extensions arrays in the installed_extensions.php files.

Re: deactivate_extension not dropping tables

@joe: any pointers on the "official" way to get FA to drop tables created by extensions when they get deactivated for a specific company?

Re: deactivate_extension not dropping tables

I guess we should address this to Janusz.

Joe

5 (edited by apmuthu 09/26/2015 09:19:05 pm)

Re: deactivate_extension not dropping tables

Fixed it! Pull it in from my repo.

There was a missing function deactivate_hooks() which has now been added.

Post's attachments

extn_deactivation_patch.zip 15.7 kb, 2 downloads since 2015-09-26 

You don't have the permssions to download the attachments of this post.

Re: deactivate_extension not dropping tables

The above changes working for module deactivation. but its not working for the module activation. Please go through it once again. mine php version is 5.6.3 and its XAMPP.

Subscription service based on FA
HRM CRM POS batch Themes

7 (edited by apmuthu 09/27/2015 08:18:47 am)

Re: deactivate_extension not dropping tables

FA 2.3.x is designed for PHP 5 to 5.3 and a few have reported success with PHP 5.4 possibly with some mods.

In case you are testing with PHP 5.6+, please see what functions are deprecated / dropped in 5.4, 5.5 and then 5.6 and make sure you make appropriate changes / replacements.

There is no intention of the devs and myself in supporting any later versions of PHP other than 5.3 for FA v2.3.x. You are pretty much on your own here.

To assist others however, you may post your findings in a separate thread for such later PHP versions. Such findings posted in this thread will only serve to confuse end users - yes, it will make for big consultancy business...... but FA has been designed with simplicity in mind and for self service.

The code portion for activating extensions has not been touched in this fix and if it doesn't work for you now, then it never did in the first place prior to this fix as well.

8 (edited by apmuthu 09/29/2015 02:22:56 pm)

Re: deactivate_extension not dropping tables

@joe: you can update FA repo with the patch in the 5th post in this thread.

Re: deactivate_extension not dropping tables

I must get Janusz to look at this first.

I am out of office right now, but will address him when I am back.

Joe

Re: deactivate_extension not dropping tables

Function check_table is used to check existence of table , or specific field in table, and works properly, but you are right - for purity the lines should be swapped.

The $hooks->deactivate_extension() is not being called anywhere in FA

Well, in your patch you have removed the line where the function was used indirectly by hook_invoke method (the method name was passed to be executed in this function) as callback to drop the spare tables. Could you explain what was the reason for this?

Janusz

Re: deactivate_extension not dropping tables

I removed the indirect calling using hook_invoke method as the global $Hooks array is empty when it reaches inside the $hooks->deactivate_extension() function just as it does when it reaches inside the $hooks->activate_extension() function which was why the latter was used to overcome it. Unless we get to where and when the global $Hooks array gets populated (or re-initialised / overwritten), we will need this workaround.

Re: deactivate_extension not dropping tables

Please describe exact scenario, where I can see this problem. I guess you mean the hook_invoke call in install_module.php file, but all seems to be right here.

13 (edited by apmuthu 10/04/2015 08:28:36 pm)

Re: deactivate_extension not dropping tables

First off, the current callback method of deactivating the extensions is simply not working as far as the dropping of the extension's tables are concerned - wonder when was the last time it worked, if ever.

Lines 213 to 225 of the current admin/inst_module.php file are:

    foreach($exts as $i => $ext) {
        if ($ext['package'] && ($ext['active'] ^ check_value('Active'.$i))) {
            if (!$ext['active'])
                $activated = activate_hooks($ext['package'], $comp);
            else
                $activated = hook_invoke($ext['package'], check_value('Active'.$i) ?
                 'activate_extension':'deactivate_extension', $comp, false);
            if ($activated !== null)
                $result &= $activated;
            if ($activated || ($activated === null))
                $exts[$i]['active'] = check_value('Active'.$i);
        }
    }

The second line (first if) above has an XOR (^) which means that the code fragment is executed only if either

  1. the extension was already active or

  2. the activate extension checkbox has just been ticked and submitted

but not both.

The second "if" activates the extension using the direct function activate _hooks() if it was inactive which because of the XOR condition above would mean that the activate extension checkbox had just been ticked. This works correctly and the module gets installed. However, this does not happen through the callback portion of the hook_invoke() function through which it is never called. The use of the direct function activate _hooks() was necessary since the value of the $Hooks global array variable is empty when it enters the activate_extension() method by callback if the hook_invoke() was used.

The "else" of the second "if" triggers the callback only for the deactivate_extension() method when the activate extension checkbox has just been unticked whilst the extension was active. Here, the value of the $Hooks global array variable is empty when it enters the said deactivate_extension() method which exits without executing the deactivation sql if any. It is for this reason that the new direct function deactivate_hooks() was used to do the job.

The esoteric callback method in hook_invoke() seems to make the code quite a bit of a challenge for the average user to comprehend and I was initially blinded to conclude that the said deactivate_extension() method was never called. We should try to use simple and easy to understand constructs for ease of debugging / troubleshooting by the average user for mass trusted adoption.

Steps to reproduce:
1.  Install and activate for one company any extension (currently, only dashboard extension has a drop sql separately) that creates tables and has a means to drop them as well. Alternatively create an extension that only creates one table and then on deactivation drops that table and then install and activate it for one company.
2. Now choose to deactivate it for the company for which it is currently active.
3. Check and find that the newly created table(s) have not been dropped despite deactivation.
4. Use my new function method and then repeat the steps above and you will find successful deactivation with the newly created table(s) dropped.

Re: deactivate_extension not dropping tables

Well, it is always better to find exact reason of encountered problems than ommit it with workaround at random place where it first appeared, leaving the real solution for later. After testing both FA versions I can say this problem is absent in stable 2.3.24. I will investigate this issue further to fix the beta version.

NB The XOR usage in above code is rather standard - this is just simple test which is true if and only if  'active' flag changes.
Janusz

15 (edited by apmuthu 10/05/2015 05:08:50 am)

Re: deactivate_extension not dropping tables

My tests have been on a plain vanilla FA 2.3.24+ install and the problem exists in it. It is imperative to fix it in the stable version as well based on the commit in my repo. The current activate_hooks is in itself a workaround on which the deactivate_hooks is based and the comment above the former would attest to this in includes/hooks.inc after the class definition:

/*
    Non active hooks are not included in $Hooks array, so we can use special function to 
    activate.
*/

Only, in this case, at this point of entry into the hooks_invoke even the active hooks are not in it! There are a host of functions that are outside the hooks class definition that create a new instance of the hooks class and then invoke the hooks methods!

Install the extension in the default company and activate it for a non default company alone and then try to de-activate it and see if the tables get dropped.

Only the official dashboard extension has the drop sql in it.

Try to only install the dashboard extension (and not the theme) and then activate it and then de-activate it and you will see that the tables remain. At first I thought that it was to save the tables for a re-installation so that user changes are persistent but then most extensions drop the table if it exists before re-creating them during the activation phase and hence it gives us an illusion of having been deleted during deactivation unless the tables are checked just after deactivation.

The usage of XOR is may be standard even if not very widely used, but the usage of the hook_invoke by callback isn't where a simple if...else could have sufficed.

The real solution however, would be to make sure that the global $Hooks array gets pupulated earlier than this invocation and does not get overwritten thereafter. In fact, we have @kvvaradha to thank as this was investigated during the vetting of his hrm extension based on the dashboard skeleton.

It is pertinent to note that solution may lie in the fact that the extension install and uninstall works perfectly in includes/packages.inc wherein the $Hooks array gets populated with the active extensions in:

            if (($ext['active'] == true) && file_exists($path_to_root.'/'.$ext['path'].'/hooks.php'))
            {
                // we need to include the new hooks file to activate extension
                include_once($path_to_root.'/'.$ext['path'].'/hooks.php');
                foreach($db_connections as $comp => $db)
                    activate_hooks($ext['package'], $comp);
            }

Perhaps something like this should precede the hooks_invoke in admin/inst_module.php.

Re: deactivate_extension not dropping tables

Ok,  the module deactivation works right on my installation  by accident, but surely there is some bug here. I will push solution to repo later today.

Janusz

Re: deactivate_extension not dropping tables

Issue has been fixed in stable repo
Thanks for patient cooperation wink.

Janusz

18 (edited by apmuthu 10/06/2015 05:52:49 pm)

Re: deactivate_extension not dropping tables

Thanks Janusz.

Very elegantly done avoiding another function (should it now be toggle_hooks? wink ).

It certainly was worth the wait.

Synched and committed in my repo as well.

Re: deactivate_extension not dropping tables

glad  to hear that the core problem was solved.

Subscription service based on FA
HRM CRM POS batch Themes