Foliotek Developer Blog

LiveZilla On Azure App Service

LiveZilla Azure App Service

This guide will walk you through the basics of setting up LiveZilla to run on PaaS service such as Azure App Service. In my case I had to migrate an existing LiveZilla instance running on a VM and run it on Azure App Service. This guide does require you to jump into the LiveZilla config files and update a few things. The LiveZilla version I was using was 5.4.0.2. I'm not going to go into every detail of Azure / App Service you should already have some experience on this front for you to be successful setting this up.

  • MAKE SURE You Disable Slow Query Log and General Log otherwise you will max out your disk space QUOTA, this might be a an issue only because the feature is still in preview I'm not sure. See below.

LiveZilla Database Import

  • Log into your physical VM MySQL Database you will have to export 3-4 tables at a time and import them into PHPMyAdmin, the reason for this is currently Azure PHPMyAdmin has a max upload size of 8MB you cannot change this by modifying maxupload size in configs. I would recommend not zipping them as this also seems to crash PHPMyAdmin, its a little brittle Its a static setting from Azure at the writing of this article.
  • Once you have each table imported into your new LiveZilla DB in PHPMyAdmin you will now modify the connection strings.These are all Base64 encoded. https://www.base64decode.org/ This will help you view / edit the values.
  • All in all once the DB is in App Service its very fast, and a huge improvement over ClearDB (which by the way runs like a complete turd)
Parse Azure MySQL Environmental Variable

App Service Instance | Edit File: site/wwwroot/_config/config.inc.php add this below line 39.

//Loop Through And Pull Settings From Azure ENV Variable
    foreach ($_SERVER as $key => $value) {
    if (strpos($key, "MYSQLCONNSTR_localdb") !== 0) {
        continue;
    }
    //Set Base 64 Version
    $_CONFIG[0]["gl_db_host"] = base64_encode(preg_replace("/^.*Data Source=(.+?);.*$/", "\\1", $value));
    $_CONFIG[0]["gl_db_name"] = base64_encode("livezilla");
    $_CONFIG[0]["gl_db_user"] = base64_encode(preg_replace("/^.*User Id=(.+?);.*$/", "\\1", $value));
    $_CONFIG[0]["gl_db_pass"] = base64_encode(preg_replace("/^.*Password=(.+?)$/", "\\1", $value));

        $connectstr_dbhost = preg_replace("/^.*Data Source=(.+?);.*$/", "\\1", $value);
        $connectstr_dbname = preg_replace("/^.*Database=(.+?);.*$/", "\\1", $value);
        $connectstr_dbusername = preg_replace("/^.*User Id=(.+?);.*$/", "\\1", $value);
        $connectstr_dbpassword = preg_replace("/^.*Password=(.+?)$/", "\\1", $value);
    }
Issues

If you run into DB connection issues search through all the LiveZilla code and look for other places the connection string is used, replace the value with the above parsed out values, make sure you Base64 encode everything.


iText SimSun Degree Symbol Spacing

I had a rather odd issue come up a few weeks ago for a client that generates data sheets for it’s Chinese distributors. These data sheets are generated via iText pdf using the SimSun font.

Below is a screen shot of the issue that came up when SimSun renders the degree symbol notice the trailing white space.

Since this was an actual issue with the font and the client requested that we not change the font due to Chinese standards I came up with the following solution.

SimSun supports the ‘Masculine Ordinal’ symbol. This symbol does not have the trailing white space that the ‘degree’ symbol does. So on the fly when it comes time for iText to generate a Chinese Datasheet I replace all Degree symbols with Masculine Ordinals. A little hacky but def the best solution available at the time.

 myString.Replace(‘\u00B0′, ‘\u00BA’); //replace degree with masculine ordinal  

Convert HTML to BBCode in C#

Here is a code snippet that will convert HTML into BBCode using C#.

I know you are not suppose to use regular expressions to manipulate HTML and that I should have used agility pack, but this was the quickest solution to write.
[sourcecode language='csharp']

public string FormatHtmlIntoBBCode(string desc)
{
desc = Regex.Replace (desc, @”
“, “[br]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”

]>”, “[ulist]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[/ulist]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @” ]
>”, “[olist]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[/olist]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”6. “, “[]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “”, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[b]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”>”, “[/b]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”
“, “[strong]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[/strong]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[u]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[/u]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”
“, “[i]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[/i]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”*“, “[em]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[/em]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[sup]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[/sup]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[sub]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[/sub]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”- - - - - -

]*>”, “[hr]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[strike]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”“, “[/strike]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”

“, “[h1]“, RegexOptions.IgnoreCase);

desc = Regex.Replace (desc, @”“, “[/h1]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”

“, “[h2]“, RegexOptions.IgnoreCase);

desc = Regex.Replace (desc, @”“, “[/h2]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”

“, “[h3]“, RegexOptions.IgnoreCase);

