Avoiding Mod Conflicts

From TNG_Wiki
Jump to navigation Jump to search

Mod conflicts can show up as:

  • Not Installed where a previously installed mod now shows locations as Not Installed because both insert into the same line fragment
  • Bad Target where a previously installed mod now shows locations as Bad Target because the other mod changed the code in the %location: so it no longer is the same.
  • Mod shows as installed but the code does not behave correctly

The following suggestions are offered on how you might avoid creating mod conflicts:

  • Use %insert: instead of %replace:
  • Add line rather than replace
  • use %triminsert with code fragments if you need to modify the same line
  • use conditional tags

Using Insert Instead of Replace

Insertions are less likely to cause conflicts than replacements, though insertions certainly can result in conflicts, and replacement are sometimes essential.

For example, the Census Plus International mod uses

%target:personlib.php%

        Location 1 - add check for Census Event before line 500
%location:%
        switch( $data['type'] ) {
%end:%
%insert:before%
        // added for Census Plus International mod
        $addcenplus = "";
        if ((stristr($data['tag'], 'cens')) || (stristr($data['text'], 'census'))) {
                $addcenplus = "Y";
                }
        //echo "Data text is {$data['text']} and data tag is {$data['tag']} Add Census Plus flag is set to $addcenplus <br />";
        //end of added code for Census Plus International mod
                
%end:%

When a multiline replacement seems to be the easiest way to make a necessary change, it is sometimes safer to use more than one insertion.

Add Line Rather Than Replace

For example, the Edit User Profile Mod conflicted with the Site Menu Mod, and potentially other personal mods that need to add globals in function tng_icons. The goal here was to the variable $currentuser as a global. The initial approach was to search for the entire global statement, and to replace it with a new statement, like this:

%target:genlib.php%
%location:%
function tng_icons( $instance ) {
global $text, $allow_admin_db, $chooselang, $languages_table, $mylanguage, $tngconfig, $cms, $tngprint, $mediatypes, $languages_path;
%end:%
%replace:%
function tng_icons( $instance ) {
global $text, $allow_admin_db, $chooselang, $languages_table, $mylanguage, $tngconfig, $cms, $tngprint, $mediatypes, $languages_path, $currentuser;
%end:%

As a result, any mod with a similar need that searched for the entire native global statement would not find it. It is much more succint, and safer, to add the new global variable with a new global statement, like this:

%target:genlib.php%
%location:%
function tng_icons( $instance ) {
%end:%
%insert:after%
global $currentuser;  //Changed by the Edit User Profile mod
%end:%

Note the comment on the inserted statement. A unique comment on the inserted line is essential if the statement global $currentuser; needs to be inserted in more than one place in the same file. Without such a comment, Mod Manager would produce "not unique" errors on the relevant target locations, and would not be able to install the more.

It is useful to add a comment that refers to the mod name (and perhaps the location number) to all insertions so that other mod programmers can easily recognize modded code. This is especially important when a mod presents a "bad target" error, but you don't kow which mod caused the conflict.

Some mod programmers prefer to use the end-of-line statement delimiter # rather than // to help identify comments that came from mods, as opposed to comments in the native code.

Code Fragments

When two mods need to make changes within the same line, and the two changes are not overlapping, they can both do so if they use %triminsert% or %trimreplace:%, the two "Code Fragment" directions. The term "Code Fragment" is used because the search the and insertion text are both fragments of a line.

This comes up frequently when two mods want to change the same line of a SQL statement, or add a table column when two columns are generated in the same line.

In at least PHP file, three mods have changed the same SQL statement. I'll describe the relevant target locations from two of those mods.

Here's the original one-line SQL statement from admin_branches.php:

$query = "SELECT $branches_table.gedcom as gedcom, branch, $branches_table.description as description, personID, treename FROM $branches_table LEFT JOIN $trees_table ON $trees_table.gedcom = $branches_table.gedcom $wherestr ORDER BY $branches_table.description LIMIT $newoffset" . $maxsearchresults;

The target location in the Admin Branches mod adds four new fields, "agens", "dgens", "dagens", and "inclspouses", to the SELECT clause of the query, like this:

%location:%
$branches_table.gedcom as gedcom
%end:%
%triminsert:after%
, agens, dgens, dagens, inclspouses /*###Admin Branches Location 6*/
%end:%

Note the SQL in-line comment in the insertion. As noted above, that comment helps other programmers see where code has been inserted, and which mod inserted the code.

The target location in the Branch Timestamps mod adds eight new fields, using the same search text as Admin Branches, but using %triminsert:before% instead of %triminsert:after%

%location:%
$branches_table.gedcom as gedcom
%end:%
%triminsert:before%
datefinished, actionfinished, $branches_table.people, $branches_table.families, datequeued, datestarted, actionrequested, $branches_table.username /*###Branch Timestamps Location 2*/,
%end:%

(In this example, as in most SQL queries in TNG, the order of fields in the SELECT clause is not important, and I believe that the two mods would still work together if they both used %triminsert:after% (and, of course, if Branch Timestamps placed its commas in the same way that Admin Branches does.

The resulting SQL statement (with the insertions highlighted) is:

$query = "SELECT datefinished, actionfinished, $branches_table.people, $branches_table.families, datequeued, datestarted, actionrequested, $branches_table.username /*###Branch Timestamps Location 2 - Get new fields*/,$branches_table.gedcom as gedcom , agens, dgens, dagens, inclspouses  /*###Admin Branches Location 6: 4 new fields*/, branch, $branches_table.description as description, personID, treename FROM $branches_table LEFT JOIN $trees_table ON $trees_table.gedcom = $branches_table.gedcom $wherestr ORDER BY $branches_table.description LIMIT $newoffset" . $maxsearchresults;

Note: It may seem odd that the insertion text in Admin Branches started with a comma and a space. This was not essential, but was done in light of the fact that the Mod Manager

  1. Deletes trailing whitespace from the end of lines in the target file, and from the end of lines in search and replacement text, and
  2. Ignores leading spaces in the search text.

The concern was that

  • If the search text was $branches_table.gedcom as gedcom, and the insertion text was agens, dgens, dagens, inclspouses /*###Admin Branches Location 6*/ (with a leading space), that leading space might be ignored by the Mod Manager, and result in the loss of a space in the PHP file.
  • Likewise, if the search text was $branches_table.gedcom as gedcom, (with a trailing space) and the insertion text was agens, dgens, dagens, inclspouses, /*###Admin Branches Location 6*/, that trailing space would be removed, and might result in a net loss of a space in the PHP file.

Changes at the Same Location

(The example just above did address two mods trying to insert code at the same location, but this section deals with broader considerations.)

If two mods need to change the code in the same location,

  1. If it doesn't matter which code comes first, and you just do an Insertion, it is often possible for more than one mod to use the same search text and action direction (i.e. %insert, %replace, %triminsert, etc.).
  2. Similarly, it is often possible for one mod to insert new text before the search text, and another mod to insert new text after the search text (as with the example above).
  3. At times, however, one mod will need to find search text above or below the search text used by the other. This is especially true if one mod or the other replaces text.

Here's an example of the third approach just above:

The ShowTable Mod and Media - Census Plus International mods both insert parameters in the ShowTable function in showmedialib.php

function showTable($imgrow, $medialinktext, $albumlinktext) {
    global $text, $rootpath, $usefolder, $showextended, $imagetypes, $size, $info;

where the conflict was resolved by

%location:%
function showTable($imgrow, $medialinktext, $albumlinktext
%end:%
%triminsert:after%
, $mediadescription, $medianotes
%end:%

and by verifying for the location that follows the function line

%location:%
) {
    global $text, $rootpath, $usefolder, $showextended, $imagetypes, $size, $info;
%end:%
%triminsert:before%
, $cppersonID, $cptreeID
%end:%

(Note that the code fragment search string just above covered two lines, but just a fragment of the first line.)

Another example of a same location change conflict was the Geocode Assist Mod and the MediaWiki Integration mod which both changed the same location in personlib.php to output the icon for the wiki page and the geocode globe icon.

Mod Manager conflict shows as not installed.png

The conflict was resolved by changing the MediaWiki Integration mod to use %triminsert:before% to insert its code which also remove the install dependency.

The original code in MediaWiki Integration mod v9.2.0.3 was

%location:%
<a href=\"$placesearch_url" . "psearch=" . urlencode($data['place'])
%end:%
%triminsert:before%
" . linkWiki("Places",$data['place']) . " 
%end:%

The code in MediaWiki Integration mod v10.1.0.5 is now

%location:%
            if( !isset($data['np']) ) {
                $treestr = $tngconfig['places1tree'] ? "" : "&amp;tree=$tree";
                $output .= " 
%end:%
%triminsert:after%
" . linkWiki("Places",$data['place']) .  "
%end:%

Use TNG v12 Conditional Tags

The Mod Manager in TNG v12+ added conditional processing tags that can be used to resolve conflicts between two mods.

See Secret Notes for Admin for an example of how the %textexists: conditional handles the conflict with the Display Private Notes and allows the mod to be installed whether the Display Private Notes mod is installed or not.

Related Links

Mod Manager

Mod Manager Controls

For Mod Developers

Technical

Developer Tools

Example Mods