I’ll admit it: I am a Drupal newbie. We chose it for our CMS last year, but we haven’t really been using it as a CMS as much as a place to dump and view our internal key indicator reports.  But finally! Ah, yes… we finally have a feature request that is actual content management - an event calendar.

Imagine my surprise when I go looking for an event calendar module and find nothing. How could such a huge CMS not have a simple event calendar? A big part of this misunderstanding was that I didn’t understand Drupal - at all. I still don’t really, but figuring this feature out has helped a lot.

One thing to realize is that Drupal is a content management system. So (eventually) it would make sense to create event content and display that content in a specific way. I surprisingly couldn’t find much documentation on this, so below are the steps I took to get my event calendar up and running. They’re kind of abbreviated, so just ask if you need any clarification. Keep in mind this was a learning experience, so if you have any tips for me, I would like those as well. ;)

  1. ## Install Modules
    • View
    • Date
    • CCK
    • Calendar
    • Don’t forget to give yourself permissions to administer these modules and enable them!
  2. ## Create Calendar Event Content Type
    1. Name: Event
    2. Type: event
    3. Title Field Label: Event
    4. Body Field Label: Details
    5. Do not promote to front page.
    6. Save
    7. Go to Manage Fields for Event in the content type list
    8. Add a field called Date of type fieldeventdate with a textfield/popup calendar+repeat selector. Configure it to be required and blank by default, have a reasonable format, and to hide the repeat options by default.
    9. Add a field called Location of type field_event_location that is a single-row text field. Configure it to be 100 chars long and required.
  3. ## Set up A Calendar View
    1. Go to Administer > Views
    2. Enable the default calendar view and clone it.
      • View Name: a_calendar
      • View Description: Calendar
    3. Change the title to “Whatever you want Calendar”
    4. Change Arguments > Date: Date (Node: Updated date) to Content: Date (field_event_date value) Scroll down a bit to see the checkboxes (under Date fields)
    5. Add a sort criterion for Content: Date (field_event_date value)
    6. Add a filter for Node: Type is one of Event
    7. Click the Calendar page side-tab and change the path to “a_calendar”
    8. Click the Calendar block side-tab and change the block settings admin to “A Calendar”
    9. Save the view.
    10. Navigate to ?q=a_calendar to make sure it is working.
  4. ## Set up Roles
    1. Add a new role called Calendar_User and add the following permissions:
      • Edit/view field_event_*
      • (Create | delete own | edit own) event content
    2. Give this role to whoever you want to create event entries. (Let’s call them eventries for fun)
  5. ## Add a link somewhere and enjoy!

Google driving directions in C# - Update

| 12 Comments | No TrackBacks
I've updated my GDirections C# class to allow for easier access to the actual driving directions.  The code file can be found here.  As usual, you will need your own Google Maps API key, which you can get for free at http://code.google.com/apis/maps/signup.html.  You will also need JSON.NET to parse the JSON response from Google's service.

Example code:

GDirections directions = new GDirections(startAddress, endAddress);

if (directions.IsValid)
    Response.Write(directions.RoutesHtml[0]);
else
    Response.Write("Route not valid.");


I've been using ASP.NET MVC since preview 1, and have loved every bit of it.  Between preview releases, none of my code broke -- even between 2 and 3...  Wait that can't be right...

So when the MVC beta dropped recently, I went ahead and upgraded my three projects.  Little did I know that when I was upgrading all of those other times, I was forgetting to update my assembly references!  Big d'oh!  I had been using preview 2 this whole time!

Upgrading from preview 2 to the beta can be a bit of a pain, so here are some hints to make the process easier, if anyone out there actually needs to do this.

  • Update your strongly typed ViewData references to use ViewData.Model - This one is pretty self-explanitory.  If you have strongly typed ViewData (by typing ViewData<Foo> in your codebehind) you need to reference ViewData.Model in your views now.
  • ViewData is now ViewDataDictionary - If you have custom ViewData-derived classes, you should change them to inherit ViewDataDictionary.  I just got rid of mine completely and created some ViewInfo classes to pack up all the common data I needed to send over that wasn't my model object.
  • RenderView("Page") is now return View("Page") - Much, much nicer.  Make sure you change those public voids to public ActionResults as well.  Good work on this change!
  • Default.aspx has a codebehind page now - If you're getting blank pages, you probably want to update your Default.aspx that is included in the project template.  Just create a new MVC beta project and you should have it ready to copy into your old project.
  • Update your Web.config file - There are a few new entries:
In compilation/assemblies: <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
In pages/namespaces: <add namespace="System.Web.Mvc.Html" />

Happy coding!

For the past week or so I've been getting weird problems with Visual Studio 2008.

  • "Could not load breakpoint" - This was a mere nuisance really.  Just a few error dialogs when I loaded our solution.
  • Completely locks up when closing solution, does not save environment - This started happening a few weeks after the breakpoint issue.  Amazingly annoying, since every time I load Visual Studio, I have to set the right startup project, set breakpoints, etc...
  • Closes without an error message when starting 2nd debug session - This is when I got annoyed enough to try to figure out what was happening.  I was losing productivity having to reload and reset my environment every time I wanted to start a new debug session.
Luckily, I found a solution rather quickly.  I don't have the link anymore unfortunately, but all of these problems were fixed by deleting the .suo file in my solution folder.  That kind of a fix should have been obvious to me, but I'm just happy to be back to 100% productivity. :)

Really slick PHP/Ajax FTP batch upload script

| 4 Comments | No TrackBacks
I like to keep a pretty efficient development environment.  I have several projects I manage at work, and I have these projects all to myself for the most part.  Regardless of whether you are working in a huge IT consulting firm or in a basement by yourself, it's a great idea to automate as many mundane processes as possible.

One of the tasks that annoys me most is moving my code to our production web servers.  It's a royal pain the ass to fire up SmartFTP/FileZilla/whatever, navigate your folders, connect, and so on, especially if you are frequently publishing.  On top of this, manual FTP uploading can also be error prone.  I'd like a nickel for every time I've overwritten production-specific config files with development copies.