desc = Regex.Replace (desc, @”“, “[/h3]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @” desc = Regex.Replace (desc, @"“, “[/url]“, RegexOptions.IgnoreCase);
desc = Regex.Replace (desc, @”‘>”, “‘]”, RegexOptions.IgnoreCase);

//match on image tags
var match = Regex.Matches(desc, @”!“, RegexOptions.IgnoreCase);
if(match.Count > 0)
desc = Regex.Replace (desc, match[0].ToString(), “[img]“+ match[0].Groups[1].Value + “[/img]“, RegexOptions.IgnoreCase);

return desc;
}
[/sourcecode]

There you have it. ?A simple piece of code that will convert a simple html block into BBcode.

**


Plupload Custom File Upload UI

Plupload is a tool that allows you to upload files using HTML5 Gears, Silverlight, Flash, BrowserPlus, normal forms, and provides features such as upload progress, image resizing and chunked uploads.

I’m going to discuss the process I went through to implement a custom Plupload UI.

Here is a video of it in action:

http://www.youtube.com/watch?v=cYxjlHXXXe4&w=480&h=390

Here is the simplified source:

http://dl.dropbox.com/u/249320/CustomPluploadExample.zip

Now I’ll go into details on how it was built using the Plupload framework.
I modeled my version of the plupload custom example here.

First of all I init an instance of plupload and set the options. I set the drop area, and the container that the file progress will be displayed in.
Note: Only FF and Chrome support drag’n drop in Plupload.

