Lessening restrictions for trusted authors

When userstyles.org started, I had to keep in mind the various ways people could use the system in ways I hadn’t intended. People might create spam entries, use the site to host images, post things that aren’t CSS, etc. I wrote code to put restrictions on styles so that I wouldn’t have to deal with them.

Unfortunately, some of these restrictions have negative consequences for good intentioned style authors. For example, authors weren’t allowed to link to their home pages or a gallery of screenshots because of the restriction meant to prevent spam.

I’m going to start loosening these restrictions. As a first step, I’ve added the ability for styles to be up to 200KB (instead of 64KB). Authors who want really long styles can contact me and I’ll consider the requests on a case by case basis. The first style to be allowed the higher limit is Gmail Redesigned (by Globex Designs). Some of the criteria as to whether I’ll allow the higher limit:

  • Is the style predominantly CSS as opposed to large data URI images?
  • Could the style be modified to fit within the limit, such as by removing large comments?
  • Is the style popular enough to make it worth it?

I’m going to remove some other restrictions for everyone (subject to some objective criteria like install counts). Some ideas I have:

  • Link to any site rather than just those whitelisted.
  • Larger (in code size and in display size) images.
  • Add information such as home page and contact info to the user page.

Hopefully these changes will give legitimate authors more freedom while still stopping spammers and the like. If you have further ideas, please make your suggestion in this blog post or on the forum.

Make Google Earth Hour permanent

Google turned black today day because of Earth Hour. If you’d like to make this change permanent, you can use Stylish with Google Web Search - dark gray redesign (vC) or any of the other dark Google styles.

In fact, when I first saw the black Google page, I thought it was a bug in Stylish where it installed one of those styles by itself!

Stylish is top 3 awesomest for Songbird

Stylish has won a spot in the top three extensions ported to Songbird. I’m not too sure about the criteria for being in the top 3 other than “awesome”. Nice to be recognized, in any case. I give my thanks back to Stephen Lau of Songbird for the help.

In case you’re wondering, the style in the write-up is Google Web Search - dark gray redesign (vC), and yes, it does look really good with matching feather.

Stylish 0.5.5 released

Available here.

0.5.5 welcomes back Russian users, fixes an issue with running Songbird with different “feathers”, added compatibility with eMusic Remote and Firefox 3 beta 3.

0.5.4, which got lost on addons.mozilla.org somehow, added Songbird and Firefox 3 beta 2 support and the zh-CN locale.

Plans for Stylish 1.0

The next version of Stylish is under development and will be Toolkit 1.9 (Firefox 3) only. Major goals of this release include:

  • Taking advantage of updates to the Mozilla platform. This includes functional additions, performance improvements, and code reductions.
  • Injection of Stylish UI into logical places in the existing Firefox UI, rather than throwing new UI elements where they don’t really make sense.
  • UI changes in accordance to various HIGs and user expectations.
  • Drop underused features added in previous versions.

I think with these changes, I’ll drop the beta-esque version number and jump ahead to 1.0. :) Once the development reaches a usable state, alphas will be posted.

The Stylish 0.5 series will continue with maxVersion updates and any compatibility fixes needed to maintain the same functionality. No new features will be added to this series. In addition, userstyles.org will continue to support integration with this series.

Giving back to style authors

As you may have noticed, or may not have if you have some sensible styles, there are text ads all over the site. These ads pay fairly well (especially considering I don’t have to do any work for the money), so I thought I might give back to those who have the site so successful, the style authors.

I wouldn’t know how to pick someone, so I was thinking of making it random. Maybe choose randomly from all the styles, weighted by number of installs over some time period. The author of the style would get the cash.

Would you guys be interested in something like this? Is there a better way of doing this? Discuss in the forum.

Update - Dec 15, 2007

The authors have spoken - they say I should keep the money myself.

Rails, bandwidth, and HTTP content encoding

After moving from Dreamhost to HostingRails I noticed my bandwidth had gone up. On Dreamhost, I was averaging 40GB per month, and on HostingRails, I was averaging 60GB/month. This was a problem because I had bought the 50GB per month plan on HostingRails, and had to either pay extra or let the site go down for the last few days of the month.

