RSS Feed

Building a Transport Package: The Right Way

I want to say up front that a great deal of the code in this package was written by Shaun McCormick for the build scripts of various MODx Revolution components or inspired by Shaun's code. Shaun's modExtra package was immensely helpful in the development of this package.

This sample build script will build an actual Transport Package that can be installed in MODx Revolution. It installs a number of useless resources, elements, menus, system settings, and a category called MyComponent. It also creates language strings for elements and for element properties. The elements have properties with property descriptions and they're placed in the MyComponent category. The resources are placed in a resource group called MyComponent. The plugins are attached to a couple of system events. In addition, there is a script that interacts with the user during the install, a couple of file resolvers that transfer the assets and core directories, and a script resolver that does some final work at the end of the install process. There is also a readme.txt file, a GPL license file, and a readme.md file for GitHub.

The script is designed so that when you need to create a package, you can copy it to a directory and modify it to meet your needs.

No build script could do everything you need for every package, but this one should handle almost any kind of package you might want.

Don't be alarmed by the size and complexity of the script. Most of it will go unused in many packages, and it's easy to make the script skip things you don't need.

By changing the variables at the top of the build.transport.php file, you can customize the build script to include just the components you need. Of course you can also delete the files and directories you don't need.

A Proper Transport Package

In creating a Transport Package, it's not enough to just install the various pieces of the component. Even a simple snippet will have properties (parameters). Those properties should usually be installed as default properties of the snippet. The properties also need descriptions, which should be presented as lexicon strings so that they can be translated into other languages. Any messages displayed by the snippet (e.g., prompts and error messages) should also be in the form of MODx lexicon strings.

The snippet also needs a namespace with a corresponding namespace path so that MODx can find the lexicon strings. In addition, the snippet and any other parts of the package should be in a separate category named for your package. The files in the package also need to have appropriate comments at the top saying who wrote them, what they do, and in the case of executable files, a short version of the GPL license.

All that sounds a lot more difficult than it is. The MyComponent package is designed to make it as easy as possible by providing example templates for all the files necessary for a proper package and putting them in their proper places on your site.

The assumption is that you will put the files for any MODx add-ons you develop under the assets/mycomponents/ directory. When located there, MODx will leave them alone during site upgrades and they will not interfere with your component when you install it on the same site. MyComponent, which serves as an example and a template for each add-on, will be installed in the assets/mycomponents/mycomponent/ directory. If you develop an add-on called MySnippet, all of its files would then go in the assets/mycomponents/mysnippet/ directory.

Let's take a quick look at the process of developing an add-on using MyComponent

A Brief Overview

To build a Transport package for a component of your own, you'd first copy the mycomponent directory, and all the files under it, to a new directory under assets/mycomponents/ and rename it to match the name of your package.

The next step is to paste your own code into the appropriate files in the new directory, then rename them and edit the build.transport.php file (the script that actually builds the Transport Package) to meet your needs. You can do global search-and-replace operations to change the various strings in the files, to match your name, the package name, your site name, your email, etc.

You'll also need to edit the lexicon files to contain the strings necessary for your package and edit any validator or resolver files if you need them for your package. Don't forget to edit the variables at the top of the resolver file (like the ones in build.transport.php).

When you run build.transport.php, your package will be created in the core/packages directory of your site. The final step is to submit that Transport Package .zip file at http://modxcms.com/extras

Getting Started

Because the MyComponent package builds a package, it can't be installed directly. It would be missing the _build directory and it would actually install a number of objects on your site that you don't want.

Instead, install the GetMyComponent package in Package Manager. It will create the the assets/mycomponents directory (a good place for all your components) and put the mycomponent directory below that. When you execute the mycomponent/_build/build.transport.php file, it will create a transport package called MyComponent and place it in your core/packages directory. Try that first, and then go to Package Manager and click on "Add Package" and "Search Locally for Packages."

Try installing the MyComponent package and look around for it's various parts. Then try uninstalling it.

Tools

