Latest Tweets

Find Posts by Category
Find Posts by Tag
Twitter

Entries in communityserver (14)

Tuesday
Dec112007

Adding a Simile Timeline to Community Server

In case you have visited this site recently, you may have noticed the cool timeline in the Blog Archives page. This is an amazing bit of Javascript that displays a any content you like on a long virtual sheet. Its kind of like those great "entire history of the world" posters that you grew up with. I found this after looking at Scott Hanselman's web site a few days ago. I figured that I had plenty of time, so I did the same thing. It wasn't very hard, but it wasn't exactly brain dead easy either.

To start off, I went up to the source page for the project at MIT. The examples on there are great. The documentation is a bit basic, but most of your needs are covered. After searching around a bit, I found a few other articles that were helpful, including a couple from LifeCycle Solutions and Ryan Kanno. Then it was just a matter of trying it out.

First, I added the javascript tag on my Blog Archives page:
<script type="text/javascript" src="http://simile.mit.edu/timeline/api/timeline-api.js"></script>

Further down I added a div where the timeline was actually going to go:
<div id="my-timeline" style="height: 450px; border: 1px solid #aaa; margin-right:20px;" />

Next I created a new .js file to hold my javascript. I think I will use this file for lots of other stuff, so I made a new namespace just for the timeline code. Here is the entire file:

   1:  var TECHNOVANGELIST = {};
2: TECHNOVANGELIST.TimeLine = function() {
3: var tl;
4: var resizeTimerID = null;
5:
6: return {
7:  
8: createTimeLine: function () {
9: var eventSource = new Timeline.DefaultEventSource();
10: var theme = Timeline.ClassicTheme.create();
11: theme.event.bubble.width=520;
12: theme.event.bubble.height=120;
13: var curdate = new Date()
14: var bandInfos = [
15: Timeline.createBandInfo({
16: eventSource: eventSource,
17: date: curdate.toGMTString(),
18: width: "80%",
19: intervalUnit: Timeline.DateTime.WEEK,
20: intervalPixels: 50
21: }),
22: Timeline.createBandInfo({
23: showEventText: false,
24: trackHeight: 0.5,
25: trackGap: 0.2,
26: eventSource: eventSource,
27: date: curdate.toGMTString(),
28: width: "20%",
29: intervalUnit: Timeline.DateTime.MONTH,
30: intervalPixels: 100
31: })
32: ];
33: bandInfos[1].syncWith = 0;
34: bandInfos[1].highlight = true;
35: bandInfos[1].eventPainter.setLayout(bandInfos[0].eventPainter.getLayout());
36: tl = Timeline.create(document.getElementById("my-timeline"), bandInfos);
37: Timeline.loadXML(http://technovangelist.com/BlogTimelineSource.ashx,
function(xml, url) { eventSource.loadXML(xml, url); });
38: },
39:
40: onResize: function() {
41: if (resizeTimerID == null) {
42: resizeTimerID = window.setTimeout(function() {
43: resizeTimerID = null;
44: tl.layout();
45: }, 500);
46: }
47: }
48: };
49: } ();

Back in the Blog Archives page, I added the pointer to that js file:

<CSControl:Script src="../Common/TVLIB.js" runat="server" />
<script type="text/javascript">
   window.onload=TECHNOVANGELIST.TimeLine.createTimeLine;
   window.onresize=TECHNOVANGELIST.TimeLine.onResize;
</script>

You can try that out to see if it works so far, but your eventsource isn't working yet. I have that pointing to a BlogTimelineSource.ashx which we haven't created...yet. So here is the full file for BlogTimelineSource.ashx:

   1:  <%@ WebHandler Language="C#" Class="BlogTimeLineSource" Debug="true" %>
