Mod Manager - Config File Creation Example

From TNG_Wiki
Jump to navigation Jump to search

Introduction

This article

  • Describes a small change that might be desirable in a TNG page,
  • Shows the direct PHP code edits that would implement that change, and
  • Explains how to automate those PHP edits through a TNG mod.

You should already know how to use mods, know why mods are useful even if you just want to make small changes to your own site, and have some idea of the structure and syntax of mod configuration files. However, this article may actually be a good way for you learn about that syntax and structure. See also

  • Mod Manager - Creating Config Files, which really just describes a simple mod without covering a process for creating it and
  • that can be reached through the Mod Manager link under the "TNG Guides" heading in the menu bar to the left.

The Programming Process

This article does not try to cover the programming process. It presumes that a desired change has already been implemented though direct edits to PHP code in TNG files. It focuses on how using Mod Manager to automate that change by

  1. Using a text editor to create a new Mod .cfg file by copying and modifying an existing one,
  2. Using a file-comparison tool to compare the edited PHP files with either
    • A backup of the files, made just before editing them, or
    • The corresponding "pristine" files from the TNG release, and
  3. Copying-and-pasting snippets of code from the TNG files into a .cfg file.

PHP programmers don't need to know about the Mod Manager to edit TNG PHP programs, but a mod programmer does need to be sensitive to certain programming and code-documentation conventions, and to the idea that other programmers change the same file and even the same code without being aware of the changes other programmers are making. This is very much unlike a classical programming setting in which programmers are assigned to implement a change to the that are while making the code changes. In particular, it is important to

  1. Try not to edit code that has been inserted by other mods.
  2. Avoid making unnecessary changes to pristine TNG code. Such changes increase the risk of
    • Accidentally causing conflicts with mods that you have not installed, and
    • Creating conflicts with future TNG updates.
  3. In particular, avoid the temptation to "improve" pristine TNG code. Most of the TNG code has been functioning well for a long time. While it is true that coding conventions for both TNG and PHP have changed in the intervening years, if it ain't broke, don't fix it!
    (Mea culpa: I have been known to decide that - as long as I am digging around in the PHP code - it would be "better" to turn <td> elements in table headings into <th> elements, merge <span> elements into the parent element, try to "simplify" code by reducing the number of transitions between PHP mode and HTML mode, and even use new-fangled CSS techniques to reduce the number of classes and styles that are repeated in multiple <td> elements. But, in truth, I've spent too much time "improving" code that worked just fine and caused too many unintended conflicts by doing so.)

This process is applicable to CSS files, JavaScript files, and PHP files, and to the PHP, HTML, CSS, and JavaScript code that can be found inside PHP files, plus raw text inside those and other files. A key to understanding Mod Manager .cfg files is that Mod Manager is completely oblivious to the language(s) being used in files that Mod Manager modifies. Mod Manager simply looks for text (which may or may not be "code") in the file being edited, and then either replaces that text or inserts new text.

Programming Tools

It is essential to have a good programmer's editor such as Notepad++, and a good file comparison utility such as BeyondCompare. Other similar tools are used by some TNG programmers.

Editing the TNG Files

Once the programmer has made the necessary changes to a file, the programmer must take uniqueness of search and replacement strings into account in order to:

  1. Effectively place comments about the changes in the file, and
  2. Decide how much text (how much code) must be in the search text and replacement or insertion text in order to make it unique.

Note that comments can be extremely useful in assuring that replacement and insertion text is unique.

The Example

The mod we will build is a very simple one. It takes the TNG Whats New program and removes any reference to the user who made the edits listed by Whats New. This mod is most applicable to sites where only one person has edit rights, and thus the username of the editor is pointless.

To implement the change, we will need to modify two PHP programs:

  1. whatsnew.php, which displays the Individuals (from the People database table) and Families
  2. function.php, where the doMedia function displays each media type.
whatsnew.php Before the mod whatsnew.php - What we want it to look like
Mmexample-before.png Mmexample-after2.png

Editing a PHP file

Two essentially-identical locations in whatsnew.php must be changed. Let's focus on one for the moment. Here's the PHP code that displays the "Last Modified" data cells for each person:

1.    $changedby = $row['changedby'];
2.    $changedbydesc = isset($userlist[$changedby]) ? $userlist[$changedby] : $changedby;
3.    echo "<td class=\"databack nw\">" . displayDate( $row['changedatef'] ) .
            ($currentuser ? " ({$changedbydesc})" : "") . "</td></tr>\n";

Here's what this code does:

  1. Grab the userID of the person who made the change and save it in the variable $changedby.
  2. User the userID to lookup the user's fullname in the array $userlist, and save the fullname in $changedbydesc.
  3. In the echo statement, display the fullname only if the current user is logged in (if $currentuser is not empty).

Since we don't want to display the user's name at all, we don't need lines 1 & 2, and can remove the reference to the full name from the echo statement. That is, we want this code:

    echo "<td class=\"databack nw\">" . displayDate( $row['changedatef'] ) . "</td></tr>\n";

So, this change replaces 3 lines of code from the "native" file with a single line - except that, as I suggested earlier in this article, we want to flag all of our changes with comments that identify the mod, describe the the purpose of the edit, and include a sequence number that guarantees that, even if we make identical changes to more than one place in the file, our replacements will be unique.

The Mod Manager scripting language describes the operation with

  1. A %location: command that says "Here's the location in the file where we're going to make a change"
  2. A search string that is terminated with a line that contains only %end:%
  3. The action command %replace:%, and
  4. The replacement string, which is also terminate with %end:%
%location:%
    $changedby = $row['changedby'];
    $changedbydesc = isset($userlist[$changedby]) ? $userlist[$changedby] : $changedby;
    echo "<td class=\"databack nw\">" . displayDate( $row['changedatef'] ) . ($currentuser ? " ({$changedbydesc})" : "") . "</td></tr>\n";
%end:%
%replace:%
    ###Whats New No User Mod Location 2: In function DoMedia, remove references to the user who edited the Media record.
    echo "<td class=\"databack nw\">" . displayDate( $row['changedatef'] ) . "</td></tr>\n";
%end:%

Now, why terms, that meansin the second box above with the t way avoid displaying the username would be to set $ and The userID is used to look up n is read from the array $userlist in the field 'changedby' and the program looks up the full name of that user in the PHP array $userlist. Then, it displays the username ($row['changedby']</code), and looks up that username in an array (that contains the names of all users in the Users table). The PHP snippet ($currentuser ? " ({$changedbydesc})" : "") and the data of change from a result set (which was read from People table), and, with the code%name:Whatsnew Hide Editor% %version:v10.0.0.1% %description:Suppress the display of the username of the person who made each edit. This mod is primarily useful for sites where there is only one administrator and that username is redundant.
Mod developer is <a href="http://tng.lythgoes.net/wiki/index.php?title=User:Robinrichm" target="_blank">Robin Richmond</a>
Wiki article: <a href="http://tng.lythgoes.net/wiki/index.php?title=Whatsnew_Hide_Editor" target="_blank">Whatsnew_Hide_Editor</a> % %wikipage:Whatsnew_Hide_Editor%

The word "location" needs some explanation

  • In the comment, the location numbers just represent the sequence of %location:% commands (i.e. changes) in file.
  • But a Not all TNG programmers insert comments when they make changes to TNG files. But it is very useful to incorporate comments into your changes, for several reasons, such as:
  1. Comments that contain the mod name flag the fact that text has been changed by the mod.
  2. In this particular case, both edits simply remove code. You can't remove text in Mod Manager without replacing it with something else, such as a comment.
  3. In cases such as this, where the two code changes are identical, comments can assure that the replacement text is unique.
  4. In general, comments are helpful to programmers who happen upon the code and don't otherwise know what is going on.

Some TNG programmers like to add new lines before and after a change to clearly mark where a change has been made. However, in simple one-line changes, the comments can often fit nicely on the same line as the change.

In the present example, there are two files:

  • In whatsnew.php, brief comments about the code changes will be placed within the same line as the code changes, as shown above.
  • In functions.php, the one code change will be associated with a more explict comment on the line above the code change.
  • And in both files, a comment will be placed at the top of the file to describe the effect of the mod.

Top-of-file comments

It is also very useful to add a comment at the top of any file that you edit. This comment serves to document the fact that a mod has modified the file, and to explain what the purpose of the mod is. Here is whatsnew.php with a new explanatory comment at the beginning of the file, right after the first <?php directive.
Mmexample2.png

Testing the code changes

This article will not try to cover the testing and approval process, which should be independent of whether Mod Manager is involved. For the purposes of this article, we will

  • Assume that the edits shown above meet the "project" requirements, and
  • The necessary edits to any other files also are tested and approved.

In this example, function.php must also be edited. Those edits will be considered as they apply to the Mod Manager .cfg file.

Creating the Mod Manager .cfg File

The simplest way to start a new Mod Manager .cfg file is to copy a simple config file and change it. Here is a screenshot of the beginning of another mod .cfg file. Note the syntax highlighting - with orange percent signs, green keywords, and the mod description highlighted in yellow. This syntax highlighting is a result of installing the special Mod Manger language plugin for Notepad++, which is described in the Wiki article Notepad++ Mod Manager Language.
Mmexample3.png

And here is the beginning of the file after it has been modified to become the WhatsNew Hide Editor mod. Please note that is very important that you remember to do a Save As to the new filename before you save any changes. It is usually a good idea to do that Save As before you actually make any changes.
Mmexample4.png

Note that the name of the new file is whatsnew_hide_editor_v11.0.0.1.cfg. The mod's version number is defined both in the filename, and within the file. This version number (11.0.0.1) that the mod works for TNGv11.0.0 and above, and that this is version 1 of the Whatsnew Hide Editor mod.

In the screenshot above, you can also see

  • The beginning of a Change History comment in the .cfg file. There is no special syntax to mark Mod Manager comments because any text that is not within a Mod Manager directive is a comment.
  • The "%target directive, which specifies that the whatsnew.php file is to be modified by the subsequent directives.

One File's Target Locations

We know that there are three change locations in whatsnew.php. To describe them to Mod Manager, we need to specify unique text to search for, and unique insertion or replacement text. In this example, the first target location will insert the top-of-file comment, and then the second and third target locations will replace PHP code.

An effective way to figure out the search text and replacement or insertion text for each location, and to put that text in the .cfg file, is to

  1. Display a side-by-side comparison of the edited file and the previous version.
    • The screen shots in this example use the file comparison tool BeyondCompare.
    • WinMerge is less powerful, but free file comparison tool for Microsoft Windows.
    • Notepad++ has a reasonably useful side-by-side comparison function (which must be installed as a plugin), but if you are using NotePad++ to edit the .cfg file, then you can't really use it to compare the two versions of the edited file.
  2. Simultaneously have the .cfg file open in an editor. This example will continue to use Notepad++ to edit the .cfg file.
  3. With the side-by-side comparison and the editor all open at once, copy-and-paste snippets of text from the new and old PHP files to the .cfg file.

The follow screen shots illustrate this process. For the sake of display space, illustrations show

  • a side-by-side comparison in BeyondCompare with narrower panels
  • a smaller Notepad++ editor window, and
  • more overlap than you would probably have on your PC screen.

Location 1

The first illustration of this process shows the beginning of each copy of whatsnew.php, with the top-of-file comment, and, as it turns out, a change from a different mod that just happens to be close to the top of the file. We will ignore the target location from the Living Color mod. Note that the edited file is on the left, and the previous version is on the right.
Mmexample5.png

In this screenshot, you can clearly see the three lines that make up the top-of-file comment (lines 2-5 on the left), and you must inspect the corresponding and adjacent lines in the previous version to determine whether to do a replacement or insertion, and to select text that will make the search text unique.

In the Notepad++ window, where the .cfg file whatsnew_hide_editor_v10.0.0.1.cfg is being edited, note that the target location that was in the file we started from has already been modified as follows:

  • Line 10 - The %target directive now specifies whatsnew.php,
  • Line 15 - The Mod Manager comment now also specifies whatsnew.php. As it turns out, the description of this first location has not changed. It is still "Top-of-file comment".
  • Line 17 - The search text from the first location in the old mod has been removed, and there is nothing specified between the %location% directive and the %end:% direction that bracket the search text.
  • Line 19 - Similarly, the insertion text from the first location in the old mod has been removed, and there is nothing specified between the action directive (%insert:before%, which we could change) and the %end:% directive that bracket the insertion or replacement text.
  • Note also that the comment for location 2 in line 21 still has the old filename, since that comment has not yet been modified.

As we look at Figure 5 above, it should be clear that that the changed lines (2-4) on the left are new (since there are no matching lines on the right). And it should be easy to at least guess that the line that follows the insertion (line 2 on the right) may be sufficient to serve as unique search text.

It is usually a good idea to highlight the candidate search text (all of line 2 on the right side), and then use the BeyondCompare search command (control-f) to see if that text occurs elsewhere. In this case, it doesn't, so we can just copy and paste line that line between lines 16 and 17 in the .cfg file. Then we can copy and paste the insertion text between the %insert directive and the %end:% directive in the .cfg file. Note that we can leave the %insert:before% action directive alone, since the new mod, like the old one, will insert the new text before the search text.

The resulting target location is shown in the next illustration. The .cfg file has search text in what is now line 17, and it has insertion text in what is now lines 19-21.
Mmexample6.png

Location 2

Now, here, knowing that we made the exact same edit in locations 2 and 3, we have to be very careful to select unique search text. If the original lines of code for locations 2 and 3 are actually identical, our search text will have to incorporate enough lines of code to make it unique. But, conveniently, the text in the two locations is not identical. As can be seen in this side-by-side comparisons of the two pristine locations (a repeat of figure 1a),
Mmexample1.png
there is a slight different between the two lines. The code snippet that we have eliminated is identical, but location 2 (on the right) includes the HTML attribute and value valign=\"top\" and location 1 on the left doesn't.

Thus, to get unique search text, we can work within the line of code. (When the search text is just part of a line, it is known to Mod Manager as a "code fragment".) We simply have to make sure that our search text includes something before the place where valign=\"top\" occurs in location 3. Rather than just grab a small part of an HTML attribute, we'll start with "<td class=" near the beginning of the line. Note that, in BeyondCompare, with our narrow panels, each line of text is cut off because BeyondCompare does not wrap long lines. But at the bottom of the BeyondCompare window, we can see the two selected lines above and below each other, so we can see most of each line.
Mmexample7.png

Therefore, we can select unique search text from the right-hand panel by selecting the portion of the line highlighted in blue at the bottom of the BeyondCompare window. And we can select the corresponding replacement text from the left panel. Note that both text snippets begin with a space, and end with a non-space character, because it is prudent to avoid trailing spaces in code fragments.

The search text and replacement text are shown pasted into the .cfg file in Figure 8. Also, note that the action directive is %trimreplace:%. The "trim" prefix is needed when you work with a code fragment.
800×316px

In Figure 8, note the two Mod Manager comments:

  • One listing the line number (within the pristine file) where the change occurs. It is not particularly useful to list the line number in the changed file (where the change actually is), because the changed file could also have changes from any number of mods, so it is impossible for the mod programmer to know what the target line number will be in the changed file.
  • A comment simply intended to inform future programmers about the similarity between the two target locations.

Location 3

We'll describe location 3 more succinctly than we did locations 1 or 2, since location 3 is virtually the same as location 2. In fact, rather that start location 3 by using location 3 from the old file, I copied-and-pasted the completed location 2, and used that text as my starting point for location 3.

We're also using a code fragment for location 3, and, will make sure to

  • include the text valign=\"top\" to distinguish this location from location 2, and
  • Start with a space and end with a non-space character as before


800×374px

Figure 10 shows a completed location 3 for whatsnew.php.
Mmexample10.png

The Second File's Target Locations

We also must remove the same code snippet from the doMedia function in functions.php that we removed twice from whatsnew.php. But, search, replacement, and insertion text only has to be unique within a file, so we don't have to worry about whether the functions.php search text is different from the 2 locations in whatsnew.php.

To start the second file's Mod Manager instructions, we need a %target directive, just as we did to start the first file.

Location 1

Location 1 in functions.php is a top-of-file comment, just like Location 1 in whatsnew.php. So Figure 11 is analogous to Figure 5. It shows a comparison of the beginning of the edited functions.php file with the beginning of the pristine functions.php, along with the portion of .cfg file ready for us to paste in the search text and insertion text for location 1 of functions.php I "seeded" location 1 by copying-and-pasting location 1 from whatsnew.php, and then removing the search text and replacement text. (This time I didn't add dummy text to show where the search text and replacement text will go, as I did with Figure 5.)
Mmexample11.png

As with location 1 of whatsnew.php, it is easy to recognize the new text to be inserted at this location in the left panel, and easy to recognize that the one-line function declaration at line 2 in the pristine file will serve as unique text.

So a completed location 1 for functions.php looks like this:
Mmexample12.png

Location 2

Now, location 2 in functions.php is almost identical to location 2 in whatsnew.php. In fact, since we don't have to worry about whether it is distinct from similar location in functions.php, we can use a shorter code fragment.

But, for illustration purposes, we'll take a completely different tack this time. Rather than use a code fragment, as we did with whatsnew.php, we will target the entire line of PHP code, and replace it with a comment on a line by itself plus a full line of modified PHP code, like this:
Mmexample13.png
Note that this style of change is particularly applicable when the change involves several lines in the target file, in which case, it is also useful to place a comment line at the end of the insertion/replacement.

Location 3 in the .cfg file thus looks like this:
800x334

We have now specified all of the changes needed for both target files, and can proceed to test the new mod.

Testing the Mod

Testing involves the following activities:

  1. Making sure that the mod status is "Installed"
  2. Uninstalling & Reinstalling the mod, to make sure that the mod returns the target files to their previous state.
  3. Retesting the TNG application, and making sure that the modifications perform as they should.

Installed Status

It is important to note that, using the programming process described at the beginning of this article, the TNG programs to be modified by the mod have already been changed. So, when you run the Mod Manager in the test environment, the mod's status should be Installed. If the status is anything else, we have not built it correctly, and we need to edit either the target files or the .cfg file, or perhaps both.

  • If the status is Not Installed, then something is very wrong, since that means that none of the mods locations have been installed correctly. The mod .cfg file or the edited target files are probably in the wrong folders.
  • If the status is Cannot Install or Partially Installed, then at least one of the target locations in the .cfg file is out of sync with its target file. You need to open the status cell in Mod Manager and look at the list of locations.
    • A location status of "Bad Target" means that neither the search text nor the replacement/insertion text was found in the target file.
    • A location status of "Not Installed" means that the search text was found, but not the replacement/insertion text.
    In either case, you can visually compare the target locations in the .cfg file and the target file, or, in BeyondCompare or Notepad++, you can compare the .cfg file to the target file. There will be very few matches between those two files, of course, but your search text and/or replacement/insertion text should match. So look for blank spots in the file map at the left of the BeyondCompare window, or search for text that should be in the search, replacement, or insertion text to see where the mismatch occurs. Small typos such as trailing spaces can cause these errors.

Once you achieve a mod status of Installed, you're through with this phase.

Uninstall & Reinstall

You need to make sure that your mod both installs and uninstalls correctly, and that, when uninstalled, it returns the file to its previous state. As you go through multiple edit-and-test cycles, it is easy to accidentally incorporate text - or, more particularly, whitespace - into your target locations in ways that don't return the file to its previous state.

The development process described above called on you to save the previous version of each file you change. Now, you should

  1. Backup your edited version so that you can make sure not to lose important code when you uninstall and reinstall.
  2. Uninstall the mod.
  3. Then, for each file:
    1. Compare the brand-new version with your backed-up previous version, and correct any differences.
    2. Reinstall the mod.
    3. Compare the even-newer version you now have with your backed-up edited version, and correct any differences.
    4. Repeat as necessary.

Test the App

As you must have done multiple times in the development process, make sure that TNG works correctly once you have re-installed your mod. It is a good idea to get help from other TNG user to test your mod in another production environment.

Check for Conflicts

As you install and uninstall your mod in your environment, make sure that no other mods change status. If other mods change status, then you have conflicts with those mods that you need to resolve. Make sure that anyone else who tests the mod for you does the same.

Also, (especially, but not only if you plan to publish your mod for others to use) you should search the TNG wiki for other mods that change the same files, and at least inspect those mods for possible conflicts. Ideally, you should install each of those mods, and look for both Mod Manager conflicts and functional conflicts. That is, some mods can be installed together without Mod Manager conflict, but don't work together.

Publishing your Mod

This is a very light treatment of this topic... If you want to make your mod available to others, then you need to

  • Pay extra attention to your internal documentation of your changes,
  • Pay extra attention to testing your mod against other published mods,
  • Write a TNG Wiki article about your mod, in which you
    • Describe the features of your mod,
    • Note which mods conflict with and/or coordinate with your mod,
    • Provide visualizations of the effects your mod has on TNG, and
    • Provide a way for users of your mod to contact you.

The mod in this article is actually a real mod that has been published at Mod User Support Form mod.

The Complete .cfg File

The mod described in this artice is a real mod that has been published on the TNG Wiki. The mod name has been changed to Whatsnew Hide Edit User, so you can go to the wiki article Whatsnew Hide Edit User to download the TNG version 10 mod described here, which is zipped-up as whatsnew_hide_editor_v10.0.0.1.zip. The updated (and renamed) copy of the mod in the wiki article is valid for TNGv11, TNGv12, and TNGv13, so if you want to install this mod, you'll probably need to install the updated copy.

You can view the mod right in this article by clicking on the [Expand] link on the right.
%name:Whatsnew Hide Editor%
%version:v10.0.0.1%
%description:Suppress the display of the username of the person who made each edit.  This mod is primarily useful for sites where there is only one administrator and that username is redundant. 
<br/>Mod developer is <a href="http://tng.lythgoes.net/wiki/index.php?title=User:Robinrichm" target="_blank">Robin Richmond</a>
<br/>Wiki article: <a href="http://tng.lythgoes.net/wiki/index.php?title=Whatsnew_Hide_Editor" target="_blank">Whatsnew_Hide_Editor</a>
%
%wikipage:Whatsnew_Hide_Editor%

Change history:
v1 - 10 Oct 2015 - Initial version

********************************************************************************
%target:whatsnew.php%

*************** whatsnew.php Location 1 - Top-of-file comments
%location:%
$textpart = "whatsnew";
%end:%
%insert:before%
# Modified by the Whatsnew Hide Editor mod to suppress display of the person who made each edit.
# It must make two identical changes in this file for the People and Families tables. Both remove
# a conditional expression that usually returns the username of the person who made the edit.
# Locations 2 and 3 are code fragment changes and are not fully documented by comments in those
# locations.  Location 2 handles Individuals (the People table), and Location 3 handles Families.

%end:%

*************** whatsnew.php Location 2 - Remove the username from the list of changes to Individuals.
*** line 175 in the pristine tng10.1.2 whatsnew.php ***
*** Note that this target location is nearly identical to location 3 ***
%location:%
"<td class=\"databack nw\">" . displayDate( $row['changedatef'] ) . ($currentuser ? " ({$row['changedby']})" : "")
%end:%
%trimreplace:%
"<td class=\"databack nw\">" . displayDate( $row['changedatef'] ) /* ### Whatsnew Hide Editor location 2 */
%end:%

*************** whatsnew.php Location 3 - Remove the username from the list of changes to the Families table.
*** line 243 ***
%location:%
valign=\"top\">" . displayDate( $row['changedatef'] ) . ($currentuser ? " ({$row['changedby']})" : "")
%end:%
%trimreplace:%
valign=\"top\">" . displayDate( $row['changedatef'] ) /* ### Whatsnew Hide Editor location 3 */
%end:%

*******************************************************************
%target:functions.php%

*************** functions.php Location 1 - Top-of-file comments
%location:%
function get_item_id( $result, $offset, $itemname ) {
%end:%
%insert:before%
# The doMedia function in this file has been modified by the Whatsnew Hide Editor mod v1 to suppress 
# display of the username of the person who made each edit.

%end:%

*************** functions.php Location 2 - Remove the username from the lists of media changes for the Whats New report
*** line 239 ***
%location:%
            $mediatext .= "<td valign=\"top\" class=\"databack\">" . displayDate( $row['changedatef'] ) . ($currentuser ? " ({$row['changedby']})" : "") . "</td></tr>\n";
%end:%
%replace:%
            ### Whatsnew Hide Editor Location 2 - One line change - remove the username of the person who made the change.
            $mediatext .= "<td valign=\"top\" class=\"databack\">" . displayDate( $row['changedatef'] ) . "</td></tr>\n";
%end:%

Related Links

Mod Manager

Mod Manager Controls

For Mod Developers

Technical

Developer Tools

Example Mods