Wednesday, May 8, 2013

Active Reports HTML viewer skinning

My company recently migrated from Microsoft's reporting services to Active Reports.  I wrote a post here about my complaints and assessment of the product.

One of the big problems with the HTML viewer is the HTML code it generates and its heavy reliance on absolute positioning.  It is some of the worst code I have seen a control generate, I suppose it is supposed to be for flexibility, however none of the flexibility has been made available to us yet.

In order to make this control work for my companies spreadsheet style reporting, I had to come up with a cross between the HtmlView and the RawHTML output.  The raw HTML worked better in page flow usually, but it didn't have paging which is a pretty nice feature.  So I generating the following CSS snippet in an attempt to make the HtmlView just a little closer to the RawHTML output.

.rptDiv and #controlTable and #rptFilter are my own personal divs wrapping the viewer output and my custom parameters, they should not be necessary for the CSS to function correctly.

There are three main features that this CSS changes.  Overflow to remove the extra scrollbars that the viewer adds to the page.  Left and top positioning to get rid of the dumb one inch white space margin that the viewer creates.  And Z-index because moving the main panel one inch up and left causes an invisible portion of the viewer control to be placed on top of any controls in the nearby area; and so it needs to be stuff behind them.

#viewer-layout-main-panel > div > div > div > div > span {
    border:solid 1px;
    border-color:white;
}
.rptDiv,
.viewer-layout-container,
.viewer-layout-container div {
    overflow:visible !important;
    z-index:2;
}
#controlTable,
.viewer-layout-main-panel {
    border:none !important;
}
.viewer-layout-main-panel > div > div {
    left:-1in;
    top:-1in;
    width:0px !important;
           
}
#rptFilter,
#viewer-layout-toolbar-panel {
    z-index:3 !important;
    position:relative;
}

Custom serialization

I recently ran into an issue where using Session variables to maintain state was not a good fit for my web application.  But I had far too many variables to easily be able to save and retrieve them all from a database each time I wanted one.

So I ended up creating a custom object to hold all of my variables, then wrote a serialization piece to dump the object to a string that could be stored as well as used to rebuild the object.

While this does seem to be a pretty common need across the net, I have seen very few complete examples of how to implement it.  So here is the first version of my parameters class:

Notice the ToString method as well as the constructor where all of the cool stuff happens.  A quick note, I am using fields in this example, however if you wanted to hide your fields behind property get and set statements you could easily convert the reflection code from field references to property references.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace MyNameSpace {
  public class ContactListParams {
    public string sFilter = "";
    public string sText = "";
    public string trcCL_grdPI = "";
    public string trcCL_grdSE = "";
    public string trcCL_grdSD = "";
    public string trcCL_Disp = "";
    public string trcCL_Comp = "";
    public string trcCL_FName = "";
    public string trcCL_LName = "";
    public string trcCL_Addr3 = "";
    public string trcCL_MSt = "";
    public string trcCL_Zip = "";
    public string trcCL_Phone = "";
    public string trcCL_Industry = "";
    public string trcCL_CID = "";
    public string PCB_Status = "";
    public string progType = "";
    public string userCanReassign = "";
    public string iRecordCount = "";
    public string sPriority = "";
    public string trcCntLstSort_Des = "";

    public ContactListParams() { } // basic constructor

    ///

    /// Creates a new contactlistparams from the data returned through the ToString method.
    ///

    ///
    public ContactListParams(string InitialData) {
      Type type = typeof(ContactListParams);
      string[] fielddata = InitialData.Split(Convert.ToString((Char)3));
      foreach (string field in fielddata) {
        if (field != null && field != "") {
          string[] data = field.Split(Convert.ToString((Char)1));
          FieldInfo fi = this.GetType().GetField(data[0]);
          if(fi != null) fi.SetValue(this, data[1]);
        }
      }
    }

    public override string ToString() {
      string ret = "";
      Type type = typeof(ContactListParams);
      FieldInfo[] fields = type.GetFields();
      foreach (var field in fields) {
        object value = field.GetValue(this);
        ret += field.Name + Convert.ToString((Char)1) + value.ToString() + Convert.ToString((Char)3);
      }
      return ret;
    }
  }
}

Wednesday, April 17, 2013

Active Reports "OpenedReport is undefined" or "ViewerViewModel is undefined"

Active Reports is one of the top reporting systems available for .NET, unfortunately it has a couple of major shortcomings.

The first problem is the nearly complete lack of documentation.  It kind of reminds me of the early days of Microsoft Windows and trying to find documentation on any given API call.  Also, while friendly, their support staff seems lacking in basic technical knowledge if anything out of the ordinary goes wrong.

The second problem is the systems focus on windows development.  This focus is so heavy that their web offering has been all but neglected.  They do have a couple of nice viewers for the web, but some of the basic functionality, like exporting, is missing from them; the HTML viewer doesn't even have the ability to print.  All this functionality is supported by the system, but has to be written and added by the developer using the product; a huge shortcoming in my opinion.  This lack of integrated details is consistent throughout the entirety of their web offering.