2: 
3: using System;
4: using System.Web;
5: using CommunityServer.Blogs.Controls;
6: using CommunityServer.Blogs.Components;
7: using System.Collections.Generic;
8: using CommunityServer.Components;
9: 
10: public class BlogTimeLineSource : IHttpHandler {
11:
12: public void ProcessRequest (HttpContext context) {
13: context.Response.ContentType = "text/xml";
14: context.Response.Write("<data>");
15: BlogThreadQuery query;
16: query = BlogThreadQuery.CreateNewAggregateQuery();
17:
18: query.PostConfig = BlogPostConfig.IsAggregated;
19: query.PageIndex = 0;
20: query.PageSize = 5000;
21: query.IncludeCategories = true;
22: query.BlogPostType = BlogPostType.Post | BlogPostType.Article;
23: 
24: ThreadSet ts = WeblogPosts.GetBlogThreads(query);
25: 
26: foreach (WeblogPost p in ts.Threads)
27: {
28: context.Response.Write("<event start=\"" + p.PostDate + "\" link=\""
+ p.ViewPostURL + "\" title=\"" + p.Subject + "\">");
29: context.Response.Write(context.Server.HtmlEncode(p.ForceExcerpt) + "</event>");
30: }
31: context.Response.Write("</data>");
32: }
33:
34: public bool IsReusable {
35: get {
36: return false;
37: }
38: }
39: 
40: }
At this point you run into a problem. The ashx file probably can't be found still. Its there, but Community Server doesn't know about it yet, so you may have to modify you SiteUrls_Override.config. I just added the following in an <override> block:
    <location name="BlogTimeLineSource" themeDir="common"  path="/" >
      <url name = "BlogTimelineSource"  path="/BlogTimelineSource.ashx" 
              pattern="/BlogTimelineSource.ashx" physicalPath="##themeDir##" 
              vanity="{2}" page="BlogTimelineSource.ashx"/>
    </location>

That's it!! The toughest parts for me were trying to figure out how to reference the javascript file on a CS site, figuring out Javascript namespaces, and then doing the query in the ashx file. Then I spent a while tweaking the look...changing the height (though its referenced as width) of the bands and changing the size of the popup. Let me know if this is useful for you...

Tuesday
Dec112007

A new theme in the works...posted now

So I am updating the theme on this site. Its not done, but there are already a lot of changes I am pretty excited about. Its still not perfect, but I have a bit of time on my hands right now.

Whats working and is pretty cool:

  • Current Twitter Status - ok that was there before
  • Upcoming Travel from 30Boxes - Its grabbing my future travel schedule from 30B. When a date goes past, it will no longer show up on the page...automatically
  • Recent Del.icio.us Tags - Its grabbing the last 5 or 6 days worth of links I have tagged.
  • Captaris Developer Program Posts - I also occasionally post to the Captaris Developer Program. This is a list of all of those posts. As with the above items, this is dynamic.
  • Blog Flare - Took a while to figure this out. Each post can be added to Digg, Facebook, Technorati....ok, they won't, but they could.
  • Full vs Excerpts - The most recent 3 posts are the full post. Then there are a bunch more that are just excerpts.
  • Cool icons from FamFamFam: Silk
  • Simile Timeline - This is one of the coolest new features. Rather than a traditional calendar of previous items, I am now showing an interactive timeline of all blog posts here.

Click to read more ...

Monday
Aug062007

Community Server 2007 Theme

A few months ago, Community Server 2007 was released. I updated for one main reason, a new theming engine called Chameleon. I was determined to come up with a new theme that WASN'T the paperclip or the CS tree that is on everyone else's site. So I started from scratch to design something amazing. Unfortunately I discovered that it was a helluva tough to do it all from scratch. So I ended up with a theme that was pretty lame. When the Telligent guys announced they were doing a theme contest, I wanted in and planned to do something great, but didn't quite get to it. The deadline came and went and I had nothing. But then they extended the deadline. Whew! But then it came again....I noticed on the day that the time was up. Thankfully I am in Amsterdam and the 9AM CST end time was several hours away. I took the new Basic theme as a starting point and within a couple of hours, I felt I had something pretty cool. So I submitted it. I figured I would be one of a few dozen, but there were only 6 submissions. But it was so easy?!?! Whats up with that??? Anyway, go check out the themes and vote. Mine is obviously TVBrown. I screwed up a bit with the way IE shows the theme so I am going to update that soon (looks better in Firefox).

Click to read more ...

Monday
Aug062007

Update to my TwitterBadge for Community Server 2007

