Building Viget.com In EE (Part 2)

In my last EE post, I covered some ExpressionEngine tricks we used for managing multiple sites, removing the /comments URL segment, and setting up a simple "Preview" function. This time, we're going to use URL segments to create some dynamic user profiles, sort a portfolio page, and make a flexible upcoming event list.

Team Pages

The team pages had to be two things: Full of info (Twitter updates, posts, photos, links) and easy for team members to update themselves. A lot of this could probably be accomplished using custom user profile fields, but we opted for creating separate weblog entries about each user instead, making listing and maintaining these pages a little easier.

So we had three separate objects: A user, a weblog entry about a user, and the "page" itself that needed to display all this stuff. The easiest way to thread these all together was with a constant string: in our case, the user's username, which we set up as a lowercase string (in my case: 'davery'). This string was my username, my weblog entry's URL Title, and the actual URL of my profile page.

One immediate question was how to pull back a weblog of recent entries when the whole page is already an entry (and remember, you can't nest weblogs). The solution: cut the page into three weblogs.

{exp:weblog:entries weblog="profiles" url_title="{segment_3}" limit="1" } ... {/exp:weblog:entries} {exp:weblog:entries username="{segment_3}" weblog="fourlabs| ... |advance" limit="5"} ... {/exp:weblog:entries} {exp:weblog:entries weblog="profiles" url_title="{segment_3}" limit="1" } ... {/exp:weblog:entries}

So, we're pulling back entries with the URL Title of "davery" for the first and last third of the page, and pulling back entries with the username of "davery" for the middle third. This way, we're using the URL segment to steer EE towards the data we want. You could take this idea further: use the "davery" string to pull from a weblog called "davery," pull entries with the term "davery" in the text, or even use URL segments like "month=12" or "Monday" to pass specific start dates and ranges into the weblog tag. It's an quick way to use pre-existing, human-readable strings both as URLs and as parameters for retrieving data.

The Work Page

The idea for "Work" was to tag entries with the type of work done (SEO, design, branding) and allow users to sort the page based on these tags. This was already a little tricky, but we threw in an additional requirement: The tags had to be easy for project managers to create and edit. We naturally decided to use categories as the tags, which can work in a many-to-one relationship with each EE entry. The first step was to generate the lists at the top of the page, with two tags like this:

{exp:weblog:categories weblog="work" category_group="9" backspace="1" style="linear" } <a href="#" rel="{category_url_title}"> {category_name}</a> <span class="hide" id="{category_url_title}"> {category_description}</span>, {/exp:weblog:categories}

The two Category Groups house the different "things" and "clients" category types, and help us divide them visually. The "category_url_title" outputs a lowercase string for each category, making it perfect for re-use in CSS or javascript. The hidden "category_description" appears in the top right corner (in the yellow sticky note) when a user selects a category.

Now for the work pieces themselves:

 {exp:weblog:entries weblog="work"} <li><a href="/work/{url_title}/" class="{categories} {category_url_title} {/categories} {url_title}"> ... {/exp:weblog:entries} 

Here, we're spitting out a list of category_url_titles as classes for each item of work. So now, we have multiple classes on each work link that match the rel tags of the links we already made. Time to link them up with some jQuery (courtesy of Rob Soulé):

$('#work_wrap a').click(function() { if($(this).hasClass('selected')) {} else { $('.selected').removeClass('selected'); $(this).addClass('selected'); $('#work_groups li a.'+$(this).attr('rel')).parent().each(function() { $(this).addClass('remove'); $('#work_groups ul').prepend(''); }); $('.remove').remove(); $('#work_groups ul li.selected').fadeIn(450); $('#work_paper').html('<p>' + $(this).next().html() + '</p>'); }//if return false; });

So, when a user clicks one of our tags, we:

  • Remove the "selected" state from anything currently selected
  • Add a "selected" state to the tag link
  • Get the parent li of any link with class matching the clicked rel tag
  • Add the "remove" class to those parent list items
  • Prepend new list items with the same HTML (and the "selected" class) to the list, and hide them
  • Remove all list items with the "remove" class from the DOM
  • Fade the new hidden list items in form their hidden state.

This is a pretty cool reusable answer to sorting, and can be used for any list that needs to be shuffled based on user interaction. With a little more work, you could find a way to slide open the space for the prepended items, which would look awesome on a more traditional list.

Events

We attend (and even host) a lot of events here at Viget, and needed an easy, flexible way to post them. Stuff on the wishlist:

  • The dates listed in a readable, standard format (July 10-15, September 10, August 29-September 2)
  • The ability to give events a span of days
  • The ability to add times, if necessary
  • The ability to completely buck the format and write up an event in a unique way
  • The ability to link to a blog entry or related website from an event's title
  • The ability for an event to automatically expire after it occurs

The dates were by far the trickiest bit. Sometimes events had start times, sometimes they had end times, sometimes they spanned two months, sometimes they were a single day, and the formatting needed to read correctly in all cases. The solution was a sort of IF statement spaghetti:

{exp:weblog:entries weblog="events" ... show_future_entries="yes"} {if full_override == ""} {if event_url}<a href="{event_url}"> {title}</a>{if:else}{title}{/if} - {if start_time}{start_time} {if end_time != "" AND expiration_date == ""}-{end_time}{/if}, {/if} {entry_date format="%F %j"} {if "{entry_date format="%m"}" == "{expiration_date format="%m"}"} {if expiration_date AND expiration_date != entry_date}- {expiration_date format="%j"} {if end_time}, {end_time}{/if} {/if} {if:else} {if expiration_date AND expiration_date != entry_date}to {if end_time}{end_time}, {/if} {expiration_date format="%F %j"} {/if} {/if} {description} {if:else} {full_override} {/if} {/exp:weblog:entries}

Eesh. Here's an easier way to read it, with the IF statements nested and indented:

Are we totally overriding the entry format?
   Yes: Output the HTML the user entered instead of the normal stuff.
   No: Output the standard event layout.
      Is there an event URL?
         Yes: Link the title to it.
         No: Just show the title without a link.
      Was a "start time" (a custom field in the entry) specified?
         Yes: Show it.
            Was an "end time" specified, and did the user set
            an expiration date?

               Yes: Show the end time, so the start+end will read "7 PM-8 PM"
      Show the events's month and date
      Do the beginning and end dates occur in the
      same month (set with a custom field)?

         Yes: Is there an expiration date set, and is it
         later than the start date?

            Yes: Show just the numerical date
            (so the final event might read "June 10-11")
               Is an end time set?
                  Yes: Show it
         No: Is there an expiration date set,
         and is it later than the start date?

            Yes: Show the month and numerical date (June 30-August 1)
               Is an end time set?
                  Yes: Show it

It's a pretty complex way to handle the date, but given the goals and restrictions, it was the best solution we found at the time.

That about covers our favorite EE pieces of Viget.com for now, but we're going to keep working with EE and posting any new ideas we find. If you have any suggestions on how to reuse, tighten up, or totally redo some of these ideas, we'd love to hear them in the comments.

Doug Avery

,
Posted in Article Category: #Design & Content
on