With the shortcomings out of the way, on to the problem this post is about.  The pesky "OpenedReport" error.  There are lots of posts all over the web about this error, but few people seem to really understand it, and Active Reports staff just point developers to the HttpHandlers setup page to solve the problem.

This error occurs when the page is unable to find or load the primary javascript libraries used by Active Reports.  The "ViewerViewModel" error has the same root cause, it just shows up when using the flash viewer rather than the PdfReader viewer.  Active Reports hosts all of their javascript in a dll called "GrapeCity.ActiveReports.Web.v7" (or whatever version you are using).  The files are then accessed using the following calls to the web server:


 src="Command=ArResource;ScriptId=lib.jquery-1.7.2.min.js.ar7"
 src="Command=ArResource;ScriptId=lib.json2.js.ar7"
 src="Command=ArResource;ScriptId=resources-7.1.7470.0-en-US.js.ar7"
 src="Command=ArResource;ScriptId=RSU-7.1.7470.0.js.ar7"

Those calls can all be seen in the source code for the page.  If you notice all the file extensions end in .ar7 which is not a standard web extension.  This is where the HttpHandlers come in, they are supposed to recognize this extension and redirect it to the correct dll to serve up the desired content.  However, the request must first make it to the asp.net engine in order for the web.config file to be able to handle the request.  This means that IIS must either pass all extensions through to the asp.net engine, or have the .ar7 extension added to it's list.

I am running IIS6, so I went in to the properties for my website, the Home Directory tab and the Configuration button and added a new extension of .ar7 pointing to aspnet_isapi.dll.  If you don't know the path of your dll look at any of the other extensions, almost all of them will point to this dll.  If you wished you could also point them directly to the activereports.web.v7 dll, but I prefer .net to handle all my requests and so pointed it to the generic handler to keep things consistent.

When adding this new extension mapping there are two check boxes which are VERY important.  "Script engine" should be checked, this tells IIS that this is an executable file and to go ahead and run it.  The "Verify that file exists" must be UN-checked; since all the files that active reports will be requesting are virtual files IIS will never be able to find them and would reject all requests for them if this box is checked.

Adding that extension along with the web.config handlers should solve your problem, this is my detailed analysis on this error.

Monday, April 15, 2013

Blank page in Active Reports using flash viewer

I started using Active Reports for the first time and wanted to try out the various viewers.  After spending hours trying to figure out why the flash viewer was not showing anything but a blank page I started to be convinced that there was a problem with the plugin in my browser.

So, I downloaded a free swf video off the net and hard coded some HTML to display it on my page.  When that worked I returned to troubleshooting Active Reports itself.  Viewing the source for my page I found my hard coded flash code and compared with with the auto generated flash code from Active Reports.

Every help site I had been to said to put the Active Report swf files ( there are two of them ) into the root of your application, which is where mine were.  Looking at the HTML output however, I noticed that it was actually looking for them in the same folder that the page was being executed it; which was not the root of my site.  Moving the two swf files and related Theme folder into the correct sub-directory everything started to work.

Thursday, March 7, 2013

Google Spreadsheets Data Aggregation

Google Spreadsheets has been getting better and better over the last couple of years, and they have added many features that I have waited for, however there is still one much requested feature that they have not yet implemented.

The charts within Google Spreadsheets are not able to manipulate the data, they can simply report on it.  Many people, myself included, have wanted a chart that is able to aggregate data and report on the totals obtained.  For example a call log spreadsheet where employees use a form to enter data about calls they have received.  Let's say the spreadsheet has the following columns:

Date | Name | Call Length | Notes

Now let's say that the employees each receive between 10 and 20 calls per day for varying lengths of time.  We want a chart that will give us a pretty graph on how much time was spent on the phone by each employee for each day.  But since all the data is spread out by call there is no chart that can currently provide this.

To solve this problem we must first create a new sheet with the following columns:

Date | Joe | Sue | Greg | John

Where the names are an exhaustive list of the employees receiving the calls.  It's not a pretty method of doing things, but it is the method I have found to work.  The Date column will have the formula

=Unique(MainSheet![DateCellStart]:MainSheet![DateCellEnd])

The cells below it will be auto filled with the CONTINUE formula and google spreadsheets will keep them updated with a unique list of dates from your original spread sheet.  In each of the employee columns you will put the value:

=SUMIF(MainSheet![DateCellStart]:MainSheet![DateCellEnd] , [UniqueDateColumnAndCell], MainSheet![Call Length Cell])

This will tell google spreadsheets to hunt through the main date column and find all the dates that match the unique date referenced.  It will then sum up all the call length values that correspond to those dates.  Now we have a spreadsheet that has the data all summed up nicely in a way that can be easily read by either a human or the pretty google charts.  Simply apply a chart to the columns in the newly created sheet and you are done.

Saturday, December 8, 2012

Nested Folders for PicasaWeb & Google Plus