I had a few theories as to why my bandwidth had gone up.

  • I had used up a lot of bandwidth moving the data to the new site. The second month’s bandwidth was also high, so that wasn’t it.
  • The more reliable server meant more people were using the site. After checking the stats, this wasn’t the case.
  • Someone was repeatedly pulling some feeds that had no size limit. These feeds could get up to 1MB in size. After adding a limit to the size of these feeds, the problems continued.

I started looking at using HTTP content encoding to get my bandwidth down. The browser tells the server what kinds of encoding it understands, the server responds by encoding (compressing) the content in a suitable format and sending it to the browser. Net result: less bandwidth, more processing for the server and client.

Anyway, I realized that Dreamhost does have HTTP encoding by default, and HostingRails doesn’t. After a quick search through the HostingRails forum, I found that they indeed don’t have it on, but they also don’t grant requests to have it turned on in Apache.

Having it turned on in Apache is ideal because then it’ll affect static content as well, but there are ways to do it in Rails (and probably other languages, but I only care about Rails). Courtesy Tom Fakes, here are short and sweet instructions.

  1. Pick a page on your site and use Live HTTP Headers to see the size of the downloaded file. Look for Content-Length in the response; this is how many bytes had to be transferred across the network to download the page. While you’re there, check for Content-Encoding. If you see it, congratulations (or condolences), you’re already compressing your output.
  2. Put compress.rb in your app’s lib directory.
  3. In app/controllers/applications.rb, add require 'compress.rb' at the start and after_filter OutputCompressionFilter after the start of the class. See update below.
  4. Restart Rails, and check Content-Length of the page again.

Result for me: 1/3 lower bandwidth.

Update, Nov 17 2007: I discovered that turning on compression in ApplicationController causes problems if you’re using page caching. The compression filter happens before the caching filter, so the compressed form gets cached. When the page is retrieved from cache, it doesn’t get sent with the proper headers. The solution to this is to add the after_filter line to each individual controller after the caches_page lines you use. This will make the caching happen first.

PHP includes in Rails

This is how to include a PHP script inside a Rails template. Got this useful tidbit from Rails Forum. The equivalent of:

<?php include("path/to/menu.php"); ?>

is

<%=`php path/to/menu.php`%>
  • Those are back quotes, otherwise known as a capital tilde.
  • You’ll obviously need PHP installed on your server for this to work.

Stylish in PC World magazine

Stylish has made the pages of PC World Magazine! Not just PC World Magazine Online, but the actual magazine you can go and buy. It’s one of Eight Great, Simple Ways to Hack the Web. It even includes a screenshot of Valacar’s Google Web Search - dark blue redesign (vC). Nice thing to include in the ol’ scrapbook of my life.

Modifying Vanilla to include additional data on threads

Since I put in a feature to directly link to the forums directly from each style’s page, I’ve been finding that users assume that the forum will know which style they’re talking about. The two solutions to this are to convince the user otherwise or to make the assumption true. I’ve gone for the latter.

Now, whenever users click on the forum link on style pages and then actually create a discussion thread, the forum will store the ID of the style they came from and display it in various useful places. Here’s how I did it.

First, I created a new column in LUM_Discussions called “StyleID”. This is either 0 (meaning the discussion isn’t about a style) or the ID of an existing style. The styles table has an id field and a short_description field.

Here’s a quick overview of what the code changes do. The site includes a style parameter in the forum link. The forum takes this parameter and puts it in a hidden input field. When the user posts the new discussion, the forum reads this style ID and stores it with the other discussion data. When the forum displays that discussion, it’ll look up the style’s short description and display it along with the discussion title entered by the user.

Below are diffs with explanations as to why there was a change. A full diff is available at the bottom.

Vanilla stores all database table and column names in a configuration file. Here I added the names I used.

diff -r forum.old/appg/database.php forum/appg/database.php
150c150,157
< ?>
\ No newline at end of file
---
> 
> //userstyles.org customization
> $DatabaseTables['styles'] = 'styles';
> $DatabaseColumns['Discussion']['StyleID'] = 'StyleID';
> $DatabaseColumns['styles']['id'] = 'id';
> $DatabaseColumns['styles']['short_description'] = 'short_description';
> 
> ?>

Vanilla assumes that most tables start with “LUM_”. I had to tell it that my styles table didn’t have this prefix.

