Automatically Re-size Height in a Facebook Page Tab App to Prevent Scrolling

Facebook Page Tab Apps allow you to show any web content in an app that displays on your Facebook page.  This could be anything from a contact form to a survey, or even a newsletter signup form.  The content is displayed on Facebook through an IFrame, but it resides on your server, allowing you to connect to any necessary databases or web services to create a rich user experience.

If you have created a Page Tab App, you know that they are restricted to a maximum of 810 pixels in width, but no maximum height is specified in Facebook’s documentation.  During testing, you may find that you have to scroll in both the Facebook window and in the IFrame to view all of your app’s content, resulting in very unfriendly behavior for the end user.

With a small snippet of JavaScript and HTML, you can automatically re-size your Page Tab App’s IFrame to the size of your content.  You just need to include Facebook’s JavaScript SDK and a call to your app’s canvas.

Here is the code:

<div id="fb-root"></div>
<script>
  window.fbAsyncInit = function() {
	FB.init({
	  appId      : '{PASTE_YOUR_APP_ID_HERE}',
	  status     : true,
	  xfbml      : true
	});
	FB.Canvas.setAutoGrow();
  };

  (function(d, s, id){
	 var js, fjs = d.getElementsByTagName(s)[0];
	 if (d.getElementById(id)) {return;}
	 js = d.createElement(s); js.id = id;
	 js.src = "//connect.facebook.net/en_US/all.js";
	 fjs.parentNode.insertBefore(js, fjs);
   }(document, 'script', 'facebook-jssdk'));
</script>

Just include this code on your page and Facebook will automatically re-size your app to the correct height so you no longer have to scroll inside the IFrame. The great thing about the setAutoGrow method is that it sets a timer to automatically re-size your app’s canvas at a 100ms interval, so it will update if you have loaded any dynamic content on your page. Nice and easy!

Manually Repairing Crashed Tables for WordPress

Any number of server issues can cause your MySQL tables to crash, requiring your instance of WordPress to be repaired.  You may see this issue emerge in the form of the error message, “wp_options table marked as crashed”, though it can occur with any table and not just wp_options.

At this point, WordPress will try and walk you through the use of its own database repair tool, which requires first adding this line to your wpconfig.php file.

define('WP_ALLOW_REPAIR', true);

This will allow the tool to run and you should be able to repair your database with no trouble.  However, you may see the “Failed to repair the wp_options table. Error: Table is marked as crashed” message.  What happens when the WordPress repair tool is unable to fix the crashed database table?  If you have phpMyAdmin on your database server (you likely do), you can repair the table manually.

Log in to phpMyAdmin and select the table you need to repair from the left hand navigation.  On the following details page for that table, if you scroll down to the bottom of the page, you will see a section titled “Table Maintenance”.

The Repair Table Link in phpMyAdmin

The Repair Table Link in phpMyAdmin

From the “Table Maintenance” section, click the “Repair table” link.  You should see a message that the table was repaired successfully.  You will need to do this individually for any tables that the WordPress repair tool reports that it is unable to repair.  Once this task is complete, you will be able to access your WordPress instance again.

Thanks for reading!  Feel free to leave a question or a comment.

Generating an Image of a PDF Page

I recently completed a project that required a thumbnail image automatically be generated from the first page of every PDF file that is uploaded to the system.  I was rather surprised to find that there was no drop in solution for such a thing.  There are many libraries out there that can create a PDF file from HTML content or an image, but no standalone libraries that could go from PDF to an image.  After testing out a couple of methods, I was able to find what I believe to be the easiest and least invasive method to implement it in a web application..

What You Will Need

To generate images from PDF in your project, you will need a couple of things.

Ghostscript, a set of libraries for working with a PDF.  You will need to download the version specific to your environment (32 bit/64 bit).  You can download those here.

GhostscriptSharp, a wrapper for using the Ghostscript libraries in .NET.  You can download it here.  It is written in C#, so if you are using VB.NET you will need to create a code sub-directory in your Web.config to use the file in your project.

Setup

You will need to do a couple of things to get the Ghostscript components to function before you write any code.  First, you need to extract the gsdll file to a location on your system.  (Either gsdll32.dll or gsdll64.dll depending on your CPU.)

Then, you need to modify GhostscriptSharp.cs to specify the path to the Ghostscript library that you extracted before.  Look for this code on line 12 of the GhostscriptSharp.cs file:

#region Hooks into Ghostscript DLL

[DllImport("gsdll64.dll", EntryPoint = "gsapi_new_instance")]

private static extern int CreateAPIInstance(out IntPtr pinstance, IntPtr caller_handle);

[DllImport("gsdll64.dll", EntryPoint = "gsapi_init_with_args")]

private static extern int InitAPI(IntPtr instance, int argc, string[] argv);

[DllImport("gsdll64.dll", EntryPoint = "gsapi_exit")]

private static extern int ExitAPI(IntPtr instance);

[DllImport("gsdll64.dll", EntryPoint = "gsapi_delete_instance")]

private static extern void DeleteAPIInstance(IntPtr instance);