I have always been a fan of Google's products, but was not willing to spend the kind of money that most android devices cost.  However, Ting changed all that; I was able to get an android phone with a plan I liked at a price I liked.

I have always used Google's online picture storage applications, first PicasaWeb, and more recently Google+.  And I have always been bothered by the fact that I was unable to have nested folders in them.  I understand that the world is moving away from the folder mentality for base storage, but it still exists as a viewing option in most applications.  The desktop application Picasa even offers this feature, but for some reason it was left out of PicasaWeb.

With my android phones auto upload feature for photos I started taking a lot more pictures, and my online picture storage started getting messier and messier.  Finally I could not handle it and decided that if Google was not going to fix the issue after five years of people asking, then I would fix it.  I have always liked PicasaWeb more than Google+, probably just personal preference, but I decided to write a plugin for PicasaWeb to give me the folders I wanted.

The original Labs project for GMail folders gave me the idea.  After a few hours of hacking apart the PicasaWeb HTML interface I had a nice little Chrome extension that worked in the background and turned:

Vacations/2012/Beach

Into the three level folder hierarchy I wanted.  Now granted it was not quite perfect, no flash icon or anything like that, but I decided to put it into the Chrome web store for others to have if they wanted.  A day later I went to look at it and the lack of images looked horrible, so I went back, added a setting feature to remove the right side bar and create more view-able area for the folders, added an icon and an example image and uploaded the second version.

Now this little tool works great for me and does what I need.  Before I started putting a lot of effort into it I figured I would wait and see if others felt the same way.  So now I wait, this major missing feature is now available for Chrome users, time will tell just how major others feel the feature is.

Edit: I have finally found the time to port this extension over to Google+.  The project ended up being more difficult than I had anticipated.  Google+ makes it very difficult to modify the pages DOM, so I ended up having to actually replace all the album objects with my own custom version.  The port is pretty good for a first version.  The biggest complaint I have is that sometimes the javascript navigation that Google+ uses doesn't trigger it to run.  But it works for now.

Friday, September 28, 2012

Ting: The customizable cell plan

I have spent the last year looking for a cell phone plan that was right for me.

I used to have Verizon, but I feel like they are becoming more and more tailored to businesses rather than individuals.  The do have the best network in the USA, but that network comes at a very steep price.  Their plans are very expensive, far more than I feel they are worth, and they don't reduce the cost of the plan when you finally finish paying off the subsidized cell phone you purchased from them. They don't allow any other phones on their network so you are stuck getting a device from them; this is actually a complaint I have about all of the major cell networks, there is no reason not to accept phones just because they were branded by another network.  They come up with all sorts of excuses as to why they can't accept other phones, but in the end they are just that, excuses.  Also, they lock you into plans, typically 2 years, which is a very long contract for a cell phone.

Anyway, so on my recent deployment to Afghanistan, I cut all ties with Verizon and told myself I would never get into a contract for a cell phone again.  I also wanted more flexibility in my plan.  Because I know a little bit about technology I started playing around with various prepaid options trying to see what I could build for myself using the VOIP and SIP technologies available to me.

Finally, I found republicwireless.com who promised everything I wanted.  Their phone was a bit expensive, but their plan was $20/month unlimited everything.  It relied on a home wireless network to offset the usage costs on the cell phone.  So I got on their waiting list and started monitoring their forums for months to see how things were going.  I started seeing reports of horrible customer service and a few problems with the phones.  Now I can live with one or two issues for a really great plan, but an over priced cell phone (even for being un-subsidized) along with customer service issues when trying to work out cell problems was too much for me.  I was right on the border still liking the idea, but not sure that this company was for me.

Then I found ting.com.  Ting was running a $50 off special, so I purchased a $65 LG Optimus droid from them for a grand total of $15.  Then they allowed me to fully customize my plan taking off all minutes, and text messaging, and just keeping a small amount of data.  I figured I could make this work since texting, email, and gps was what I primarily did on my phone.  My total cost came to $20/month for 500MB.  It may not seem like a fantastic deal, but as long as I am using less than 1GB in normal usage then my cost is far cheaper than a regular plan.  Also, Tings customer service is great.

In comparison, Ting is overall more expensive than republicwireless if you are a heavy cell user.  However, Ting has much better customer service, and they have a wide selection of phones at a far cheaper price.  And there is no waiting list with Ting.  These two options should give you some great choices for finally breaking away from contracts with the big cell providers and actually getting what you want.

Both Ting and republicwiress run on Sprints network.  Ting allows roaming in the USA for minutes and text messages, but not for data; you won't get charged roaming fees at all, data simply will not work.  If you allow roaming on a Ting phone and a Verizon tower is closer than a Sprint tower, it will choose the Verizon tower for better signal and you will lose data connectivity.  This is resolved by simply not allowing roaming (you might have to restart your phone after turning it off).  From what I have heard, republicwireless does allow unlimited roaming in the USA for everything, but I have not had the chance to verify that.