Blame | Last modification | View Log | Download
Developers Guide================Reference for developers and community members----------------------------------------------[IMAGE]MantisBT Development Team<mantisbt-dev@lists.sourceforge.net>Copyright © 2016 MantisBT team. This material may only be distributedsubject to the terms and conditions set forth in the GNU FreeDocumentation License (GFDL), V1.2 or later (the latest version ispresently available at http://www.gnu.org/licenses/fdl.txt).AbstractThis book is targeted at MantisBT developers, contributors and pluginauthors. It documents the development process and provides referenceinformation regarding the MantisBT core, including the database schema aswell as the plugin system including an events reference.------------------------------------------------------------------------Chapter 1. Contributing to MantisBT===================================MantisBT source code is managed with Git. If you are new to this versioncontrol system, you can find some good resources for learning andinstalling it in Section 8.1, “Git References”.1.1. Initial Setup------------------There are a few steps the MantisBT team requires of contributors anddevelopers when accepting code submissions. The user needs to configureGit to know their full name (not a screen name) and an email address theycan be contacted at (not a throwaway address).To set up your name and email address with Git, run the followingcommands, substituting your own real name and email address:git config --global user.name "John Smith"git config --global user.email "jsmith@mantisbt.org"Optionally, you may want to configure Git to use terminal colors whendisplaying file diffs and other information, and also alias certain Gitactions to shorter phrases to reduce typing:git config --global color.diff "auto"git config --global color.status "auto"git config --global color.branch "auto"git config --global alias.st "status"git config --global alias.di "diff"git config --global alias.co "checkout"git config --global alias.ci "commit"1.2. Cloning the Repository---------------------------The official MantisBT source code repository is hosted at GitHub. Thisdocument assumes that you have already signed up for and setup a GitHubaccount.1.2.1. Determining the Clone URLWhich URL you will use to clone the repository before you startdeveloping depends on your situation.MantisBT Core Team DevelopersMantisBT developers have push access to the official repository.Benefiting from this access requires a special URL that uses yourSSH key to handle access permissions:git@github.com:mantisbt/mantisbt.git. Alternatively, an HTTPS linkcan be used as well, in which case you will have to provide yourGitHub User ID and password when Git requests it:https://github.com/mantisbt/mantisbt.git.NotePushes will fail if you do not have access or your public SSH keyis not set up correctly in your GitHub profile.ContributorsFor other people, the MantisBT repository and the related cloneURLs git@github.com:mantisbt/mantisbt.git (SSH). orhttps://github.com/mantisbt/mantisbt.git (HTTPS) will always beread-only.It is therefore strongly advised to create your own fork ofMantisBT where you will be able to push your changes, and then usethe fork's URL instead to clone, which will look like this:git@github.com:MyGithubId/mantisbt.git. orhttps://github.com/MyGithubId/mantisbt.git1.2.2. Initializing the CloneTo clone the repository, execute the following command from your targetworkspace:git clone YourCloneURLAfter performing the cloning operation, you should end up with a newdirectory in your workspace, mantisbt/, containing the MantisBTrepository with a remote named origin pointing to your Clone URL.MantisBT uses Composer to pull libraries and components from Packagistand Github. Install Composer and run the following command:composer installWarningFailure to execute the submodule initialization commands will result incritical components being missing from /vendor folder, which will thencause errors when running MantisBT.1.2.3. Adding remotesIf you are planning to use your own fork to push and maintain yourchanges, then we recommend setting up an upstream remote for MantisBT'sofficial repository, which will make it easier to keep your repositoryup-to-date.git remote add --tags upstream git://github.com/mantisbt/mantisbt.git1.2.4. Checking out branchesBy default, the new clone will only track code from the primary remotebranch, master, which is the latest development version of MantisBT. Ifyou are planning to work with stable release or other developmentbranches, you will need to set up local tracking branches in yourrepository.The following command will set up a tracking branch for the currentstable branch, master-1.3.x.git checkout -b master-1.3.x origin/master-1.3.xNoteWith the introduction of submodules for some of the third-partylibraries, you may encounter issues when switching to an older branchwhich still has code from those libraries in a subdirectory of /libraryrather than a submodule:$ git checkout old_brancherror: The following untracked working tree files would be overwritten by checkout(list of files)AbortingTo resolve this, you first have to get rid of the submodules directoriesbefore you can checkout the branch. The command below will move allsubmodules to /tmp:sed -rn "s/^.*path\s*=\s*(.*)$/\1/p" .gitmodules |xargs -I{} mv -v {} /tmpgit checkout old_branchAlernatively, if you don't care about keeping the changes in thesubmodules directories, you can simply executegit checkout -f old_branchgit clean -dfWhen switching back from the older branch, the submodules directorieswill be empty. At that point you can either*Update the submodules to reclone themgit submodule update*Restore the directories previously moved to /tmp back into the emptydirectories, e.g.sed -rn "s/^.*path\s*=\s*(.*)$/\1/p" .gitmodules |xargs -n 1 basename |xargs -I{} mv -v /tmp/{} libraryFor further reference: Pro Git book1.3. Maintaining Tracking Branches----------------------------------In order to keep your local repository up-to-date with the official one,there are a few simple commands needed for any tracking branches that youmay have, including master and master-1.3.x.First, you'll need to get the latest information from the remoterepository:git fetch originNoteIf you cloned from your personal GitHub fork instead of the officialMantisBT repository as explained in Section 1.2.3, “Adding remotes”, thenyou should instead execute:git fetch upstreamThen for each tracking branch you have, enter the following commands:git checkout BranchNamegit rebaseAlternatively, you may combine the fetch and rebase operations describedabove into a single pull command (for each remote tracking branch):git checkout mastergit pull --rebase1.4. Preparing Feature Branches-------------------------------For each local or shared feature branch that you are working on, you willneed to keep it up to date with the appropriate master branch. There aremultiple methods for doing this, each better suited to a different typeof feature branch. Both methods assume that you have already performedthe previous step, to update your local tracking branches (see Section1.3, “Maintaining Tracking Branches”).1.4.1. Private BranchesIf the topic branch in question is a local, private branch, that you arenot sharing with other developers, the simplest and easiest method tostay up to date with master is to use the rebase command. This willappend all of your feature branch commits into a linear history after thelast commit on the master branch.git rebase master featureNoteRebasing changes the ID for each commit in your feature branch, whichwill cause trouble for anyone sharing and/or following your branch.The resulting conflict can be fixed by rebasing their copy of your branchonto your branch:git checkout featuregit fetch remote/featuregit rebase remote/feature1.4.2. Public BranchesFor any publicly-shared branches, where other users may be watching yourfeature branches, or cloning them locally for development work, you'llneed to take a different approach to keeping it up to date with master.To bring public branch up to date, you'll need to merge the currentmaster branch, which will create a special "merge commit" in the branchhistory, causing a logical "split" in commit history where your branchstarted and joining at the merge. These merge commits are generallydisliked, because they can crowd commit history, and because the historyis no longer linear. They will be dealt with during the submissionprocess (see Section 1.5, “PHPUnit tests”).git checkout featuregit merge masterAt this point, you can push the branch to your public repository, andanyone following the branch can then pull the changes directly into theirlocal branch, either with another merge, or with a rebase, asnecessitated by the public or private status of their own changes.1.5. PHPUnit tests------------------MantisBT has a suite of PHPUnit tests, which can be found in the /testsdirectory. It is organized in 3 distinct Test Suites, defined in thephpunit.xml file:*mantis: MantisBT Core tests*soap: SOAP API tests*rest: REST API testsWarningThe unit tests are currently only covering a small part of the code base,focusing mainly on the REST and SOAP APIs. The MantisBT Core functionsare for the most part only tested indirectly through the APIs.You are encouraged to improve the test suite, by adding new tests for thepatches you are submitting. Please remember that your changes must notbreak existing tests.New Tests should be stored in the subdirectory corresponding to the TestSuite they belong to; the file should be named XxxxxTest.php, where Xxxxxis the Test's name. The Test.php suffix allows the script to beautomatically picked up by PHPUnit when running the Test Suite. Withoutthis suffix, the new script will have to be declared manually with a<file> tag in the phpunit.xml file's relevant <testsuite> section.1.5.1. Initial SetupThis section describes the initial configuration required to run the unittests.PHPUnitThe software is normally installed via Composer when the MantisBTrepository is initialized with composer install, which ensures thatyou have the appropriate version to run the unit tests.It is also possible to use another instance of PHPUnit, in whichcase you should refer to the composer.json file to find out whichversion to use.MantisBTMantisBT must be installed and operational on your computer, perthe admin guide. Additionally, the following seed data must becreated to ensure that all test cases can be executed*1 Project*3 Project Versions: Old (released, obsolete), Active (released,not obsolete) and Future (not released, not obsolete)*2 Tags*Anonymous user account (protected, enabled and with VIEWERaccess level)*1 REST API Token for the Administrator account used to run thetestsWarningIf the MantisBT instance is to be used for other purposes than unittesting, it is strongly recommended to create a dedicated projectfor the unit tests, to avoid any side-effects as the tests willgenerate a large number of various types of database records.ConfigurationThe following variables must be set in your config_inc.php file toensure all PHPUnit tests are executed. Without this, some testswill be skipped$g_allow_no_category = ON;$g_due_date_update_threshold = DEVELOPER;$g_due_date_view_threshold = DEVELOPER;$g_enable_product_build = ON;$g_enable_project_documentation = ON;$g_time_tracking_enabled = ON;$g_time_tracking_enabled = ON;$g_allow_anonymous_login = ON;$g_anonymous_account = 'username'; # of the anonymous account created abovePHPUnit Bootstrap fileThe tests are configured using a bootstrap.php file, which must beadjusted to run the tests on your local installation. Thetests/boostrap.php.sample file contains the settings you will needto adjust to run all the tests.At the minimum, you should set the following variables*MANTIS_TESTSUITE_USERNAME, MANTIS_TESTSUITE_PASSWORD andMANTIS_TESTSUITE_PROJECT_ID*For the SOAP Tests: MANTIS_TESTSUITE_SOAP_ENABLED andMANTIS_TESTSUITE_SOAP_HOST*For the REST API Tests: MANTIS_TESTSUITE_REST_ENABLED,MANTIS_TESTSUITE_REST_HOST and MANTIS_TESTSUITE_API_TOKENSOAP extensionThe PHP SOAP extension is required to run the SOAP tests.1.5.2. Running the testsTo run the tests, run the following command from the MantisBTrepository's root./vendor/bin/phpunitExecution can be limited to a subset or individual test units./vendor/bin/phpunit --testsuite rest./vendor/bin/phpunit tests/Mantis/ConfigParserTest.phpFor further details and options, refer to the PHPUnit manual.1.6. Submitting Changes-----------------------This section describes what you should do to submit a set of changes toMantisBT, allowing the project developers to review and test, your code,and ultimately commit it to the MantisBT repository.The actual submission can be done using several methods, described laterin this section:*Recommended: Github Pull Requests (see Section 1.6.2, “Submission ViaGithub Pull Requests”)*Other public Git repository Pull Requests (see Section 1.6.4,“Submission Via Public Repository”)*Git Formatted patches (see Section 1.6.3, “Submission Via FormattedPatches”)1.6.1. Before you submitBefore submitting your contribution, you should make sure that1.Your code follows the MantisBT coding guidelines2.You have tested your changes locally (see Section 1.5, “PHPUnittests”)3.Your local branch has been rebased on top of the current Masterbranch, as described in Section 1.4.1, “Private Branches”.1.6.2. Submission Via Github Pull RequestsSince the official MantisBT repository is hosted there, using GitHub isthe recommended (and easiest) way to submit your contributions.With this method, you can keep your changesets up-to-date with theofficial development repository, and likewise let anyone stay up to datewith your repository, without needing to constantly upload and downloadnew formatted patches whenever you change anything.The process below describes a simple workflow that can help you make yoursubmission if you are not familiar with Git; note that it is by no meansthe only way to do this.NoteWe'll assume that you have already forked MantisBT, cloned it locally asdescribed in Section 1.2, “Cloning the Repository” (remote upstream beingthe official MantisBT repository and origin your personal fork), andcreated a new feature branch (see Section 1.4, “Preparing FeatureBranches”) for your contribution, which we'll call MyBranch.1.Make sure that the MyBranch feature branch is up-to-date with themaster branch by rebasing it, resolving any conflicts if necessary.git fetch upstreamgit rebase upstream/master MyBranch2.Push the branch to your Github forkgit push origin MyBranch3.Go to your Fork on Github (https://github.com/MyGithubId/mantisbt)4.Initiate a Pull Request from your feature branch, following theguidelines provided in Github Help.Please make sure you provide a detailed description of the changesyou are submitting, including the reason for it and if possible areference (link) to an existing issue on our bugtracker. The teamwill usually review your changes and provide feedback within 7 days(but your mileage may vary).1.6.3. Submission Via Formatted PatchesFormatted patches are very similar to file diffs generated by other toolsor source control systems, but contain far more information, includingyour name and email address, and for every commit in the set, thecommit's timestamp, message, author, and more. They allow anyone toimport the enclosed changesets directly into Git, where all of the commitinformation is preserved.Assuming that you have an existing local that you've kept up to date withmaster as described in Section 1.4, “Preparing Feature Branches”currently checked out, generating a formatted patch set should berelatively straightforward, using an appropriate filename as the targetof the patch set:git format-patch --binary --stdout origin/master..HEAD > feature_branch.patchOnce you've generated the formatted patch file, you can easily attach itto a bug report, or even use the patch file as an email to send to thedeveloper mailing list. Developers, or other users, can then import thispatch set into their local repositories using the following command,again substituting the appropriate filename:git am --signoff feature_branch.patch1.6.4. Submission Via Public RepositoryIf you are not able or not willing to make use of a fork of the officialGitHub repository but have another publicly available one to host yourchanges, for example on a free hosting for public repository such as*Bitbucket*GitLabyou can still use it to submit a patch in a similar fashion to the Githubmethod described above, although the process is slightly morecomplicated.We'll assume you've already set up a publicly accessible repository atURL git@githosting.com:contrib.git, kept it up-to-date with MantisBT'sofficial repository, and that you have pushed your feature branchMyBranch to it.1.Generate the Pull RequestThis will list information about your changes and how to access them.The process will attempt to verify that you've pushed the correctdata to the public repository, and will generate a summary ofchanges.git request-pull origin/master git@githosting.com:contrib.git MyBranch2.Paste the output of the above command into a bug report or an emailto the developer mailing listOnce your pull request has been posted, developers and other users canadd your public repository as a remote, and track your feature branch intheir own working repository using the following commands, replacing theremote name and local branch name as appropriate:git remote add feature git@githosting.com:contrib.gitgit checkout -b MyBranch feature/MyBranchIf the feature is approved for entry into MantisBT core, then the branchshould first be rebased onto the latest HEAD so that Git can remove anyunnecessary merge commits, and create a linear history. Once that'scompleted, the feature branch can be merged into master:git rebase master featuregit checkout mastergit merge --no-ff featureChapter 2. Database Schema Management=====================================2.1. The MantisBT schema------------------------The MantisBT database schema (excluding plugins) is described in theEntity-Relationship diagram (ERD) below. There is also a PDF versionavailable for download.Figure 2.1. MantisBT Entity-Relationship DiagramMantisBT Entity-Relationship Diagram2.2. Schema Definition----------------------TODO: Discuss the ADODB datadict formats and the format MantisBT expectsfor schema definitions.2.3. Installation / Upgrade Process-----------------------------------TODO: Discuss how MantisBT handles a database installation / upgrade,including the use of the config system and schema definitions.Chapter 3. Event System=======================3.1. General Concepts---------------------The event system in MantisBT uses the concept of signals and hookedevents to drive dynamic actions. Functions, or plugin methods, can behooked during runtime to various defined events, which can be signalledat any point to initiate execution of hooked functions.Events are defined at runtime by name and event type (covered in the nextsection). Depending on the event type, signal parameters and returnvalues from hooked functions will be handled in different ways to makecertain types of common communication simplified.3.2. API Usage--------------This is a general overview of the event API. For more detailed analysis,you may reference the file core/event_api.php in the codebase.Declaring EventsWhen declaring events, the only information needed is the event nameand event type. Events can be declared alone using the form:event_declare( $name, $type=EVENT_TYPE_DEFAULT );or they can be declared in groups using key/value pairs of name =>type relations, stored in a single array, such as:$events = array($name_1 => $type_1,$name_2 => $type_2,...);event_declare_many( $events );Hooking EventsHooking events requires knowing the name of an already-declaredevent, and the name of the callback function (and possibly associatedplugin) that will be hooked to the event. If hooking only a function,it must be declared in the global namespace.event_hook( $event_name, $callback, [$plugin] );In order to hook many functions at once, using key/value pairs ofname => callback relations, in a single array:$events = array($event_1 => $callback_1,$event_2 => $callback_2,...);event_hook( $events, [$plugin] );Signalling EventsWhen signalling events, the event type of the target event must bekept in mind when handling event parameters and return values. Thegeneral format for signalling an event uses the following structure:$value = event_signal( $event_name, [ array( $param, ... ), [ array( $static_param, ... ) ] ] );Each type of event (and individual events themselves) will usedifferent combinations of parameters and return values, so perusingChapter 5, Events Reference is recommended for determining the uniqueneeds of each event when signalling and hooking them.3.3. Event Types----------------There are five standard event types currently defined in MantisBT. Eachtype is a generalization of a certain "class" of solution to the problemsthat the event system is designed to solve. Each type allows forsimplifying a different set of communication needs between event signalsand hooked callback functions.Each type of event (and individual events themselves) will use differentcombinations of parameters and return values, so perusing Chapter 5,Events Reference is recommended for determining the unique needs of eachevent when signalling and hooking them.EVENT_TYPE_EXECUTEThis is the simplest event type, meant for initiating basic hookexecution without needing to communicate more than a set of immutableparameters to the event, and expecting no return of data.These events only use the first parameter array, and return valuesfrom hooked functions are ignored. Example usage:event_signal( $event_name, [ array( $param, ... ) ] );EVENT_TYPE_OUTPUTThis event type allows for simple output and execution from hookedevents. A single set of immutable parameters are sent to eachcallback, and the return value is inlined as output. This event isgenerally used for an event with a specific purpose of adding contentor markup to the page.These events only use the first parameter array, and return valuesfrom hooked functions are immediately sent to the output buffer via'echo'. Another parameter $format can be used to model how theresults are printed. This parameter can be either:*null, or omitted: The returned values are printed without furtherprocessing*<String>: A string to be used as separator for printed values*<Array>: An array of (prefix, separator, postfix) to be used forthe printed valuesExample usage:event_signal( $event_name, [ array( $param, ... ) ], [ $format ] );EVENT_TYPE_CHAINThis event type is designed to allow plugins to successively alterthe parameters given to them, such that the end result returned tothe caller is a mutated version of the original parameters. This isvery useful for such things as output markup parsers.The first set of parameters to the event are sent to the first hookedcallback, which is then expected to alter the parameters and returnthe new values, which are then sent to the next callback to modify,and this continues for all callbacks. The return value from the lastcallback is then returned to the event signaller.This type allows events to optionally make use of the secondparameter set, which are sent to every callback in the series, butshould not be returned by each callback. This allows the signallingfunction to send extra, immutable information to every callback inthe chain. Example usage:$value = event_signal( $event_name, $param, [ array( $static_param, ... ) ] );EVENT_TYPE_FIRSTThe design of this event type allows for multiple hooked callbacks to'compete' for the event signal, based on priority and executionorder. The first callback that can satisfy the needs of the signal isthe last callback executed for the event, and its return value is theonly one sent to the event caller. This is very useful for topicslike user authentication.These events only use the first parameter array, and the firstnon-null return value from a hook function is returned to the caller.Subsequent callbacks are never executed. Example usage:$value = event_signal( $event_name, [ array( $param, ... ) ] );EVENT_TYPE_DEFAULTThis is the fallback event type, in which the return values from allhooked callbacks are stored in a special array structure. This allowsthe event caller to gather data separately from all events.These events only use the first parameter array, and return valuesfrom hooked functions are returned in a multi-dimensional array keyedby plugin name and hooked function name. Example usage:$values = event_signal( $event_name, [ array( $param, ... ) ] );Chapter 4. Plugin System========================4.1. General Concepts---------------------The plugin system for MantisBT is designed as a lightweight extension tothe standard MantisBT API, allowing for simple and flexible addition ofnew features and customization of core operations. It takes advantage ofthe new Event System (see Chapter 3, Event System) to offer developersrapid creation and testing of extensions, without the need to modify corefiles.Plugins are defined as implementations, or subclasses, of theMantisPlugin class as defined in core/classes/MantisPlugin.class.php.Each plugin may define information about itself, as well as a list ofconflicts and dependencies upon other plugins. There are many methodsdefined in the MantisPlugin class that may be used as convenient placesto define extra behaviors, such as configuration options, eventdeclarations, event hooks, errors, and database schemas. Outside aplugin's core class, there is a standard method of handling languagestrings, content pages, and files.At page load, the core MantisBT API will find and process any conformingplugins. Plugins will be checked for minimal information, such as itsname, version, and dependencies. Plugins that meet requirements will thenbe initialized. At this point, MantisBT will interact with the pluginswhen appropriate.The plugin system includes a special set of API functions that provideconvenience wrappers around the more useful MantisBT API calls, includingconfiguration, language strings, and link generation. This API allowsplugins to use core API's in "sandboxed" fashions to aid interoperationwith other plugins, and simplification of common functionality.4.2. Building a Plugin----------------------This section will act as a tutorial to build a new plugin, from the barebasics all the way up to advanced topics.A general understanding of HTML, PHP and the concepts covered in the lastsection is assumed, as well as knowledge of how the event system works.Later topics in this section will require knowledge of database schemasand how they are used with MantisBT.This walk-through will be working towards building a single end result:the Example Plugin. You may refer to the final code along the way (seeSection 4.2.8, “Example Plugin source code”), although every part of itwill be built up in steps throughout this section.4.2.1. Plugin StructureThis section will introduce the general concepts of plugin structure, andhow to get a bare-bones plugin working with MantisBT. Not much will bementioned yet on the topic of adding functionality to plugins, just howto get the development process rolling.The backbone of every plugin is what MantisBT calls the basename, asuccinct, and most importantly, unique name that identifies the plugin.It may not contain any spacing or special characters beyond the ASCIIupper- and lowercase alphabet, numerals, and underscore. This is used toidentify the plugin everywhere except for what the end-user sees. For our"Example" plugin, the basename we will use should be obvious enough:Example.Every plugin must be contained in a single directory, named to match theplugin's basename, as well as contain at least a single PHP file, alsonamed to match the basename, as such:Note that for plugins that require a database schema to operate, thebasename is also used to build the table names, using the MantisBT tableprefixes and suffix (please refer to the Admin Guide's Configurationsection for further information). If our Example plugin were to create atable named 'foo', assuming default values for prefixes and suffix inMantisBT configuration, the physical table name would bemantis_plugin_Example_foo_table.Example/Example.phpWarningDepending on case sensitivity of the underlying file system, these namesmust exactly match the plugin's base name, i.e. example will not work.This top-level PHP file must then contain a concrete class deriving fromthe MantisPlugin class, which must be named in the form of%Basename%Plugin, which for our purpose becomes ExamplePlugin.Because of how MantisPlugin declares the register() method as abstract,our plugin must implement that method before PHP will find itsemantically valid. This method is meant for one simple purpose, andshould never be used for any other task: setting the plugin's informationproperties including the plugin's name, description, version, and more.Please refer to Section 4.2.2, “Properties” below for details aboutavailable properties.Once your plugin defines its class, implements the register() method, andsets at least the name and version properties, it is then considered a"complete" plugin, and can be loaded and installed within MantisBT'splugin manager. At this stage, our Example plugin, with all the possibleplugin properties set at registration, looks like this:Example/Example.php<?phpclass ExamplePlugin extends MantisPlugin {function register() {$this->name = 'Example Plugin'; # Proper name of plugin$this->description = 'Example Plugin from MantisBT Developers Guide';# Short description of the plugin$this->page = ''; # Default plugin page$this->version = '2.0'; # Plugin version string$this->requires = array( # Plugin dependencies'MantisCore' => '2.0', # Should always depend on an appropriate# version of MantisBT);$this->author = 'MantisBT Team'; # Author/team name$this->contact = 'mantisbt-dev@lists.sourceforge.net';# Author/team e-mail address$this->url = 'https://mantisbt.org'; # Support webpage}}This alone will allow our Example plugin to be installed with MantisBT,and is the foundation of any plugin. More of the plugin developmentprocess will be continued in the next sections.4.2.2. PropertiesThis section describes the properties that can be defined whenregistering the plugin.nameYour plugin's full name. Required value.descriptionA full description of your plugin.pageThe name of a plugin page for further information andadministration of the plugin; this is usually the Plugin'sconfiguration page. MantisBT will create a link to the specifiedpage on the Manage Plugins page.versionYour plugin's version string. Required value. We recommendfollowing the Semantic Versioning specification, but you are freeto use any versioning scheme that can be handled by PHP'sversion_compare() function.requiresAn array of key/value pairs of basename/version plugindependencies.NoteThe special, reserved MantisCore basename can be used to specifythe minimum requirement for MantisBT core.The version string can be defined as:*Minimum requirement: the plugin specified by the given basenamemust be installed, and its version must be equal or higher thanthe indicated one.*Maximum requirement: prefixing a version number with '<' willallow the plugin to specify the highest version (non-inclusive)up to which the required dependency is supported.NoteIf the plugin's minimum dependency for MantisCore isunspecified or lower than the current release (i.e. it does notspecifically list the current core version as supported) andthe plugin does not define a maximum dependency, a default onewill be set to the next major release of MantisBT. (i.e. for2.x.y we would add '<2').This effectively disables plugins which have not beenspecifically designed for a new major Mantis release, thusforcing authors to review their code, adapt it if necessary,and release a new version of the plugin with updateddependencies.*Both minimum and maximum: the two version numbers must beseparated by a comma.Here are a few examples to illustrate the above explanations,assuming that the current Mantis release (MantisCore version) is2.1:*Old release without a maximum version specified$this->requires = array( 'MantisCore' => '1.3.1' );The plugin is compatible with MantisBT >= 1.3.1 and < 2.0.0 -note that the maximum version (<2) was added by the system.*Current release without a maximum version specified$this->requires = array( 'MantisCore' => '2.0' );The plugin is compatible with MantisBT >= 2.0 and < 3.0 (thelatter is implicit); code supporting older releases (e.g. 1.3)must be maintained separately (i.e. in a different branch).*Only specify a maximum version$this->requires = array( 'MantisCore' => '< 3.1' );The plugin is compatible up to MantisBT 3.1 (not inclusive).*Old release with a maximum version$this->requires = array( 'MantisCore' => '1.3, < 4.0' );The plugin is compatible with MantisBT >= 1.3 and < 4.0.usesAn array of key/value pairs of basename/version optional (soft)plugin dependencies. See requires above for details on how tospecify versions.authorYour name, or an array of names.contactAn email address where you can be contacted.urlA web address for your plugin.4.2.3. Pages and FilesThe plugin API provides a standard hierarchy and process for adding newpages and files to your plugin. For strict definitions, pages are PHPfiles that will be executed within the MantisBT core system, while filesare defined as a separate set of raw data that will be passed to theclient's browser exactly as it appears in the filesystem.Your Plugin's pages must:*be placed in the pages/ directory,*be named using only letters, numbers and underscores, and*have a .php file extensionTo generate a URI to a page in MantisBT, the API function plugin_page()should be used. Our Example plugin will create a page named foo.php,which can then be accessed via plugin_page.php?page=Example/foo:Example/pages/foo.php<?phplayout_page_header();layout_page_begin();?><p>Here is a link to <a href="<?php echo plugin_page( 'foo' ) ?>">page foo</a>.</p><?phplayout_page_end();NoteThe calls to layout_page_begin() and layout_page_end() trigger thestandard MantisBT header and footer portions, respectively, which alsodisplays things such as the menus and triggers other layout-relatedevents. layout_page_header() pulls in the CSS classes for alternating rowcolors in the table, amongst other things.Adding non-PHP files, such as images or CSS stylesheets, follows a verysimilar pattern as pages. Files should be placed in the plugin's files/directory, and can only contain a single period in the name. The file'sURI is generated with the plugin_file() function.For our Example plugin, we'll create a basic CSS stylesheet, and modifythe previously shown page to include the stylesheet:Example/files/foo.cssp.foo {color: red;}Example/pages/foo.php...<link rel="stylesheet" type="text/css" href="<?php echo plugin_file( 'foo.css' ) ?>" /><p class="foo">Our custom stylesheet paints this text red.</p>NoteWhile plugin_page() expects only the page's name without the extension,plugin_file() on the other hand requires the entire filename, so that itcan distinguish e.g. between foo.css and a potential image file calledfoo.png.The plugin's filesystem structure at this point looks like this:Example/Example.phppages/foo.phpfiles/foo.css4.2.4. EventsPlugins have an integrated method for both declaring and hooking events,without needing to directly call the event API functions. These take theform of class methods on your plugin.To declare a new event, or a set of events that your plugin will trigger,override the events() method of your plugin class, and return anassociative array with event names as the key, and the event type as thevalue.Let's add to our Example plugin, a new event foo that does not expect areturn value (an "execute" event type), and another event bar expecting asingle value that gets modified by each hooked function (a "chain" eventtype):Example/Example.php<?phpclass ExamplePlugin extends MantisPlugin {...function events() {return array('EVENT_EXAMPLE_FOO' => EVENT_TYPE_EXECUTE,'EVENT_EXAMPLE_BAR' => EVENT_TYPE_CHAIN,);}}When the Example plugin is loaded, the event system in MantisBT will addthese two items to its list of events, and will then allow other pluginsor functions to hook them.NoteNaming the events "EVENT_PLUGINBASENAME_EVENTNAME" is not required, butis considered best practice to avoid conflicts between plugins.Hooking events, whether they are your own plugin's or defined elsewhere,is almost identical to declaring them. Instead of passing an event typeas the value, your plugin must give the name of a class method that willbe called when the event is triggered.NoteIn order to hook an event defined by another plugin, that plugin must berequired in the "client" plugin's register() method (i.e. declared in therequires property array).For our Example plugin, we'll create a foo() and a bar() methods in ourplugin class, then hook them to the events we declared earlier. We'llalso hook the standard EVENT_MENU_MAIN event, to link the custom page wecreated in Section 4.2.3, “Pages and Files”.Example/Example.php<?phpclass ExamplePlugin extends MantisPlugin {...function hooks() {return array('EVENT_MENU_MAIN' => 'menu','EVENT_EXAMPLE_FOO' => 'foo','EVENT_EXAMPLE_BAR' => 'bar',);}}Function signatures vary depending on the hooked event's type (seeSection 3.3, “Event Types”). They generally should accept the $p_eventparameter, which contains the event name triggering the method (allowinga single method to respond to multiple events). The bar() method alsoaccepts and returns a second parameter, in order to match theexpectations of chained events.Now that we have our plugin's events declared and hooked, let's definethe hook methods and modify our earlier page so it triggers the newevents:Example/Example.php<?phpclass ExamplePlugin extends MantisPlugin {...function menu() {$t_menu[] = array('title' => $this->name,'url' => plugin_page( 'foo' ),'access_level' => ANYBODY,'icon' => 'fa-smile-o');return $t_menu;}function foo( $p_event ) {echo 'In method foo(). ';}function bar( $p_event, $p_chained_param ) {return str_replace( 'foo', 'bar', $p_chained_param );}}Example/pages/foo.php...<p>Custom event hooks:<?phpevent_signal( 'EVENT_EXAMPLE_FOO' );$t_string = 'A sentence with the word "foo" in it.';$t_new_string = event_signal( 'EVENT_EXAMPLE_BAR', array( $t_string ) );echo $t_new_string;?></p>When the first event "foo" is signaled, the Example plugin's foo() methodwill execute and echo a string. After that, the second event "bar" issignaled, and the page passes a string parameter; the plugin's bar() getsthe string and replaces any instance of "foo" with "bar", and returns theresulting string. If any other plugin had hooked the event, that plugincould have further modified the new string from the Example plugin, orvice versa, depending on the loading order of plugins. The page thenechos the modified string that was returned from the event.4.2.5. ConfigurationSimilar to events, plugins have a simplified method for declaringconfiguration options, as well as API functions for retrieving or settingthose values at runtime.Declaring a new configuration option is achieved just like declaringevents. By overriding the config() method on your plugin class, yourplugin can return an associative array of configuration options, with theoption name as the key, and the default option as the array value. OurExample plugin will declare an option "foo_or_bar", with a default valueof "foo":Example/Example.php<?phpclass ExamplePlugin extends MantisPlugin {...function config() {return array('foo_or_bar' => 'foo',);}}Retrieving the current value of a plugin's configuration option isachieved by using the plugin API's plugin_config_get() function, and canbe set to a modified value in the database using plugin_config_set().With these functions, the config option is prefixed with the plugin'sname, in an attempt to automatically avoid conflicts in naming.Our Example plugin will demonstrate this by adding a secure form to a newconfiguration page we'll call config.php, and handling the form on aseparate config_update.php page that will modify the value in thedatabase, and redirect back to the config page, just like any other formand action page in MantisBT:NoteThis code sample is a bare-bones implementation, focusing onfunctionality and meant for illustration purposes. Please refer toSection 4.2.7, “Layout” for a more realistic example, including layoutand styling.Example/pages/config.php<?php$t_foo_or_bar = plugin_config_get( 'foo_or_bar' );?><form action="<?php echo plugin_page( 'config_update' )?>" method="post"><?php echo form_security_field( 'plugin_Example_config_update' ) ?><label>Foo or Bar?<input name="foo_or_bar" value="<?php echo string_attribute( $t_foo_or_bar ) ?>" /></label><br><label>Reset<input type="checkbox" name="reset" /></label><br><input type="submit" /></form>Example/pages/config_update.php<?phpform_security_validate( 'plugin_Example_config_update' );$f_foo_or_bar = gpc_get_string( 'foo_or_bar' );$f_reset = gpc_get_bool( 'reset', false );form_security_purge( 'plugin_Example_config_update' );if( $f_reset ) {plugin_config_delete( 'foo_or_bar' );} elseif( $f_foo_or_bar == 'foo' || $f_foo_or_bar == 'bar' ) {plugin_config_set( 'foo_or_bar', $f_foo_or_bar );} else {error_parameters( 'foo_or_bar', string_attribute( $f_foo_or_bar ) );trigger_error( ERROR_CONFIG_OPT_INVALID, ERROR );}print_header_redirect( plugin_page( 'config', true ) );NoteThe form_security_*() functions are part of the Form API, and preventCSRF attacks against forms that make changes to the system.The last step is to add a link our new config page from the ManagePlugins page. This is as simple as referencing the config page's name inthe register() method, which will turn the Plugin's name into anhyperlink.Example/Example.phpfunction register() {...$this->page = 'config'; # Default plugin page4.2.6. Language and LocalizationMantisBT has a very advanced set of localization tools, which allow allparts of of the application to be localized to the user's preferredlanguage. This feature has been extended for use by plugins as well, sothat a plugin can be localized in much the same method as used for thecore system. Localizing a plugin involves creating a language file foreach localization available, and using a special API call to retrieve theappropriate string for the user's language.All language files for plugins follow the same format used in the core ofMantisBT, should be placed in the plugin's lang/ directory, and named thesame as the core language files. Strings specific to the plugin should be"namespaced" in a way that will minimize any risk of collision.Translating the plugin to other languages already supported by MantisBTis then as simple as creating a new strings file with the localizedcontent; the MantisBT core will find and use the new language stringsautomatically.We'll use the pages from the previous examples, and dress them up withlocalized language strings. First we need to create a language file forEnglish, MantisBT's default language which is also used as fallback incase some strings have not been localized to the user's language:Example/lang/strings_english.txt<?php$s_plugin_Example_title = 'Example Plugin';$s_plugin_Example_description = 'Example Plugin from MantisBT Developers Guide';$s_plugin_Example_configuration = 'Configuration';$s_plugin_Example_foo_or_bar = 'Foo or Bar?';$s_plugin_Example_reset = 'Reset Value';We can now use these strings to localize our code using theplugin_lang_get() API function, replacing the hardcoded text and addingpage title:Example/Example.phpfunction register() {...$this->description = plugin_lang_get( 'title' );Example/pages/foo.php<?phplayout_page_header( plugin_lang_get( 'title' ) );layout_page_begin();?>...Example/pages/config.php<?phplayout_page_header( plugin_lang_get( 'title' ) );layout_page_begin();$t_foo_or_bar = plugin_config_get( 'foo_or_bar' );?><form action="<?php echo plugin_page( 'config_update' )?>" method="post"><?php echo form_security_field( 'plugin_Example_config_update' ) ?><label><?php echo plugin_lang_get( 'foo_or_bar' ) ?><input name="foo_or_bar" value="<?php echo string_attribute( $t_foo_or_bar ) ?>" /></label><br><label><?php echo plugin_lang_get( 'reset' ) ?><input type="checkbox" name="reset" /></label><br><button type="submit"><?php echo lang_get( 'change_configuration' )?></button></form>4.2.7. LayoutTo finalize our plugin, we'll add some markup and flourishes to make theconfig page look like a standard MantisBT page.NoteAlternating colors are applied automatically to the table's rows withCSS, it is no longer necessary to rely on the now-deprecatedhelper_alternate_rows() API function.Example/pages/config.php<?phplayout_page_header( plugin_lang_get( 'title' ) );layout_page_begin();$t_foo_or_bar = plugin_config_get( 'foo_or_bar' );?><div class="col-md-12 col-xs-12"><div class="space-10"></div><div class="form-container"><form action="<?php echo plugin_page( 'config_update' ) ?>" method="post"><?php echo form_security_field( 'plugin_Example_config_update' ) ?><div class="widget-box widget-color-blue2"><div class="widget-header widget-header-small"><h4 class="widget-title lighter"><?phpprint_icon( 'fa-text-width', 'ace-icon' );echo " ";echo plugin_lang_get( 'title') . ': '. plugin_lang_get( 'configuration' );?></h4></div><div class="widget-body"><div class="widget-main no-padding table-responsive"><table class="table table-bordered table-condensed table-striped"><tr><th class="category"><label for="foo_or_bar"><?php echo plugin_lang_get( 'foo_or_bar' ) ?></label></th><td><input type="text" id="foo_or_bar" name="foo_or_bar"value="<?php echo string_attribute( $t_foo_or_bar ) ?>"/></td></tr><tr><th class="category"><label for="reset"><?php echo plugin_lang_get( 'reset' ) ?></label></th><td><input type="checkbox" id="reset" name="reset" class="ace" /><span class="lbl"> </span></td></tr></table></div><div class="widget-toolbox padding-8 clearfix"><button class="btn btn-primary btn-white btn-round"><?php echo lang_get( 'change_configuration' )?></button></div></div></div></form></div></div><?phplayout_page_end();4.2.8. Example Plugin source codeThe complete source code for the Example Plugin built in this tutorial isavailable in the mantisbt-plugins GitHub organization.It has been released into the public domain under the Unlicense, whichmeans you are free to use it in any way you want, without warranty of anykind.4.3. API Usage--------------This is a general overview of the plugin API. For more detailed analysis,you may reference the file core/plugin_api.php in the codebase.Chapter 5. Events Reference===========================5.1. Introduction-----------------In this chapter, an attempt will be made to list all events used (orplanned for later use) in the MantisBT event system. Each listed eventwill include details for the event type, when the event is called, andthe expected parameters and return values for event callbacks.Here we show an example event definition. For each event, the eventidentifier will be listed along with the event types (see Section 3.3,“Event Types”) in parentheses. Below that should be a concise butthorough description of how the event is called and how to use it.Following that should be a list of event parameters (if any), as well asthe expected return value (if any).EVENT_EXAMPLE (Default)This is an example event description.Parameters*<Type>: Description of parameter one*<Type>: Description of parameter twoReturn Value*<Type>: Description of return value5.2. System Events------------------These events are initiated by the plugin system itself to allow certainfunctionality to simplify plugin development.EVENT_PLUGIN_INIT (Execute)This event is triggered by the MantisBT plugin system after allregistered and enabled plugins have been initialized (theirinit() functions have been called). This event should always bethe first event triggered for any page load. No parameters arepassed to hooked functions, and no return values are expected.This event is the first point in page execution where allregistered plugins are guaranteed to be enabled (assumingdependencies and such are met). At any point before this event,any or all plugins may not yet be loaded. Note that the coresystem has not yet completed the bootstrap process when thisevent is signalled.Suggested uses for the event include:*Checking for plugins that aren't require for normal usage.*Interacting with other plugins outside the context of pagesor events.EVENT_CORE_HEADERS (Execute)This event is triggered by the MantisBT bootstrap process justbefore emitting the headers. This enables plugins to emit theirown headers or use API that enables tweaking values of headersemitted by core. An example, of headers that can be tweaked isContent-Security-Policy header which can be tweaked usinghttp_csp_*() APIs.EVENT_CORE_READY (Execute)This event is triggered by the MantisBT bootstrap process afterall core APIs have been initialized, including the plugin system,but before control is relinquished from the bootstrap processback to the originating page. No parameters are passed to hookedfunctions, and no return values are expected.This event is the first point in page execution where the entiresystem is considered loaded and ready.EVENT_REST_API_ROUTES (Execute)This event is triggered by MantisBT to enable plugins to registertheir own routes to be accessible via the REST API. All APIsbelonging to a plugin named 'Example', MUST live under'https://.../api/rest/plugins/Example/'. The route registrationis done using the Slim Framework app instance that is passed as aparameter. A route group should be used to include all routes forthe plugin. The name of the route group should be retrieved viacalling plugin_route_group(). See MantisGraph core plugin for anexample and Slim Framework router documentation.Before calling into the plugin routes, the user would be alreadyauthenticated and authorized for API access in general. However,it is the responsibility of the plugin to do its own pluginspecific authorization.EVENT_LOG (Execute)This event is triggered by MantisBT to log a message. Thecontents of the message should be hyper linked based on thefollowing rules: #123 means issue 123, ~123 means issue note 123,@P123 means project 123, @U123 means user 123. Logging pluginscan capture extra context information like timestamp, currentlogged in user, etc. This event receives the logging string as aparameter.Parameters*<String>: the logging stringEVENT_AUTH_USER_FLAGS (First)An event that enables plugins to return a set of flags thatcontrol the authentication behaviors for the user who is loggingin or logged in. In some cases, the user will be in the system,but there will be cases where the username provided by the userdoesn't exist. In case the user doesn't exist, it is up to theauthentication plugin whether to fail the login, validatecredentials then fail, or validate credentials thenauto-provision the user based on information the plugin is awareof (e.g. IDP or some db of accounts). If no plugin is registeredfor events, then defaults are used. If plugin sets a subset ofthe options, then the default will be used for the rest.Checkout SampleAuth plugin for more details.EVENT_CRONJOB (Execute)This is an event that is triggered by the scripts/cronjob.phpon some recurring schedule (once an hour is recommended).Plugins handle this event to execute recurring tasks.5.3. Output Modifier Events---------------------------5.3.1. String DisplayThese events make it possible to dynamically modify output strings tointerpret or add semantic meaning or markup. Examples include thecreation of links to other bugs or bugnotes, as well as handling urls toother sites in general.EVENT_DISPLAY_BUG_ID (Chained)This is an event to format bug ID numbers before being displayed,using the bug_format_id() API call. The result should beplain-text, as the resulting string is used in various formatsand locations.Parameters*<String>: bug ID string to be displayed*<Integer>: bug ID numberReturn Value*<String>: modified bug ID stringEVENT_DISPLAY_EMAIL (Chained)This is an event to format text before being sent in an email.Callbacks should be used to process text and convert it into aplaintext-readable format so that users with textual emailclients can best utilize the information. Hyperlinks and othermarkup should be removed, leaving the core content by itself.Parameters*<String>: input string to be displayedReturn Value*<String>: modified input stringEVENT_DISPLAY_EMAIL_BUILD_SUBJECT (Chained)This is an event to format the subject line of an email before itis sent.Parameters*<String>: input string for email subjectReturn Value*<String>: modified subject stringEVENT_DISPLAY_FORMATTED (Chained)This is an event to display generic formatted text. The string tobe displayed is passed between hooked callbacks, each taking aturn to modify the output in some specific manner. Text passed tothis may be processed for all types of formatting and markup,including clickable links, presentation adjustments, etc.Parameters*<String>: input string to be displayedReturn Value*<String>: modified input string*<Boolean>: multiline input stringEVENT_DISPLAY_RSS (Chained)This is an event to format content before being displayed in anRSS feed. Text should be processed to perform any necessarycharacter escaping to preserve hyperlinks and other appropriatemarkup.Parameters*<String>: input string to be displayed*<Boolean>: multiline input stringReturn Value*<String>: modified input stringEVENT_DISPLAY_TEXT (Chained)This is an event to display generic unformatted text. The stringto be displayed is passed between hooked callbacks, each taking aturn to modify the output in some specific manner. Text passed tothis event should only be processed for the most basicformatting, such as preserving line breaks and specialcharacters.Parameters*<String>: input string to be displayed*<Boolean>: multiline input stringReturn Value*<String>: modified input string5.3.2. Menu ItemsThese events allow new menu items to be inserted in order for new contentto be added, such as new pages or integration with other applications.EVENT_MENU_ACCOUNT (Default)This event gives plugins the opportunity to add new links to theuser account menu available to users from the 'My Account' linkon the main menu.Return Value*<Array>: List of HTML links for the user account menu.EVENT_MENU_DOCS (Default)This event gives plugins the opportunity to add new links to thedocuments menu available to users from the 'Docs' link on themain menu.Return Value*<Array>: List of HTML links for the documents menu.EVENT_MENU_FILTER (Default)This event gives plugins the opportunity to add new links to theissue list menu available to users from the 'View Issues' link onthe main menu.Return Value*<Array>: List of HTML links for the issue list menu.EVENT_MENU_ISSUE (Default)This event gives plugins the opportunity to add new links to theissue menu available to users when viewing issues.Parameters*<Integer>: bug IDReturn Value*<Array>: List of HTML links for the issue page menu.This is an associative array, with the link's label as keyand the link target as value, e.g.array( plugin_lang_get( 'mypage_label' ) => plugin_page( 'mypage' );For compatibility with MantisBT versions before 2.21.0, it isalso possible to return an array of cooked links :array( '<a href="' . plugin_page( 'mypage' ) . '">' . plugin_lang_get( 'mypage_label' ) . '</a>' );However, this usage is deprecated.EVENT_MENU_ISSUE_RELATIONSHIP (Default)This event allows plugins to add new links to the menu under theRelationships section in the View Issue Details page.Parameters*<Integer>: bug IDReturn Value*<Array>: List of elements to add to the menu.This is an associative array, with the link's label as keyand the link target as value, e.g.array( plugin_lang_get( 'mypage_label' ) => plugin_page( 'mypage' );EVENT_MENU_MAIN (Default)This event gives plugins the opportunity to add new menu optionsto the main menu. New links will be added AFTER the standard menuoptions.Return Value*<Array>: Hooked events may return an array of menu options.Each array entry will contain an associate array with keys'title', 'url', 'access_level', and 'icon' (e.g. fa-pencilfrom Font Awesome).return array(array('title' => 'My Link','access_level' => DEVELOPER,'url' => 'my_link.php','icon' => 'fa-random'),array('title' => 'My Link2','access_level' => DEVELOPER,'url' => 'my_link2.php','icon' => 'fa-shield'));EVENT_MENU_MAIN_FRONT (Default)This event gives plugins the opportunity to add new menu optionsto main menu. New links will be added BEFORE the standard menuoptions.Return Value*<Array>: Hooked events may return an array of menu options.Each array entry will contain an associate array with keys'title', 'url', 'access_level', and 'icon' (e.g. fa-pencilfrom Font Awesome).return array(array('title' => 'My Link','access_level' => DEVELOPER,'url' => 'my_link.php','icon' => 'fa-random'),array('title' => 'My Link2','access_level' => DEVELOPER,'url' => 'my_link2.php','icon' => 'fa-shield'));EVENT_MENU_MAIN_FILTER (Chained)This event gives plugins the opportunity to modify the completemain menu before it is output (for example re-order items, additems in the middle, remove built-in items, change icons ofbuilt-in items).A single parameter is passed as an array of associate arrayscontaining the items in the main sidebar menu. The format of thisarray is the same as EVENT_MENU_MAIN and EVENT_MENU_MAIN_FRONT,and will also contain any entries added as a result of theseevents.Plugins should do any modifications needed and return the arrayback. Returning an empty array will hide the main menu.Parameters*<Array>: menu itemsReturn Value*<Array>: modified menu itemsNoteWhen returning the updated menu, the plugin's hook functionneeds to wrap it in an array, i.e. return array( $p_menu_data); and not return $p_menu_data;. The reason for this is thatthe Event API relies on call_user_func_array() to executecallback functions.EVENT_MENU_MANAGE (Default)This event gives plugins the opportunity to add new links to themanagement menu available to site administrators from the'Manage' link on the main menu. Plugins should try to minimizeuse of these links to functions dealing with core MantisBTmanagement.Return Value*<Array>: List of HTML links for the management menu.EVENT_MENU_MANAGE_CONFIG (Default)This event gives plugins the opportunity to add new links to theconfiguration management menu available to site administratorsfrom the 'Manage Configuration' link on the standard managementmenu. Plugins should try to minimize use of these links tofunctions dealing with core MantisBT configuration.Return Value*<Array>: List of HTML links for the manage configurationmenu.EVENT_MENU_SUMMARY (Default)This event gives plugins the opportunity to add new links to thesummary menu available to users from the 'Summary' link on themain menu.Return Value*<Array>: List of HTML links for the summary menu.5.3.3. Page LayoutThese events offer the chance to create output at points relevant to theoverall page layout of MantisBT. Page headers, footers, stylesheets, andmore can be created. Events listed below are in order of runtimeexecution.EVENT_LAYOUT_RESOURCES (Output)This event allows plugins to output HTML code from inside the<head> tag, for use with CSS, Javascript, RSS, or any othersimilar resources. Note that this event is signaled after allother CSS and Javascript resources are linked by MantisBT.Return Value*<String>: HTML code to output.EVENT_LAYOUT_BODY_BEGIN (Output)This event allows plugins to output HTML code immediately afterthe <body> tag is opened, so that MantisBT may be integratedwithin another website's template, or other similar use.Return Value*<String>: HTML code to output.EVENT_LAYOUT_PAGE_HEADER (Output)This event allows plugins to output HTML code immediately afterthe MantisBT header content, such as the logo image.Return Value*<String>: HTML code to output.EVENT_LAYOUT_CONTENT_BEGIN (Output)This event allows plugins to output HTML code after the top mainmenu, but before any page-specific content begins.Return Value*<String>: HTML code to output.EVENT_LAYOUT_CONTENT_END (Output)This event allows plugins to output HTML code after any page-specific content has completed, but before the bottom menu bar(or footer).Return Value*<String>: HTML code to output.EVENT_LAYOUT_PAGE_FOOTER (Output)This event allows plugins to output HTML code after the MantisBTversion, copyright, and webmaster information, but before thequery information.Return Value*<String>: HTML code to output.EVENT_LAYOUT_BODY_END (Output)This event allows plugins to output HTML code immediately beforethe </body> end tag, to so that MantisBT may be integrated withinanother website's template, or other similar use.Return Value*<String>: HTML code to output.EVENT_VIEW_BUG_ATTACHMENT (Output)This event allows plugins to output HTML code immediately afterthe line of an attachment. Receives the attachment data as aparameter, in the form of an attachment array from within thearray returned by the file_get_visible_attachments() function.Parameters*<Array>: the attachment data as an array (seecore/file_api.php)Return Value*<String>: HTML code to output.5.4. Bug Filter Events----------------------5.4.1. Custom Filters and ColumnsEVENT_FILTER_FIELDS (Default)This event allows a plugin to register custom filter objects(based on the MantisFilter class) that will allow the user tosearch for issues based on custom criteria or datasets. Theplugin can return either a class name (which will be instantiatedat runtime) or an already instantiated object. The plugin mustensure that the filter class has been defined before returningthe class name for this event.Return Value*<Array>: Array of class names or objects for custom filtersEVENT_FILTER_COLUMNS (Default)This event allows a plugin to register custom column objects(based on the MantisColumn class) that will allow the user toview data for issues based on custom datasets. The plugin canreturn either a class name (which will be instantiated atruntime) or an already instantiated object. The plugin mustensure that the column class has been defined before returningthe class name for this event.Return Value*<Array>: Array of class names or objects for custom columns5.5. Bug and Bugnote Events---------------------------5.5.1. Bug ViewEVENT_VIEW_BUG_DETAILS (Execute)This event allows a plugin to either process information ordisplay some data in the bug view page. It is triggered after therow containing the target version and product build fields, andbefore the bug summary is displayed.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Integer>: Bug IDEVENT_VIEW_BUG_EXTRA (Execute)This event allows a plugin to either process information ordisplay some data in the bug view page. It is triggered after thebug notes have been displayed, but before the history log isshown.Any output here should be contained within its own <table>element.Parameters*<Integer>: Bug ID5.5.2. Bug ActionsEVENT_REPORT_BUG_FORM (Execute)This event allows plugins to do processing or display formelements on the Report Issue page. It is triggered immediatelybefore the summary text field.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Integer>: Project IDEVENT_REPORT_BUG_FORM_TOP (Execute)This event allows plugins to do processing or display formelements at the top of the Report Issue page. It is triggeredbefore any of the visible form elements have been created.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Integer>: Project IDEVENT_REPORT_BUG_DATA (Chain)This event allows plugins to perform pre-processing of the newbug data structure after being reported from the user, but beforethe data is saved to the database. At this point, the issue ID isnot yet known, as the data has not yet been persisted.Parameters*<Complex>: Bug data structure (see core/bug_api.php)Return Value*<Complex>: Bug data structureEVENT_REPORT_BUG (Execute)This event allows plugins to perform post-processing of the bugdata structure after being reported from the user and being savedto the database. At this point, the issue ID is actually known,and is passed as a second parameter.Parameters*<Complex>: Bug data structure (see core/bug_api.php)*<Integer>: Bug IDEVENT_UPDATE_BUG_FORM (Execute)This event allows plugins to do processing or display formelements on the Update Issue page. It is triggered immediatelybefore the summary text field.Parameters*<Integer>: Bug IDEVENT_UPDATE_BUG_FORM_TOP (Execute)This event allows plugins to do processing or display formelements on the Update Issue page. It is triggered immediatelybefore before any of the visible form elements have been created.Parameters*<Integer>: Bug IDEVENT_UPDATE_BUG_STATUS_FORM (Execute)This event allows plugins to do processing or display formelements in the bug change status form. It is triggeredimmediately before the add bugnote fields.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Integer>: Bug ID*<Integer>: New StatusEVENT_UPDATE_BUG_SHOW_CUSTOM_FIELD (Default)This event allows plugins to show a non-required custom field onthe bug_change_status_page, when the bug/ticket is in a customstate (non-native). It is only possible to show fields, not tohide them trough the plugin.Parameters*<Complex>: Bug data structure (see core/bug_api.php)*<Integer>: Customfield IDEVENT_UPDATE_BUG_DATA (Chain)This event allows plugins to perform pre-processing of theupdated bug data structure after being modified by the user, butbefore being saved to the database.Parameters*<Complex>: Updated bug data structure (see core/bug_api.php)*<Complex>: Original bug data structure (see core/bug_api.php)Return Value*<Complex>: Updated bug data structure (see core/bug_api.php)EVENT_UPDATE_BUG (Execute)This event allows plugins to perform post-processing of the bugdata structure after being updated.Parameters*<Complex>: Original bug data structure (see core/bug_api.php)*<Complex>: Updated bug data structure (see core/bug_api.php)EVENT_BUG_ACTIONGROUP_FORM (Execute)This event allows plugins to do processing or display formelements in the bug group action page form. It is triggeredimmediately after the standard fields, and before bugnote fields(if applicable).Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Array>: Array of event data elementsParameter array contentsThe parameter array contains elements for these indexes:*'action' => <String>: Action title (see bug_actiongroup.php)*'bug_ids' => <Array>: Array of selected bug ids*'multiple_projects' => <Boolean>: Flag if selected bug idsspan multiple projects*'has_bugnote' => <Boolean>: Flag if current group action formcontains a bugnote inputDepending on the action, any of these indexes may appear:*'custom_field_id' => <Integer>: If action is 'CUSTOM',contains the custom field id selected for updateEVENT_BUG_ACTION (Execute)This event allows plugins to perform post-processing of groupactions performed from the View Issues page. The event will getcalled for each bug ID that was part of the group action event.Parameters*<String>: Action title (see bug_actiongroup.php)*<Integer>: Bug IDEVENT_BUG_DELETED (Execute)This event allows plugins to perform pre-processing of bugdeletion actions. The actual deletion will occur after executionof the event, for compatibility reasons.Parameters*<Integer>: Bug ID5.5.3. Bugnote ViewEVENT_VIEW_BUGNOTES_START (Execute)This event allows a plugin to either process information ordisplay some data in the bug notes section, before any bug notesare displayed. It is triggered after the bug notes section title.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Integer>: Bug ID*<Complex>: A list of all bugnotes to be displayed to the userEVENT_VIEW_BUGNOTE (Execute)This event allows a plugin to either process information ordisplay some data in the bug notes section, interleaved with theindividual bug notes. It gets triggered after every bug note isdisplayed.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Integer>: Bug ID*<Integer>: Bugnote ID*<Boolean>: Private bugnote (false if public)EVENT_VIEW_BUGNOTES_END (Execute)This event allows a plugin to either process information ordisplay some data in the bug notes section, after all bugnoteshave been displayed.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Integer>: Bug ID5.5.4. Bugnote ActionsEVENT_BUGNOTE_ADD_FORM (Execute)This event allows plugins to do processing or display formelements in the bugnote adding form. It is triggered immediatelyafter the bugnote text field.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Integer>: Bug IDEVENT_BUGNOTE_DATA (Chained)This event allows plugins to preprocess bugnote contents.Parameters*<String>: Bugnote text*<String>: Bug IdReturn value*<String>: Updated Bugnote textEVENT_BUGNOTE_ADD (Execute)This event allows plugins to do post-processing of bugnotes addedto an issue.Parameters*<Integer>: Bug ID*<Integer>: Bugnote ID*<array>: Files info (name, size, id), starting 2.23.0EVENT_BUGNOTE_EDIT_FORM (Execute)This event allows plugins to do processing or display formelements in the bugnote editing form. It is triggered immediatelyafter the bugnote text field.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Integer>: Bug ID*<Integer>: Bugnote IDEVENT_BUGNOTE_EDIT (Execute)This event allows plugins to do post-processing of bugnote edits.Parameters*<Integer>: Bug ID*<Integer>: Bugnote IDEVENT_BUGNOTE_DELETED (Execute)This event allows plugins to do post-processing of bugnotedeletions.Parameters*<Integer>: Bug ID*<Integer>: Bugnote IDEVENT_TAG_ATTACHED (Execute)This event allows plugins to do post-processing of attached tags.Parameters*<Integer>: Bug ID*<Array of Integers>: Tag IDsEVENT_TAG_DETACHED (Execute)This event allows plugins to do post-processing of detached tags.Parameters*<Integer>: Bug ID*<Array of Integers>: Tag IDs5.6. Notification Events------------------------5.6.1. Recipient SelectionEVENT_NOTIFY_USER_INCLUDE (Default)This event allows a plugin to specify a set of users to beincluded as recipients for a notification. The set of usersreturned is added to the list of recipients already generatedfrom the existing notification flags and selection process.Parameters*<Integer>: Bug ID*<String>: Notification typeReturn Value*<Array>: User IDs to include as recipientsEVENT_NOTIFY_USER_EXCLUDE (Default)This event allows a plugin to selectively exclude individualusers from the recipient list for a notification. The event issignalled for every user in the final recipient list, includingrecipients added by the event NOTIFY_USER_INCLUDE as describedabove.Parameters*<Integer>: Bug ID*<String>: Notification type*<Integer>: User IDReturn Value*<Boolean>: True to exclude the user, false otherwise5.7. User Account Events------------------------5.7.1. Account PreferencesEVENT_ACCOUNT_PREF_UPDATE_FORM (Execute)This event allows plugins to do processing or display formelements on the Account Preferences page. It is triggeredimmediately after the last core preference element.Any output here should follow the format found inaccount_prefs_inc.php. As of 1.3.x this is no longer tableelements.Parameters*<Integer>: User IDEVENT_ACCOUNT_PREF_UPDATE (Execute)This event allows plugins to do pre-processing of form elementsfrom the Account Preferences page. It is triggered immediatelybefore the user preferences are saved to the database.Parameters*<Integer>: User IDEVENT_USER_AVATAR (First)This event gets the user's avatar as an instance of the Avatarclass. The first plugin to respond with an avatar wins. Hence, incase of multiple avatar plugins, make sure to tweak thepriorities. Avatars should return null if they don't have anavatar for the specified user id.Parameters*<Avatar>: Avatar instance or null.5.8. Management Events----------------------EVENT_MANAGE_OVERVIEW_INFO (Output)This event allows plugins to display special information on theManagement Overview page.Any output here should be defining appropriate rows and columnsfor the surrounding <table> elements.Parameters*<Boolean>: whether user is administrator5.8.1. Projects and VersionsEVENT_MANAGE_PROJECT_PAGE (Execute)This event allows plugins to do processing or display informationon the View Project page. It is triggered immediately before theproject access blocks.Any output here should be contained within its own <table>element.Parameters*<Integer>: Project IDEVENT_MANAGE_PROJECT_CREATE_FORM (Execute)This event allows plugins to do processing or display formelements on the Create Project page. It is triggered immediatelybefore the submit button.Any output here should follow the format found inmanage_proj_create_page.php. As of 1.3.x this is no longer tableelements.EVENT_MANAGE_PROJECT_CREATE (Execute)This event allows plugins to do post-processing of newly-createdprojects and form elements from the Create Project page.Parameters*<Integer>: Project IDEVENT_MANAGE_PROJECT_UPDATE_FORM (Execute)This event allows plugins to do processing or display formelements in the Edit Project form on the View Project page. It istriggered immediately before the submit button.Any output here should follow the format found inmanage_proj_edit_page.php. As of 1.3.x this is no longer tableelements.Parameters*<Integer>: Project IDEVENT_MANAGE_PROJECT_UPDATE (Execute)This event allows plugins to do post-processing of modifiedprojects and form elements from the Edit Project form.Parameters*<Integer>: Project IDEVENT_MANAGE_PROJECT_DELETE (Execute)This event allows plugins to do pre-processing of projectdeletion. This event is triggered prior to the project removalfrom the database.Parameters*<Integer>: Project IDEVENT_MANAGE_VERSION_CREATE (Execute)This event allows plugins to do post-processing of newly-createdproject versions from the View Project page, or versions copiedfrom other projects. This event is triggered for each versioncreated.Parameters*<Integer>: Version IDEVENT_MANAGE_VERSION_UPDATE_FORM (Execute)This event allows plugins to do processing or display formelements on the Update Version page. It is triggered immediatelybefore the submit button.Any output here should follow the format found inmanage_proj_ver_edit_page.php. As of 1.3.x this is no longertable elements.Parameters*<Integer>: Version IDEVENT_MANAGE_VERSION_UPDATE (Execute)This event allows plugins to do post-processing of modifiedversions and form elements from the Edit Version page.Parameters*<Integer>: Version IDEVENT_MANAGE_VERSION_DELETE (Execute)This event allows plugins to do pre-processing of versiondeletion. This event is triggered prior to the version removalfrom the database.Parameters*<Integer>: Version ID*<String>: Replacement version to set on issues that arecurrently using the version that is about to be deleted.5.8.2. UsersEVENT_MANAGE_USER_CREATE_FORM (Execute)This event allows plugins to do processing or display formelements on the Create User page. It is triggered immediatelybefore the submit button.Any output here should follow the format found inmanage_user_create_page.php.EVENT_MANAGE_USER_CREATE (Execute)This event allows plugins to do post-processing of newly-createdusers. This event is triggered for each user created. The Userscreate form is one possible case for triggering such events, butthere can be other ways users can be created.Parameters*<Integer>: User IDEVENT_MANAGE_USER_UPDATE_FORM (Execute)This event allows plugins to do processing or display formelements in the Manage User page. It is triggered immediatelybefore the submit button.Any output here should follow the format found inmanage_user_edit_page.php.Parameters*<Integer>: User IDEVENT_MANAGE_USER_UPDATE (Execute)This event allows plugins to do post-processing of modifiedusers. This may be triggered by the Manage User page or someother path.Parameters*<Integer>: User IDEVENT_MANAGE_USER_DELETE (Execute)This event allows plugins to do pre-processing of user deletion.Parameters*<Integer>: User IDEVENT_MANAGE_USER_PAGE (Execute)This event allows plugins to do processing or display informationon the View User page. It is triggered immediately after thereset password segment.Any output here should be contained within its own container.Parameters*<Integer>: User IDEVENT_MANAGE_PROJECT_USER_CREATE (Execute)This event allows plugins to do post-processing of newly-addeduser on project.Parameters*<Integer>: User ID*<Integer>: Project IDEVENT_MANAGE_PROJECT_USER_UPDATE (Execute)This event allows plugins to do post-processing of modified useron project.Parameters*<Integer>: User ID*<Integer>: Project IDEVENT_MANAGE_PROJECT_USER_DELETE (Execute)This event allows plugins to do pre-processing of user deletionon project. This event is triggered prior to the user removalfrom the project in database.Parameters*<Integer>: User ID*<Integer>: Project IDChapter 6. MantisBT REST API============================The primary means of integrating with MantisBT with web services is withthe bundled REST API, which is accessible athttps://server.com/mantis/api/rest/.6.1. Postman------------Postman is a tool to enable REST API development and collaboration. It isused by MantisBT team to share the API definitions and enables thecommunity to experiment with the API. Download Postman.Once Postman is installed, you should be able to bootstrap it with theMantisBT API collection via the MantisBT API documentation posted onPostman. Download Collection.The MantisBT API Postman collection is also distributed with MantisBT inthe `api/rest/api.postman_collection.json`. The Postman environmenttemplate is also available at `api/rest/env.postman_environment.json`.6.2. Exporting Postman Collection---------------------------------The bundled Postman collection can be updated by clicking on the "..."next to "Mantis Bug Tracker REST API" and choosing "Export...". Thenselect "Collection v2.1" and click "Export" and select`api/rest/api.postman_collection.json`. Then follow the standard pullrequest and checkin process.Chapter 7. MantisBT SOAP API============================The now deprecated MantisBT SOAP API is accessible at:https://server.com/mantis/api/soap/mantisconnect.php.7.1. Java integration---------------------7.1.1. Prebuilt SOAP stubs using AxisFor ease of integration of the Java clients, SOAP stubs are maintainedand deployed in the Maven central repository. For example:<dependency><groupId>biz.futureware.mantis</groupId><artifactId>mantis-axis-soap-client</artifactId><version>1.2.15</version></dependency>To include them in your project, download the latest available version.7.1.2. Usage in OSGi environmentsIf you would like to use Axis in an OSGi environment, it is recommendedthat you use a ready-made bundle, such as the Axis bundle available fromEclipse Orbit7.2. Compatibility between releases-----------------------------------The SOAP API signature will change between minor releases, typically toadd new functionality or to extend existing features.Some of these changes might require a refresh of the client librariesgenerated, for instance Apache Axis 1 SOAP stubs must be regenerated if acomplex type receives a new property. Such changes will be announcedbefore the release of the new MantisBT version on the mantisbt-soap-devmailing list. Typically there will be two weeks time to integrate the newSOAP stubs.7.3. Support------------The primary means of obtaining support for Web Services and the SOAP APIis through the mantisbt-soap-dev mailing list.Chapter 8. Appendix===================8.1. Git References-------------------*The Git SCM web site offers a full reference of Git commands, as wellScott Chacon's excellent Pro Git book.*Github's Git Reference*Official documentation (from kernel.org)*Manual Page*Tutorial*Everyday Git With 20 Commands*Git Crash Course for SVN Users*Git From the Bottom Up (PDF)Appendix A. Revision History============================Revision HistoryRevision 2.26-0Sun Oct 15 2023Damien RegadRelease 2.26.0Revision 2.25-0Mon Mar 8 2021Damien RegadRelease 2.25.0Revision 2.24-1Sun May 3 2020Victor BoctorRelease 2.24.1Revision 2.24-0Sun Mar 15 2020Victor BoctorRelease 2.24.0Revision 2.23-0Sun Dec 9 2019Victor BoctorRelease 2.23.0Revision 2.22-1Thu Sep 26 2019Victor BoctorRelease 2.22.1Revision 2.22-0Sun Aug 25 2019Victor BoctorRelease 2.22.0Revision 2.21-2Mon Aug 19 2019Victor BoctorRelease 2.21.2Revision 2.21-1Thu Jun 13 2019Victor BoctorRelease 2.21.1Revision 2.21-0Sat Apr 20 2019Victor BoctorRelease 2.21.0Revision 2.20-0Sat Mar 16 2019Victor BoctorRelease 2.20.0Revision 2.19-0Wed Jan 2 2019Victor BoctorRelease 2.19.0Revision 2.18-0Tue Oct 16 2018Victor BoctorRelease 2.18.0Revision 2.17-1Mon Sep 24 2018Victor BoctorRelease 2.17.1Revision 2.17-0Mon Sep 3 2018Victor BoctorRelease 2.17.0Revision 2.16-0Sun Jul 29 2018Victor BoctorRelease 2.16.0Revision 2.15-0Tue Jun 5 2018Victor BoctorRelease 2.15.0Revision 2.14-0Sun Apr 29 2018Victor BoctorRelease 2.14.0Revision 2.13-1Wed Apr 4 2018Victor BoctorRelease 2.13.1Revision 2.13-0Sun Apr 1 2018Victor BoctorRelease 2.13.0Revision 2.12-0Sat Mar 3 2018Victor BoctorRelease 2.12.0Revision 2.11-0Tue Feb 6 2018Victor BoctorRelease 2.11.0Revision 2.10-0Sat Dec 30 2017Victor BoctorRelease 2.10.0Revision 2.9-0Sun Dec 3 2017Victor BoctorRelease 2.9.0Revision 2.8-0Sat Oct 28 2017Victor BoctorRelease 2.8.0Revision 2.7-0Sun Oct 8 2017Victor BoctorRelease 2.7.0Revision 2.6-0Sun Sep 3 2017Victor BoctorRelease 2.6.0Revision 2.5-1Sat Jun 17 2017Victor BoctorRelease 2.5.1Revision 2.5-0Sun Jun 4 2017Victor BoctorRelease 2.5.0Revision 2.4-1Sat May 20 2017Victor BoctorRelease 2.4.1Revision 2.4-0Sun Apr 30 2017Victor BoctorRelease 2.4.0Revision 2.3-3Sun Apr 30 2017Victor BoctorRelease 2.3.2Revision 2.3-2Sun Apr 17 2017Victor BoctorRelease 2.3.1Revision 2.3-1Fri Mar 31 2017Victor BoctorRelease 2.3.0Revision 2.2-3Wed Mar 22 2017Damien RegadRelease 2.2.2Revision 2.2-2Sun Mar 12 2017Victor BoctorRelease 2.2.1Revision 2.2-1Sun Feb 26 2017Victor BoctorRelease 2.2.0Revision 2.1-2Sun Feb 26 2017Victor BoctorRelease 2.1.1Revision 2.1-1Tue Jan 31 2017Victor BoctorRelease 2.1.0Revision 2.0-2Fri Dec 30 2016Victor BoctorRelease 2.0.0Revision 2.0-1Sat Nov 26 2016Damien RegadRelease 2.0.0-rc.2