en ru | docs

How to write a forum modification

This is a complete documentation on integrating MapBBCode into engines. If you have skills to do that, please make a plugin implementing all those features and publish it under an open license. By doing this you will help open maps conquer the internet.

A simpler way to embed MapBBCode into a website for people who cannot program or are too lazy is MapBBCode Loader: a single script that can serve as a plugin. A complete 5-step walkthrough is included in the repository.

  1. Quick Start
    1. Include libraries in a page header
    2. Process [map] bbcode
    3. Add a button to the posting page
  2. Improvements
    1. [mapid] bbcode
    2. Localization
    3. Add-ons
    4. Disable maps in signatures
    5. Configuration
    6. Administration panel
    7. Libraries only when needed
    8. Global on/off switch
  3. Checklist
    1. Forum posts
    2. Private messages
    3. External maps
    4. Administration

Quick start

Writing forum modifications is hard, especially if you haven't done it before, so we'll start with minimum number of steps to display [map] bbcode in threads.

Include libraries in a page header

First of all, find a place in the forum engine directory tree where you can put all MapBBCode libraries. It shouldn't be a theme directory, so if a forum administrator has a lot of themes, libraries don't get copied into all of them. Preferably it's scripts or includes directory in the root. Modify paths in javascript code according to your directory structure.

