Easing Txp plugin making

n: ied_plugin_composer | v: 1.2.1 | f: Development / Textpattern plugins

Documentation for the Textpattern plugin ied_plugin_composer by Stef Dawson follows this short message from our sponsor ;-)

Plugin list buttonPlugin download buttonCompressed plugin download button


Create, publish and edit plugins from within Textpattern CMS.

Creates a new page under the Extensions tab where you can edit and export plugins that are already installed in Textpattern, as well as create or upload new plugins created by the community.


  • Create, edit, upload or install plugin code and documentation, then publish your wares in the standard Textpattern plugin format.
  • Full support for the official zem_tpl.php plugin template and the modified “code first” version by net-carver, which also incorporates a ‘style’ segment for your help text (although it should be rarely needed).
  • Optional syntax checker to protect the site / admin side from bad code.
  • Export plugins in a variety of formats, allowing you to switch between editing in the plugin composer or your favourite editing program. You can export:
    • a standard BASE64-encoded text file.
    • a gzipped version (useful for large plugins).
    • a php file in the standard template format.
    • Textpacks on their own — any combination of languages.
  • Support for all plugin types: Library, Public, and Admin (with or without AJAX).
  • Specify a recommended plugin load order if your plugin needs special powers.
  • Documentation can be written in Textile or HTML.
  • Take advantage of the TinyMCE WYSIWYG editor for the help section, or a variety of javascript syntax highlighters / editors for code. See the setup section for more on the available editors.
  • Built-in Textile help viewer (thanks to net-carver’s Plugin Help Viewer) to allow you to preview your Help text during development.
  • Set a code “restore point” and roll back to that point if things go sideways. Also useful for returning plugin source code to its as-installed state.

Installation and uninstallation

Requires Textpattern 4.6.0+

Download the plugin (v0.5 and above) from stefdawson.com, paste the code into Textpattern’s Admin->Plugins page, install and enable the plugin.

The default preferences are automatically created when you install the plugin or visit the setup screen, by clicking the Setup button in the top right corner of the Extensions->Plugin composer panel. See the setup section for details.

To remove the plugin composer (noooo!) simply delete it as normal from the Admin->Plugins panel. All the preferences will automatically be removed as well. NOTE: deleting the plugin from the plugin composer window itself will not delete the preferences unless you have set the plugin to respond to ‘delete’ lifecycle events.

Alternatively, this plugin can be installed using Composer:

$ composer require bloke/ied_plugin_composer:*

List panel

At the top of the main page is a collapsible area labelled Installation. This is dealt with in the Creating plugins section; the rest of the page lists all installed plugins. The columns are:

Click the plugin name to edit it. If the plugin has preferences associated with it, you will also see an [Options] link.
If available, click the author to visit their site.
Version (Modified)
Shows the current version number and whether the plugin has been modified from when it was either created or installed. If it has, you can click the version number to restore the plugin code to its installed state or last restore point.
A brief one-line overview of what the plugin does.
Three links
Publish : exports the plugin as a .txt file for distribution to other Textpattern users.
Zip : exports the plugin as a compressed (gzipped) .txt file for distribution.
Help : displays the (textile processed) plugin documentation, if there is any.
All these tasks (and more) can be performed from the edit panel.
The recommended plugin load order from 1 (loaded first) to 9 (loaded last). 5 is default.
Note that if using the plugin cache directory this feature is only available if the $plugin['order'] string is in the template to begin with. So if your load order keeps returning to ‘5’, edit your template directly to add that string, or paste your plugin into the relevant parts of the empty plugin template and upload it.
Enable or disable a plugin by clicking entries in this column — this will only trigger any enabled/disabled lifecycle event in your plugin if you have set the plugin to do so.

Use the multi-edit checkboxes (you can shift click them to select more than one at a time) to delete plugins or make mass changes. Deletion will only trigger the ‘deleted’ lifecycle event if you have told the composer to allow plugins to respond to lifecycle events.