diff -r forum.old/library/Framework/Framework.Functions.php forum/library/Framework/Framework.Functions.php
519c519,520
< 	if ($Key == "User") {
---
> 	//userstyles.org customization
> 	if ($Key == "User" or $Key == "styles") {
817c818
< ?>
\ No newline at end of file
---
> ?>

I needed to tell Vanilla to pull in StyleID and StyleName (from short_description) when reading a discussion from the database.

diff -r forum.old/library/Vanilla/Vanilla.Class.DiscussionManager.php forum/library/Vanilla/Vanilla.Class.DiscussionManager.php
43c43,44
< 		$s->AddSelect(array('DiscussionID', 'FirstCommentID', 'AuthUserID', 'WhisperUserID', 'Active', 'Closed', 'Sticky', 'Sink', 'Name', 'DateCreated', 'LastUserID', 'DateLastActive', 'CountComments', 'CategoryID'), 't');
---
> 		//XXX userstyles.org customization
> 		$s->AddSelect(array('DiscussionID', 'FirstCommentID', 'AuthUserID', 'WhisperUserID', 'Active', 'Closed', 'Sticky', 'Sink', 'Name', 'DateCreated', 'LastUserID', 'DateLastActive', 'CountComments', 'CategoryID', 'StyleID'), 't');
51a53,56
> 
> 		//XXX userstyles.org customization
> 		$s->AddJoin('styles', 'st', 'id', 't', 'StyleID', 'left join');
> 		$s->AddSelect('short_description', 'st', 'StyleName');

Also when reading a list of discussions.

456a462,463
> 						//XXX userstyles.org customization
> 						$s->AddFieldNameValue('StyleID', $Discussion->StyleID);
522c529
< ?>
\ No newline at end of file
---
> ?>

I had to add the ID and name to the discussion class.

diff -r forum.old/library/Vanilla/Vanilla.Class.Discussion.php forum/library/Vanilla/Vanilla.Class.Discussion.php
44a45,47
> 	//XXX userstyles.org customization
> 	var $StyleID;
> 	var $StyleName;
77a81,83
> 		//XXX userstyles.org customization
> 		$this->StyleID = 0;
> 		$this->StyleName = 0;
104a111,113
> 		//XXX userstyles.org customization
> 		$this->StyleID = @$DataSet['StyleID'];
> 		$this->StyleName = @$DataSet['StyleName'];
179a189,190

When saving a style, I told it to read the StyleID parameter.


> 		//XXX userstyles.org customization
> 		$this->StyleID = ForceIncomingInt('StyleID', 0);
221c232
< ?>
\ No newline at end of file
---
> ?>

I included a link back to the style on the discussion page if there was a style for this discussion.

diff -r forum.old/themes/comments.php forum/themes/comments.php
21a22,25
>          //XXX userstyles.org customization
>          if ($this->Discussion->StyleID > 0) {
>             $CommentList .= '<a href="/styles/'.$this->Discussion->StyleID.'">'.htmlspecialchars($this->Discussion->StyleName).'</a>: ';
>          }
150c154
< ?>
\ No newline at end of file
---
> ?>

I got it to take the style parameter that the site appends to the forum link and put it in a hidden field called StyleID.

diff -r forum.old/themes/discussion_form.php forum/themes/discussion_form.php
8a9
>       .'<input type="hidden" name="StyleID" value="'.htmlspecialchars(array_key_exists('style', $_GET) ? $_GET['style'] : '').'"/>'  //XXX userstyles.org customization
73c74
< ?>
\ No newline at end of file
---
> ?>

I included the name of the style in the discussion list, if there is one.

diff -r forum.old/themes/discussion.php forum/themes/discussion.php
9a10
> //XXX userstyles.org customization
17c18
<          <span>'.$this->Context->GetDefinition('DiscussionTopic').'</span><a href="'.$UnreadUrl.'">'.$Discussion->Name.'</a>
---
>          <span>'.$this->Context->GetDefinition('DiscussionTopic').'</span><a href="'.$UnreadUrl.'">'.($Discussion->StyleID == 0 ? '' : htmlspecialchars($Discussion->StyleName).': ').$Discussion->Name.'</a>
53c54
< ?>
\ No newline at end of file
---
> ?>

Full diff:

diff -r forum.old/appg/database.php forum/appg/database.php
150c150,157
< ?>
\ No newline at end of file
---
> 
> //userstyles.org customization
> $DatabaseTables['styles'] = 'styles';
> $DatabaseColumns['Discussion']['StyleID'] = 'StyleID';
> $DatabaseColumns['styles']['id'] = 'id';
> $DatabaseColumns['styles']['short_description'] = 'short_description';
> 
> ?>
diff -r forum.old/library/Framework/Framework.Functions.php forum/library/Framework/Framework.Functions.php
519c519,520
< 	if ($Key == "User") {
---
> 	//userstyles.org customization
> 	if ($Key == "User" or $Key == "styles") {
817c818
< ?>
\ No newline at end of file
---
> ?>
diff -r forum.old/library/Vanilla/Vanilla.Class.DiscussionManager.php forum/library/Vanilla/Vanilla.Class.DiscussionManager.php
43c43,44
< 		$s->AddSelect(array('DiscussionID', 'FirstCommentID', 'AuthUserID', 'WhisperUserID', 'Active', 'Closed', 'Sticky', 'Sink', 'Name', 'DateCreated', 'LastUserID', 'DateLastActive', 'CountComments', 'CategoryID'), 't');
---
> 		//XXX userstyles.org customization
> 		$s->AddSelect(array('DiscussionID', 'FirstCommentID', 'AuthUserID', 'WhisperUserID', 'Active', 'Closed', 'Sticky', 'Sink', 'Name', 'DateCreated', 'LastUserID', 'DateLastActive', 'CountComments', 'CategoryID', 'StyleID'), 't');
51a53,56
> 
> 		//XXX userstyles.org customization
> 		$s->AddJoin('styles', 'st', 'id', 't', 'StyleID', 'left join');
> 		$s->AddSelect('short_description', 'st', 'StyleName');
456a462,463
> 						//XXX userstyles.org customization
> 						$s->AddFieldNameValue('StyleID', $Discussion->StyleID);
522c529
< ?>
\ No newline at end of file
---
> ?>
diff -r forum.old/library/Vanilla/Vanilla.Class.Discussion.php forum/library/Vanilla/Vanilla.Class.Discussion.php
44a45,47
> 	//XXX userstyles.org customization
> 	var $StyleID;
> 	var $StyleName;
77a81,83
> 		//XXX userstyles.org customization
> 		$this->StyleID = 0;
> 		$this->StyleName = 0;
104a111,113
> 		//XXX userstyles.org customization
> 		$this->StyleID = @$DataSet['StyleID'];
> 		$this->StyleName = @$DataSet['StyleName'];
179a189,190
> 		//XXX userstyles.org customization
> 		$this->StyleID = ForceIncomingInt('StyleID', 0);
221c232
< ?>
\ No newline at end of file
---
> ?>
diff -r forum.old/themes/comments.php forum/themes/comments.php
21a22,25
>          //XXX userstyles.org customization
>          if ($this->Discussion->StyleID > 0) {
>             $CommentList .= '<a href="/styles/'.$this->Discussion->StyleID.'">'.htmlspecialchars($this->Discussion->StyleName).'</a>: ';
>          }
150c154
< ?>
\ No newline at end of file
---
> ?>
diff -r forum.old/themes/discussion_form.php forum/themes/discussion_form.php
8a9
>       .'<input type="hidden" name="StyleID" value="'.htmlspecialchars(array_key_exists('style', $_GET) ? $_GET['style'] : '').'"/>'  //XXX userstyles.org customization
73c74
< ?>
\ No newline at end of file
---
> ?>
diff -r forum.old/themes/discussion.php forum/themes/discussion.php
9a10
> //XXX userstyles.org customization
17c18
<          <span>'.$this->Context->GetDefinition('DiscussionTopic').'</span><a href="'.$UnreadUrl.'">'.$Discussion->Name.'</a>
---
>          <span>'.$this->Context->GetDefinition('DiscussionTopic').'</span><a href="'.$UnreadUrl.'">'.($Discussion->StyleID == 0 ? '' : htmlspecialchars($Discussion->StyleName).': ').$Discussion->Name.'</a>
53c54
< ?>
\ No newline at end of file
---
> ?>
Adventures in development - Web standards and Firefox extensions