#endregion

You will need to change the paths in the DllImport constructors to the location of the file on your machine.  The DllImportAttribute only accepts a constant string, so you cannot pass a variable with the path or using a Web.config value.  This prevents you from using Server.MapPath to generate the path to your project’s bin folder, which is also unfortunate.

The Code

The actual implementation of the image generation from PDF is very simple once you have reached this point.  You just need to make a call to the GhostscriptWrapper.GeneratePageThumb method with the path to the PDF, and the path where the image should be saved.  You also specify the page number you want to generate from and the height and width of the final image.  A call to GeneratePageThumb might look like this:

// Creates a 100 x 100 thumbnail of page 1.
GhostscriptWrapper.GeneratePageThumb(pdfFileName, outputFileName, 1, 100, 100);

You will also need to import the GhostscriptSharp namespace in your code file.  That is all that there is to it!  After running your application, you will see the image file in the specified path.

Its important to note that GeneratePageThumb is a shortcut method that will only generate a JPG image.  If you need to have more control over the output of your image, then you will need to use the GenerateOutput method and pass in a GhostscriptSettings object that contains all of your required values.  The GhostscriptSharp link above provides more detailed examples if you need them.

Thanks for reading!  If you have any troubles with the process, feel free to leave a comment below.

Fixing reCAPTCHA.net 404 Errors

Last week, Google decommissioned the reCAPTCHA API components that were hosted on recaptcha.net.  According to reCAPTCHA support, the change was supposed to have occurred back in April, but for some reason or another the site has only now been replaced with a 404 error.

The good news is that Google has not made any changes to the reCAPTCHA API, so you will just need to change the reCAPTCHA path from the old location to the new location hosted with Google.

In your application, all instances of this URL:

http://api.recaptcha.net

need to be replaced with this:

http://www.google.com/recaptcha/api

You will need to change all references to that URL, including the recaptcha.js or recaptcha_ajax.js files and the /verify URL.  It is as simple as that.  Keep in mind that if your site uses SSL then you need to change the “http” in the URL to “https”.

Many Joomla users have been affected by this problem as well.  Luckily a proposed fix has already been committed to Joomla’s GitHub repository.  To view those changes or to download the updated file, click here.  If you’re not confident enough to fix the issue on your own, a formal patch should be on it’s way from Joomla soon.

Feel free to leave a comment if you have any questions.  Thanks for reading!

Iterating Over an Enumerable – Creating a “Days of the Week” Drop Down Menu

Enumerations are extremely valuable for managing sets of numeric values such as status codes or days of the week.  Most of the time, you would create an enumerable to simplify values used in multiple points that have the same representation, in order to improve the readability and re-usability of your code.  However, there are some situations where using the enumeration’s name and value directly on the presentation layer can make sense.

For example, let’s say we have a form where we need the user needs to choose a day of the week to assign to a particular record.  We can easily generate a drop down list of values using the built in DayOfWeek enum that is included by default in the System library.

Code

Let’s create a Drop Down List on our page.  We will generate the list items in code behind, so we will just leave those blank.  It should look something like this:

<br />&lt;asp:DropDownList ID="ddlDay" runat="server"&gt;&lt;/asp:DropDownList&gt;<br />

In our code behind file, we will use a couple of helpers from the System.Enum namespace to allow us to loop through our enumerable's values and get their names for use in the list item.  Here's the code for our page load event:

<br />Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load<br /><%%KEEPWHITESPACE%%>    If Not Page.IsPostBack() Then<br /><%%KEEPWHITESPACE%%>        ' Get the values of the DayOfWeek Enum (0-6).<br /><%%KEEPWHITESPACE%%>        Dim daysOfTheWeek = System.Enum.GetValues(GetType(DayOfWeek))<br /><br /><%%KEEPWHITESPACE%%>        For Each value In daysOfTheWeek<br /><%%KEEPWHITESPACE%%>            ' Get the name that corresponds to each value.<br /><%%KEEPWHITESPACE%%>            Dim name = System.Enum.GetName(GetType(DayOfWeek), value)<br /><br /><%%KEEPWHITESPACE%%>            ' Add a new list item, using our name and value.<br /><%%KEEPWHITESPACE%%>            ddlDay.Items.Add(New ListItem(name, value))<br /><%%KEEPWHITESPACE%%>        Next<br /><%%KEEPWHITESPACE%%>    End If<br />End Sub<br />

It's that simple.  Now you know that the value that gets posted back to the server will match directly with the values defined in the enumeration, even if the values were to change at a later date.

A Caveat

One thing to keep in mind are that this is only a user friendly option for single word names.  For example, if you have a value that represents a process' status that is named "InProgress", it would appear as a single word in the drop down as well.  You could parse the name to add the space, but there are cases where that could over complicate the process.

Thanks for reading!

Integrating Google Apps With Your Application Using GData and Extension Elements

Google Apps for Business is quickly becoming the go-to solution for email, calendar and file applications.  Thanks to the available web service API for developing extensions and utilities on Google Apps, you can easily integrate your existing systems as needed.