If you are using the plugin_cache_dir (Admin->Advanced Prefs), any plugins in the standard template format uploaded to this directory will be available immediately for editing and testing. You can edit and publish the plugins in the same manner as the regular, installed plugins above, with the following exceptions:

  • There is no concept of ‘modified’ or of restore points as the files always represent the most up-to-date version. Use your own external versioning.
  • Cached plugins are “always on” and do not need to be installed. To deactivate the plugin the file must be removed from the plugin cache directory.
  • It follows that the lifecycle notification events (installed, deleted, enabled, disabled) are not triggered from plugins in the cache directory.
  • The recommended load order cannot be changed from the list panel; it can only be altered via the edit panel. The specified load order is only written to the file when the plugin is exported. Load order is alphabetical for all plugins in the plugin_cache directory.

Creating, installing and naming plugins

There are a few ways of creating plugins from the composer’s list panel. Expand the ‘Installation’ twisty at the top of the composer’s list panel to see them all.

The first is to create a blank, empty plugin; use the first text box for this and the Create new plugin button. Points to note:

  • If you use a standard plugin name (e.g. abc_my_plugin) it will be created in the database.
  • If you add .php to the plugin name, it will be created in the plugin cache directory in the standard template format.
  • Avoid specifying the version of the plugin in the name — you should use the Rename file or Export options in the edit panel to alter the filename into your preferred filename format (see setup).

The second method of creating a plugin is to upload one using the Browse/upload box. Please note:

  • You can upload code as either:
    • a standard plugin base64 .txt file.
    • a standard plugin template (either code-first or help-first, complete with CSS areas).
    • a PHP file containing raw code only.
  • The plugin will always be installed in the database when using this method (upload template plugins manually via FTP or create a new one with a .php extension and paste the code into it if you want it in the plugin cache).
  • If the plugin exists it will be updated with your new info.
  • This is the only place you can upload raw PHP with <?php ?> markers. Everywhere else you should use a full template.
  • If you add a plugin Name in the box above the upload widget before clicking Upload, your plugin will take that name.

You may also install a plugin just like on the Admin->Plugins panel by copying and pasting the contents of a standard plugin .txt file into the Install textarea and hitting the Install button. Note there are a few differences from the core’s installer:

  • There is no preview step; the plugin is installed immediately, so be sure you trust the code!
  • You can choose whether the plugin is allowed to run its ‘install’ lifecycle event by using the radio buttons that appear.
  • You can elect to have the plugin auto-enable itself by selecting the appropriate radio button entry prior to clicking Install.
  • The radio buttons take on the default settings from the setup panel.

By convention, every plugin should be created with a three-letter prefix, an underscore, then the name of the plugin. You are free to choose your own three-letter prefix (usually your initials) subject to avoiding ones already taken by other plugin authors so people can get to know your work. Plus, it groups your plugins together in the plugin respository. All functions, variables (including DOM nodes), CSS classes, Textpack strings and anything else you inject into the global scope should be prefixed by at least your three letter code; preferably the whole plugin name (or abridged version thereof) to avoid namespace clashes with your own and other plugins.

Edit panel

This panel is where you craft your masterpiece. It is divided into various sections and boxes. The Panels area allows you to switch between the various parts of the plugin. Each plugin has:

  • Meta content such as name, version, and flags.
  • PHP code.
  • Textpack strings.
  • Documentation / styling.

You edit each part separately to reduce on-screen clutter, and when you switch subpanels your selection is remembered so that panel appears when editing other plugins. Hitting Save commits changes from the whole page — including subpanels that aren’t currently visible.

Meta subpanel

