Modular plug-in for customizable infobox control

As many of you know, the Bing Maps Locator Wizard is a portal that allows you to generate a Bing Maps Locator in minutes, and then have it hosted in Windows Azure for free. One of the steps in the wizard allows you to customize the style of various elements of the locator.
I was recently working on the wizard and required a highly customizable infobox control that would likely be put in an iframe. This article highlights some of the key features of the modular plug-in we created to allow for this requirement. The result far exceeded our expectations and InKnowledge has since released the code to the Bing Maps community so you can use it in your own applications. Feel free to download the complete source code, with sample implementations and documentation.


Modular Design The Bing Maps 7.0 Control was released last fall and is a complete rebuild from the ground up when compared to previous versions of Bing Maps. We have seen huge improvements in API performance and the size of the control. One feature of the map control was to use a modular framework; this allows you to register and load modules as they are needed. One result of this is faster loading speeds. Those interested in creating your own reusable modular plug-ins for Bing Maps should have a look at the documentation.
Custom Infoboxes When the 7.0 Control was originally released there were no infoboxes. The infobox control that Microsoft soon added was a bit different from past versions and was designed to be more flexible. While this is a great improvement, there are a couple of caveats to using this infobox control. The first is that the infobox always points up and to the right and does not reposition itself. This means that infoboxes that are anchored near the top are displayed outside of the map. This can be an issue if the map is being put in an iframe or the map is placed at the end of a webpage. The second caveat is that using custom HTML requires you to also generate the infobox arrow and content area, in addition to the content you want to display. Ideally we would be able to put custom HTML into the frame of the built-in infobox.
Positioning Our goal was to have the content positioned in the center of the map to ensure users can see the infobox. The solution: break the map into quadrants and modify the layout of the infobox such that the content is positioned in the middle of the map. For example, if the anchor point of an infobox is in the lower-left part of the map we want the infobox to be positioned to the right of the anchor point and pushed up. The easiest way to figure out what quadrant the anchor is located in is to convert the anchor coordinate to a pixel coordinate relative to the upper-left corner of the map. To determine if the anchor is to the left or right of the center of the map, simply check to see if the x-pixel coordinate is more or less than half the width of the map. Similar logic can be used to figure out if the anchor is above or below the middle of the map. Once we know if it is left or right of the center and above or below the center, we simply need to place the infobox so that it is in the opposite direction.
The Infobox Arrow One common issue that developers run into when trying to create customizable infoboxes is with the infobox arrow. In the past, these were almost always images that required edits if you wanted to change something as simple as the color. After a lot of research and experimenting, I found an ingenious solution: CSS borders. Years ago, when the web was young, webpages used to use really big borders to make things look like they were in a frame. As time went on the borders got smaller and smaller to the point where they are either not used or only 1 or 2 pixels wide. So where am I going with this? Well, if you look at a picture frame you might notice that the sides of the frame are connected in the corner at a 45-degree angle. This angle also occurs with CSS borders. If we take this and use a bit of fancy styling, we can modify the color and width of four edges of the border CSS class and generate an arrow. Not only can we generate an arrow, but with a little bit of logic and CSS styling, we can control the width, length. and color of the arrow. Check out this good article on creating angled shapes using the border style.
Additional Features The API for this custom infobox control is pretty basic. There are only four public methods that are exposed: hide, show, getOptions, and setOptions. The show method is pretty straightforward; it takes in a coordinate and content (either text or HTML). The content will be placed inside the infobox frame, rather than replacing it. By making the input generic we are able to use this infobox with pushpins, polylines, polygons, or on its own. As for options, not only can you set the dimensions and color of the arrow, you can also:
•    Set the background color of the content area of the infobox.
•    Set the color of the border on the infobox.
•    Specify minimum width and height for the content area.
•    Specify an offset distance from the anchor point.
•    Hide the arrow.
•    Hide the default close button.
•    Tether the infobox. The tether option allows one of two user experiences. When enabled the infobox will stay open and reposition itself as you move the map around until you close it. When disabled, the infobox will close when the map moves.
Implementing the Plug-in Implementing the custom infobox is easy. The first thing you must do is register the plug-in with the map control. You can do this using code similar to the following:
var map, customInfobox;
function GetMap() {
map = new Microsoft.Maps.Map(document.getElementById(“myMap”), { credentials: “Your_Bing_Maps_Key” });
//Register and load the Custom Infobox Module
Microsoft.Maps.registerModule(“CustomInfoboxModule”, “scripts/V7CustomInfobox.min.js”);
Microsoft.Maps.loadModule(“CustomInfoboxModule”, { callback: function () {
//Create an instance of the custom infobox control
customInfobox = new CustomInfobox(map);
}
});
}
To display an infobox, specify a coordinate and some content like the following:
customInfobox.show(map.getCenter(), “<b>Hello World</b>”);
We hope you find the plug-in helpful.