Usually there is a main theme, like subSilver or proSilver in phpBB. Find a template for a page header in it. There can be more than one ("full" and "simple"). In some cases page header is generated by a script (e.g. in SMF). In any case, you should add the following code right before the </head> HTML tag (don't forget to write proper paths):

<link rel="stylesheet" href="includes/mapbbcode/leaflet.css" />
<link rel="stylesheet" href="includes/mapbbcode/leaflet.draw.css" />
<script src="includes/mapbbcode/leaflet.js"></script>
<script src="includes/mapbbcode/leaflet.draw.js"></script>
<script src="includes/mapbbcode/mapbbcode.js"></script>
<script src="includes/mapbbcode/LayerList.js"></script>
<script language="Javascript" type="text/javascript">
<!--
var mapBBcode = new MapBBCode({
    windowPath: 'includes/mapbbcode/',
    layers: 'OpenStreetMap',
    defaultZoom: 2,
    defaultPosition: [22, 11],
    viewWidth: 600,
    viewHeight: 300,
    fullViewHeight: 600,
    editorHeight: 400,
    windowWidth: 800,
    windowHeight: 500,
    fullFromStart: false,
    preferStandardLayerSwitcher: true
});
//-->
</script>

Later this whole block will be conditional, depending on a presence of bbcode tags in a current page.

Process [map] bbcode

[map]...[/map] sequence should be replaced with the following template code:

<div id="map{DIVID}">{MAPBBCODE}</div>
<script language="javascript">if(mapBBcode) mapBBcode.show('map{DIVID}');</script>

Here {MAPBBCODE} is the entire bbcode, including the enclosing [map] tags (they could contain map zoom and coordinates). {DIVID} is a unique identifier of this bbcode. Not a hash of bbcode string, and not a post identifier (imagine two identical maps in a single post). Ideally this is a big random number.

How this template is included depends on a forum engine. For example, in phpBB 2 {DIVID} part is pre-generated with the following regular expression:

$mapre = '#(\[map(?:=[0-9,.-]+)?)(:[a-fA-F0-9]+)?(\].*?\[/map\])#si';
$text = preg_replace_callback($mapre,
    create_function('$m','return $m[1].":".make_bbcode_uid().$m[3];'), $text);

And then grouping quotes are parsed into {DIVID} (#2) and {MAPBBCODE} (#1+#3).

In phpBB3 adding a bbcode didn't require any code modifications, only a database entry in table phpbb_bbcode. It essentially transforms to a function call:

preg_replace('!\[map:($uid)(=[0-9,.]+)?\](.*?)\[/map:$uid\]!se',
'<div id="map${1}'.$i.'">[map${2}]${3}[/map]</div><script language="javascript">'.
'if(mapBBcode) mapBBcode.show(\'map${1}'.($i++).'\');</script>', $message);

Add a button to the posting page

On a posting page there is usually a row of buttons for inserting bbcode. Add a "Map" button there, with the following code in onclick parameter:

javascript:true ? mapBBcode.editorWindow(document.getElementById('post_area'))
                : mapBBcode.editor('mapedit', document.getElementById('post_area'));

It unconditionally opens an editor window — for now. Later true will be replaced with a configuration parameter, so an administrator can choose where does the editor appear. For a inline panel option, you have to add the following tag somewhere inside a posting form, before bbcode buttons.

These three steps are enough to enable [map] bbcode on a forum. Test it by writing some posts with maps. If you found issues in the library code while testing (not in your modification), please report them to MapBBCode github.

Improvements

For a production-grade forum modification, changes above are not enough. There is no locatization, no configuration panel, and several hundred kilobytes of javascript and css are included in every page, even in the forum index.

[mapid] bbcode

The code to insert maps from an external site is very similar in implementation to the [map] bbcode. You have to add another bbcode, [mapid], with the same unique <div> id, but instead of {MAPBBCODE} it would have {MAPID} parameter, that gets passed to mapBBcode.showExternal('map{DIVID}', '{MAPID}'). The relevant phpBB 3 entry would look like this:

preg_replace('!\[mapid:($uid)\]([a-z]+)\[/mapid:$uid\]!e',
'<div id="map${1}'.$i.'"></div><script language="javascript">'.
'if(mapBBcode) mapBBcode.showExternal(\'map${1}'.($i++).'\', \'${2}\');</script>', $message);

Of course, some adminitrators would like to disable the interaction with external sites, so the code for converting [mapid] to HTML should be enclosed in a conditional block.

Localization

Translation strings for MapBBCode are kept in the library's directory, in lang/{language}.js and lang/{language}.config.js files. You would need to include the file for a current language after mapbbcode.js. The name for the file can be kept in a plugin translation script.

Add-ons

Forum administrators will definitely want to include some add-ons or proprietary layers. So your plugin should be easily extensible. That is, the number of places an administrator needs to edit in order to add an add-on script should be kept to a mininum. Almost in all cases it is possible to make it two files: some of yours and mapbbcode-window.html. If there is an installation process, you can modify that html's contents in it, so it would be needed to add add-ons only in one place. See existing plugins to learn different approaches to that task.

Disable maps in signatures

Allowing maps in user signatures is highly undesirable, like having videos or big images in them. If a forum engine processes signatures even slightly different than message bodies, you should find a way to leave [map] bbcode in signatures as is. For example, in phpBB there are options for disabling some of bbcode in signatures, so turning [map] off was just a matter of copying and pasting.

Configuration

Administrators should be able to configure the look of the map. There is 11 constructor options plus editorWindow mentioned above. They are better put into a main configuration table (e.g. phpbb_config for phpBB), since their names and number is constant, and all values are strings. Also this way you won't have to alter the database structure. All options should be prefixed with mapbb_ to not clash with other parameters.

All constructor options should be read in a page header. Most likely they already are, if you decided to keep them in the main configuration table. The library constructor options would be changed to:

layers: '{LAYERS}'.split(','),
defaultZoom: {DEFAULT_ZOOM}+0,
defaultPosition: [{DEFAULT_POS}],
viewWidth: {VIEW_WIDTH}+0,
viewHeight: {VIEW_HEIGHT}+0,
fullViewHeight: {FULL_HEIGHT}+0,
editorHeight: {EDITOR_HEIGHT}+0,
windowWidth: {WINDOW_WIDTH}+0,
windowHeight: {WINDOW_HEIGHT}+0,
fullFromStart: {ALWAYS_FULL},
uploadButton: {ENABLE_EXTERNAL},
preferStandardLayerSwitcher: {STANDARD_SWITCHER}

Note that {LAYERS} variable may contain apostrophes. The last three properties are boolean, so you should either cast them to true/false in the initialization code, or use longer expressions:

property: '{PROPERTY)' === 'true' || '{PROPERTY}' === '1'

Finally, {EDITOR_WINDOW} parameter goes into a page with a posting form, replacing the true constant. It is also boolean, so you may want to use the expression above.

To initialize those options, you will need to execute a number of INSERT statements. Consult with the developer's guide for your forum engine on where to put them. Good default values are included at the top of this document.

Administration panel

Configuration panels are usually separate files. Just use a regular key-value panel as a template for this one. In the template you need a restricted set of scripts:

<link rel="stylesheet" href="../includes/mapbbcode/leaflet.css" />
<script src="../includes/mapbbcode/leaflet.js"></script>
<script src="../includes/mapbbcode/mapbbcode-config.js"></script>

Eleven properties are edited with a javascript user interface, and in a form these are represented with hidden fields (your field names may be different, depending on a forum engine's coding style):

<input type="hidden" name="default_zoom" value="{DEFAULT_ZOOM}" />
<input type="hidden" name="default_pos" value="{DEFAULT_POS}" />
<input type="hidden" name="view_width" value="{VIEW_WIDTH}" />
<input type="hidden" name="view_height" value="{VIEW_HEIGHT}" />
<input type="hidden" name="full_height" value="{FULL_HEIGHT}" />
<input type="hidden" name="editor_height" value="{EDITOR_HEIGHT}" />
<input type="hidden" name="window_width" value="{WINDOW_WIDTH}" />
<input type="hidden" name="window_height" value="{WINDOW_HEIGHT}" />
<input type="hidden" name="always_full" value="{ALWAYS_FULL}" />
<input type="hidden" name="editor_window" value="{EDITOR_WINDOW}" />
<input type="hidden" name="layers" value="{LAYERS}" />

First in the page goes the map with controls to edit all these properties. It is a drop-down list of available layers, a button to add them to the map, an initially hidden field to enter a developer key for a layer, and a placeholder for the map.

<div>
    <select id="layer_select" size="1"></select>
    <input type="button" value="{L_ADD_LAYER}" id="layer_add"/>
    <span id="key_form">
        <span id="key_title"></span>
        <input type="text" maxlength="80" size="40" id="key_value" />
    </span>
</div>
<div id="panel_config"></div>

Then there should be regular key-value configuration fields for other properties. In phpBB modifications they include:

And finally there is a large block of javascript code linking the form and the map UI. Alter form field names and template variables if needed.

// converting PHP "boolean" to javascript boolean
function isTrue(val) {
    return val && val !== '0' && val !== 'false';
}

// if you want to have read-only values in the key-value table for map dimensions, update them here
function updateTableValues() {
}

// creating the map panel configuration user interface
var config = new MapBBCodeConfig({
    layers: '{LAYERS}'.split(','),
    defaultZoom: {DEFAULT_ZOOM}+0,
    defaultPosition: [{DEFAULT_POS}],
    viewWidth: {VIEW_WIDTH}+0,
    viewHeight: {VIEW_HEIGHT}+0,
    fullViewHeight: {FULL_HEIGHT}+0,
    editorHeight: {EDITOR_HEIGHT}+0,
    windowWidth: {WINDOW_WIDTH}+0,
    windowHeight: {WINDOW_HEIGHT}+0,
    fullFromStart: isTrue('{ALWAYS_FULL}'),
//    editorTypeFixed: true, // uncomment if needed
    editorInWindow: isTrue('{EDITOR_WINDOW}') // set to true or false is needed
});

// translation strings.
config.setStrings({
    view: '{L_VIEW}',
    editor: '{L_EDITOR}',
    // ... See src/strings/English.Config.js in MapBBCode repository for the full list
    keyNeededAlert: 'This layer needs a developer key'
});

// update hidden input fields and available layers list
config.on('show change', function(options) {
    var f = document.forms['mapfm'];
    f.elements['default_zoom'].value = options.defaultZoom;
    f.elements['default_pos'].value = '' + options.defaultPosition[0] + ',' +
        options.defaultPosition[1];
    f.elements['view_width'].value = options.viewWidth;
    f.elements['view_height'].value = options.viewHeight;
    f.elements['full_height'].value = options.fullViewHeight;
    f.elements['editor_height'].value = options.editorHeight;
    f.elements['window_width'].value = options.windowWidth;
    f.elements['window_height'].value = options.windowHeight;
    f.elements['layers'].value = options.layers.join(',');
    f.elements['always_full'].value = options.fullFromStart ? '1' : '';
    f.elements['editor_window'].value = options.editorInWindow ? '1' : '';
    updateTableValues();
});
config.bindLayerAdder({
    select: 'layer_select',
    button: 'layer_add',
    keyBlock: 'key_form',
    keyBlockDisplay: 'inline', // you'll probably need to change this to 'block' or 'table-row'
    keyTitle: 'key_title',
    keyValue: 'key_value'
});
config.show('panel_config');

On the server side, this page should receive its values from the database and update them afterwards. Again, you should use another key-value page as a template. When finished, check that changes made in the configuration panel are reflected on forum pages.

Remember that localization and add-ons also apply to the administration page.

Libraries only when needed

Total size of all libraries and stylesheets is over 250 kilobytes. If they are included on every page of a forum, this would put a strain both on servers and users' bandwidth. So those should only be included in pages that actually use them. This is probably the most complex task in writing the modification.

The additional HTML code in page header templates should be wrapped in conditional template directives, so it is displayed only when a certain variable is set to true. It may require a modification of a script producing the header. This variable should also control adding localization string variables and pulling configuration properties from the database.

Then in every template that may produce messages with bbcode in them, this variable must be set depending on whether [map] bbcode is present on a page. In some cases is would be always true (a posting page), in other it would require parsing all messages:

$mapbbcode_present = false;
for($i = 0; $i < $total_posts; $i++)
{
    if (preg_match('/\[map(?:=[0-9.,-]+)?\].*?\[\/map\]|\[mapid\][a-z]+\[\/mapid\]/',
            $postrow[$i]['post_text'])) {
        $mapbbcode_present = true;
        break;
    }
}

The variable must always be set before page header template is processed (for some forum engines that could mean anywhere at all), and at least the following pages should be modified:

When finished, check that pages that don't feature posts and topics without maps don't have MapBBCode scripts in them.

Global on/off switch

Since with some forum engines it is possible to disable parsing [url] or other tags, why not add the same configuration for [map]? When adding this feature, check how this is done for other tags. You will need to add a configuration parameter to the database and to the administation page. Then add a check to a page header, in addition to $mapbbcode_present. On a posting page, the "Map" button should be hidden in case maps are disabled, and there should be a notification in line with other similar notifications.

Checklist

After the modification is complete, install a clean forum instance and apply your modification to it. Once it has been installed without errors, log in as a moderator and check the following use cases.

Forum posts

  1. The front page, topic list page and default topic page sources do not contain any mapbbcode mention.
  2. Create new post, click a "Map" button. Map editor opens in a new window.
  3. Draw a line and a polygon, place two markers: one with a two-letter title.
  4. Check help window under the "?" button.
  5. Click "Apply", so the code is inserted into the text area.
  6. Click inside the code, then "Map" again. Check that the features are loaded, then click "Cancel".
  7. Post the message and check that it is displayed properly.
  8. Click "quote" button on the map message, then "Preview". A map should be displayed in a quote box.
  9. If there are forum messages displayed on the posting page, check that the map is displayed there.
  10. Add a map without features and post the message. Both maps in the message should be displayed properly.
  11. Open a list of topics and check again for absence of any mapbbcode scripts.
  12. Create a new topic with a map and check it out afterwards.
  13. Log out and check maps in topics.

Private messages

  1. Create another user.
  2. Open private messages panel and start writing a PM to that user.
  3. Add a map to the private message, check its preview, and then send it.
  4. There should be no mapbbcode scripts in the private messages page now.
  5. Log in as another user and open the received message. The map should be displayed properly.
  6. Click reply, add a map, click preview to check that it's working.

External maps

  1. Enable external maps in the administration panel.
  2. Create a new post, click "Map" button and ensure there is an "Upload" button.
  3. Click it and type a random word into the input field, then press "Apply". An error message should appear.
  4. Press "Close", then draw some lines and markers, then press "Upload".
  5. You would be presented with a link to MapBBCode Share website. Open it in a new tab.
  6. Press "Close" button in the map editor. [mapid]code[/mapid] should appear in a text area.
  7. Preview the post, checking that the map is displayed, and there are "Export" and "Outer link" buttons.
  8. Submit the post, check that the map is displayed.
  9. Press "Export" button, select "GeoJSON" and download the file. Then check that its contents are adequate.
  10. Switch to the tab with MapBBCode Share, make adjustments to the map and click "Save".
  11. Go back to the forum and refresh the page. The map should be updated.
  12. Click an outer link button and copy the URL of the page to the clipboard.
  13. Create a new post, click "Map" button, and then "Upload".
  14. Paste the URL into the input field and press "Apply". [mapid]code[/mapid] should be added to a text area.
  15. Cancel the posting, go to the administration panel and disable external maps.
  16. Return to the forum thread and check that [mapid] code is not parsed.
  17. Create a new pos, click "Map" and ensure there is no "Upload" button.

Administration

  1. Log in as administrator and open MapBBCode configuration panel.
  2. Change dimensions of a view panel, then click "View" to switch mode and change dimensions of an editing window.
  3. If enabled, click "Window" button in editor mode, so the editor is opened in a panel.
  4. Toggle layer switcher.
  5. Add some layers, including a Bing layer (you'll see the link to get the developer key).
  6. Save settings. Open configuration panel again to check that they are loaded correctly.
  7. Open a forum topic with maps. Check that panel sizes have been changed and layer switcher is different.
  8. Switch layers, check that each of them displays properly, including Bing Satellite layer.
  9. Reply to a post with a map. Open map editor.
  10. Check that layers in the editor are the same as in topic posts.
  11. If it's a window, check that its dimensions were changed. Otherwise check that editing works and after clicking "Apply" button changes are saved to the text area.

Now is the time to let someone in to your forum and check out the maps. If they find a bug, either fix it (if it is in your modification) or file an issue to MapBBCode github. Write some installation notes for you modification and contact Ilya Zverev (zverik@textual.ru) to get access to the github project and publish your modification there, along with other modifications. And of course, publish your modification on a forum engine's website. Be prepared to fix bugs in the following months.