The name of your plugin, which you can change at any time. Note that if you are editing a file from the plugin cache directory and the $plugin['name'] row is commented out in the file, you will not be able to change the plugin name; it will always be the name of the file.
The current version of your plugin. You are free to choose your own versioning convention.
To the right of these boxes will be some optional items that appear depending on the current mode of operation:
Restore point (in-database plugins only): selecting this checkbox will (upon clicking the Save button) store the current code as a baseline to which you may “roll back” to at a later date. See restore points.
Rename file (cache_dir plugins only): by default, when you save a file in the plugin cache directory, it is overwritten with your changes. Once a plugin is released you would normally keep a copy, taken from the plugin_cache_dir, for safekeeping. If you then subsequently modify the plugin and increase the version number, you may wish to alter the filename as well. Checking this box will (upon clicking the Save button) rename the file in the plugin cache directory to reflect the current version number. See the setup panel for details on customising the filename format.
Enable (in-database plugins only): switch the plugin on or off after Save.
(file name) (cache_dir plugins only) : the current filename you are editing.
Very brief one-liner describing your plugin’s core function / reason for existence. 255 characters maximum.
Author :
Your home page or plugin page. Will be hyperlinked to your Author name in the list panel.
Plugin type
Choose one of the types that best fits the intended use of your plugin. If you choose ‘Public’ and try to access the admin side in your code, a warning will be issued when the plugin is saved so you can choose a more appropriate type.
Choose which plugin flags are to be associated with the plugin:
Has prefs : check this to indicate your plugin responds to the plugin_prefs.your_plugin_name event.
Event notify : check this to indicate your plugin responds to the plugin_lifecycle.your_plugin_name event/steps.
Load order
Choose the recommended order in which you think your plugin should be loaded by users. Most of the time, the default of ‘5’ is fine, but for special cases where your plugin has to set up an environment or has to wait for other plugins to load first, you might require one of the numbers either side. Be aware that this is a recommendation and is overridable by the site administrator. If the plugin is already deployed on a site, the load order that is already set will be used regardless of the setting of this value; only new installations will be set to this value by default.

Plugin code

Your plugin code goes here. Write your masterpiece and either click the main Save button (which saves everything: code, help, meta data, etc) or if you are simply updating just the code portion, use the Save code button at the top-right of the subpanel for rapid background saving. During this save process, the textarea dims to show it is working, though you can still continue to type if you wish. When the textarea returns to full visibility, it means the save process is complete. Your code is (optionally) run through a rudimentary syntax checker when using the Save Code button and any error is highlighted. If a syntax error is detected, your plugin is not saved.

Plugins are limited to 16Mb of code so there is also a character countdown just below the edit box. If you start approaching the limit(!), it might be worth considering splitting your plugin into a few parts or working for Micro$oft, where code bloat is acceptable.

In Firefox, Chrome and IE10+ you can use the Jump to line: textbox. Enter a line number and press Enter to jump to that line in the code. In other browsers, ymmv.

Textpack strings

Before Textpattern 4.3.0, any time you displayed a plugin-specific string to the user it made sense to write your own gTxt() function which packaged up the strings into one area of your plugin. While convenient, this meant that anyone who wanted to use your plugin in another lanugage had to edit the plugin code and rewrite your strings into the target language, which caused upgrade hassles.

This is no longer an issue, as Textpattern now has Textpacks: redistributable text files that contain language strings for direct insertion into the database.

A word of caution: the Textpack area works a little differently to the rest of the edit panel: most changes happen live as you type.

Before you begin you need to define a textpack prefix for the plugin. This is usually your three letter plugin prefix plus some unique identifier with which all strings in use by the plugin will begin. For example, the plugin composer uses ied_plugin (though it could have used ied_pcomp or ied_pc, etc). Note that using just your three letter prefix is probably not wise because your own future plugins might require a similarly-named replacement and the strings would clash. Of course, you might want to take advantage of this feature!

Once you enter the prefix and your cursor leaves the box, the composer will store the prefix and search the plugin code immediately for any references to such prefixed strings inside any function call with gTxt in it. Any it does find will be listed and you can immediately begin entering your replacement text in the currently selected language. Whenever the cursor leaves a text box its contents is saved directly to the database.

Textpack strings can be used on the Admin side, the Public site, or both. Choose the most appropriate location from the dropdown against each string.

If you create or rename a gTxt string in the code, when your cursor leaves the textarea the new string(s) will be created for you in the textpack area at the top of the list. Note however they are not written to the database until you supply a replacement string.

During the process of creating/renaming replacement strings, if it orphans another string then the orphan will be highlighted and an [x] button will appear next to it. If you wish to copy the old content out of the box and paste it into your renamed string, now is the time to do so. Once you’re sure you no longer need the string, hit the [x] button to immediately delete it from the database. It will be removed from all languages.