Google Map Maker: Map the Midwest

During one of the Map Maker Community team’s recent college campus events in Chicago, we had the pleasure of meeting Phil Swanson, an international studies and business student, who travelled nearly 200 miles to join our mapping event at the University of Chicago.

As soon as we started talking with Phil, his passion for mapping and computers came shining through, so we were excited to learn more about what drives him to add to, edit, and create using Google Map Maker.
According to Phil, it all started with retrofitting an interactive campus map when he was a web developer at the University of Wisconsin, Platteville. There was an existing version that utilized the Google Maps API, which he further developed, resulting in this useful campus map. Phil says about the project, “I love to make simple things for people to use, and I’ve always been interested in maps, so that project definitely created an interest in maps and mapping in general.”


Once Phil realized the positive impact his mapping could have on the university, he continued using Google Map Maker to ensure all of the campus details were correct. He began seeing his edits appear not only in Google Maps, but also on Google Maps for mobile on his phone when he searched maps near the university. “Seeing edits transition from Map Maker to Google Maps on mobile and filling in the details make maps that much more useful for me and the world,” Phil said about mapping his town and the campus.


What aspect of the maps does Phil enjoy most? “I love mapping buildings. Seeing the buildings from my University show up on my phone is the best.” Phil has developed a handy system of finding what needs to be fixed on the map, going out with his phone to take pictures, then adding those photos to a web album which he can then include with his edits in Map Maker. “This is especially useful for buildings . . . . It might sound obvious, but I find that my edits are much more likely to get approved if I describe what I’m doing and link to a photo for a more complex edit.” What a helpful technique to use during the review process!

We want to thank Phil for taking the time to hang out with us in Chicago, and also for sharing with us what excites him about mapping. We look forward to seeing more great mapping from Phil, and hope other students are inspired to make the maps of their own colleges and universities more comprehensive and detailed!

For more useful tips like Phil’s, visit the Map Your World Community forum to see what others are doing.

How To Customize Mailing Labels

Adding a Custom Field to a Mailing Label

The first task was to modify the default fields displayed on the mailing label (name, address, postcode etc.) to include a custom field. This organisation delivers a lot of its local mail by hand rather than through the royal mail, so the custom field was used to assign addresses into a delivery walk, and this was to be printed on the label so it could be given to the appropriate deliverer.

Fortunately, CiviCRM uses a token replacement system, and every user-created custom field is assigned a unique token. The only problem in including the deliverer custom field on the mailing label  was finding out what the token corresponding to the appropriate field was.
 


 
I’m sure there must be a better way, but the quick solution that worked for me was to bring up the “Print PDF Letter for Contacts” action from a search result

On the next screen, click the “Insert Tokens” link at the top right to be presented with a modal dialog box listing all the names of all the available tokens in your system. Clicking on the name of a token will insert it into the PDF letter, allowing you to see the token behind it. In the screenshot below, for example, the Geo Code 1 field associated with each individual contact can be inserted into any mailing (or mailing label) using the {contact.geo_code_1} token.

Having identified the appropriate token for a custom field (in my case, it was{contact.custom_4} ), you can then add this to the mailing label template by going toAdminister >> Configure >> Global Settings >> Address Settings and editing the Mailing Label Format to include the relevant token, as below:

{contact.addressee}
{contact.street_address}
{contact.supplemental_address_1}
{contact.supplemental_address_2}
{contact.city}{, }{contact.state_province}{ }{contact.postal_code}
{contact.country}
{contact.custom_4}

Sorting the Mailing List

Having ensured that the custom field was printed onto each label, the next step was to sort the list of labels based on the value of that field. As explained before, the mailings were to be hand-delivered, and it would save a lot of time sorting through the envelopes if they could be printed ready-sorted into batches for the appropriate deliverer, as specified by the custom field. A quick search of the internet reveals that sorted mailings are also required in other circumstances as well, such as sorting by ZIP code when submitting batch mailings to the U.S. Postal Service. However, although reported as a major bug in CiviCRM, the ability to sort mailing lists in anything other than alphabetical surname order of addressee is currently not possible. So I had to hack something together.