One of the handiest things you can have is a code editor that will do multi-file search and replace. You can use that to change MyComponent to the name of your package (but don't do it yet).

It's particularly handy to have a code editor that will "refactor" for you. Both NetBeans and JetBrain's PhpStorm will do this. If you right-click on a file and select refactor -> rename, you can change the name of the file and the editor will search for all references to that file in your project and rename them to match. Always do this before editing the file content.

Most of the components in the MyComponent package have names like "mychunk1.chunk.tpl," "mysnippet1.snippet.php," "myplugin1.events.php," "properties.mysnippet1.php," etc. You can refactor these to use the appropriate names for your components and the references to them in the various files of the project will change to match. If you do the search-and-replace operations and the refactoring in the order specified later in this tutorial, you should have very little work left to finish naming everything in the package.

Rather than replacing the existing files with your own, it's almost always better to paste your own code into the files or edit them to meet your needs. That way, the PhpDoc comments at the top of the files will be correct and any necessary GPL license language will be there as well.

Starting a New Package

It's recommended that you not change anything in the mycomponent directory, with the exception of the changes you want in all your packages, e.g., author names, URLs, email addresses, copyright notices, etc. Don't change MyComponent.

Once you've made those changes, it's not a bad idea to make it read only so that you won't modify anything by accident. It's easy to get confused about where you're working or to accidentally run search-and-replace or refactor on too many files. Another good way to protect it is to just put it in a safe place, far away from your development directory or in a .zip file.

Start by copying the whole mycomponent directory (or just the parts of it you need) to a new directory (with a new name) under the assets/mycomponents directory. Name the directory for your package. If you want a package called "BobsPackage," for example, you'd create a directory under assets/mycomponents called bobspackage, containing the _build, core, and assets directories and the readme.md file.

Rename the two mycomponent directories inside your package: core/components/mycomponent and assets/components/mycomponent. Use lowercase names. You'll have to modify the references in the _build/build.transport.php file to match the changes.

Do the Renaming and Refactoring

  • Start by changing the language string prefix in the language files with search-and-replace. All the language strings have the mc_ prefix (mc for my component). Change that to an appropriate prefix for your package. You could use the full name of your package to make sure there won't be collisions with MODx strings and those of other packages.
  • If you didn't do so earlier, do a search-and-replace for Your Name and replace it with your actual name. Then search for http://bobsguides/com and replace it with your email address. Search for YourGitHubUserName and replace it with your GitHub username (if any).
  • Refactor the file names. Change the name of all the elements and resources to the names you want to use in your package. Watch what's happening to make sure that nothing outside your package is changed. If you're using PhpStorm, be sure to check the options to search for references and to look in comments and strings. Change any property file names.
  • Search-and-replace Operations
    1. Do a case-sensitive global search-and-replace to change mycomponent to the lowercase name of your package.
    2. Do another case-sensitive search-and-replace to change MyComponent to the camel-case name of your component.
    3. Search for 1/1/11 and change it to the current date.
    4. Search for 2011 and replace it with the current year.
    5. Search for yoursite.com and change it to the domain name of your site.
    6. Check the file paths in the _build/data/transport.*.php files.
    7. Search for Description and add a description for each file.
  • Check the lines that refer to other files in the build.transport.php file, and the transport.*.php files to makes sure they match the renamed files.
  • Look through the files to see if anything was missed. In most cases, you'll get a warning during the build for any path or filename references that are not correct.
  • Rewrite the readme.txt file. If you use GitHub, rewrite the readme.md file. It's in MarkDown format, use the tool at http://daringfireball.net/projects/markdown/dingus to get it to look right before pushing to GitHub. Paste the text of the file into the input section at the to and click on the "Convert" button. Scroll down to the bottom to see the rendered version of the text. If it doesn't look right (and it probably won't), edit the text at the top and try again until it looks good, then paste it back into your readme.md file.

Configuring the Build

Edit the variables at the top of _build/build.transport.php to set the package name and version and to eliminate the components that your package doesn't have. You can delete the unwanted files and directories, or you can leave them there in case your package gets more complicated in the future. As long as the variables are set to false, the script will ignore them and they won't be placed in the package. Edit the variables at the top of _build/resolvers/install.script.php to meet your needs.

Here is the list of the variables that you'll need to set:

  • define('PKG_NAME','MyComponent');
  • define('PKG_NAME_LOWER','mycomponent');
  • define('PKG_VERSION','1.0.0');
  • define('PKG_RELEASE','beta1');
  • define('PKG_CATEGORY','MyComponent');

  • $hasAssets = true; /* Transfer the files in the assets dir. */
  • $hasCore = true; /* Transfer the files in the core dir. */
  • $hasSnippets = true;
  • $hasChunks = true;
  • $hasTemplates = true;
  • $hasResources = true;
  • $hasValidator = true; /* Run a validator before installing anything */
  • $hasResolver = true; /* Run a resolver after installing everything */
  • $hasSetupOptions = true; /* HTML/PHP script to interact with user */
  • $hasMenu = true; /* Add items to the MODx Top Menu */
  • $hasSettings = true; /* Add new MODx System Settings */
  • $hasTemplateVariables = true;
  • $hasTemplates = true;
  • $hasPlugins = true;
  • $hasPluginEvents = true;

Note that if you want to connect Template Variables to their templates or connect plugin events to their plugins, you'll do that in a resolver (see _build/resolvers/install.script.php).

Plug In Your Code

Paste your plugin, template, snippet, resource content, chunk content, etc. into the appropriate files. After you change each file, edit the related _build/data file(s) and lexicon files to match.

After pasting your snippet code into the *.snippet.php file, for example, edit the _build/transport.snippets/php file to set the snippet name and the other snippet fields. Put the appropriate language strings into the core/lexicon/en/default.inc.php file. If your snippet (or other element) has default properties, edit the appropriate _build/properties file and then add the property strings to the core/lexicon/en/properties.inc.php file.

Put any class files in the core/model/mycomponent/ directory and make sure the references to them in any plugin or snippet code are correct. The correct reference would be something like:

MODX_CORE_PATH . components/mycomponents/model/mycomponents/myclass.class.php

Edit the transport scripts in the _build/data directory to remove the sections for items you don't need, correct the fields for your elements and resources (if any), and correct the paths to the elements.

Validators run before the package components have been installed. They're often used to test for necessary items for your package to run (e.g., a graphics library or another installed element your package uses, such as getResources). If you need a validator, edit the code in _build/validators/preinstall.script.php to meet your needs.

Resolvers run after the package components have been installed. They perform operations that aren't done in the install, such as connecting TVs to their templates, connecting plugin events to their plugins, setting the values of existing System Settings, setting System Settings or properties that depend on the the IDs of installed elements or resources (which aren't available during the install), etc. If you need a script resolver, edit the code in _build/resolvers/install.script.php to meet your needs.

Do a global search for Description and edit the description of each item.

Run the Build Script

Execute the _build/build.transport.php script either in your editor or from the command line. You should see warnings and errors about anything that is not correct. Fix those.

Install the Package

The package will be placed in the core/packages directory (the one for the main MODx install, not the one in your package).

There is a potential source of confusion here. Let's say your package is called yourcomponent. If you install your package in the same install of MODx that contains your _build directory, you'll have duplicate files in

    assets/mycomponents/yourcomponent/assets/yourcomponent

and

    assets/yourcomponent

as well as

    core/mycomponents/yourcomponent/core/yourcomponent

and

    core/yourcomponent

There's no harm in this, but it's easy to get confused and edit the wrong file when you're working on the package. One solution is to install the package a completely separate MODx installation. The other solution is to be really careful about where you are when working on the package.

To install the package, once the transport .zip file has been copied to the core/packages directory, go to Package Manager and click on "Add Package" and "Search Locally for Packages." You should see your package in the grid. Install it and then check to make sure it's all there. Check the assets/components/yourcomponent and core/components/yourcomponent directories to make sure the files were transferred.

If you have to modify the build script or any files in the package, uninstall and remove (force removal) your package. Then you can run the build again, copy the .zip file, and try reinstalling.

Running Your Component

If you install your component it should run. If you need to make changes to any parts of it, remember that those changes won't show up when you run it until you uninstall, rebuild, and reinstall because MODx is running the version you installed, not the version you are building.

There's a way around that, thanks to a method developed by Shaun McCormick, but you have to remember what you're doing or it's easy to get confused.

If you would like to actually run the code for plugins and snippets that your build script is packaging, you can create new System Settings with the paths and URLs for that version. Then in the code of your snippets and plugins, get that path with a line like this:

    $path = $modx->getOption('mc.core_path',null,
        $modx->getOption('core_path').'components/yourcomponent/');

If, for example, you set the mc.core_path system setting to:

{assets_path}components/yourcomponent/core/components/yourcomponent/

You can load your class file in the snippet with code like this:

$iFile = $path . "model/yourcomponent/yourclass.inc.php";
require_once $iFile;

For you, the path will resolve to the area with your build script. For others who install the package, however, the system setting will come back empty, so the path will resolve to that of the installed component.

You can also replace the snippet and plugin code with something like this:

    return include $modx->getOption('mc.core_path') .
        'core/components/yourcomponent/elements/snippets/yoursnippet.snippet.php';

If you do this, however, don't forget that you are running off the build files. If any files are missing or mis-referenced in the build script, you won't know about it until you run your component in a MODx install that doesn't have the system settings.

If that's all too confusing, just skip it and remember to uninstall, build, and install your package each time you make a change.

Submitting Your Package

As we said earlier, when you run build.transport.php the Transport Package .zip file will be created in your site's core/packages directory. The final step is to submit that .zip file at http://modxcms.com/extras. You'll need to log in and then click on the "submit a new extra" link.

Be sure to fill in all the appropriate sections of the submission form and designate the file you attach as a Transport Package. If you have installed the package on your site, there will also be another file with the same name, but without the .zip extension. Be sure to submit the .zip file rather than the other file.

 

 

My book, MODX: The Official Guide is now available for order here. The book is currently being shipped.

If you have the book and would like to download the code, you can find it here.

If you have the book and would like to see the updates and corrections page, you can find it here.

MODX: The Official Guide is 772 pages long and goes far beyond this web site in explaining beginning and advanced MODX techniques. It includes detailed information on:

  • Installing MODX
  • How MODX Works
  • Working with MODX resources and Elements
  • Using Git with MODX
  • Using common MODX add-on components like SPForm, Login, getResources, and FormIt
  • MODX security Permissions
  • Customizing the MODX Manager
  • Using Form Customization
  • Creating Transport Packages
  • MODX and xPDO object methods
  • MODX System Events
  • Using PHP with MODX

Go here for more information about the book.

Thank you for visiting BobsGuides.com

  —  Bob Ray