<chapter id="admin.customize">
    <title>Customizing MantisBT</title>

    <section id="admin.customize.customfields">
        <title>Custom Fields</title>

	<section id="admin.customize.customfields.overview">
	<title>Overview</title>

        <para>Different teams typically like to capture different information
	as users report issues, in some cases, the data required is even
	different from one project to another.  Hence, MantisBT provides the
	ability for managers and administrators to define custom fields as way
	to extend MantisBT to deal with information that is specific to their
	teams or their projects.  The aim is for this to keep MantisBT native
	fields to a minimum.

	Following are some facts about the implementation of
        custom fields in MantisBT:
            <itemizedlist>
                <listitem>
                    <para>Custom fields are defined system wide.</para>
                </listitem>
                <listitem>
                    <para>Custom fields can be linked to multiple
                        projects.
                    </para>
                </listitem>
                <listitem>
                    <para>The sequence of displaying custom fields can be different
                        per project.
                    </para>
                </listitem>
                <listitem>
                    <para>Custom fields must be defined by users with access level
                        ADMINISTRATOR.
                    </para>
                </listitem>
                <listitem>
                    <para>Custom fields can be linked to projects by users with
                        access level MANAGER or above (by default, this can be
                        configurable).
                    </para>
                </listitem>
                <listitem>
                    <para>Number of custom fields is not restricted.</para>
                </listitem>
		<listitem>
		    <para>Users can define filters that include custom fields.</para>
		</listitem>
		<listitem>
		    <para>Custom fields can be included in View Issues, Print
		    Issues, and CSV exports.</para>
		</listitem>
		<listitem>
		    <para>Enumeration custom fields can have a set of static
		    values or values that are calculated dynamically based on
		    a custom function.</para>
		</listitem>
            </itemizedlist>
        </para>
        </section>

	<section id="admin.customize.customfields.definitions">
	    <title>Custom Field Definition</title>

        <para>
            The definition of a custom field includes the following logical
            attributes:

            <itemizedlist>
                <listitem>
					<para>Caption variable name.
						This value is supplied to the lang_get() API; it is
						therefore mandatory to set this to a
						<ulink url="http://php.net/language.variables.basics.php">valid PHP identifier</ulink>
						(i.e. only letters, numbers and underscores; no spaces)
						if you intend to translate the field label
						(see <xref linkend="admin.customize.customfields.localize">).
					</para>
					<note><para>If the specified variable is not found in the
						language files or in custom strings, then it will be
						displayed as-is.
					</para></note>
                </listitem>
                <listitem>
                    <para>Custom field type (string, numeric, float,
		    enumeration, email, checkbox, radio, list, multi-selection list, date).
                    </para>
		    <para>Type 'string' is used for strings of up to 255 characters.</para>
		    <para>Type 'numeric' is used for numerical integer values.</para>
		    <para>Type 'float' is used for real (float / double) numbers.</para>
		    <para>Type 'enumeration' is used when a user selects one entry from a list.  The user interface for such type is a combo-box.</para>
		    <para>Type 'email' is used for storing email addresses.</para>
		    <para>Type 'checkbox' is like enumeration but the list is shown as checkboxes and the user is allowed to tick more than one selection.  The default value and the possible value can contain multiple values like 'RED|YELLOW|BLUE' (without the single quote).</para>
			<para>Type 'radio' is like enumeration but the list is shown as radio buttons and the user is allowed to tick on of the options.  The possible values can be 'RED|YELLOW|BLUE', where the default value can be 'YELLOW'.  Note that the default value can't contain multiple values.</para>
		    <para>Type 'list' is like enumeration but the list is shown as a list box where the user is only allowed to select one option.  The possible values can be 'RED|YELLOW|BLUE', where the default value can be 'YELLOW'.  Note that the default value can't contain multiple values.</para>
		    <para>Type 'multi-selection list' is like enumeration but the list is shown as a list box where the user is allowed to select multiple options.  The possible values can be 'RED|YELLOW|BLUE', where the default value can be 'RED|BLUE'.  Note that in this case the default value contains multiple values.</para>
		    <para>Type 'date' is for date values.  The default value can be empty, or {tomorrow}, {yesterday}, {next week}, {last week}, {+3 days}, {-2 days}.</para>
                </listitem>
                <listitem>
                    <para>Enumeration possible values (eg: RED|YELLOW|BLUE).
                        Use the pipe ('|') character to separate possible values for an
                        enumeration. One of the possible values can be an
			empty string.  The set of possible values can also be
			calculated at runtime.  For example, "=versions" would
			automatically resolve into all the versions defined
			for the current project.
                    </para>
                </listitem>
                <listitem>
                    <para>Default value - see details above for a sample default value for each type.</para>
                </listitem>
                <listitem>
                    <para>Minimum/maximum length for the custom field value (use 0
                        to disable).  Note that these metrics are not really relevant to custom fields that are based on an enumeration of possible values.
                    </para>
                </listitem>
                <listitem>
                    <para>Regular expression to use for validating user input (use <ulink url="http://www.php.net/manual/en/reference.pcre.pattern.syntax.php">PCRE syntax</ulink>).
                    </para>
                </listitem>
                <listitem>
                    <para>Read Access level: Minimum access level for users to be
                        able to see the value of the custom field.
                    </para>
                </listitem>
                <listitem>
                    <para>Write Access level: Minimum access level for users to be
                        able to edit the value of the custom field.
                    </para>
                </listitem>
                <listitem>
                    <para>Display when reporting issues? - If this custom
		    field should be shown on the Report Issue page.
                    </para>
                </listitem>
                <listitem>
                    <para>Display when updating issues? - If this custom
		    field should be shown on the Update Issue page.
                    </para>
                </listitem>
                <listitem>
                    <para>Display when resolving issues? - If this custom
		    field should be shown when resolving an issue.  For
		    example, a "root cause" custom field would make sense to
		    set when resolving the issue.
                    </para>
                </listitem>
                <listitem>
                    <para>Display when closing issues? - If this custom
		    field should be shown when closing an issue.
                    </para>
                </listitem>
                <listitem>
                    <para>Required on Report - If this custom
		    field is a mandatory field on the Report Issue page.
                    </para>
                </listitem>
                <listitem>
                    <para>Required on Update - If this custom
		    field is a mandatory field on the Update Issue page.
                    </para>
                </listitem>
                <listitem>
                    <para>Required on Resolve - If this custom
		    field is a mandatory field when resolving an issue.
                    </para>
                </listitem>
                <listitem>
                    <para>Required on Close - If this custom
		    field is a mandatory field when closing an issue.
                    </para>
                </listitem>
            </itemizedlist>
        </para>

        <para>All custom fields are currently saved to a field of type
        VARCHAR(255) in the database. However, in future releases, it is
        possible to support custom fields of different types (eg: memo,
        file).</para>

        <para>
            If the value of a custom field for a certain defect is not found,
            the default value is assumed.
        </para>
    </section>

    <section id="admin.customize.customfields.editing">
        <title>Adding/Editing Custom Fields</title>

            <para>
                <itemizedlist>
                    <listitem>
                        <para>The logged in user needs $g_manage_custom_fields_threshold
                            access level.
                        </para>
                    </listitem>
                    <listitem>
                        <para>Select "Manage" from the main menu.</para>
                    </listitem>
                    <listitem>
                        <para>Select "Manage Custom Fields" from the management
                            menu.
                        </para>
                    </listitem>
                    <listitem>
                        <para>In case of edit, click on the name of an existing
			custom field to edit its information.
                        </para>
                    </listitem>
                    <listitem>
                        <para>In case of adding a new one, enter the name of the
			new custom field then click "New Custom Field".
                        </para>
                    </listitem>
                </itemizedlist>
            </para>

	    <note><para>Added custom fields will not show up in any of the issues
	    until the added custom field is linked to the appropriate
	    projects.</para></note>
	</section>

	<section id="admin.customize.customfields.linking">
		<title>Linking/Unlinking/Ordering Existing Custom Fields in Projects</title>

            <para>
                <itemizedlist>
                    <listitem>
                        <para>The logged in user needs to have access level that is
                            greater than or equal to $g_custom_field_link_threshold and
                            $g_manage_project_threshold.
                        </para>
                    </listitem>
                    <listitem>
                        <para>Select "Manage" from the main menu.</para>
                    </listitem>
                    <listitem>
                        <para>Select "Manage Projects".
                        </para>
                    </listitem>
                    <listitem>
                        <para>Select the name of the project to manage.</para>
                    </listitem>
                    <listitem>
                        <para>Scroll down to the "Custom Fields" box.</para>
                    </listitem>
                    <listitem>
                        <para>Select the field to add from the list, then click "Add
                            This Existing Custom Field".
                        </para>
                    </listitem>
                    <listitem>
                        <para>To change the order of the custom fields, edit the
                            "Sequence" value and click update. Custom fields with smaller
                            values are displayed first.
                        </para>
                    </listitem>
                    <listitem>
                        <para>To unlink a custom field, click on "Remove" link next to
                            the field.
                            Unlinking a custom field will not delete the values that are
                            associated with the issues for this field. These values are only
                            deleted if the custom field definition is removed (not unlinked!)
                            from the database. This is useful if you decide to re-link the
                            custom field. These values may also re-appear if issues are moved to
                            another project which has this field linked.
                        </para>
                    </listitem>
                </itemizedlist>
            </para>

        <formalpara>
            <title>Moving Issues</title>
            <para>
                When an issue is moved from one project to another, custom
                fields that are not defined for the new project are not deleted.
                These fields will re-appear with their correct values if the issue is
                moved back to the original project, or if these custom fields are
                linked to the new project.
            </para>
        </formalpara>
	</section>

 	<section id="admin.customize.customfields.localize">
	    <title>Localizing Custom Field Names</title>

		<para>It is possible to localize the custom fields' labels.
			This can be done as follows:

			<orderedlist>
				<listitem>
					<para>Define the custom field
						(see <xref linkend="admin.customize.customfields.definitions">),
						keeping in mind that its name must be a
						<ulink url="http://php.net/language.variables.basics.php">valid PHP identifier</ulink>.
					</para>
					<para>As an example, we will use
						<emphasis>my_start_date</emphasis>
						for a custom field of type "Date", storing the date when
						work on an issue was initiated.
					</para>
				</listitem>
				<listitem>
					<para>Set the localization strings
						<itemizedlist>
							<listitem><para>In the main MantisBT directory, locate and
								edit file <emphasis>custom_strings_inc.php</emphasis>
								(create it if it does not exist)
							</para></listitem>
							<listitem><para>Localize the custom field's label
								<emphasis>my_start_date</emphasis> by adding
								the following code