A snoop around revealed that the bulk of the work in preparing the mailing labels occurred in the script located at \sites\all\modules\civicrm\CRM\Contact\Form\Task\Label.php. Within the CRM_Contact_Form_Task_Label class in this file, I created a new comparison function which would sort an array of the items to be included in the mailing list based on the custom_4 field, as follows:

[php]
function cmpCustomField($a, $b) {
if ($a[‘custom_4’] == $b[‘custom_4’]) {
return 0;
}
return ($a[‘custom_4’] < $b[‘custom_4’]) ? -1 : 1;
}

[/php]

Through a bit of experimentation, I determined the best place to sort the records was after they had been run through the “Merge same address”/“Merge same household” scripts, but before being formatted for display. So I inserted a call to the PHP uasort function (sorts an associative array while keeping indexes) that used my comparison function at around line 330, as follows:

[php]

if ( isset( $fv[‘merge_same_household’] ) ) {
$rows = $this->mergeSameHousehold( $rows );
$individualFormat = true;
}

// INSERT CUSTOM SORT HERE
uasort ( $rows , array($this, ‘cmpCustomField’));

// format the addresses according to CIVICRM_ADDRESS_FORMAT (CRM-1327)
require_once ‘CRM/Utils/Address.php’;
foreach ($rows as $id => $row) {

[/php]

The mailing list labels were now produced in nicely ascending order based on the custom field.

Changing the Mailing Label Paper Template

CiviCRM supports a handful of common avery label formats out-of-the-box, but this organisation had recently purchased a bulk load of non-standard labels (A4, 24 labels per page, 8×3) which did not fit any of the supplied templates. They therefore couldn’t be used.

To create a new paper template, changes to two files must be made (both called “Label.php”, but in different directories).

Firstly, in the \sites\all\modules\civicrm\CRM\Utils\PDF\Label.php look for the $averyLabels array and add a new element to the array with the margins and other dimensions of your label sheet. Here’s the settings I used for my new paper (NX and NY are the number of columns and rows of labels on the sheet, width and height determined by dividing the overall dimensions of the page by the number of labels, and the margins determined mostly by guesswork):

[php]

$averyLabels = array (

‘Custom_3x8’ => array(
‘name’ => ‘NGP_3x8’,
‘paper-size’ => ‘A4’,
‘metric’ => ‘mm’,
‘lMargin’ => 5,
‘tMargin’ => 10,
‘NX’ => 3,
‘NY’ => 8,
‘SpaceX’ => 0,
‘SpaceY’ => 0,
‘width’ => 70,
‘height’ => 37.125,
‘font-size’ => 9
)
);

[/php]

Having defined the settings for the Custom paper template, to actually get these settings to be selectable when producing mailing labels, you also need to edit the file at\sites\all\modules\civicrm\CRM\Contact\Form\Task\Label.php

Near the top is a buildQuickForm() function that contains an array of templates to populate a select list. Add the name of your new paper template to the end of the array, exactly as you defined it in the $averyLabels array above:

[php]
$label = array(
"5160" => "5160",
"5161" => "5161",
"5162" => "5162",
…,
"Custom_3x8" => "Custom_3x8"
);

[/php]

Save the file, and your new template will magically become available for use:

A few tweaks…

Finally, there were just a few tweaks to be made. The new paper template should be always be selected by default for mailing labels (they’d bought reams of the stuff, so it was going to be used for all labels for quite a while yet), and also the “merge contacts with the same address” should always be selected. Adding these defaults was a simple matter of amending the $defaults array declared in the setDefaultValues() function near the top of the \sites\all\modules\civicrm\CRM\Contact\Form\Task\Label.php file.

[php]
function setDefaultValues()
{
$defaults = array();
$defaults[‘do_not_mail’] = 1;

// Modified by AA
$defaults[‘merge_same_address’] = 1;
$defaults[‘label_id’] = "Custom_3x8";

return $defaults;
}

[/php]

I’m no CiviCRM expert, and I’ve certainly no idea if the approach I took to solving any of these problems represents “best practice” in any way, but it seems to have got the job done and met the immediate needs of my client, so hopefully it might be helpful to somebody else too!