Please note:

  • if you programmatically refer to strings (e.g. by concatenating string parts together to form the gTxt string name, or iterating over a loop and using variable substitution) then they will not be automatically be detected. You will have to use the ‘+’ button to add such strings manually.
  • any programmatically-derived Textpack strings, or ones not inside a gTxt() function will show up with an [x] button. You should of course not delete these strings!
  • after you do a full save (i.e. not just a Save Code) all strings that are defined for the plugin will be displayed. Any derived strings will still be highlighted as potential orphans.
  • switching language from the select list will immediately load strings for that language.
  • clicking Load does the same task as switching language and is there in case you only have one language installed or wish to refresh the strings in the current language.

Importing strings from the current plugin

The success of the automatic find facility relies on two things:

  • that the strings are all prefixed.
  • your plugin’s Type is one of the AJAX types (4 or 5).

If you’re converting an old plugin to the Textpack methodology then you may have hundreds of strings that would be a bind to copy one by one. The composer can try to help you out, but it only works if you have a function or method in your plugin where the strings are replaced and returned. If you don’t have that, now might be the time to do so to save yourself some effort! It doesn’t have to be referenced in the code, it just has to exist and return a string for a given name.

Once you have that in place, here are some steps you can go through to convert your plugin and grab all the necessary strings en masse:

  1. inside your plugin’s gTxt() function, ensure that all keys are prefixed with your nominated plugin prefix.
  2. globally replace any reference to abc_plugin_gTxt(' with abc_plugin_gTxt('abc_prefix_, if not already.
  3. save the plugin from the composer window.
  4. enter the Textpack prefix if it’s not already set.
  5. give the name of your gTxt function, e.g. ied_plugin_gTxt in the Load strings from function box. If your gTxt() method is inside a class, specify class_name::method_name instead.
  6. hit ‘Go’.

If the composer can execute your chosen function then it’ll do so and return the strings as defined inside the function, populate each Textpack string and save it for you in the database automatically. Once that’s done and all strings are populated you can then:

  1. globally replace any calls to abc_plugin_gTxt with the core gTxt in your plugin code.
  2. delete your abc_plugin_gTxt function.

Textpack strings are also written to the file of any plugin you are editing in the cache_dir when you Save the plugin, as long as you are using a recent template that has the $plugin['textpack'] string in it.

Switching language

If at any time you want to see the installed textpack strings in other languages, simply use the select list to choose one. Any defined strings will be loaded into the textpack fields. You’ll see a counter whizzing up to show you how far it’s gone. As a translation aid, the equivalent string in your nominated default language (see setup) will be displayed as you hover over the textpack entry. You can choose to translate strings yourself or you can defer translation to other members of the community after the plugin is published. Textpacks can be linked to your textpattern.org plugin page by contributors and installed at any time from Textpattern’s Admin->Languages tab.

Plugin help

Documentation for detailing the plugin usage. Can (probably should!) be written using Textile. There are some documentation guidelines that serve as a good starting point. Note that the character countdown here is only approximate because when your plugin is saved and the help is converted to HTML, it usually takes up more space than Textile; please check that your help file renders correctly when your plugin is exported.

The Style box is for any CSS style rules you wish to apply to your documentation, although you should not need this with modern admin themes. You are encouraged to reuse the admin’s core CSS rules as often as possible, but if there isn’t one that suits, it’s best to target your documentation specifically by surrounding the entire Plugin Help section with something like: <div id="abc_help">h1. Docs go here...</div>.

Note that both the Plugin Help and Style are governed by a size limit. Since they are both stored in the same 64KB field, the size is shared between them. Styles are not passed through the Textile processor and you don’t need to add the <style> tags; the composer will do that for you (but see the notes).

Click the [Docs] link alongside the Plugin help heading to show the documentation as users will see it (i.e. Textile processed into HTML). Any help text over 64KB in length is truncated so you can verify that all your documentation fits into the users’ destination database.


Once you have saved your plugin, there is a section that allows you to export your plugin in a variety of formats. A language select box governs how Textpacks are dealt with. If you choose the first (empty) item then no Textpack information is included in the exported file. The only exception is when you save Textpacks directly: the first item is a shortcut for ‘all languages’ to save you having to select them all.

  • Plugin code for distribution : is a direct copy ‘n’ paste area that contains your entire plugin + docs. You can take this entire area and paste it into the ‘Install plugin’ box. Since this adds to the time it takes to do a full save of the plugin, this portion is optional and can be enabled from the setup panel.
  • Export as abc_myplugin.txt : converts the plugin help to HTML and saves the plugin to your computer as a redistributable text file for other Textpattern users to install. If you have chosen any Textpack languages from the adjacent select list, they will be bundled with your plugin and thus installed automatically if the language matches the one in use by the administrator who installs the plugin.
  • Export as abc_myplugin.txt (compressed) : converts the plugin help to HTML and saves the plugin as a redistributable, gzipped text file for other Textpattern users to install. Useful for large plugins or to offer an alternative for people who have stringent size limits imposed by their host. Textpack strings are bundled too if you have chosen at least one language from the select list.
  • Export as abc_myplugin.php : saves the plugin in a Textpattern standard template format. Useful for keeping the plugin for yourself — complete with Textiled help markup — so it can be later edited and re-issued / updated or shared with other developers who have the plugin composer or zem_tpl.php compiler. Textpack strings are bundled as part of the template if you choose.
  • Export Textpack(s) : choose one or more languages from the select list to gather all your plugin’s Textpack strings and download them as a redistributable text file for other textpattern users to install or modify. You (or anyone else) will (one day) be able to upload links to Textpacks on textpattern.org so they are available from a central location.

Note that when exporting as a standard plugin, the Textile processor attempts to decide if you have used Textile or not; it simply looks for a textiled header (h1. through h6.). Running pure HTML through Textile may occasionally cause encoding issues depending on the original character set used, so it is always best to try and stick to Textile as the documentation system.

Restore points

When a plugin is installed, a copy is kept in the database. If the plugin code (not help text) is subsequently edited, the plugin is considered “modified”; indicated in the plugin composer’s list panel. Sometimes you may wish to revert any changes back to the as-installed state.

Any time a plugin is marked as ‘modified’, the version number becomes clickable from the list panel. Clicking it (and confirming you are sure) will wipe out any changes you made and return the plugin to its installed state.

During the editing process of your own plugins, it may be that at certain times you wish to put a stake in the ground and say “this is my current baseline that I might want to return to later”. Perhaps you are about to make some major edits or try something experimental and want an easy fallback mechanism. That’s where the “Restore point” checkbox comes in.

By checking the box when you fully save your plugin, the current code will become your new rollback point and the plugin will no longer be marked “modified”. Any changes made beforehand will not be recoverable so you will have to rely on your own backups if you wish to go back further. Any edits you make after creating a restore point can be undone by visiting the list panel and clicking the version number next to your plugin. Currently, only one rollback point can be stored in the database. Note that the “Restore point” checkbox is ignored during saving of meta data only, since you are not saving any code.

Setup panel

Clicking Setup from the main list panel allows access to the plugin settings.

Plugin editor
You may choose to edit the code using a 3rd party syntax highlighter. Current support is for EditArea, CodeMirror and CodePress.
Choose the system here, and specify the URL of the scripts and stylesheets in the box beneath. If your chosen editor requires more than one file to be loaded then list them all, separated by commas. If any of the files are stylesheets, prefix the name with css: so the plugin knows to insert a <link rel="stylesheet" ...> instead of a <script> tag.
If you want to supply any configuration options to the editor, type them in the Plugin editor configuration options textarea. Write them as name:value pairs separated by comma, exactly as you would if initialisaing the script according to their documentation.
Plugin editor width
The size of the textareas on the edit panel. Set to suit your screen resolution. Include the units (px, em, %, etc) if you wish; it’ll default to pixels if you omit them.
Help editor
You may choose to edit the help manually via Textile or use TinyMCE for a more WYSIWYG experience.
In a similar manner to the plugin code editor you need to tell the composer the URL to the javascript file. You can also supply configuration options. The defaults should give you a head start on how to use them: consult the TinyMCE documentation for more.
Optional interface elements
Choose which parts of the edit panel you want to be visible in the interface.
Perform lifecycle actions on
When plugins are installed, enabled, disabled or deleted, plugins can run code to perform installation or cleanup actions. Choose which ones you wish to permit when installing, enabling/disabling or deleting plugins from the composer’s list panel.
The value of the ‘install’ checkbox chosen here becomes the default option in the Perform post-install actions radio button in the Installation subpanel.
Auto-enable plugins on install
Whether to allow plugins to automatically switch on after installation, or to retain their previous status.
The setting chosen here becomes the default radio button selection under the Install textarea on the list panel.
Syntax check on code save
Whether to run the code through the syntax checker or not when you use the Save Code button. Highly recommended to save your admin side from breakage during development.
Textpack language list
Whether to only allow editing of strings from the currently installed languages, or all available Textpattern languages.
Default Textpack language
The primary language for your plugin strings (default = any). If you choose a language that is not installed (and you’re limiting the language list to only those installed) the default language will revert to your current admin-side language.
Note that if you set a language here, when you export Textpacks or build plugins, that will be the language expected to be installed in a user’s Textpattern. If you leave it at ‘any’, the language marker is omitted from the Textpack and thus the strings bundled with the plugin (which may be in English) will install into whichever language is the user’s default (which may be something other than English). By forcing the default language, you force the language marker in the plugin. Thus if your users don’t have that language installed (e.g. someone only has nl-nl installed and not English) and they install your plugin, it will look at the installed language (nl-nl), compare it to the one in the plugin (en-gb), find it doesn’t match and will skip the Textpack installation. This will leave the user’s interface with lots of ugly abc_plugin_some_item strings instead of the actual translated content.
This could be handy if you are distributing Textpacks separately and have a good stock of them, or have loads of Textpacks bundled with the plugin, but for 95% of cases it is best to leave the default textpack language at Any so users of your plugin are guaranteed to get some translated strings, even if they are not in their ‘local’ language. They can then at least translate them and make the Textpack available to other users.
PHP export order
When saving your plugin in the standard template format, this governs whether you prefer the code block to be at the top of the file and the help block below, or vice versa.
Export plugin filename format
Export compressed filename format
Export template filename format
Textpack filename format
These define the format of the filenames when you export plugins/Textpacks. The first is for when you export standard BASE-64 plugins; the second is for compressed plugins; the third is for exporting a standard PHP template, and finally for exporting Textpacks.
Wherever you type {name}, the plugin name will appear. Similarly, {version} will be replaced with the current plugin version number. And {lang} will be replaced with either 1) the chosen language code, like en-gb; 2) ‘all’ if you chose to export all textpacks as one file; 3) an abbreviated list of countries to which the languages in the pack apply if you choose to export more than one, e.g. en+fi+nl+fr+de.
You can type anything you like in these boxes, but it’s more useful to include the replacement strings somewhere in each box so you don’t get name / version clashes. For example, if you don’t like the fact that zipped plugins are exported as pfx_my_plugin_v0.1_zip.txt, you can change it. Perhaps you may prefer pfx_my_plugin-compressed-0.1.txt. In which case, set the 2nd box to {name}-compressed-{version}.txt.
Note the extension should usually be specified so your system/browser knows the file’s type when it is exported, but it’s not mandatory as the MIME type is given, so (good) browsers should read that.
Cache Textiled help path
If you wish to take advantage of help cacheing, put the path to a temporary directory in the box. Empty the box if you prefer saves and exports to be slower!
Defaults to Textpattern’s temporary directory.

Notes / known issues

  1. When a plugin is saved to the plugin_cache_dir, if you have not put <style> markers in your CSS block they will be added for you in the text area but not saved in the actual template until you save it again (exporting as a PHP file is unaffected). So if you are in the habit of manually downloading the file from your FTP client immediately after a save, just save the plugin again to be sure.
  2. Loading Textpack strings from a gTxt function won’t work unless you (perhaps temporarily) switch your plugin to one of the AJAX types.

Writing a plugin

You should be aware of the Plugin Author Resources topic on the Textpattern Support Forum, and you might also want to have a look at the tutorials and guides for Plugin development in the Textpattern documentation.

Happy plugin authoring :-)


Original plugin: Yura Linnyk
Modifications (v0.5+): Stef Dawson
A touch of class: Steve Dickinson
Plus help from a plethora of forum contributors too numerous to mention. You know who you are, thank you ;-)

Source code

If you’d rather spend time with the bytes, you’ll need to step into the view source page.

Legacy software

If, for some inexplicable reason, you need the un-current version of a plugin, it can probably be found on the plugin archive page.

Experimental software

If you’re feeling brave, or fancy trying it without protection, you can test out some of my beta code. It can be found on the plugin beta page.