The challenge: creating a travelling virtual exhibit for locations with no internet access.

by Kathy Bryce Wednesday, September 15, 2010 10:47 AM

UFA Co-operative Limited celebrated their centennial last year and has created a virtual exhibit, 101 Years of Enriching Rural Life which features artifacts, music and an interactive display.   In 1909, farmers across Alberta joined together to create a co-operative that would improve conditions for agricultural producers and bring modern conveniences to rural areas. From 1921-1935 UFA was a political party and formed the provincial government, and since then it has expanded into businesses including agriculture, petroleum, construction and outdoor adventure.  UFA now has 120 locations throughout Alberta, BC and Saskatchewan.

The centennial exhibit is travelling throughout rural Alberta, often to very small centres with no resources or technical support to set up a public internet connection in the local museum.  The UFA archives uses Inmagic DB/TextWorks and the Andornot Archives Starter Kit to manage their archival accessions, descriptions and artifacts, so they asked us if we could help create a display of their photos and digitized documents.
UFA screenshot
Our solution was to use the free, search only, version of DB/TextWorks. This has been loaded onto the PC that gets shipped around to the various locations along with its display stand and monitor.  We created a subset of the textbase with a specially designed menu screen and display forms.   All the menu screen links were to saved sets within the database and the Options were set to always show results in the Report window to avoid users ever seeing a query screen.   Virtually all the icons were stripped off the toolbars, and script buttons added for users to navigate through the records viewing descriptive information and the images themselves.  This ensures that people who have never seen DB/TextWorks before can simply click to view information.

The image file paths had to be batch modified for the subset exhibit database and of course any changes had to be made using the full version of DB/TextWorks. This meant a lot of copying over textbases again every time a minor change was needed.  Designing for the low resolution small monitor for the travelling exhibit was also a challenge. However, now that the procedure is in place, the database can be updated to add new material regularly, ie. for a new town on the exhibit circuit.

“The exhibit has been popular and importantly is easy for people to use.  The staff member at the exhibit's current location had extremely little computer experience and was quite comfortable exploring the database within minutes. The United Farmers Historical Society (UFHS) is pleased that it was able to utilize the software it already owned  and records that had been previously created;  Andornot's solution saved UFHS both time and money.” [Carolyn Foard, History Coordinator, UFA Co-operative Limited.]

The DB/TextWorks search-only version can also be used to master DVDs for distribution or to provide a search only version of the textbases for public use.  Check out the DB/TextWorks online help under Run-time version or contact Andornot for more information.  Ultimately most organizations would choose to use WebPublisher PRO to provide a web based search interface to their databases, but this project shows what is still possible with limited resources!

Errors: Sending the Right Message (Redux Covering ASP.NET 3.5/4.0)

by Ted Jardine Monday, September 06, 2010 12:41 PM

If you've read and followed up on my previous posts about handling errors, you might have found yourself pulling out your hair when you discovered that your error handling went south again when you changed your target ASP.NET framework from 2.0 to 3.5/4.0 (applies to WebForms only of course). So here I am to save your day again (and yes, I wasted an entire Friday evening figuring this one out - good times).

In a nutshell, upgrade to 3.5 and your awesomely handled 500 and 404 errors start going back to 302 and 200 redirects the ol' fashioned (and completely idiotic) ASP.NET way of doing things. Of course, if you are like me, you'd waste an entire evening on it. But fortunately, you're not so you won't.

There's two issues/solutions:

  1. As of ASP.NET 3.5 there's now a redirectMode="ResponseRewrite" setting for your customErrors setting in your Web.config (yeah!). Put it in and you're halfway there (the default is the aforementioned moronic "ResponseRedirect"). Now your:
  2.             Context.Response.Clear();
                Context.Response.TrySkipIisCustomErrors = true;
                Context.Response.StatusCode = statusCode;
                Context.Response.Status = status;
               
                Context.Server.Transfer(errorPage, false);
    is back firing on all cylinders again.
  3. Except that your 404s are fine now, but your 500 errors return your error page twice in the body of your error page! Genius that I am, I went back to my experiences with IIS 6 and wrapped error pages and figured out that IIS 7/7.5 was sending back "its own" error page and then my server transfer was getting tacked on as well. Again, this does not effect 404s - I'm guessing because we clear the error (see previous post). So remove the Server.Transfer for non-404s and we're golden again.

In summary, for IIS 7/7.5 and .NET 3.5/4.0, use the following as a starting point for your Global.asax.cs or, better yet, an error module (for good measure figure out how it could be all tested):

        private readonly static ILog Log = LogManager.GetLogger(typeof(Global));
        private string _errorPageLocation;
        private string _error404PageLocation;


        protected void Application_Start(object sender, EventArgs e)
        {
            // Wire up log4net database connection string if desired.


            if (Log.IsInfoEnabled)
                Log.Info("Log4Net initialized - Ignore.");
        }


        protected void Application_Error(object sender, EventArgs e)
        {
            InitConfigurationFields();


            Exception exception = Context.Server.GetLastError();

            if (exception is HttpUnhandledException)
                if (exception.InnerException != null)
                    exception = exception.InnerException;


            if (exception is HttpException)
            {
                var ex = (HttpException)exception;
                var statusCode = ex.GetHttpCode();

                if (statusCode == 404)
                {
                    ServerTransfer(_error404PageLocation,
"404 Not Found", statusCode);
                    return;
                }
            }


            Log.Error("Unhandled error caught in error module.", exception);

            //Todo: Any way to get the correct status statement for a specifc code?
//Hard-coding all to "500 Internal Server Error" here.
            ServerTransfer(_errorPageLocation, "500 Internal Server Error", 500);
        }


        /// <summary>
        /// Gets configuration fields for database connection (if applicable),
/// and customError redirects.
        /// </summary>
        private void InitConfigurationFields()
        {
            var section = WebConfigurationManager.GetSection("system.web/customErrors");

            if (section != null && section is CustomErrorsSection)
            {
                var customErrorsSection = (CustomErrorsSection)section;
                _errorPageLocation = customErrorsSection.DefaultRedirect;

                var error404 = customErrorsSection.Errors["404"];

                if (error404 != null)
                    _error404PageLocation = error404.Redirect;
            }


            if (string.IsNullOrEmpty(_errorPageLocation))
                throw new NullReferenceException(
"customErrors DefaultRedirect must be specified in Web.config.
...For i.e. ~/error.aspx"
);


            if (string.IsNullOrEmpty(_error404PageLocation))
                throw new NullReferenceException(
"customErrors 404 statusCode redirect must be specified
...in Web.config. For i.e. ~/page-not-found.aspx"
);
        }


        private void ServerTransfer(string errorPage, string status, int statusCode)
        {
            // If customErrors is off, just let ASP.NET default happen.
            if (Context == null || !Context.IsCustomErrorEnabled)
                return;


            // Want the error around so that we can provide a little more
// descriptive message to end user in error page.
            // However, for 404 errors (only!), not clearing the error causes IIS7 to
// still hijack the process and the custom 404 error page.
            if (statusCode == 404)
                Server.ClearError();

            Context.Response.Clear();
            Context.Response.TrySkipIisCustomErrors = true;
            Context.Response.StatusCode = statusCode;
            Context.Response.Status = status;

            // For .NET 3.5, doing a Server.Transfer combined with customErrors
// redirectMode="ResponseRewrite" returns the error page body *twice*.
            if (statusCode == 404)
                Context.Server.Transfer(errorPage, false);
        }

Tags: ASP.NET | C# | IIS 7

Month List