<programlisting>
&lt;?php
switch( $g_active_language ) {
	case 'french':
		$s_my_start_date = 'Date de d&eacute;but';
		break;

	default:
		# Default language, as defined in config_inc.php
		# ($g_default_language, English in this case)
		$s_my_start_date = 'Start Date';
		break;
}
</programlisting>
								<warning><para>Do NOT call
									<emphasis>lang_get_current()</emphasis>
									from custom_strings_inc.php, as doing so
									will reset the active_language, causing the
									code to return incorrect translations if the
									default language is different from English.
									Always use the <emphasis>$g_active_language</emphasis>
									global variable instead.
								</para></warning>
							</para></listitem>
						</itemizedlist>
					</para>
				</listitem>
			</orderedlist>
		</para>

		<note><para>Had we decided to use <emphasis>start_date</emphasis>
			as the custom field's name, then it would not have been necessary to
			modify custom_strings_inc.php, since MantisBT would have used the
			existing, already localized string from the standard language files.
			To check for standard strings, inspect lang/strings_english.txt.
		</para></note>
	</section>

        <section id="admin.customize.customfields.defaults">
            <title>Dynamic default values</title>

            <section id="admin.customize.customfields.defaults.date">
                <title>Dynamic defaults for Date fields</title>

                <para>Custom fields of type date can be defaulted to a specific dates or to relative dates.  Typically relative dates is the scenario that makes sense in most of the cases.  The format for specific dates is an integer which indicates the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT), which is the format consumed by the PHP <ulink url="http://www.php.net/date">date()</ulink> method.  The relative scenario expects default values like {tomorrow}, {yesterday}, {+2 days}, {-3 days}, {next week}, etc.  The curly brackets indicate that this is a logical value which is then evaluated using the PHP <ulink url="http://www.php.net/strtotime">strtotime()</ulink> function.</para>
            </section>
        </section>

	<section id="admin.customize.customfields.dynamic">
	    <title>Dynamic values for Enumeration Custom Fields</title>

	    <para>As discussed earlier, one of the possible types of a custom
	    field is "enumeration".  This type of custom field allows the user
	    to select one value from a provided list of possible values.  The
	    standard way of defining such custom fields is to provide a '|'
	    separated list of possible values.  However, this approach has two
	    limitations: the list is static, and the maximum length of the list
	    must be no longer than 255 characters.  Hence, the need for the
	    ability to construct the list of possible values dynamically.</para>


            <section id="admin.customize.customfields.dynamic.default">
	       <title>Dynamic possible values included by default</title>

	    <para>MantisBT ships with some dynamic possible values, these
	    include the following:

	    	<itemizedlist>
		    <listitem><para>=categories - a list of categories defined
		    in the current project (or the project to which the issue
		    belongs).</para></listitem>
		    <listitem><para>=versions - a list of all versions defined
		    in the current project (or the project to which the issue
		    belongs).</para></listitem>
		    <listitem><para>=future_versions - a list of all versions that
		    belong to the current project with
		    released flag set to false.</para></listitem>
		    <listitem><para>=released_versions - a list of all versions
		    that belong to the current project with released flag set to
		    true.</para></listitem>
		</itemizedlist>
	    </para>

	    <note><para>The '=' before the name of the dynamic list of options is used
	    to tell MantisBT that this is a dynamic list, rather than a static
	    list with just one option.</para></note>

	    </section>

	    <section id="admin.customize.customfields.dynamic.custom">
	        <title>Defining Custom Dynamic Possible Values</title>

		<para>If the user selects =versions, the actual custom function
		that is executed is custom_function_*_enum_versions(). The reason
		why the "enum_" is not included is to have a fixed prefix for all
		custom functions used for this purpose and protect against users
		using custom functions that were not intended for this purpose.
		For example, you don't want the user to use
		custom_function_*_issue_delete_notify() which may be overridden
		by the web master to delete associated data in other
		databases.</para>

		<para>Following is a sample custom function that is used to
		populate a field with the categories belonging to the currently
		selected project:

<programlisting># --------------------
# Construct an enumeration for all categories for the current project.
# The enumeration will be empty if current project is ALL PROJECTS.
# Enumerations format is: "abc|lmn|xyz"
# To use this in a custom field type "=categories" in the possible values field.
function custom_function_override_enum_categories() {
    $t_categories = category_get_all_rows( helper_get_current_project() );

    $t_enum = array();
    foreach( $t_categories as $t_category ) {
        $t_enum[] = $t_category['category'];
    }

    $t_possible_values = implode( '|', $t_enum );

    return $t_possible_values;
}</programlisting>
</para>

<para>Notice the following:

<itemizedlist>
	<listitem><para>The custom function doesn't take any
	parameters.</para></listitem>
	<listitem><para>The custom function returns the possible values in the
	format (A|B|C).</para></listitem>
   	<listitem><para>The custom function uses the current
	project.</para></listitem>
	<listitem><para>The custom function builds on top of the already
	existing APIs.</para></listitem>
</itemizedlist>
</para>

<para>To define your own function \u201c=mine\u201d, you will have to define it
with the following signature:

<programlisting>
# --------------------
# To use this in a custom field type "=mine" in the possible values field.
function custom_function_override_enum_mine() {
    $t_enum = array();

    :

    $t_possible_values = implode( '|', $t_enum );

    return $t_possible_values;
}
</programlisting>
</para>