What You Will Need

I’ll be using the GData API for .NET and the Google Calendar Service for the below examples, but you can access Google’s web services in pure XML form on any platform.  Alternatively, Google provides many pre-built libraries for many popular programming languages.  You can find them here.

You can also interchange any of the other Google Apps services, such as the Contacts Service and perform the same actions to meet your needs.

Extension Elements

Extension Elements are configurable attributes that can be attached to all Google Apps entities.  These attributes are only able to be accessed through the API, so your end users will not notice any difference in how they use Google Apps.

With an Extension Element, we can add an external identifier for an outside system that will allow us to connect the two entities as necessary in the future.  For example, let’s say that we want to use a custom system that contains metadata for calendar events specific to our business and we want to keep them synchronized.  We can create a calendar event using the API and attach the ID to our system’s records.

Creating a Record With Extension Data

Here is an example of how to create a new calendar event with an external identifier.

public EventEntry CreateEvent(Guid Id)
{
	EventEntry entry = new EventEntry();

	// Set the information for your event here.
	entry.Title.Text = "Event Title";

	// Create an ExtensionElement and add your external system's ID.
	ExtendedProperty prop = new ExtendedProperty();
	prop.Name = "ExternalIdentifier";
	prop.Value = Id.ToString();

	entry.ExtensionElements.Add(prop);

	// Create the service and insert the new event.
	CalendarService service = new CalendarService("YourAppNameHere");
	service.setUserCredentials("UserName", "Password");

	return service.Insert(new Uri("https://www.google.com/calendar/feeds/default/private/full"), entry);
}

Getting the Extension Element Value with LINQ

Once we have our events created with our external identifier, we need to be able to access that information later when we synchronize the metadata.  Here is a quick shortcut for handling that with LINQ.

// Get events from Calendar.
CalendarService service = new CalendarService("YourAppNameHere");
service.setUserCredentials("UserName", "Password");

EventQuery query = new EventQuery("https://www.google.com/calendar/feeds/default/private/full");

EventFeed events = service.Query(query);

foreach (EventEntry entry in events)
{
	// See if our entry contains the external identifier.
	ExtendedProperty prop = entry.ExtensionElements.SingleOrDefault(x => x is ExtendedProperty && ((ExtendedProperty)x).Name = "ExternalIdentifier");

	if (prop != null)
	{
		// Do our work for each system here.
	}
}

Conclusion

This only scrapes the surface of what is possible with Google Apps.  You could add any type of data here that you would like.  You could even store all of the information that your application needs on the event itself instead of in an external database.  The Google Apps API makes custom integration with your systems easy.

Thanks for reading.  Any questions?  Feel free to leave a comment!

NullReferenceException in DotNetNuke 7 FriendlyUrlProvider

A recent client was having trouble with their DotNetNuke site search following an upgrade to version 7.  For some queries, the search function would return no results found even when there were a number of modules whose content it matched.  For others it would work just fine.  Deleting and re-indexing the search content still returned the same result.

The Event Viewer for the portal contained the following error:

System.NullReferenceException: Object reference not set to an instance of an object. at DotNetNuke.Entities.Urls.BasicFriendlyUrlProvider.FriendlyUrl(TabInfo tab, String path, String pageName, PortalSettings settings) at DotNetNuke.Services.Url.FriendlyUrl.DNNFriendlyUrlProvider.FriendlyUrl(TabInfo tab, String path, String pageName, PortalSettings settings) at DotNetNuke.Common.Globals.NavigateURL(Int32 tabID, Boolean isSuperTab, PortalSettings settings, String controlKey, String language, String pageName, String[] additionalParameters) at DotNetNuke.Services.Search.Controllers.ModuleResultController.GetDocUrl(SearchResult searchResult) at DotNetNuke.Services.Search.Controllers.SearchControllerImpl.GetSecurityTrimmedResults(SearchQuery searchQuery, LuceneQuery luceneQuery) at DotNetNuke.Services.Search.Controllers.SearchControllerImpl.GetResults(SearchQuery searchQuery) at DotNetNuke.Services.Search.Controllers.SearchControllerImpl.SiteSearch(SearchQuery searchQuery) at DotNetNuke.Web.InternalServices.SearchServiceController.GetGroupedDetailViews(SearchQuery searchQuery, Int32& totalHits, Boolean& more) at DotNetNuke.Web.InternalServices.SearchServiceController.Search(String search, String culture, Int32 pageIndex, Int32 pageSize, Int32 sortOption)

I downloaded the DotNetNuke source package and was able to determine that the error was caused by a null value being passed to the friendly URL provider for the portal alias associated with the site.

The fix was simple.  There was no default portal alias set for that site.  I updated the portal alias record in Site Settings under the Admin menu and I was able to see the missing search results.

It is possible that after the upgrade there was something with the portal aliases that was saved improperly and it just needed a refresh.  Nonetheless, I wanted to provide my steps to fix the problem in case it helps.

Thanks for reading!  Leave a comment and let me know if this fix works for your site.