[sourcecode lang="javascript"]
$(document).ready(function(){
var uploader = new plupload.Uploader({
// General settings
runtimes: ‘html5,flash,gears,browserplus,silverlight,html4′,url:,
browsebutton : “pickfiles”,
button
browsehover : true,
drop
element : “dropArea”,
autostart : true,
maxfilesize: ’100mb’,
container: “FileContainer”,
chunksize: ’1mb’,
unique
names: true,
// Flash settings
flashswfurl: ,
// Silverlight settings
silverlightxapurl:
});
[/sourcecode]

Plupload doesn’t automatically hide the drop area when the current browser doesn’t support it so I bind some custom logic to the plupload “Init” event below. Drag and drop support requires that the browser support FileReader and will not work under the flash or silverlight runtimes.

[sourcecode lang="javascript"]
uploader.bind(‘Init’, function(up, params){
try{
if(!!FileReader && !((params.runtime “flash”) || (params.runtime “silverlight”)))
$(“#dropArea”).show();
$(“#fileSelectMsg”).hide();
}
catch(err){}});
uploader.init();
[/sourcecode]

So now we need to be able to display files in our “FileContainer” div the script below fires on the FilesAdded event. It is called when a file is added or dropped in the drop area. When this event is fired I create a div that contains the elements we will need to display upload progress and the name of the file.

[sourcecode lang="javascript"]
uploader.bind(‘FilesAdded’, function(up, files) {
$.each(files, function(i, file) {
$(‘#filelist’).append(

fileItem”>
‘ +
file.name + ‘ [cancel](#)
‘ + plupload.formatSize(file.size) + ‘‘ + ‘
‘+ ‘percentComplete“>’); //Fire Upload Event up.refresh(); // Reposition Flash/Silverlight uploader.start(); //Bind cancel click event $(‘#cancel’+file.id).click(function(){ $fileItem = $(‘#’ + file.id); $fileItem.addClass(“cancelled”); uploader.removeFile(file); $(this).unbind().remove(); }); //Set ico_Ok to Uploading $confirmNext.attr(“disabled”, “disabled”); $confirmNext.html(“Uploading..”); $confirmNext.unbind(“click”).click(function(e){e.preventDefault();}); }); }); [/sourcecode] Now that we have files uploading we need to update the progress bars for each file div in the “FileContainer”. So yet again Plupload conveniently has an event called “UploadProgress” that fires after each chunk has been successfully uploaded. [sourcecode lang="javascript"] uploader.bind(‘UploadProgress’, function(up, file) { var $fileWrapper = $(‘#’ + file.id); $fileWrapper.find(“.plupload_progress”).show(); $fileWrapper.find(“.plupload_progress_bar”).attr(“style”, “width:”+ file.percent + “%”); $fileWrapper.find(“.percentComplete”).html(file.percent+”%”); $fileWrapper.find(‘#cancel’+file.id).remove(); }); [/sourcecode] So now we have files uploading and being added to the container and updating the upload progress in real-time. So now I want to grey out the div once the file has been completed. In order to do this I add a class to that file’s div. The “FileUploaded” event gets fired after each file is successfully uploaded. [sourcecode lang="javascript"] uploader.bind(‘FileUploaded’, function(up, file) { $fileItem = $(‘#’ + file.id); $fileItem.addClass(“completed”); $confirmNext.removeAttr(“disabled”); $confirmNext.html(“Ok”); $confirmNext.unbind().click(function(e){window.location.href = window.location.href;}); $(‘#cancel’+file.id).unbind().remove(); }); [/sourcecode] Last but not least I’ll show you how to display errors that happen server-side when a chunk is uploaded. On “ChunkUploaded” event I check the response message if it contains “Error:” in the response string. I kill the current upload and display that error message to the user and also log the error in my application. The following snippet performs just this. [sourcecode lang="javascript"] uploader.bind(“ChunkUploaded”, function(up, file, response){ //Should return a status 200 if the chunk was uploaded successfully if(response.status != null) { if(response.status != “200″ || (response.response.indexOf(“Error”) != -1)) { if(response.response.indexOf(“Error”) != -1) { //Prompt the user with the custom error message $(“div.error:first”).show().html(‘ ‘+response.response+’ ‘); } else { //Log this as an error //Custom line of code to log error on server would go here //Notify user of error $(“div.error:first”).show().html(‘ There was an error uploading your file ‘+ file.name +’ Support has been notified. ‘); } $(‘#’ + file.id).addClass(“cancelled”); uploader.stop(); } } }); [/sourcecode] That’s how I built my custom UI wrapper around the Plupload core. Hopefully that will help get you going in the right direction.

Integrating Bing Search Results Within A Web App Using .NET

A few months ago I was faced with the challenge of including a site search in a web system we maintain.

The main goals for the search tool were meaningful results, ease of use, and low cost of implementation.

After evaluating the search offerings from Google, Bing and Yahoo we went with Bing. Microsoft offers high flexibility and no fees for using the Bing Search API. The only thing they request is that you show some type of image or statement crediting Bing with the search results.

The Bing Search API is part of Project Silk Road it offers many options for choosing the source type and output protocol (JSON, SOAP, XML) as well as flexible presentation options (No restrictions on ordering and blending of result). This allows you to integrate custom results with the Bing results without violating the usage terms.

Below are some code snippets to reference:

Retrieves the XML document containing search results from Bing and loads them into an object collection.

[sourcecode lang="csharp"] public BingSearchResult(string title, string description, string url) { this.Title = title; this.Description = description; this.Url = url; } private static BingSearchResultCollection GetResults(string searchQuery, int pageNum, int resultsPerPage) { int offset = ((pageNum - 1) * resultsPerPage); string url = "http://api.search.live.net/xml.aspx?Appid={0}&sources={1}&query={2}&{1}.count={3}&{1}.offset={4}"; string completeUri = String.Format(url, appID, "web","site:www.yoursite.org " + searchQuery,resultsPerPage, offset); HttpWebRequest webRequest = null; HttpWebResponse webResponse = null; XmlReader xmlReader = null; webRequest = (HttpWebRequest)WebRequest.Create(completeUri); webResponse = (HttpWebResponse)webRequest.GetResponse(); //Handles an invalid xml document try { xmlReader = XmlReader.Create(webResponse.GetResponseStream()); } catch (System.Security.SecurityException) { return new BingSearchResultCollection(); } XDocument xmlDoc = XDocument.Load(xmlReader); //Loops through the results and creates BingSearchResult objects if (GetElementsByName(xmlDoc, "SearchResponse").Any()) { XElement searchResponse = GetElementsByName(xmlDoc, "SearchResponse").First(); if(GetElementsByName(searchResponse, "Web").Any()) { XElement web = GetElementsByName(searchResponse, "Web").First(); if (GetElementsByName(web, "Total").Any()) { int totalResults = Convert.ToInt32(GetElementsByName(web, "Total").First().Value); var list = new BingSearchResultCollection(totalResults, searchQuery); if (GetElementsByName(web, "Results").Any()) { XElement results = GetElementsByName(web, "Results").First(); if (GetElementsByName(web, "Results").Any()) { if (GetElementsByName(results, "WebResult").Any()) { var webResults = GetElementsByName(results, "WebResult"); foreach (XElement el in webResults) { string resTitle = ""; string resUrl = ""; string resDescr = ""; if (GetElementsByName(el, "Title").Any()) resTitle = GetElementsByName(el, "Title").First().Value; if (GetElementsByName(el, "Url").Any()) resUrl = GetElementsByName(el, "Url").First().Value; if (GetElementsByName(el, "Description").Any()) resDescr = GetElementsByName(el, "Description").First().Value; ; if (IsValidUrl(resUrl)) list.Add(new BingSearchResult(resTitle, resDescr, resUrl)); } return list; } } } } } } return new BingSearchResultCollection(); } private static IEnumerable GetElementsByName(XContainer cont, String name) { return (from el in cont.Elements() where el.Name.LocalName == name select el); } [/sourcecode]

The reason I ended up using a custom LINQ function to traverse the tree was because I couldn’t figure out how to get a collection of WebResult elements.

The XML document returned from Bing is structured like this:

[sourcecode lang="xml"]

site:www.yoursite.com SearchTerm13500000Result TitleDescription of Resulthttp://www.resulturl.comhttp://cc.bingj.com/cache.aspx?somecashobjectherewww.resulturl.com2010-07-02T20:27:32Z [/sourcecode]

Hopefully this gives you an idea of what you need to hit the floor running when implementing the Bing Search API in your project(s).

Here are some helpful resources:
http://www.bing.com/developers/
http://www.bing.com/developers/s/API%20Basics.pdf

If you need a powered by Bing image you can use the one I created here.