<para>Notice "override" in the function name. This is because this method is
defined by the MantisBT administrator/webmaster and not part of the MantisBT source.
It is OK to override a method that doesn't exist.</para>

<para>As usual, when MantisBT is upgraded to future releases, the custom functions
will not be overwritten. The difference between the "default" implementation and
the "override" implementation is explained in more details in the custom
functions section.</para>

	</section>

        </section>

    </section>
    <section id="admin.customize.enums">
        <title>Enumerations</title>


        <para>Enumerations are used in MantisBT to represent a set of
            possible values for an attribute. Enumerations are used for access
            levels, severities, priorities, project statuses, project view
            state, reproducibility, resolution, ETA, and projection.  MantisBT
            provides the administrator with the flexibility of altering the
            values in these enumerations. The rest of this topic explains how
            enumerations work, and then how they can be customised.
        </para>

        <formalpara>
            <title>How enumerations work?</title>

            <para>
                <filename>core/constant_inc.php</filename> defines the
                constants that correspond to those in the enumeration. These are
                useful to refer to these enumerations in the configs and the code.
                <programlisting>
                    define( 'VIEWER', 10 )
                    define( 'REPORTER', 25 )
                    define( 'UPDATER',  40 )
                    define( 'DEVELOPER', 55 )
                    define( 'MANAGER', 70 )
                    define( 'ADMINISTRATOR', 90 )
                </programlisting>
            </para>
        </formalpara>

        <para>
            <filename>config_defaults_inc.php</filename> includes the defaults for the
            enumerations. The configuration options that are defaulted here are
            used in specifying which enumerations are active and should be used
            in MantisBT.  However, the strings included in the enumerations here
            are just for documentation purpose, they are not shown to the user
            (due to the need for localisation). Hence, if an entry in this
            enumeration is not found in the corresponding localised enumeration
            (i.e. 70:manager), then it will be printed to the user as @70@.

            <programlisting>
                $g_access_levels_enum_string =
                '10:viewer,25:reporter,40:updater,55:developer,70:manager,90:administrator';
            </programlisting>
        </para>
        <para>
            <filename>lang/strings_german.txt</filename> provide the
            localised strings (in this case, in german) for enumerations. But again, the master list is
            the enumeration in the configs, the ones in the language files are
            just used for finding the localised equivalent for an entry. Hence,
            if a user changes the config to have only two types of users
            developers and administrators, then only those will be prompted to
            the users even if the enumerations in the language files still
            includes the full list.
            <programlisting>

                $s_access_levels_enum_string =
                '10:Betrachter,25:Reporter,40:Updater,55:Entwickler,70:Manager,90:Administrator';
            </programlisting>
        </para>

        <formalpara>
            <title>How can they be customised?</title> <para>Let say we want to remove access level
                "Updater" and add access level "Senior
                Developer".
            </para>
        </formalpara>
        <para>
            The file <filename>custom_constants_inc.php</filename> is supported for the
            exclusive purpose of allowing administrators to define their own
            constants while maintaining a simple upgrade path for future
            releases of MantisBT. Note that this file is not distributed with
            MantisBT and you will need to create it if you need such
            customisation. In our example, we need to define a constant for the
            new access level.
            <programlisting>
                define ( 'SENIOR_DEVELOPER', 60 );
            </programlisting>
        </para>

        <para>
            In <filename>config_inc.php</filename>
            <programlisting>
                // Remove Updater and add Senior Developer
                $g_access_levels_enum_string =
                '10:viewer,25:reporter,55:developer,60:senior_developer,70:manager,90:administrator';
                // Give access to Senior developers to create/delete custom field.
                $g_manage_custom_fields_threshold = SENIOR_DEVELOPER;
            </programlisting>
        </para>

        <para>
            The file <filename>custom_strings_inc.php</filename> is introduced for a similar reason
            to that of custom_constants_inc.php, which is to define custom
            strings. The advantage of defining them here is to provide a simple
            upgrade path, and avoid having to re-do the changes when upgrading
            to the next MantisBT release. Note that you will need to create this
            file if you need such customisation. The file is automatically
            detected and included by MantisBT code.
            <programlisting>
                # Note that we don't have to remove the Updater entry from the
                localisation file if the current language is 'english' ) {
                $s_access_levels_enum_string =
                '10:Betrachter,25:Reporter,40:Updater,55:Entwickler,60:Senior
                Developer,70:Manager,90:Administrator'; }
            </programlisting>
        </para>

        <formalpara>
            <title>Conclusion</title><para>We have covered how enumerations work in general, and how
                to customise one of them. If you are interested in customising
                other enumerations, a good starting point would be to go to "MantisBT
                Enum Strings" section in config_defaults_inc.php. This section
                defines all enumerations that are used by MantisBT.
                For versions that are older than 0.18.0, custom_*_inc.php files are
                not supported, and hence you will need to change in the actual
                constants / language files directly.
            </para>
        </formalpara>

    </section>


    <section id="admin.customize.email">
        <title>Email Notifications</title>

        <para>See Email in the Configuration section.</para>

        <para>Examples:
            <itemizedlist>
                <listitem>
                    <para>Notify only managers of new issues.
                        <programlisting>
                            $g_notify_flags['new']['threshold_min'] = MANAGER;
                            $g_notify_flags['new']['threshold_max'] = MANAGER;
                        </programlisting>
                    </para>
                </listitem>
                <listitem>
                    <para>Notify Developers and managers of all project events,
                        except, exclude developers from the 'closed' events.
                        <programlisting>$g_default_notify_flags['threshold_min'] = DEVELOPER;
                            $g_default_notify_flags['threshold_max'] = MANAGER;
                            $g_notify_flags['closed']['threshold_max'] = MANAGER;
                            $g_notify_flags['closed']['threshold_max'] = MANAGER;
                        </programlisting>
                    </para>
                </listitem>
                <listitem>
                    <para>Exclude those who contributed issue notes from getting
                        messages about other changes in the issue.
                        <programlisting>$g_default_notify_flags['bugnotes'] = OFF;</programlisting>
                    </para>
                </listitem>
                <listitem>
                    <para>Exclude those monitoring issues from seeing the 'closed'
                        message
                        <programlisting>$g_notify_flags['closed']['monitor'] = OFF;</programlisting>
                    </para>
                </listitem>
                <listitem>
                    <para>Only notify developers when issue notes are added.
                        <programlisting>$g_notify_flags['bugnote']['threshold_min'] = DEVELOPER;
                            $g_notify_flags['bugnote']['threshold_max'] = DEVELOPER;
                        </programlisting>
                    </para>
                </listitem>
                <listitem>
                    <para>Notify managers of changes in sponsorship.
                        <programlisting>$g_notify_flags['sponsor']['threshold_max'] = MANAGER;
                            $g_notify_flags['sponsor']['threshold_max'] = MANAGER;
                        </programlisting>
                    </para>
                </listitem>
                <listitem>
                    <para>Notify originator and managers of changes in ownership
                        ("Assigned To:").
                        <programlisting>$g_notify_flags['owner']['threshold_max'] = MANAGER;
                            $g_notify_flags['owner']['threshold_max'] = MANAGER;
                            $g_notify_flags['owner']['reporter'] = ON;
                        </programlisting>
                    </para>
                </listitem>
                <listitem>
                    <para>I'm paranoid about mail. Only send information on issues
                        to those involved in them. Don't send mail people already know
                        about. Also send new issue notifications to managers so they can
                        screen them.
                        <programlisting>$g_mail_receive_own = OFF;
                            $g_default_notify_flags =
                            array('reporter' =&gt; ON, 'handler' =&gt; ON, 'monitor' =&gt; ON,
                            'bugnotes' =&gt; ON, 'threshold_min' =&gt; NOBODY, 'threshold_max'
                            =&gt; NOBODY);
                            $g_notify_flags['new']['threshold_min'] = MANAGER;
                            $g_notify_flags['new']['threshold_max'] = MANAGER;
                        </programlisting>
                    </para>
                </listitem>
                <listitem>
                    <para>How do I replace the $g_to_email configuration variable
                        to log all messages to an email logger.
                    </para>
                    <para>You will need to create a
                        dummy user with the appropriate access level for the notices you
                        want to log. Once this user is added to projects, they will receive
                        mail using the appropriate rules.
                    </para>
                </listitem>
            </itemizedlist>
        </para>
    </section>

	<section id="admin.customize.status">
		<title>Customizing Status Values</title>

		<para>This section describes how to add a custom status.
			<orderedlist>
				<listitem>
					<para>Define a constant to map the new status to.</para>
					<para>In the main MantisBT directory, locate and edit file
						<emphasis>custom_constants_inc.php</emphasis>;
						(create it if it does not exist)
						<programlisting>
&lt;?php
	# Custom status code
	define( 'TESTING', 60 );
</programlisting>
					</para>
				</listitem>

				<listitem>
					<para>Define the new status in the enumeration, as well as
						the corresponding color code.
					</para>
					<para>In the main mantisbt directory, edit your
						<emphasis>config_inc.php</emphasis>

						<programlisting>
# Revised enum string with new 'testing' status
$g_status_enum_string = '10:new,20:feedback,30:acknowledged,40:confirmed,50:assigned,<emphasis>60:testing,</emphasis>80:resolved,90:closed';

# Status color additions
$g_status_colors['<emphasis>testing</emphasis>'] = '#ACE7AE';
</programlisting>
						Note that the key in the $g_status_colors array must be
						equal to the value defined for the new status code in
						$g_status_enum_string.
					</para>
				</listitem>

				<listitem>
					<para>Define the required translation strings for the new
						status, for each language used in the installation.
						<itemizedlist>
							<listitem><para><emphasis>s_status_enum_string</emphasis>:
								status codes translation (refer to the original
								language strings for standard values)
							</para></listitem>
							<listitem><para><emphasis>s_XXXX_bug_title</emphasis>:
								title displayed in the change status page
							</para></listitem>
							<listitem><para><emphasis>s_XXXX_bug_button</emphasis>:
								label for the submit button in the change status page
							</para></listitem>
							<listitem><para><emphasis>s_email_notification_title_for_status_bug_XXXX</emphasis>:
								title for notification e-mails
							</para></listitem>
						</itemizedlist>
						where XXXX is the name of the new status as it was defined
						in <emphasis>g_status_enum_string</emphasis> above. If
						XXXX contains spaces, they should be replaced by
						underscores in the language strings names (e.g.
						for '35:pending user', use '$s_pending_user_bug_button')
					</para>
					<para>In the main mantisbt directory, locate and edit file
						<emphasis>custom_strings_inc.php</emphasis>;
						(create it if it does not exist)
<programlisting>
&lt;?php
# Translation for Custom Status Code: <emphasis>testing</emphasis>
switch( $g_active_language ) {

	case 'french':
		$s_status_enum_string = '10:nouveau,20:commentaire,30:accept&eacute;,40:confirm&eacute;,50:affect&eacute;,60:&agrave; tester,80:r&eacute;solu,90:ferm&eacute;';

		$s_testing_bug_title = 'Mettre le bogue en test';
		$s_testing_bug_button = 'A tester';

		$s_email_notification_title_for_status_bug_testing = 'Le bogue suivant est pr&ecirc;t &agrave; &ecirc;tre TESTE.';
		break;

	default: # english
		$s_status_enum_string = '10:new,20:feedback,30:acknowledged,40:confirmed,50:assigned,60:testing,80:resolved,90:closed';

		$s_testing_bug_title = 'Mark issue Ready for Testing';
		$s_testing_bug_button = 'Ready for Testing';

		$s_email_notification_title_for_status_bug_testing = 'The following issue is ready for TESTING.';
		break;
}
</programlisting>
						<warning><para>Do NOT call
							<emphasis>lang_get_current()</emphasis>
							from custom_strings_inc.php, as doing so
							will reset the active_language, causing the
							code to return incorrect translations if the
							default language is different from English.
							Always use the <emphasis>$g_active_language</emphasis>
							global variable instead.
						</para></warning>
					</para>
				</listitem>

				<listitem>
					<para>Add the new status to the workflow as required.
					</para>
					<para>This can either be done from the
						<link linkend="admin.lifecycle.workflow.transitions">
							Manage Workflow Transitions page
						</link>
						or by manually editing <emphasis>config_inc.php</emphasis>
						as per the example below:

						<programlisting>
$g_status_enum_workflow[NEW_]         ='30:acknowledged,20:feedback,40:confirmed,50:assigned,80:resolved';
$g_status_enum_workflow[FEEDBACK]     ='30:acknowledged,40:confirmed,50:assigned,80:resolved';
$g_status_enum_workflow[ACKNOWLEDGED] ='40:confirmed,20:feedback,50:assigned,80:resolved';
$g_status_enum_workflow[CONFIRMED]    ='50:assigned,20:feedback,30:acknowledged,80:resolved';
$g_status_enum_workflow[ASSIGNED]     ='60:testing,20:feedback,30:acknowledged,40:confirmed,80:resolved';
$g_status_enum_workflow[TESTING]      ='80:resolved,20:feedback,50:assigned';
$g_status_enum_workflow[RESOLVED]     ='90:closed,20:feedback,50:assigned';
$g_status_enum_workflow[CLOSED]       ='20:feedback,50:assigned';
</programlisting>
					</para>
				</listitem>

				<listitem>
					<para>Check and update existing workflow configurations</para>
					<para>If you do not perform this step and have existing
						workflow definitions, it will not be possible to
						transition to and from your new status.
					</para>
					<para>Go to the Workflow Transitions page
						(manage_config_workflow_page.php), and update the
						workflow as appropriate. Make sure that you have picked
						the correct Project in the selection list).
					</para>
					<para>Hint: to identify whether you have any workflows that
						should be updated, open the Manage Configuration Report
						page (adm_config_report.php) and filter on 'All Users',
						[any] project and config option = 'status_enum_workflow'.
						All of the listed projects should be reviewed to
						eventually include transitions to and from the newly
						added states.
					</para>
				</listitem>

			</orderedlist>
		</para>

	</section>

	<section id="admin.customize.customfuncs">
		<title>Custom Functions</title>

		<para>Custom functions are used to extend the functionality of
			MantisBT by integrating user-written functions into the issue
			processing at strategic places. This allows the system
			administrator to change the functionality without touching
			MantisBT's core.
		</para>
		<para>Default Custom Functions are defined in the API file
			<filename>core/custom_function_api.php</filename>
			, and are named
			<emphasis>custom_function_default_descriptive_name</emphasis>,
			where <emphasis>descriptive_name</emphasis> describes the
			particular function.
			<link linkend="admin.customize.customfuncs.defined">See below</link>
			for a description of the specific functions.
		</para>
		<para>User versions of these functions (overrides) are named like
			<emphasis>custom_function_override_descriptive_name</emphasis>,
			and placed in a file called
			<filename>custom_functions_inc.php</filename> that must be saved in
			MantisBT's root directory (This is the same place where the
			<filename>config_inc.php</filename> file resides).
			In normal processing, the system will look for override functions
			and execute them instead of the provided default functions.
		</para>
		<para>The simplest way to create a custom function is to copy the
			default one from the api to your override file
			(<filename>custom_functions_inc.php</filename>), and rename it
			(i.e. replacing 'default' by 'override').
			The specific functionality you need can then be coded into the
			override function.
		</para>

		<section id="admin.customize.customfuncs.defined">
			<title>Default Custom Functions</title>

			<para>Refer to <filename>core/custom_functions_api.php</filename>
				for further details.
			</para>

			<informaltable>
				<tgroup cols="3">
					<thead>
						<row>
							<entry>Custom Function Name</entry>
							<entry>Description</entry>
							<entry>Return value</entry>
						</row>
					</thead>
					<tbody>
						<row>
							<entry>custom_function_default_auth_can_change_password()</entry>
							<entry>Determines whether MantisBT can update the password</entry>
							<entry>True if yes, False if not</entry>
						</row>
						<row>
							<entry>custom_function_default_changelog_include_issue( $p_issue_id )</entry>
							<entry>Determines whether the specified issue should be included in the Changelog or not.</entry>
							<entry>True to include, False to exclude</entry>
						</row>
						<row>
							<entry>custom_function_default_changelog_print_issue( $p_issue_id, $p_issue_level = 0 )</entry>
							<entry>Prints one entry in the Changelog</entry>
							<entry>None</entry>
						</row>
						<row>
							<entry>custom_function_default_checkin( $p_issue_id, $p_comment, $p_file, $p_new_version, $p_fixed )</entry>
							<entry>Register a checkin in source control by adding a history entry and a note</entry>
							<entry>None</entry>
						</row>
						<row>
							<entry>custom_function_default_enum_categories()</entry>
							<entry>Build a list of all categories for the current project</entry>
							<entry>Enumeration, delimited by "|"</entry>
						</row>
						<row>
							<entry>custom_function_default_enum_future_versions()</entry>
							<entry>Build a list of all future versions for the current project</entry>
							<entry>Enumeration, delimited by "|"</entry>
						</row>
						<row>
							<entry>custom_function_default_enum_released_versions()</entry>
							<entry>Build a list of all released versions for the current project</entry>
							<entry>Enumeration, delimited by "|"</entry>
						</row>
						<row>
							<entry>custom_function_default_enum_versions()</entry>
							<entry>Build a list of all versions for the current project</entry>
							<entry>Enumeration, delimited by "|"</entry>
						</row>
						<row>
							<entry>custom_function_default_format_issue_summary( $p_issue_id, $p_context = 0 )</entry>
							<entry>Format the bug summary</entry>
							<entry>Formatted string</entry>
						</row>
						<row>
							<entry>custom_function_default_get_columns_to_view( $p_columns_target = COLUMNS_TARGET_VIEW_PAGE, $p_user_id = null )</entry>
							<entry>Defines which columsn should be displayed</entry>
							<entry>Array of the column names</entry>
						</row>
						<row>
							<entry>custom_function_default_issue_create_notify( $p_issue_id )</entry>
							<entry>Notify after an issue has been created</entry>
							<entry>In case of invalid data, this function should call trigger_error()</entry>
						</row>
						<row>
							<entry>custom_function_default_issue_create_validate( $p_new_issue_data )</entry>
							<entry>Validate field settings before creating an issue</entry>
							<entry>In case of invalid data, this function should call trigger_error()</entry>
						</row>
						<row>
							<entry>custom_function_default_issue_delete_notify( $p_issue_data )</entry>
							<entry>Notify after an issue has been deleted</entry>
							<entry>In case of invalid data, this function should call trigger_error()</entry>
						</row>
						<row>
							<entry>custom_function_default_issue_delete_validate( $p_issue_id )</entry>
							<entry>Validate field settings before deleting an issue</entry>
							<entry>In case of invalid data, this function should call trigger_error()</entry>
						</row>
						<row>
							<entry>custom_function_default_issue_update_notify( $p_issue_id )</entry>
							<entry>Notify after an issue has been updated</entry>
							<entry>In case of invalid data, this function should call trigger_error()</entry>
						</row>
						<row>
							<entry>custom_function_default_issue_update_validate( $p_issue_id, $p_new_issue_data, $p_bugnote_text )</entry>
							<entry>Validate field issue data before updating</entry>
							<entry>In case of invalid data, this function should call trigger_error()</entry>
						</row>
						<row>
							<entry>custom_function_default_print_bug_view_page_custom_buttons( $p_bug_id )</entry>
							<entry>Prints the custom buttons on the current view page</entry>
							<entry>None</entry>
						</row>
						<row>
							<entry>custom_function_default_print_column_title( $p_column, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE )</entry>
							<entry>Print a column's title based on its name</entry>
							<entry>None</entry>
						</row>
						<row>
							<entry>custom_function_default_print_column_value( $p_column, $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE )</entry>
							<entry>Print a column's value based on its name</entry>
							<entry>None</entry>
						</row>
						<row>
							<entry>custom_function_default_roadmap_include_issue( $p_issue_id )</entry>
							<entry>Determines whether the specified issue should be included in the Roadmap or not.</entry>
							<entry>True to include, False to exclude</entry>
						</row>
						<row>
							<entry>custom_function_default_roadmap_print_issue( $p_issue_id, $p_issue_level = 0 )</entry>
							<entry>Prints one entry in the Roadmap</entry>
							<entry>None</entry>
						</row>
					</tbody>
				</tgroup>
			</informaltable>

		</section>

		<section id="admin.customize.customfuncs.example">
			<title>Example Custom Function Override</title>

			<para>The following function is used to validate an issue before
				it is resolved.
			</para>

			<programlisting width="102"><![CDATA[<?php

/**
 * Hook to validate Validate field settings before resolving
 * verify that the resolution is not set to OPEN
 * verify that the fixed in version is set (if versions of the product exist)
 */
function custom_function_override_issue_update_validate( $p_issue_id, $p_bug_data, $p_bugnote_text ) {
	if( $p_bug_data->status == RESOLVED ) {
		if ( $p_bug_data->resolution == OPEN ) {
			error_parameters( 'the resolution cannot be open to resolve the issue' );
			trigger_error( ERROR_BUG_VALIDATE_FAILURE, ERROR );
		}
		$t_version_count = count( version_get_all_rows( $p_bug_data-&gt;project_id ) );
		if( ( $t_version_count > 0 ) && ( $p_bug_data->fixed_in_version == '' ) ) {
			error_parameters( 'fixed in version must be set to resolve the issue' );
			trigger_error( ERROR_BUG_VALIDATE_FAILURE, ERROR );
		}
	}
}

?>]]>
</programlisting>

			<para>The errors will also need to be defined, by modifying the
				following files
			</para>

			<itemizedlist>
				<listitem>
					<para><filename>custom_constants_inc.php</filename>
					</para>

					<programlisting>
define( 'ERROR_VALIDATE_FAILURE', 2000 );
</programlisting>
				</listitem>

				<listitem>
					<para><filename>custom_strings_inc.php</filename>
					</para>

					<programlisting>
$MANTIS_ERROR['ERROR_VALIDATE_FAILURE'] = 'This change cannot be made because %s';
</programlisting>
				</listitem>
			</itemizedlist>

		</section>
	</section>

</chapter>
