Hello,

I am wondering if there's any collective will to 

Add:

module_installed($module) 

Alongside:

module_exists($module)

In: 

core/includes/modules.inc

Here's the source for module_exists($module)

/**
* Determines whether a given module exists.
*
* @param string $module
*   The name of the module (without the .module extension).
*
* @return bool
*   TRUE if the module is both installed and enabled, FALSE otherwise.
*/
function module_exists($module) {
 $list = module_list();
 return isset($list[$module]);
}

Here's what I whipped up for module_installed($module)

/**
*
* @name
* module_installed()
*
* @description
* Determines if a module is installed or not.
*
* @param string $module
*   The name of the module (without the .module extension).
*
* @return bool
*   TRUE if the module is installed, FALSE otherwise.
*/
function module_installed($module) {

 $result = db_query("SELECT * FROM {system} WHERE type = 'module' AND name = '" . $module . "'");

 return $result->rowcount() ? TRUE : FALSE;

}

 

EDIT:  See discussion of db_query #Placeholders at:

https://www.drupal.org/docs/7/api/database-api/static-queries

The above query should be formatted differently to conform to Drupal best practices....

 $result = db_query("SELECT * FROM {system} WHERE type = 'module' AND name = :module", array(':module'=> $module);

Comments

The system table shows rows of modules that are or were present in the codebase. So it would be unreliable.

Perhaps if I understood what you're trying to accomplish. Are you saying "installed" as in the module code is present, or that the module is enabled? If the latter then module_exists() already does that. As for the former, I don't think there's a performant way to do that.

In addition to what Herb said, it's important to clarify the terminology used by Backdrop. 

  • Enabled module:  
    • Its code is included during a page request, 
    • All its tables (hook_schema) are present in the database
    • All its configurations (files placed in its config folder, and usually defined in MY_MODULE_config_info) are present in the site's active configuration folder
  • Disabled module:
    • Its code is not included in page requests
    • All its tables (hook_schema) are STILL present in the database
    • All its configurations (files placed in its config folder, and usually defined in MY_MODULE_config_info) are STILL present in the site's active configuration folder
    • The above allows the site manager to re-enable the module without losing data
  • Uninstalled module (for a previously enabled module):
    • Its code is not included in page requests
    • None of its tables (hook_schema) are present in the database. If the module defined a fieldable entity, the fields attached to it and their data have been purged (the latter is still somewhat buggy as of the time of writing this)
    • None of its configurations defined with MY_MODULE_config_info are present in the site's active config folder
    • Any configuration files present in its config folder that don't have a definition in MY_MODULE_config_info are STILL in the site's active config folder (e.g. Views installed by the module)
  • Uninstalled module that have never been enabled:
    • Its code is not included in page requests
    • There is no trace of it anywhere in the database or site's active config folder 

 

The list of modules displayed in /admin/modules is the result of reading the .info files placed within the usual module locations (modules folder, core/modules folder and sites/XXX/modules folder), and lists any module that contains type = module in those .info files. This list is stored in the system table. When you remove a module folder from the modules folder, the system table will not be updated (and the module record will not be removed) until you either clear caches, or visit the admin/module page. Therefore, your code (IF what you are trying to do is to see if the module code is in the modules folder) will be unreliable.

In addition, your syntax for db_query is not great. You don't want to concatenate strings for the query. Instead you should use placeholders.

Hello @herb and @argiepiano,

First of all, many thanks for taking the time to supply your helpful comments.

I was also puzzled by lingering module-related artifacts I found in the system.  

I too noticed that the BCMS system state was "unreliable" with respect to reporting what I would normally consider to be the "true" status of a module, which could potentially be expressed via the following states:

  • Possible:  Non-resident, but available for download
  • Enabled:  Resident and Active
  • Installed:  Resident but Inactive
  • Uninstalled:  <- obviously the wrong meta-concept
    • Perhaps Reversed or maybe Ready?
    •  "Resident and Inactive with all system-level alterations reversed"?
  • Quarantined:  Resident and Inactive and not to be Activated
    • A definite non-installation candidate
  • Deleted:  Non-Resident with no trace anywhere of having been on the system

Perhaps I've missed a state.  If so, please add it. 

In any case, it looks to me like the system has module management issues in terms of nomenclature and functionality.  My experience was that when I used the "Uninstall" feature to manipulate the status of a module, its  code remained resident on the disk and some of its data remained resident in the system table.

This system behavior might reasonably be considered counter-intuitive or even "wrong" by the uninitiated, who (based on the behavior exhibited by other systems) might expect an "uninstall" process to be the equivalent of something being purged from a system. It might even be considered by some as being immoral.

In previous eras, when disk space was more precious, people complained quite loudly if you couldn't purge a software and all of its cyber-leavings from a system via its "uninstall" process.  It was seen as a major flaw.  It forced OS vendors to publish strict guidelines and processes regarding how 3rd party software could be installed (and potentially uninstalled), mostly due to incredibly negative customer pressure.  It was the impetus for the creation of specialized, system-level install/uninstall software like Microsoft Windows Installer and Composer (and many, many other such-like "package managers").

Anyways, to illustrate why I am even looking at this situation, here's a potential use case that may help to clarify what I am thinking about:

  • A "core" or "contrib" module having its own "core" and "contrib" sub-modules
    • Say Ubercart -> Payment methods  -> PayMe (an HSBC service)

On disk, the files are laid out like this:

Drupal
└── ubercart
          └── payment
                    ├── uc_2checkout
                    ├── uc_authorizenet
                    ├── uc_credit
                    ├── uc_cybersource
                    ├── uc_google_checkout
                    ├── uc_payment
                    ├── uc_payment_pack
                    └── uc_paypal

With the above architecture, wouldn't it be nice to have the "payment" module be able to:  (1) Tell the Administrator what the Ubercart Payment sub-system is all about; then (2) Inform the Administrator about the universe of payment method options available to them by displaying something like this?

--------------------------------------------------------------------

Ubercart Payment Methods

Ubercart Payment Methods expose various methodologies by which customers might pay for things.  The Ubercart Payment Methods (uc_payment) module ships with several "core" Payment Methods pre-installed (including the highly popular "PayPal Payment Method), and there are a number of 3rd party "contributed" Payment Methods available for download.  

Payment Methods:

  • PayPal is enabled (disable | uninstall | quarantine)
  • Authorize.net is ready but disabled (enable | uninstall | quarantine)
  • Google Checkout is quarantined (ready | uninstall)
  • HSBC PayMe is installed but unready (ready | uninstall | quarantine)
  • AliPay is uninstalled (quarantine | delete)
  • X is possible (download)
  • Y is possible (download)
  • Z is possible (download)

--------------------------------------------------------------------

I may not have the above right.  I did not develop a state diagram, but what I want to do is explore the feasibility of doing something along the above lines without horribly breaking BCMS.

Finally, @argiepiano, thanks for the tips regarding coding approach, I will look up the approach you indicated, though I wonder why the method you outlined is preferred?  A few words on that topic would be much appreciated - I very much feel like "I don't know what I don't know" :)

EDIT:  Likely he meant "parameters" and the two main reasons for taking this approach are (A) Avoiding SQL injection and other nasty behaviors that result from pasting unsanitized user input directly into SQL statements ($module in this case is a programmer-derived value rather than a user-supplied value, but the point is taken. [the original approach taken in module.inc with respect to enabled modules is less direct; it chains together two functions that (1) generate a list of all modules; and  (2) searches for a match in that list for the parameter given, which in its own way would likely avoid SQL injection, as the argument/parameter is never implicated in a SQL statement, but rather a comparison]); and (B) Improved performance as parameterized queries can quite easily become stored procedures.  In short, there are both sanitization and speed advantages.

EDIT:  https://www.php.net/manual/en/mysqli-stmt.bind-param.php

@all - Does the BCMS Project have its own Official Programming Style Guide, or has it adopted the Style Guide of another project?  I'd like to understand  this subject  better so I don't have to revise my work as much, moving forward.

EDIT:  https://backdropcms.org/project/styleguide

EDIT:  https://docs.backdropcms.org/php-standards

EDIT:  https://docs.backdropcms.org/css-standards

EDIT:  https://github.com/backdrop-ops/styleguide

Thanks!

g.
----

Update, 

I saw this in the modules management area when I created a brand-new TEST module:

The following shows up in the system table:

(schema)

> show columns from system;
+----------------+--------------+------+-----+---------+-------+
| Field          | Type         | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+-------+
| filename       | varchar(255) | NO   | PRI |         |       |
| name           | varchar(255) | NO   |     |         |       |
| type           | varchar(12)  | NO   | MUL |         |       |
| owner          | varchar(255) | NO   |     |         |       |
| status         | int(11)      | NO   | MUL | 0       |       |
| bootstrap      | int(11)      | NO   |     | 0       |       |
| schema_version | smallint(6)  | NO   |     | -1      |       |
| weight         | int(11)      | NO   |     | 0       |       |
| info           | blob         | YES  |     | NULL    |       |
+----------------+--------------+------+-----+---------+-------+

(data)

> select filename, name, type, status from system where name = 'TEST';
+--------------------------------------------+------+--------+--------+
| filename                                   | name | type   | status |
+--------------------------------------------+------+--------+--------+
| sites/all/modules/_custom/test/test.module | test | module |      0 |
+--------------------------------------------+------+--------+--------+

So it looks to me like there's enough clues in the system table to determine at least some of the states I think are possible and want:

  1. Available (in system table) | Unavailable (non-existant) | Possible (listed elsewhere)
  2. Present (.module file on disk) | Absent (.module file not on disk)
  3. Enabled [exists] | Disabled [exists]
  4. Implemented (schema changed) | Unimplemented (schema reverted)
  5. Active (Enabled + Present + Available + Implemented) | Inactive 
  6. Banned (new status)
  7. Exists [legacy] (Available + Present + Enabled + Active)

Opinions?