Thursday, July 31, 2008

Related Products

I added some support for "Related Products". If you select Dark Knight, for example, the product page will also show you Batman Begins.

This involved three new tables:
- ProductGroupType
- ProductGroup
- ProductGroupMembers

Two new views:
- vwProductGroups
- vwProductGroupMembers

Stored Procedure:
- GetRelatedProducts

I added a new object data source to the page, and a gridview. The ODS calls a method that calls the stored procedure and returns a datatable (keep it simple).

Currently, it just lists them with links. That will improve. Also, a product may be associated to multiple groups. The page will have to improve to show the different groups. For now, they're all just merged into one distinct list.

I'm not going to make an effort to backfill groups. But, as new movies come up, I'll create new groups as appropriate. So far, there are groups for Stargate, Lost Boys, Hellboy, and Batman.

Wednesday, July 30, 2008

MVC Preview 4

Preview 4 came out yesterdayish (at least, that's when I first heard of it). I haven't updated my test site yet, but will. Tonight I got hung up writing some queries for my side-venture. I have another task to do for them, then I'll be back for MVC Preview 4, and new work on the DvdFriend site. (Next up, I think, is product level comments. Lets rants and speculate about movies and DVDs without actually seeing them!)

Monday, July 28, 2008

Added News and Feed

I added a NEWS section to the top of the main page. It will really be more than news, though... more like notices, perhaps.

The News support was already in there. It was displayed on the page somewhere about a year ago, but it didn't look good or wasn't useful, so I got rid of it.

Fortunately, the method to retrieve it is still there. Basically, I just call GetRecentEntries(5, "news", true).
Last 5 entries; news zone; get the entire text rather than just the title.

There's a news feed too, but its going to need work. The title of a blog entry may contain HTML (ie: the link to dark city), but a syndication title may not. I have to change the entry page to distinguish between the title and what the title links to (if anything). In the meantime, it tears the html out of the title if there is any. Then, for the permalink, it extracts the link. If the link it exists, it uses it. Otherwise, it just links to the main page.

Sunday, July 27, 2008

Web delay fixed

The production site, since moving to GoDaddy, has always taken a few seconds to load on the first hit. The problem is that the first hits are very frequent. Almost every time I go to the site, its a first hit.

It never really bothered me much because there are only a few visitors a day, but now that the embedding is enabled, its more intrusive. The hamletcode blog would pause as it waited for the dvdfriend site to fire up to serve the demo embedded review.

I poked around in the pool settings and found that the worker process was set to shut down after 5 minutes of being idle. I disabled that. I also saw that it was set to recycle ever 29 hours, so got rid of that. There's really no reason for the worker process to need to be recycled, but this will be a good test to make sure.

Since I was in there anyway, I changed the session timeout from 20 to 60 minutes. Effectively, the timeout was 5 minutes anyway since there's rarely more than one person writing a review at any given time.

This should resolve all the timing issues. Also, it will help coverup a bug with the review page; If session times out while you're writing the review, you lose it. That's definitely very bogus and I have no reason to avoid fixing it other than a complete lack of interest. In fact, that's the last thing that's stopping me from removing the "under construction" label.


In conclusion

Reviews can now be embedded

I was going to make this feature available to only people that are logged in, but then realized that it probably doesn't make sense to limit my exposure. Embed away!




The code goodies follow. ReviewHtml.GetReview() simply builds a bunch of javacript document.write() methods.

[ServiceContract]
public interface IContentService
{
[OperationContract]
[WebGet(UriTemplate = "review/{reviewId}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat=WebMessageFormat.Xml)]
Stream GetReview(string reviewId);
}

public class ContentService : IContentService
{
public Stream GetReview(string reviewId)
{
return new MemoryStream(Encoding.UTF8.GetBytes(ReviewHtml.GetReview(new Guid(reviewId))));
}
}

Here are some interesting things I learned:
- I had to return the text as a stream rather than as text. If you return it as just a string, then there's always sometype of serialization wrapper around it. Additionaly, the generated html tags get encoded. So, we get <td> instead .

- In the UriTemplate, you specify the parameters.

UriTemplate = "review/{reviewId}"

Its maps the value in {reviewId} to the reviewId parameter of the method. That's cool; very MVCish. Unlike MVC, however, it must be a string. In this case, the ID is a guid, but the mthod must accept a string.

I find this dissapointing and, if I had to guess, I'd say that will change. The framework is certainly capable of converting known types for us.


I really just sort of tripped through the WCF stuff in this case. I have to read up on the new REST capabilities.

Wednesday, July 23, 2008

Good bye faithful printer.... Good bye!

I have mixed feelings about the passing of my printer.

Well, see... there. I've lied already. In the very first sentence I'm lying like a president. The printer isn't so much "passing" as it being put down.

I have a Lexmark X125 all in one printer. It is AT LEAST 5 years old; possibly even more than 6, but I have definite milestones to peg it at least 5.

Its a cool little printer that I got for cheap and has lasted, to some extent, all this time. It still works. In fact, shortly after I bought it, I bought one for my parents too. (Coincidentally, last night I received a call about a new printer they bought. I believe that they too were still using their X125 until recently, but unconfirmed.)

So why the mixed feelings? On one hand, I bought a cheap printer and used it for 5 years. On the other hand, it annoyed me every time.

Here's the thing: the drivers suck. They always have. Lexmark support was 0 help... I gave up years ago. The printer will work for a while, then you have to kill some processes in order to get it to print again. I exchanged many emails with them just trying to get them to come clean and say "sorry, we suck", but they wouldn't.

The next logical question may be, "How did you put up with that for 5 years, you poor poor soul!?". The answer is simple: I don't do a heck of a lot of printing. I bought a box of paper years ago and I still have most of the reems (spelling?). I just don't have a need or a desire to print. If I want to read something from the computer, I just read it on the computer. It doesn't have to be paper. I keep all our digital images as digital images... I don't print them.

Lately, I've had a need to print some stuff, for expense reports, on a monthly basis. My luck has been limited. I occasionaly go to the office with the intent of printing the stuff, but then it slips my mind and I leave empty handed. Ick. I also have to fax my receipts, an the X125 was being difficult. It would often say "replace cartridge" even though it was a new cartridge. After multiple attempts, I thought it was dead. Then, it finally worked.

So now, at long last, we're at the point where it is essentially unusable. I want to be one of the cool kids that simply clicks "print" and the thing prints. Is that too much to ask? Am I being a snob by not wanting to fight to print for 5 minutes a page? I don't think so, but I value your opinion, so let me know.

Due to the fact that I work from home, my esteemed employer found it in its heart (and wallet) to equip me with a brand new HP5610. I plugged it in and clicked print. Guess what... it printed! Now I feel like a king. Rejoice!

I'll cart the ole X125 up to the recycling center on my next trip. When I dump it in the box, perhaps I will pause for just a moment and reflect on the times gone by, both good and bad, but I make no promises.

Coming Soon - Embedded Reviews

Chris has stated his desire to be able to embed his DvdFriend reviews elsewhere in the internet galaxy. Interesting idea. I haven't done that before. (For those of you just joining us, I'm not real big on building web pages. There are a lot of things I haven't done.)

Anyway, I started looking into this. The first thing that jumped to mind was left over from 1995: Add an iframe. The second thing to jump to mind was: don't be ridiculous.

I started searching on the current swell ways to do this. The goal, per normal, is keep it simple. I just want the user to drop a little piece of something on their page and have it work. I came across XSS pretty quick, then avoided it since XSS is often associated with bad mojo due to attacks. I looked at the object
tag... Couldn't get it to work with external web pages.

Eventually, I ended up back to XSS.



Created A Page called ReviewScript.aspx
I wiped out everything from the ASPX except for the server tags at the top.

The PageLoad calls Response.Clear(). It then builds a big piece of javascript that, basically, generates some html then writes it to the document.



Add a script tag to the page that references ReviewScript.aspx
Every time I paste any type of tag into this stupid thing, it loses it. And I'm currently too lazy to deal with a screen shot. So, mentally fill in angle brackets.


script language='javascript' type='text/javascript' src='http://www.dvdfriend.us/ReviewScript.aspx?id=xxx'
/script


Sweet. I started by including that on the dvdfriend main page (dev version) so that I can compare whats generated to what shows up on the page.


Templates / Make it look as it does on the main page

The generated html is based on a template. The default template is going to look exactly like a rewiew does on the main page. I started by embedding the script on the main page so that I could look at them next to each other. Once it was close, I moved it to another site altogether.



Create a new CSS

As soon as I imported the dvdfriend.css to the other site, it messed up the entire page. That was expected. I created a new css called external.css and copied over only the styles i needed. I renamed them all with a prefix of DF, just to keep them separated.


Incidentally, the css is included by document.writing a link tag.


That pretty much did it.

Template - So Far

The template has these tokens so far:
DvdFriendCss
Rating
Title
ProductTypeImage
ProductName
ProductId
CreateDate
Author - pending. Have to populate this
RatingClass
AuthorLink
ProductLink
ReadLink



The list will grow. Most of them are just pieces of data so that you can build it anyway you want. Some of them are more generic to give you something to start with. the LINK tokens, for example, automatically create the links as you see them on the home page now.



TODO

- See if there is a better way to include the CSS. If there are multiple embeds on the same page, it will import the css multiple times. Would rather do it through javascript.


- Retrieve the author. The page is built from a datatable. The script is built from a blog object which, mysteriously, doesn't already have an AUTHOR property exposed.


- Work out some additional css issues. It almost looks like it does on the site, but I still have some font issues to resolve.


- Test in production environment. I've only used it on my local machine. Lets see if it actually works out there. Furthermore, lets see what types of things, if any, prevent the xss from firing.


- LATER: Allow for users to create their own templates using the available tokens. I think that every template will be available to every user, but it can only be editted by the person who created it. We'll see.

- Brush my teeth and go to bed

- Server it as a WCF service rather than an aspx page. The aspx is a quick and dirty just to get it going. I will convert it to a WCF service much like the RSS feed. The one missing piece of info there is how to pass parameters. It shouldn't be a big deal; I just have to look into it.

Screenshot

Here's what it looks like embedded on the now neglected Clan Friend site.

The background of the review is always white. Its not inheriting it from the parent element.

Sunday, July 20, 2008

First RSS Feed is live

I deployed the first DvdFriend RSS feed. I kept it as a WCF service afterall. I'll keep it that way until i come up with a reason not to.

The deployment wasn't painless. It didn't work in production as it did in development. It was because there are multiple sites on the server that are distinguished by host headers. Ilearned in 3.0, you had to code around it. 3.5 makes it a little easier. I found this link:

http://blogs.msdn.com/rampo/archive/2008/02/11/how-can-wcf-support-multiple-iis-binding-specified-per-site.aspx

I added http://www.dvdfriend.us as a prefix. I attempted to add http://dvdfriend.us as a second prefix, but it went back to the orignal error. Go figure. I don't havea good grasp on this stuff yet, but its working, so we're ok for now

I think COMMENTS will be the next feed. I have to create a page for that anyway, so they'll have the same data source.

RSS 2 / Atom 1 Feeds

.NET 3.5 adds a WebHttp WCF binding and Synidication support. I haven't tried anything with syndication yet. Now seemed like a good time.

The DvdFriend home page shows the most recent 50 ratings/reviews. I've received more than one request to make that, and comments, available as feeds. I started with the recent activity.


Setting it up as a WCF service was pretty easy, though I'll probably end up dropping it. I'll probably just expose it as SYNDICATION.aspx or something. We'll see. Regardless, doing it as a WCF service was a good experience.

Once I had a test feed going, it was time to populate it with the actual data. I thought LINQ would be the way to go. I already have a static method that returns a list of the recent reviews as a data table. I thought I'd just write a linq query against that data table. No dice. LINQ doesn't work on data tables. Swell. (Well, not really).

I wrote a generic wrapper class to make things enumerable for linq. (Is this the best solution? I don't know. But it works.)



NOTE: Changed this code. See notes at the bottom. (Changed GetEnumerator to return _items.GetEnumerator rather than yield through it itself)

Next, I had to write a Linq query that would return a List. In the process, I had to explore some of the properties and methods to see what was what. I set the basic stuff, but there were 2 things that I wanted to set, but couldn't during initialization:

- The author. SyndicationItem.Authors is a List(), so you don't initialize it. You have to add to the existing list.

- The item link. At first, I figured this would be BASEURI, but it wasn't. You have to call item.AddPermaLink(new Uri("...")). Its a method; can't call it during initialization.

For author, I could loop through all the items after the initial query and update the individual items from the datatable. But, not interested. That didn't sound like a very good solution. I only want to hit the datatable once and be done with it. PermLink would require a loop too, but that's based on ID which is already a property of SyndicationItem, so no problem there.

I solved the author issue by creating a subclass of SynidcationItem called AllardWorksSyndicationItem. I added a property called AUTHOR, so that I can save the information during load. Then, I can loop through and update the AUTHORS collection without having to bother the data table.



The Linq query now returns a List. After the query, I loop through the items and create the permalink (based on the ID, which I already have), and I add the value of the new AUTHOR property to the existing AUTHORS property.




But wait. There's more. The SyndicationFeed object has a property called Items. You set it to a IEnumerable of SyndicationObject, not IEnumerable of AllardWorksSyndicationItem. The list needs to be converted. I achieved this by another Linq query which does the cast


Then, you return a formatter for the type of feed (I chose atom), and that's a wrap for tonight.

Final Code


Rendered in IE


Coming soon!
UPDATES
I revisited some of this.
I realized that LinqWrapper.GetEnumerator was silly, because DataRowCollection is already IEnumerable. All you have to do it
return (IEnumerator)_items.GetEnumerator();
rather than yield through it yourself.
Then I was bothered by the fact that DataRowCollection is already IEnumerable, so why do I need this stupid wrapper class?
The reason is that DataRowCollection is IEnumerable. Linq requires IEnumerable, so the wrapper converts it. I tried finding other

Monday, July 14, 2008

Email Problems

Greetings

People on my mail server haven't received any email since 7/10/2008. That's a problem. I took a look at the server, and as far as I can see, its ok. I put in a request with support.

If you'd like, you can contact me at hamletcode@gmail.com in the meantime.

Monday, July 7, 2008

Prologue Complete

The final tally for the prologue draft is just over 9 pages. Chapter 1, so far, is about a third of a page. I expect that to get larger, even if I have to resort to increasing the font size.

Sunday, July 6, 2008

Lack of direction

I'm spinning my wheels trying to find something good to do. I watched a web cast on Dynamic Data, which was great. I'm going to start reading up on Ado.Net entitites. But, I really can't come up with a project that I'm interested enough in to really pursue.

I keep thinking about a pubsub project, but haven't been able to comit. I've worked on the DVD site here and there, but nothing ground breaking. I'm lacking a sense of purpose.

Lacking anything useful to do, I've spent some time trying to write a sci-fi story. I used to write a lot more when I was younger. Now, I usually find it more rewarding to code. Since that hasn't been working out lately, I'll give writing another shot.

I've been thinking about this story for quite a while. So far, its called Savior, but I just made that up a minute ago and it probably won't stick. The prologue is mostly complete, and I should start on Chapter 1 tomorrow. It'll end up being a pretty short story; A lot of stories take 5 pages to say hello. In mine, I just say "hello". I don't know enough words to drag it out much longer than that. It certainly won't be novel length.