The easiest way I could think to publish changes is to simply push a freaking button.  So, a month or two back I found a simply FTP upload script.  The script (I can't remember where I got it) often worked, but once my projects started growing in size, it would often time out and only a portion of my code would make it up.  I figured the best solution would be to write my upload script that cycled through my project files and used a remote script to upload them one by one rather than in one big session.  Sure, it's slower, but hey, it works and all I ever have to do is click one freaking button.

Below are the spoils of my endeavor and some commenting:

Grab the code here.

ftp_tools.php: Utility functions.  Nothing to change here.  echo_jsarray() recursively browses a directory and prints a Javascript object containing information about the files in it.
ftp_upload.php: The script that actually handles uploading a file passed to it.  Here you want to change the $ACCOUNTS variable to hold the FTP login info for your various projects.  Also change the $FTPADDR string to hold your server's address.
SampleProject.migrate.php: The script that handles uploading your files, one by one.  There are some variables up at the top you'll want to change such as file exceptions and the directory you want to upload from.  In UploadFile() you'll also want to define the account you want to use.

Setup:  On our production FTP server, I have an account for each project.  The root directory of each of these accounts is the project's root folder.  Thus, I simply upload to the root folder for every project.  Dump all of these files into a folder on your test server or something, grab jQuery 1.2.6 (minified) and add it to the mix, and you should be good to go.

It's a pretty rough, environment-specific way to do this, but it should be no problem to adapt to your own development environment.  Bonus points: Figure out which files have been changed and which have not, and upload only the changed files.  Add some error handling for when files fail to upload mid-stream.

I've been working on an ASP.NET MVC project with a nice and fancy model layer. My models have a nice'n'friendly UpdateFrom(NameValueCollection) method to bind HTTP request parameters to an object. The only problem is that sometimes, I don't want to have every column on a form, and some of the related database columns should remain unchanged.

In HTML forms, this presents a problem: checkboxes that are unchecked don't post anything. How am I to tell if a field has been left off the form or if a field is unchecked? After some aimless wanderings on the inter-tubes, I found this article which documents a solution for Rails.

It's pretty simple, so I figured I would take this idea and wrap it up in a server control. We have a small set of server WebControls that play nice with the rest of our code, so it was just a matter of adding a few lines of code. You could also wrap this up in a helper method like in the rails version.

CheckBox checkbox = new CheckBox();
checkbox.InputAttributes.Add("class", CssClass);
checkbox.InputAttributes.Add("id", (string.IsNullOrEmpty(ID) ? (FieldName + "CheckBox") : ID));
checkbox.InputAttributes.Add("value", "true");
checkbox.InputAttributes.Add("onclick", 
    "document.getElementById(\"" + FieldName + "TextBox\").value = this.checked.toString()");
checkbox.Checked = Checked;
checkbox.RenderControl(writer);

TextBox textbox = new TextBox();
textbox.Attributes.Add("style", "display: none;");
textbox.Attributes.Add("id", FieldName + "TextBox");
textbox.Attributes.Add("name", FieldName);
textbox.Text = Checked ? "true" : "false";
textbox.RenderControl(writer);

Now I can tell if a checkbox field is truly meant to either just not be there or is false.

Goodbye Typo, hello Movable Type!

| No Comments | No TrackBacks
I finally got tired of typo dying on me.  Sometimes after making a post, the entire app would just blank out and return no html at all.  The cache seemed to act really weird at times as well.
So, after looking around for some new software, I decided on movable type.  Wordpress was another big alternative, but I had used it on my now-defunct guild site (Retaliation-Skullcrusher) and wanted something a little different.

Movable Type is amazing so far.  The preinstalled themes are great, the admin GUI is intuitive, and the whole thing seems rock solid.  Setup was a breeze as well. (I used this walkthrough)  No qualms so far.

Migrating from typo to Movable Type was fairly easy too.  I lost all of my post dates and images, (my fault, though) but re-entering my post content was a breeze.  I just went into my typo database, went through the contents table, and copy/pasted the markup.  Make sure you choose the "Markdown" format in MT's format selector.

Anyway, time to draw up a design comp and stick it on here.

jQuery wizard form

| 5 Comments | No TrackBacks

One of my projects needed a wizard form. Being the jQuery fanboy that I am and given that I couldn't find any existing wizard form plugins for jQuery, I made my own! You can check it out here: http://plugins.jquery.com/project/WizardForm

It's not what I would consider finished, but it works if you need a quick form and don't mind dealing with my markup choices. It behaves sort of like the jQuery tabs plugin, so if you have used that, you should be able to get started fairly quickly.

Where's the love for conditional comments?

| No Comments | No TrackBacks

I hate trying to resolve CSS and Javascript conflicts between web browsers. What I hate even worse is going about it in an "unclean" way.

Conditional comments for IE have been around a long time and I haven't really seen too many people (if any) using them for resolving style bugs. Check out what I do on every web site I build.

<link href="/public/css/style.css" rel="stylesheet" type="text/css" />
<!--[if lte IE 7]>
    <link rel="stylesheet" type="text/css" href="/public/css/style-ie.css" />
<![endif]-->
<!--[if lt IE 7]>
    <link rel="stylesheet" type="text/css" href="/public/css/style-ie6.css" />
<![endif]-->

Note that I normally don't bother with IE 5.5, but this could work for that as well. With these CSS links in place, I can simply expect a normal, sane style sheet to be included on well-behaved browsers, a special style sheet for IE7, and an even more "special" style sheet for IE6. Since these are declared in order of insanity, (from least to greatest) I can simply copy style declarations from my original style sheet, override whatever I need to, and keep everything squeaky clean.

There's my solution. If anyone reading still uses CSS hacks, I would love to know why. It may be something I just don't "get."

Google driving directions in C#

| No Comments | No TrackBacks

ALERT! A better, more complete version of this code can be found here: http://www.sagecraft-studios.com/2008/11/google-driving-directions-in-c---update.html

This is something I've been wanting to figure out for a while. What I'll be demonstrating in this post is how to get driving directions, distances, and trip durations from Google's mapping service... without using Javascript! The demonstration code below is just that - a demonstration. It is your job, dear reader, to make a friendly, accessible interface out of the underlying code.

Note: As far as I can tell, this is undocumented. If you're using this in a production environment, I would advise writing some unit tests in case URLs change, parameters change, etc...

You will need JSON.NET referenced to deserialize the data sent from Google.

using Newtonsoft.Json;

namespace xxxx
{
    public class GDirections
    {
        private string _key = "xxxx";
        private string _output = "js";
        private string _requestFormat = "http://maps.google.com/maps/nav?key={0}&output={1}&q={2}";
        private Hashtable _data = new Hashtable();

Our underlying data. Yes, you could certainly use a generic Dictionary rather than a Hashtable. In the code above, make sure to set _key to your Google Maps API key, which can be generated here.

        /// <param name="path">A path in the format "From {a} to {b}".  Example: "From Lexington, KY to Bowling Green, KY"</param>
public GDirections(string path)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
                string.Format(_requestFormat, _key, _output, Uri.EscapeUriString(path)));
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream());
            string json = reader.ReadToEnd();

            _data = readJson(new JsonReader(new StringReader(json)));
        }