A few weeks ago when I updated this site to Community Server 2007, I also updated the theme. That theme kinda sucked, but I learned a lot about how to design a CS theme using the new theming engine, Chameleon. One of the additions I made to the theme was a Twitter badge that shows what I am up to all the time (assuming I actually update my Twitter status. Unfortunately it sometimes didn't work. When I saw there was a problem, I didn't have any time to figure out why, so I just removed it. This weekend I updated the theme again. And I got a chance to see what was going on with the Twitter badge. Doh!!! Its was so easy to fix...one single line. Near the beginning I checked to see if a particular string was null. What I should have done was check if it was null or empty. I have fixed it in the original post.

Click to read more ...

Tuesday
Jun192007

How To: Integrate the AJAX Gallery Viewer with CS2007

When designing this new theme, I knew that designing the gallery would be important and a lot of work. I wanted an AJAX feel if possible, but I didn't want to have to go through a huge learning curve just for that. So when I saw the AJAX Gallery Review on Smashing Magazine, I thought I would implement one of them. The one that I thought was most interesting was Slimbox. Implementing this was far easier than I could have ever imagined. Here is the process:

  1. Go to Slimbox and download the bits.
  2. Download the mootools framework choosing all the features listed here. The default Packer compression is ok, even though it mentions PHP.
  3. Extract the files to your web directories. I put slimbox.js and the mootools js file in the common directory of my theme, then the slimbox.css in the style folder.
  4. On any page I want the AJAX effect to show up, I include the following code. Since I am using master pages, this is inside a asp:content block:
    <CSControl:Script ID="Script1" runat="server" Src="~/Themes/BW/Common/mootools.v1.11.js" />
    <CSControl:Script ID="Script2" runat="server" Src="~/Themes/BW/Common/slimbox.js" />
    
  5. Add the link for your images. Since I am using CS2007, I have a CSGallery:GalleryPostData tag with the following content. The magic happens with the linkrel value. You need to have the word lightbox in there. Anything in square brackets after lightbox is used as a group name so that lightbox knows what other photos it can view in the same session. Go ahead, hover over the picture and click the Next link, or just press the N key.
    <CSGallery:GalleryPostData runat="server" Tag="span" LinkTo="Image" LinkRel="lightbox[Gallery]">
  6. Here is the full code listing from the Gallery page to prove to you that there really was nothing else I had to do.
    <%@ Page Language="C#" MasterPageFile="../common/BW.master" AutoEventWireup="true"
        Inherits="CommunityServer.Controls.CSThemePage" %>
    
    <%@ Import Namespace="CommunityServer.Components" %>
    <%@ Register TagPrefix="MW" TagName="CSBadge" Src="~/Themes/BW/Common/CSBadge.ascx" %>
    <%@ Register TagPrefix="MW" TagName="RecentImagesSB" Src="~/Themes/BW/Common/RecentImages.ascx" %>
    <%@ Register TagPrefix="MW" TagName="TechnovangelistLogin" Src="~/Themes/BW/Common/TechnovangelistLogin.ascx" %>
    <%@ Register TagPrefix="MW" TagName="Subscriptions" Src="~/Themes/BW/Common/Subscriptions.ascx" %>
    <%@ Register TagPrefix="MW" TagName="PhotoTagCloud" Src="~/Themes/BW/Common/PhotoTagCloud.ascx" %>
    
    <asp:Content ID="Content1" ContentPlaceHolderID="PageHead" runat="Server">
    asp:Content>
    <asp:Content ID="Content2" ContentPlaceHolderID="SiteContent" runat="Server">
        <CSControl:Script ID="Script1" runat="server" Src="~/Themes/BW/Common/mootools.v1.11.js" />
        <CSControl:Script ID="Script2" runat="server" Src="~/Themes/BW/Common/slimbox.js" />
    
            <CSGallery:GalleryData Property="Name" LinkTo="ViewGallery" runat="server" CssClass="ContentTitle" />
            <div class="CommonContent">
                <CSGallery:GalleryData ID="GalleryData2" Property="Description" runat="server" />
                <div class="ClearLeft">
                     div>
            div>
            <div class="CommonContent">
                <h3 class="ContentHeader">
                    Photosh3>
                <CSGallery:GalleryPostList runat="server">
                    <QueryOverrides runat=server PagerID="Pager" PageSize="45" />
                    <ItemTemplate>
                        <CSGallery:GalleryPostData runat="server" Tag="span" LinkTo="Image" LinkRel="lightbox[Gallery]">
                            <ContentTemplate>
                                <CSGallery:GalleryPostImage ImageType="thumbnail" Height="100"
                                    Quality="60" runat="server" />
                            ContentTemplate>
                        CSGallery:GalleryPostData>
                    ItemTemplate>
                    <NoneTemplate>
                        <div class="CommonBodyContent">
                            <CSControl:ResourceControl runat="server" ResourceName="ForumMembers_NoRecords"
                                Tag="Em" />
                        div>
                    NoneTemplate>
                CSGallery:GalleryPostList>
                <div class="PictureListControls">
                    <div class="PictureListPager">
                        <CSControl:Pager ID="Pager" runat="server" ShowTotalSummary="true" />
                    div>
                div>
            div>
    asp:Content>
    

Setting this up was so incredibly simple. I was shocked at how easy it was. And I think the effect is fantastic. Do you like this or would you prefer the standard static gallery view???

Click to read more ...

Monday
Jun182007

How To: Redirecting in Community Server 2007

When I updated this site to CS2007, some of the pages broke. The two problems came up due to older configurations of this site. First, in a very old config, I had my RSS feed at /RSS/default.aspx. The other problem came about because I used to have the blog page at the root and now I have gone back to the default way of doing things. So let me show you how I solved both issues.

First the part about redirecting RSS/default.aspx. Actually this has nothing to do with Community Server. I simply went to my hosting provider's control panel site and created a virtual directory that pointed to the RSS feed, which is now hosted at Feedburner.

OK, that was easy. Redirecting the requests to blog pages at the root to now go to /blogs/technovangelist was a bit tougher. There were actually two main problems here: How do I redirect rss.aspx in the root of the site, and how do I redirect all of the calls to the archive blog postings that were under /archive to /blogs/technovangelist/archive.

The first of those two happened to be fairly easy. I simply created an rss.aspx page in the root with the following content:

Actually, it was a bit simpler than that. The actual process was search the net to find Hansleman's entry on solving the problem.

To solve the problem with the archive pages I got to take advantage of the new SiteUrls_Override.config file. Here is my config file with some of the non relevant lines removed:

xml version="1.0" encoding="utf-8" ?>
<Overrides>
  <Override xpath="/SiteUrls/locations" mode="add">
    <location name="OldBlogPathRedirect" themeDir="blogs"  path="/" 
type="CommunityServer.Blogs.Components.BlogLocation, CommunityServer.Blogs" > <url name = "Oldweblogday"
path="/blogs/technovangelist/archive/{1}/{2}/{3}.aspx"
pattern="archive/(\d{4})/(\d{1,2})/(\d{1,2})\.aspx"
physicalPath="##blogthemeDir##"
vanity="{2}?App=${{app}}&y=$1&m=$2&d=$3" page="postlist.aspx" /> <url name = "Oldweblogmonth"
path="/blogs/technovangelist/archive/{1}/{2}.aspx"
pattern="archive/(\d{4})/(\d{1,2})\.aspx"
physicalPath="##blogthemeDir##"
vanity="{2}?App=${{app}}&y=$1&m=$2&d=1" page="postlist.aspx" /> <url name = "OldweblogpostId"
path="/blogs/technovangelist/archive/{1}/{2}/{3}/{4}.aspx"
pattern="archive/(\d{4})/(\d{1,2})/(\d{1,2})/(\d+)\.aspx"
physicalPath="##blogthemeDir##"
vanity="{2}?App=${{app}}&y=$1&m=$2&d=$3&PostID=$4"
page="post.aspx" /> <url name = "OldweblogpostName"
path="/blogs/technovangelist/archive/{1}/{2}/{3}/{4}.aspx"
pattern="archive/(\d{4})/(\d{1,2})/(\d{1,2})/([a-zA-Z0-9\-\._]*?)\.aspx"
physicalPath="##blogthemeDir##"
vanity="{2}?App=Technovangelist&y=$1&m=$2&d=$3&PostName=$4"
page="post.aspx" /> <url name = "Oldweblogpostcategory"
path="/blogs/technovangelist/archive/category/{1}.aspx"
pattern="archive/category/(\d+)\.aspx"
physicalPath="##blogthemeDir##"
vanity="{2}?App=${{app}}&CT=BlogPost&CategoryID=$1"
page="postlist.aspx" /> <url name = "Oldweblogarticlecategory"
path="/blogs/technovangelist/articles/category/{1}.aspx"
pattern="articles/category/(\d+)\.aspx"
physicalPath="##blogthemeDir##"
vanity="{2}?App=${{app}}&CT=BlogArticle&CategoryID=$1"
page="postlist.aspx" /> </location> </Override> </Overrides>

As you you may be able to see, I just copied the lines from the SiteUrls.config file, then modified a few of the values. I am sure there is a more elegant way to do it, but this worked. If you know of a better way to handle this, please let me know.

Click to read more ...

Monday
Jun182007

AJAX Login for CS2007

I just found one of the cooler login controls I have ever seen. Go check out Kyle Beyer's website and click on the sign-in button at the top left. Very slick. He even covers it in detail in a blog post at http://daptivate.com/archive/2007/02/26/ajax-login-compatibility-with-asp-net-authentication.aspx. I definitely have to try that. Of course, I just blew an entire weekend working on this site and the idea of spending more time on it makes me feel a bit guilty.

Click to read more ...

Sunday
Jun172007

How To: Add a Caching Twitter Badge to CS2007

A few months ago I started playing around with Twitter. In case you are not familiar with it, Twitter is a blog of short messages. Think about all the times you want to say something, or tell people where you are, but you don't think the idea warrants an entire blog post. Those are the times you use Twitter. People can subscribe to your Twitter status and get those updates where ever they are. They can get them via IM, or SMS, or some 3rd party applications. Since I live in Europe, I subscribe to a few people's status via SMS (SMS in Europe is cheap, unlike the US where no one uses it since it is so expensive).

When I was thinking about what I wanted on this website, I thought some sort of Twitter status update would be cool. Not an entire history, just the latest item. If you go to Twitter's badge page, you can see a number of options for grabbing your current Twitter status. I tried the Javascript one first. It seemed to work, but I was annoyed at the fact that it polled Twitter on every refresh. Chances are I am not going to update my Twitter status that often, so I want to cache the information locally. But I don't want to cache the entire block of text, because the time since I made the update will change as time goes by. So I wanted to cache just the text and the time I made the update.

First I check the CSCache for my Twitter information:

string TwitterText = CSCache.Get("TwitterText") as string;
string TwitterTime = CSCache.Get("TwitterTime") as string;

If TwitterTime is null, then I grab the latest one status item from Twitter:

string TwitterPath = 
"http://www.twitter.com/statuses/user_timeline/2449901.xml?count=1";
WebClient webClient = new WebClient();
string TwitterXML = Encoding.ASCII.GetString(wClient.DownloadData(TwitterPath));

Once I have the TwitterXML string, I can parse it to get my text and time and I add that to the cache. From there I just converted the Javascript relative time code from Twitter's code to C#. Here is the full TwitterStatus.ascx:

<%@ Import namespace="System.Xml.XPath"%>
<%@ Import namespace="System.Net"%>
<%@ Import namespace="CommunityServer.Components"%>
<%@ Control Language="C#" ClassName="TwitterStatus" %>
<script runat=server language="C#">

protected int cacheTime;
public int CacheTime
{
get
{
return cacheTime;
}
set
{
cacheTime = value;
}
}
protected override void OnInit(EventArgs e)
{
string TwitterPath =
"http://www.twitter.com/statuses/user_timeline/2449901.xml?count=1";
string TwitterText = CSCache.Get(
"TwitterText") as string;
string TwitterTime = CSCache.Get(
"TwitterTime") as string;
string TwitterString =
"";

if (string.IsNullOrEmpty(TwitterTime))
{
string returnXML =
"";
WebClient wClient = new WebClient();
returnXML = Encoding.ASCII.GetString(wClient.DownloadData(TwitterPath));

XPathDocument doc = new XPathDocument(new System.IO.StringReader(returnXML));
XPathNavigator nav = doc.CreateNavigator();

//Get Twitter Time
XPathNodeIterator nodes = nav.Select(@
"/statuses/status/created_at");
nodes.MoveNext();
TwitterTime = nodes.Current.Value;
//Get Twitter Text
nodes = nav.Select(@
"/statuses/status/text");
nodes.MoveNext();
TwitterText = nodes.Current.Value;
string[] TimeValues = TwitterTime.Split(' ');
TwitterTime = TimeValues[1] +
" " + TimeValues[2]
+ " " + TimeValues[5] + " " + TimeValues[3] + " GMT";

CSCache.Insert(
"TwitterText",TwitterText, cacheTime);
CSCache.Insert(
"TwitterTime", TwitterTime, cacheTime);
}

DateTime twDate = Convert.ToDateTime(TwitterTime);

int secondsago = Convert.ToInt32(DateTime.Now.Subtract(twDate).TotalSeconds);
if (secondsago < 60)
TwitterString = "less than a minute ago";
else if (secondsago < 120)
TwitterString = "about a minute ago";
else if (secondsago < (45 * 60))
TwitterString = (secondsago / 60).ToString() + " minutes ago";
else if (secondsago < (90 * 60))
TwitterString = "about an hour ago";
else if (secondsago < (24 * 60 * 60))
TwitterString = "about " + (secondsago / 3600).ToString() + " hours ago";
else if (secondsago < (48 * 60 * 60))
TwitterString = "1 day ago";
else
TwitterString = (secondsago / 86400).ToString() +
" days ago";

TwitterTextLiteral.Text = TwitterText;
TwitterSinceLiteral.Text = TwitterString;

base.OnInit(e);
}

</script>

<div id=TwitterStatus>
According to <a href="http://twitter.com/Technovangelist" target=_blank>Twitter</a>
I am: <asp:Literal ID=TwitterTextLiteral runat=server></asp:Literal> as of
<asp:Literal ID=TwitterSinceLiteral runat=server></asp:Literal>
</div>

To use this I add the following at the top of my aspx page:

<%@ Register TagPrefix=MW TagName=TwitterStatus src="~/Themes/BW/Common/TwitterStatus.ascx" %>

And then add this where I want the TwitterStatus to go:

<MW:TwitterStatus runat=server CacheTime=300 />
This seems to be working for me for now. Let me know if you end up using it on your site. Or if you have any suggestions on improving this, I would love to hear that too.

Click to read more ...

Saturday
Jun162007

How To: Add Blog Posts to the CSCache with CS2007

After adding my new CS2007 theme, I wanted to take advantage of caching for my home page. There was some sample code in the default theme, but it wasn't exactly what I needed:

protected override void OnInit(EventArgs e)
{
List recentPosts = CSCache.Get("HomePageSearch-"
+ CurrentCSContext.User.RoleKey) as List;
if (recentPosts == null)
{
SearchQuery query = new SearchQuery();
query.StartDate = DateTime.Now.AddDays(-10);
query.EndDate = DateTime.Now.AddDays(1);
query.PageSize = 5;

recentPosts = CSSearch.Search(query).Posts;
CSCache.Insert("HomePageSearch-"
+ CurrentCSContext.User.RoleKey, recentPosts, CSCache.MinuteFactor * 5);
}
RecentPostList.DataSource = recentPosts;

base.OnInit(e);
}

This does a query for all additions to the site in the last 10 days, including images, comments, etc. But I wanted just blog entries. Looking around on the web, I found a discussion on this in the CS Forums. Using this as guide, I got exactly what I needed:

protected override void OnInit(EventArgs e)
{
int NumberOfBlogPosts = 3;
List recentPosts = CSCache.Get("HomePageBlog-"
+ CurrentCSContext.User.RoleKey) as List;

if (recentPosts == null)
{
List FilteredPosts = new List();
SearchQuery query = new SearchQuery();
query.PageSize = 20;
query.ApplicationTypes = new ApplicationType[] { ApplicationType.Weblog };
recentPosts = CSSearch.Search(query).Posts;

int PostNumber = 0;
foreach (IndexPost post in recentPosts)
if (PostNumber < NumberOfBlogPosts)
if (!post.Title.StartsWith("re:", true, null))
{
FilteredPosts.Add(post);
PostNumber++;
}


CSCache.Insert("HomePageBlog-"
+ CurrentCSContext.User.RoleKey, FilteredPosts, 300);
recentPosts = FilteredPosts;
}
RecentPostList.DataSource = recentPosts;
base.OnInit(e);
}

This finds 20 weblog posts, filters out everything that starts with "Re:", adds it to the cache, and uses it as a datasource for the blogpost list.

Click to read more ...

Saturday
Jun162007

Implementing a New CS2007 Theme

A few months ago, the amazing folks at Telligent updated their Community Server product to Community Server 2007. I updated this site to CS2007 a few weeks back and around that time I decided I should get rid of the standard paperclip theme and write my own. And of course, because I enjoy inflicting excess pain onto myself, I started from scratch. There is a lot that can be done with CS2007 and I know that even more now that I am going through this exercise. I have started using the new theme live on this site, but I am far from finished. Many of the features are working, but not looking great.

Well, the upside is that I have one more topic now to blog about. The documentation for creating themes (most of it seems to be written by Ben Tiedt)

Click to read more ...