In our constructor, we basically just grab the response from a web page. (or in this case, JavaScript object) Using Json.NET, we deserialize the object and somehow stick it in a Hashtable.

        private Hashtable readJson(JsonReader jreader)
        {
            Hashtable data = new Hashtable();
            while (jreader.Read() && jreader.TokenType != JsonToken.EndObject)
            {
                string key = null;
                object value = null;

                if (jreader.TokenType == JsonToken.PropertyName)
                {
                    key = (string)jreader.Value;
                    jreader.Read();
                    switch (jreader.TokenType)
                    {
                        case JsonToken.StartArray:
                            value = readJsonArray(jreader);
                            break;
                        case JsonToken.StartObject:
                            value = readJson(jreader);
                            break;
                        case JsonToken.Null:
                        case JsonToken.Undefined:
                            value = null;
                            break;
                        default:
                            value = jreader.Value;
                            break;
                    }
                }

                if (key != null)
                    data.Add(key, value);
            }
            return data;
        }

        private ArrayList readJsonArray(JsonReader jreader)
        {
            ArrayList value = new ArrayList();
            while (jreader.Read() && jreader.TokenType != JsonToken.EndArray)
            {
                switch (jreader.TokenType)
                {
                    case JsonToken.StartArray:
                        value.Add(readJsonArray(jreader));
                        break;
                    case JsonToken.StartObject:
                        value.Add(readJson(jreader));
                        break;
                    case JsonToken.Null:
                    case JsonToken.Undefined:
                        value.Add(null);
                        break;
                    default:
                        value.Add(jreader.Value);
                        break;
                }
            }
            return value;
        }
    }
}

This is the "somehow stick it in a Hashtable" (or Dictionary) part. The code really is pretty simple, but the resulting data structure is a huge mess. We end up with a nested dictionary-style object with arrays thrown in there just to keep things fun and complicated.

IDEALLY, we would want to create classes for each kind of object we get in our response. For example, Placemark, Status, Point, and Direction classes would need to be created. If even this is too much work, (which it shouldn't be if you are serious about implementing and fully using this functionality!) some more friendly accessor methods/properties shouldn't be a huge chore to do. For example, since we're using this thing just for driving time and duration, I implemented the following accessors

        public decimal DrivingMeters
        {
            get { return Convert.ToDecimal(((Hashtable)Directions["Distance"])["meters"]); }
        }

        public TimeSpan DrivingTime
        {
            get { return new TimeSpan(0, 0, Convert.ToInt32(((Hashtable)Directions["Duration"])["seconds"])); }
        }

        public decimal DrivingMiles
        {
            get { return Math.Round(DrivingMeters * (decimal)0.000621371192, 2); } // according to Google.
        }

        private Hashtable Directions
        {
            get { return (Hashtable)_data["Directions"]; }
        }

So yes, I'm basically saying take this foundation and make it your own. Make it friendly and then give it back to me. :)

Powered by Movable Type 4.21-en

Recent Comments

  • glompix: @derby We actually do use Subverison for our version control, read more
  • derby: I use xperl, dominos, and titan bar to customize my read more
  • derby: Thanks for the script. I was looking for some AJAX read more
  • A. burton: how do you set up a search for events created read more
  • NANERPUSS: LEXINGTON GOONTUCKY :hfive: read more
  • Matt: This is a great walkthrough. I am also using the read more
  • Jebaird: Thanks for the walk through. It was very easy to read more
  • Justin: Sounds pretty badass. I had a coworker that built something read more
  • hchang: Would like to add that also found this to be read more
  • hchang: Thanks for the insight, Firebug says "doflg=", and the resulting read more