<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5600017894340887470</id><updated>2012-02-20T07:49:56.483-08:00</updated><category term='mobile'/><category term='mobila enheter'/><category term='webb'/><category term='statistik'/><category term='web'/><category term='C'/><category term='random'/><category term='drupal'/><category term='sverige'/><category term='handheld'/><category term='sweden'/><category term='statistics'/><category term='varnish'/><title type='text'>Insert Raw Code Here</title><subtitle type='html'>This is where I post notes on Drupal development, Php development, Varnish administration, quick fixes for annoying little stuff or just about anything that tickles my fancy.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-8479956793977421110</id><published>2012-02-18T07:32:00.002-08:00</published><updated>2012-02-18T07:32:42.753-08:00</updated><title type='text'>Site performance - analyze and improve</title><content type='html'>&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;i&gt;&lt;span style="font-size: small;"&gt;Looking back at the Drupal site I built two years ago for my current employer it's starting to look a little old and worn. Sure, we're running Varnish, we have purge rules, we have memcached for the backend and we've shut off basically all cpu hogs. But what about the visitors side - apart from getting a speedy response from our cache servers, there has to be more we can do? Well - load times, page rendering speed and total data size seem like good targets for improvement since they also provide (via Firebug, YSlow and NewRelic) great metrics.&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;b&gt;&lt;span style="font-size: x-large;"&gt;Record, improve, measure, profit?&lt;/span&gt;&lt;/b&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;We start initially by collecting metrics on the site usage so we can set goals for the improvements. We use a mix-and-match approach to this, utilizing tools like Yahoos YSlow, Pingdoms Pagetest, New Relic and Firebug. Mind you, we had to disable a few rules in YSlow since they're really not applicable for us - the CDN rules (hey, we don't have a CDN), ETags (we have them but they're default Apache - YSlow hates them), Expires headers (yeah we don't want to set them 3 years into the future) and DNS Lookups (Friggin external services are really DNS-expensive)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;The pages we're measuring typically show one full article, 3 banners (flash/jpg/whatever) and up to 150 teasers consisting of an image and up to 200 characters text. Insane amount of teasers.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;b&gt;&lt;span style="font-size: small;"&gt;Initial metrics &lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;Total load time: 8sec&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;Waiting for external services: 3-12 sec&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;Data size: 2.5mb &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;DOM Elements: 1377&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;YSlow score: D (67)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: x-large;"&gt;&lt;b&gt;Changes&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;1) GZIP GZIP GZIP&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;Apparently, somewhere in the past we had to turn the mod_deflate off and never brought it back online. Bad robot! We turned it back on and are now gzipping css, js, html and anything text-based which resulted in the total data size dropping to about 2.1 megs. Still a shitload of data though.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;2) Lazy-loading of images&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;Instead of loading 150+ images from the get-go, we only load what's visible in the visitors browser. Anything else is loaded when it scrolls into view. This was the major improvement, bringing us down to about 1.5 megs. Still not slim enough!&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;3) Reduction of DOM elements&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;As we've progressed through the last two years, the site has been extended and fixed and extended again - resulting in a patchwork of DIVs and css classes. Some parts were done through Semantic Views and other parts through their own .tpl files. We went through and made sure all parts are rendered from their own .tpl so we have full control over the output. Then we got started reducing and simplifying the files, swapping out wrapper DIVs and extraneous containers. This brought us down to 1044 DOM elements and 1.1mb in size&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;4) Minimizing external services&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;We used to have a GooglePlus button on the site. It typically took anywhere between 3 and 10 seconds to load all of it's resources. Need I say it's gone? We also used to have the Facebook activity stream with Faces enabled. It required between 50 and 70 http requests to load all it's resources and took up to 8 seconds to load. No more. We also used to load the ever-ubiquitous JQ&lt;/span&gt;&lt;span style="font-size: small;"&gt;uery from Googles ajax-cdn to be sure we always had the latest version on the site. I guess we can live with having it lag a version or two if that's the price we have to pay to have a faster site so we cached it on our own servers instead.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;We still have a Twitter button and a few Facebook boxes on the page but we'll have to live with them until we develop a better method to load them (preferrably they will be loaded when the user hovers over the boxes)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-large;"&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: small;"&gt;Final metrics &lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;Total load time: 2 sec (-75%)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-size: small;"&gt;Waiting for external services: 2-3 sec&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-size: small;"&gt;Data size: 1.1mb (-56%)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-size: small;"&gt;DOM Elements 1044 (-24%)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-size: small;"&gt;YSlow score: B (89)&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;So here we are. The new site is now a lot slimmer, nearly ready for Beach 2012. Stuff that's left to consider is trimming of the master CSS, adding the responsive parts to the CSS and trimming away some of the extraneous Drupal CSS classes which it so loves to add. All in all, I think the result is quite good but surely there are a some more optimizations left to do. Or at least I hope so, this performance hunting is addictive!&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-large;"&gt;&lt;b&gt;Other possible optimizations&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;A couple of points that could be worth researching are&lt;/span&gt;&lt;/div&gt;&lt;ul style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;li&gt;&lt;span style="font-size: small;"&gt;Progressive loading of all content, not just images&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: small;"&gt;Dividing the posts into categories, thereby reducing the number of posts on any page&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: small;"&gt;Minifying *everything*&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: small;"&gt;Writing our own sharing functions that don't require hefty scripts from external sources&lt;/span&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: small;"&gt;Spreading the downloads over multiple domains to reduce browser blocking&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;span style="font-size: x-large;"&gt;&lt;b&gt;Tools used&amp;nbsp; &lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul style="font-family: &amp;quot;Helvetica Neue&amp;quot;,Arial,Helvetica,sans-serif;"&gt;&lt;li&gt;&lt;span style="font-size: small;"&gt;&lt;a href="http://getfirebug.com/" target="_blank"&gt;FireBug, a Mozilla extension&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: small;"&gt;&lt;a href="https://addons.mozilla.org/sv-se/firefox/addon/yslow/" target="_blank"&gt;Yahoo's YSlow, another Mozilla extension&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: small;"&gt;&lt;a href="http://newrelic.com/" target="_blank"&gt;New Relic, performance analysis tools for php&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: small;"&gt;Coffee and heavy metal&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-8479956793977421110?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/8479956793977421110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2012/02/site-performance-analyze-and-improve.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/8479956793977421110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/8479956793977421110'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2012/02/site-performance-analyze-and-improve.html' title='Site performance - analyze and improve'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-3327730825666257144</id><published>2012-02-14T06:51:00.001-08:00</published><updated>2012-02-14T06:54:26.819-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='random'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='varnish'/><title type='text'>VCL Random url part</title><content type='html'>Random is random, usually. Unless it's being fetched every 20th minute and included as a part of a site with heavy traffic. That's a lesson I learned the hard way by relying on an ad networks RSS aggregator that included one of our RSS feeds on it's pages. The feed is randomized on our end with a list of nodes that are straight out of Views via some custom theming. The nodes are grouped in threes from a larger set.&lt;br /&gt;&lt;br /&gt;What I noticed was that over 24 hours, what should have been an even distribution turned out to be heavily weighted towards one specific set. The ad networks RSS aggregation ran on a schedule and as bad luck would have it, Views liked to display the same set every time the RSS aggregator made a visit. Probably just bad luck but to the customer it looked like one set of nodes were favored over the others.&lt;br /&gt;&lt;br /&gt;As I run Varnish for caching I started thinking about if I could do this in another way. I thought about faux round-robin directors and complicated solutions involving dns aliases and htaccess rewrites.&lt;br /&gt;&lt;br /&gt;Then I stumbled upon &lt;a href="http://cd34.com/blog/webserver/using-varnish-to-assist-with-ab-testing/" target="_blank"&gt;this blogpost&lt;/a&gt; by Chris Davies who is just generally speaking one of the most knowledgeable techies I know of. So enter inline-C for my VCL.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;sub vcl_recv{&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (req.url == "/randomizeme") {&lt;br /&gt;C{&lt;br /&gt;char buff[5];&lt;br /&gt;sprintf(buff, "%d", rand()%4+1);&lt;br /&gt;VRT_SetHdr(sp, HDR_REQ, "\017X-Distribution:", buff, vrt_magic_string_end);&lt;br /&gt;}C&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; set req.url = "/randomizeme/" req.http.X-Distribution;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;}&lt;/div&gt;&lt;br /&gt;It works like this:&lt;br /&gt;- Varnish picks up the request on url /randomizeme&lt;br /&gt;- Varnish executes the inline C&lt;br /&gt;- Varnish sets a special header with the randomized integer (the header works as a variable)&lt;br /&gt;- Varnish then rewrites the url to /randomizeme/1-4&lt;br /&gt;- Varnish finally fetches the above url from the backend which is rigged to display different content depending on which "slot" is chosen.&lt;br /&gt;- Varnish delivers the randomized content to the visitor/aggregator/whatever&lt;br /&gt;&lt;br /&gt;A little warning: If you modify the header name "X-Distribution", note that the "\017" must be updated, it's octal for the length of the string. I forget if it should include the ":" or not. Trial and error, watch that log for segfaults!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-3327730825666257144?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/3327730825666257144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2012/02/vcl-random-url-part.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/3327730825666257144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/3327730825666257144'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2012/02/vcl-random-url-part.html' title='VCL Random url part'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-1174485058755681790</id><published>2011-11-02T08:17:00.001-07:00</published><updated>2011-11-04T04:17:43.963-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='statistics'/><category scheme='http://www.blogger.com/atom/ns#' term='sverige'/><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='webb'/><category scheme='http://www.blogger.com/atom/ns#' term='sweden'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='mobila enheter'/><category scheme='http://www.blogger.com/atom/ns#' term='handheld'/><category scheme='http://www.blogger.com/atom/ns#' term='statistik'/><title type='text'>Swedish browser statistics Q4 2011</title><content type='html'>It's been a while since I did this last but it's time for another rundown of who uses what in the Swedish (and nordic) marketspace.&lt;br /&gt;&lt;br /&gt;These figures are extracted from a 24-hr statistics collection period and represent around five million pageviews of which roughly 350K were mobile views. Total unique visitors around one million. The sources are mainstream sites that serve mainly swedish content and targets a wide array of audiences.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Desktop browsers:&lt;/span&gt;&lt;br /&gt;&lt;table class="records pie_view" id="f_table_graph" style="height: 261px; width: 472px;"&gt;&lt;tbody id="f_tbody_0"&gt;&lt;tr&gt;&lt;td style="vertical-align: top;"&gt;&lt;br /&gt;&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;Browser make&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;Now&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;Feb2011&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;Change&lt;/td&gt;&lt;/tr&gt;&lt;tr class="rank_1 highlight"&gt;&lt;td class="count"&gt;1.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment0" title="Firefox"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;Firefox  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_0"&gt;43,42 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;(51,92%)&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;-8,5%&lt;/td&gt;     &lt;/tr&gt;&lt;/tbody&gt;     &lt;tbody id="f_tbody_1"&gt;&lt;tr class="rank_2"&gt; &lt;td class="count"&gt;2.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment1" title="Chrome"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;Chrome   &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_1"&gt;42,07 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;(31,57%)&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;+10,5%&lt;/td&gt;     &lt;/tr&gt;&lt;/tbody&gt;     &lt;tbody id="f_tbody_2"&gt;&lt;tr class="rank_3 highlight"&gt; &lt;td class="count"&gt;3.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment2" title="Internet Explorer"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;Internet Explorer  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_2"&gt;9,25 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;(12,12%)&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;-3,13%&lt;/td&gt;     &lt;/tr&gt;&lt;/tbody&gt;     &lt;tbody id="f_tbody_3"&gt;&lt;tr class="rank_4"&gt; &lt;td class="count"&gt;4.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment3" title="Safari"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;Safari  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_3"&gt;3,23 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;(2,16%)&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;+1,08%&lt;/td&gt;     &lt;/tr&gt;&lt;/tbody&gt;     &lt;tbody id="f_tbody_4"&gt;&lt;tr class="rank_5 highlight"&gt; &lt;td class="count"&gt;5.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment4" title="Opera"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;Opera   &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_4"&gt;1,60 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;(1,81%)&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;-0,21%&lt;/td&gt;     &lt;/tr&gt;&lt;/tbody&gt;     &lt;tbody id="f_tbody_5"&gt;&lt;tr class="rank_6"&gt; &lt;td class="count"&gt;6.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment5" title="Mozilla Compatible Agent"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;Mozilla Compatible Agent  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_5"&gt;0,20 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;(0,01%)&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;+0,19%&lt;/td&gt;     &lt;/tr&gt;&lt;/tbody&gt;     &lt;tbody id="f_tbody_6"&gt;&lt;tr class="rank_7 highlight"&gt; &lt;td class="count"&gt;7.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment6" title="RockMelt"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;RockMelt   &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_6"&gt;0,08 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;(0,11%)&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;-0,03%&lt;/td&gt;     &lt;/tr&gt;&lt;/tbody&gt;     &lt;tbody id="f_tbody_7"&gt;&lt;tr class="rank_8"&gt; &lt;td class="count"&gt;8.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment7" title="BlackBerry9700"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;BlackBerry9700  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_7"&gt;0,04 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;---&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;+0,04%&lt;/td&gt;     &lt;/tr&gt;&lt;/tbody&gt;     &lt;tbody id="f_tbody_8"&gt;&lt;tr class="rank_9 highlight"&gt; &lt;td class="count"&gt;9.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment8" title="IE with Chrome Frame"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;IE with Chrome Frame  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_8"&gt;0,02 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;---&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;+0,02%&lt;/td&gt;     &lt;/tr&gt;&lt;/tbody&gt;     &lt;tbody id="f_tbody_9"&gt;&lt;tr class="rank_10"&gt; &lt;td class="count"&gt;10.&lt;/td&gt;   &lt;td class="text" colspan="1"&gt;&lt;div class="text_wrapper" id="f_primary_segment9" title="Mozilla"&gt;&lt;div class="text_wrapper"&gt;&lt;div class="ie_fix_wrapper"&gt;Mozilla   &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td id="f_pie_pct_9"&gt;0,01 %&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;(0,03%)&lt;/td&gt;&lt;td style="vertical-align: top;"&gt;-0,02%&lt;/td&gt;  &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Mobile browsers:&lt;/span&gt;&lt;br /&gt;&lt;table class="graph " border="0" cellpadding="5" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr class="alt"&gt;&lt;td class="itemname2 "&gt;&lt;h4&gt;&lt;img src="https://static.getclicky.com/media/useragent/iphone.gif" /&gt; iPhone&lt;/h4&gt;&lt;/td&gt;                  &lt;td class="itemvalue" nowrap="nowrap"&gt;&lt;h4&gt;396,326&lt;/h4&gt;&lt;/td&gt;         &lt;td class="small grey pr20"&gt;70.9%&lt;/td&gt;          &lt;td class="itemgraph"&gt;&lt;img src="https://static.getclicky.com/media/graph_bar_standard.gif" height="20" width="250" /&gt;&lt;/td&gt; &lt;td class="itemtrend"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;/tr&gt;                                &lt;tr class="alt"&gt;         &lt;td class="itemname2 "&gt; &lt;h4&gt;&lt;img src="https://static.getclicky.com/media/useragent/android.gif" /&gt; Android&lt;/h4&gt;&lt;/td&gt;                  &lt;td class="itemvalue" nowrap="nowrap"&gt;&lt;h4&gt;153,449&lt;/h4&gt;&lt;/td&gt;         &lt;td class="small grey pr20"&gt;27.4%&lt;/td&gt;          &lt;td class="itemgraph"&gt;&lt;img src="https://static.getclicky.com/media/graph_bar_standard.gif" height="20" width="97" /&gt;&lt;/td&gt; &lt;td class="itemtrend"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;/tr&gt;                                &lt;tr class="alt"&gt;         &lt;td class="itemname2 "&gt; &lt;h4&gt;&lt;img src="https://static.getclicky.com/media/useragent/symbian.gif" /&gt; Symbian&lt;/h4&gt;&lt;/td&gt;                  &lt;td class="itemvalue" nowrap="nowrap"&gt;&lt;h4&gt;8,076&lt;/h4&gt;&lt;/td&gt;         &lt;td class="small grey pr20"&gt;1.4%&lt;/td&gt;          &lt;td class="itemgraph"&gt;&lt;img src="https://static.getclicky.com/media/graph_bar_standard.gif" height="20" width="6" /&gt;&lt;/td&gt; &lt;td class="itemtrend"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;/tr&gt;                                                                                                        &lt;tr class="alt"&gt;         &lt;td class="itemname2 "&gt; &lt;h4&gt;&lt;img src="https://static.getclicky.com/media/useragent/blackberry.gif" /&gt; Blackberry&lt;/h4&gt;&lt;/td&gt;                  &lt;td class="itemvalue" nowrap="nowrap"&gt;&lt;h4&gt;992&lt;/h4&gt;&lt;/td&gt;         &lt;td class="small grey pr20"&gt;0.2%&lt;/td&gt;          &lt;td class="itemgraph"&gt;&lt;img src="https://static.getclicky.com/media/graph_bar_standard.gif" height="20" width="1" /&gt;&lt;/td&gt; &lt;td class="itemtrend"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;/tr&gt;                                                                                                                                        &lt;tr class="alt"&gt;         &lt;td class="itemname2 "&gt; &lt;h4&gt;&lt;img src="https://static.getclicky.com/media/useragent/windows.gif" /&gt; Windows phone&lt;/h4&gt;&lt;/td&gt;                  &lt;td class="itemvalue" nowrap="nowrap"&gt;&lt;h4&gt;351&lt;/h4&gt;&lt;/td&gt;         &lt;td class="small grey pr20"&gt;0.1%&lt;/td&gt;          &lt;td class="itemgraph"&gt;&lt;img src="https://static.getclicky.com/media/graph_bar_standard.gif" height="20" width="1" /&gt;&lt;/td&gt; &lt;td class="itemtrend"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;/tr&gt;                &lt;tr class="alt"&gt;         &lt;td class="itemname2 "&gt; &lt;h4&gt;&lt;img src="https://static.getclicky.com/media/useragent/windows.gif" /&gt; Windows CE&lt;/h4&gt;&lt;/td&gt;                  &lt;td class="itemvalue" nowrap="nowrap"&gt;&lt;h4&gt;21&lt;/h4&gt;&lt;/td&gt;         &lt;td class="small grey pr20"&gt;0%&lt;/td&gt;          &lt;td class="itemgraph"&gt;&lt;img src="https://static.getclicky.com/media/graph_bar_standard.gif" height="20" width="1" /&gt;&lt;/td&gt; &lt;td class="itemtrend"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;/tr&gt;                &lt;tr class="alt"&gt;         &lt;td class="itemname2 "&gt; &lt;h4&gt;&lt;img src="https://static.getclicky.com/media/useragent/mobile.gif" /&gt; Unknown mobile&lt;/h4&gt;&lt;/td&gt;                  &lt;td class="itemvalue" nowrap="nowrap"&gt;&lt;h4&gt;17&lt;/h4&gt;&lt;/td&gt;         &lt;td class="small grey pr20"&gt;0%&lt;/td&gt;          &lt;td class="itemgraph"&gt;&lt;img src="https://static.getclicky.com/media/graph_bar_standard.gif" height="20" width="1" /&gt;&lt;/td&gt; &lt;td class="itemtrend"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;/tr&gt;                &lt;tr class="alt"&gt;         &lt;td class="itemname2 "&gt; &lt;h4&gt;&lt;img src="https://static.getclicky.com/media/useragent/webos.gif" /&gt; WebOS&lt;/h4&gt;&lt;/td&gt;                  &lt;td class="itemvalue" nowrap="nowrap"&gt;&lt;h4&gt;2&lt;/h4&gt;&lt;/td&gt;         &lt;td class="small grey pr20"&gt;0%&lt;/td&gt;          &lt;td class="itemgraph"&gt;&lt;img src="https://static.getclicky.com/media/graph_bar_standard.gif" height="20" width="1" /&gt;&lt;/td&gt; &lt;td class="itemtrend"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;/tr&gt;        &lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusion:&lt;/span&gt;&lt;br /&gt;IE steadily declining. Firefox and Chrome dominate desktop market. Android dominates mobile market allthough browser brand and versions seems obscured.&lt;br /&gt;&lt;br /&gt;Mobile continues to grow as a platform, now at 14.7% of total visitors. iPad stands for 1.5% of that figure while Android tablets are hard to identify (and GA won't group very good on screen resolutions) so I'll leave that figure dangling until I get better data.&lt;br /&gt;&lt;br /&gt;For us CSS and HTML jockeys it sure looks like we get to play with the nice toys a bit more next year since Chrome and Firefox totally dominate the market with a whooping 85.47%. Crossbrowser hell just became a little cooler.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-1174485058755681790?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/1174485058755681790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2011/11/swedish-browser-statistics-q4-2011.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/1174485058755681790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/1174485058755681790'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2011/11/swedish-browser-statistics-q4-2011.html' title='Swedish browser statistics Q4 2011'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-3747720499714572476</id><published>2011-10-05T05:36:00.000-07:00</published><updated>2011-10-06T07:09:57.349-07:00</updated><title type='text'>Node.JS-based real-time web tracking</title><content type='html'>&lt;span style="font-size:100%;"&gt;This all started out of a need. A need for a statistics service that did not suck and that could handle our quite intense traffic (we run at 1500 req/sec max). Google Analytics failed miserably, GetClicky failed a little less miserably and other options were quite simply to expensive for our taste.&lt;br /&gt;&lt;br /&gt;Enter Node.JS and a little bit of pre-alpha code known as &lt;a href="http://hummingbirdstats.com/"&gt;Hummingbird&lt;/a&gt;. I'll let Hummingbird introduce it self:&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-style: italic;"&gt;"Hummingbird lets you see how visitors are interacting with your website in real time. And by “real time” we don’t mean it refreshes every 5 minutes—WebSockets enable Hummingbird to update 20 times per second. Hummingbird is built on top of &lt;/span&gt;&lt;a style="font-style: italic;" href="http://www.nodejs.org/"&gt;Node.js&lt;/a&gt;&lt;span style="font-style: italic;"&gt;, a new javascript web toolkit that can handle large amounts of traffic and many concurrent users."&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sounds good to me, where do I sign up? Well, as is customary these days, this project is on github and is installed by first cloning the repo and then running "npm install" in the hummingbird subdir. Of course, you need to have a recent node.js core, mongodb and libgeoip-dev 1.4.7+ if you want to get ip-location working (hummingbird has a nice little map display in the demo setup). Unfortunately there is not much documentation written (yet) so be prepared for a lot of debugging and guesswork if you want it to do anything besides the graph / total / map that's included in the demo.&lt;br /&gt;Note: On Debian Lenny, libgeoip is at 1.4.4 which means you'll have to load it from lenny-backports to get the correct version (1.4.7+). Instructions for installing from backports can be found here: &lt;a href="http://backports-master.debian.org/Instructions/"&gt;http://backports-master.debian.org/Instructions/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So how does it work? Well, the core is really a pixeltracker - including a 1x1 pixel from the system in your content will register a view with hummingbird, in effect saving a little bit of data to our mongodb collection. Hummingbird also runs a dashboard (if enabled) that can be viewed via http. The dashboard utilizes Web Sockets for rapid updates and binds the updates to jQuery elements on the dashboard page.&lt;br /&gt;&lt;br /&gt;This is about as far as I have gotten right now. The lack of documentation makes it a bit hard to develop more widgets for the dashboard but that's nothing a little trial-and-error won't fix :)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update:&lt;br /&gt;&lt;/span&gt;Managed to get another piece of the puzzle working - a custom widget for the dashboard page. I started with the backend connection by combining the code for cart_adds.js and total_views.js to get a starting point for an event-driven counter widget. Followed up by creating a very rudimentary widget endpoint that picks up on whatever is processed on the backend by hooking into the event system. So any tracking that has a specific event attached to it (basically parts of the query string for the tracking pixel) will result in a display on the dashboard that reacts to new hits by incrementing a value or otherwise adjusting the style or layout of the widget. Nothing new really but still a valuable tool for our editors who are constantly chasing those extra page views.&lt;br /&gt;&lt;br /&gt;I'll update again once I have a better implementation of it all.&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-3747720499714572476?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/3747720499714572476/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2011/10/adventures-in-nodejs-based-real-time.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/3747720499714572476'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/3747720499714572476'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2011/10/adventures-in-nodejs-based-real-time.html' title='Node.JS-based real-time web tracking'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-1016033876845993712</id><published>2011-08-18T04:18:00.001-07:00</published><updated>2011-08-18T04:47:01.983-07:00</updated><title type='text'>How to ip-migrate web sites using Varnish</title><content type='html'>This is a quick guide on how to migrate a bunch of web sites by using Varnish as a intermediary to allow for low-latency zero downtime migration between ip addresses or domain names.&lt;br /&gt;&lt;br /&gt;Requirements: Varnish 2.x up and running, access to apache config, access to dns config.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1) Configure Varnish&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt; &lt;/span&gt;&lt;br /&gt;Start out by defining your existing web sites as backends in the varnish config (default.vcl) by adding them in this format to the beginning of your vcl:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;backend website1{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    // NO CACHING&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    // website1.company.com&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    .host = "10.0.0.1";&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    .port = "80";&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    .connect_timeout = 160s;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    .first_byte_timeout = 55s;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    .between_bytes_timeout = 25s;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;backend website2{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; backend website3{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Then add some clever host/backend switching logic to your vcl:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;font-size:85%;"&gt;sub vcl_recv {&lt;br /&gt;# Find out which backend to use for the request&lt;br /&gt;if (req.http.Host == "website1.company.com"){&lt;br /&gt;  set req.backend = website1;&lt;br /&gt;  return(pass);&lt;br /&gt;}&lt;br /&gt;else if (req.http.Host == "website2.company.com"){&lt;br /&gt;  set req.backend = website2;&lt;br /&gt;  return(pass); &lt;br /&gt;}&lt;br /&gt;else {&lt;br /&gt; # maybe switch to your default backend here if you're using varnish in production&lt;br /&gt;}&lt;br /&gt;.....&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You're now ready to start using varnish as your intermediary frontend router. Varnish will PASS any requests to these sites and you are now free to change the IP:s of the sites without the lag of DNS. You remembered to load and use your varnish config too, right?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2) Alter DNS for sites&lt;/span&gt;&lt;br /&gt;Update your dns records, all www pointers should be changed to the ip address of your Varnish server.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3) Migrate!&lt;br /&gt;&lt;/span&gt;When the changes have propagated (and you've verified that it works as intended) you are free to change ip:s or whatever you need on your sites, Varnish will maintain the dnsname -&amp;gt; backend connection as long as you remember to update your backend config in vcl. And, changes are instant - allowing fast rollbacks if needed.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-1016033876845993712?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/1016033876845993712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2011/08/how-to-migrate-web-sites-without.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/1016033876845993712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/1016033876845993712'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2011/08/how-to-migrate-web-sites-without.html' title='How to ip-migrate web sites using Varnish'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-797734886125724852</id><published>2011-08-17T06:49:00.000-07:00</published><updated>2011-08-17T06:50:16.293-07:00</updated><title type='text'>How to use bash to find all used IP:s in your apache configs (following the debian config model)</title><content type='html'>&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;for i in `ls /etc/apache2/sites-enabled/*.vhost`; do echo "Working file:$i"; echo "Servername:";grep "ServerName" $i | cut -c 16-40; echo "Ipaddress:";grep "&amp;lt;VirtualHost" $i|cut -c 14-27; done&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-797734886125724852?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/797734886125724852/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2011/08/how-to-use-bash-to-find-all-used-ips-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/797734886125724852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/797734886125724852'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2011/08/how-to-use-bash-to-find-all-used-ips-in.html' title='How to use bash to find all used IP:s in your apache configs (following the debian config model)'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-8477876725419303190</id><published>2011-03-22T07:08:00.000-07:00</published><updated>2011-11-17T11:40:51.637-08:00</updated><title type='text'>Top 5 Varnish commands</title><content type='html'>Here's a brief list of my top &lt;strike&gt;&lt;/strike&gt;5 varnish commands&lt;br /&gt;&lt;br /&gt;&lt;b&gt;varnishstat&lt;/b&gt;&lt;br /&gt;This is what I use to get a birds-eye view of what we have cooking. Provides all the info you need to spot cache misses and errors.&lt;br /&gt;Usage: &lt;i&gt;varnishstat&lt;/i&gt; for continuous display or &lt;i&gt;varnishstat -i 1&lt;/i&gt; for a quick one-off. More on this command: &lt;a href="http://www.varnish-cache.org/docs/2.1/reference/varnishstat.html" target="_blank"&gt;http://www.varnish-cache.org/docs/2.1/reference/varnishstat.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;varnishhist&lt;/b&gt;&lt;br /&gt;Provides a histogram view of cache hits/misses&lt;br /&gt;Usage: &lt;i&gt;varnishhist&lt;/i&gt; More on this command: &lt;a href="http://www.varnish-cache.org/docs/2.1/reference/varnishhist.html" target="_blank"&gt;http://www.varnish-cache.org/docs/2.1/reference/varnishhist.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;varnishlog&lt;/b&gt;&lt;br /&gt;Provides detailed information on requests. I use it to debug cache operations by for example issuing the command &lt;i&gt;varnishlog -c -o ReqStart [insert ip here]&lt;/i&gt; which allows me to see what happens when I visit a cache object in my browser.&lt;br /&gt;Usage: &lt;i&gt;varnishlog -c -o ReqStart 192.168.0.1&lt;/i&gt; More on this command: &lt;a href="http://www.varnish-cache.org/docs/2.1/reference/varnishlog.html" target="_blank"&gt;http://www.varnish-cache.org/docs/2.1/reference/varnishlog.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;varnishtop&lt;/b&gt;&lt;br /&gt;I use this mainly to get lists of things. Like this one that gives me a list of the top urls hitting the backend (pass).&lt;br /&gt;Usage: &lt;i&gt;varnishtop -b -i TxURL &lt;/i&gt;More on this command: &lt;a href="http://www.varnish-cache.org/docs/2.1/reference/varnishtop.html" target="_blank"&gt;http://www.varnish-cache.org/docs/2.1/reference/varnishtop.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;varnishadm&lt;/b&gt;&lt;br /&gt;Command-line varnish administration. I mainly use this to reload vcl and purge urls.&lt;br /&gt;Usage: &lt;i&gt;varnishadm -t host:port command&lt;/i&gt;&lt;br /&gt;Reloading a vcl from the command line can be done like this:&lt;br /&gt;Load (your changed vcl): &lt;i&gt;varnishadm -t localhost:6082 vcl.load load01 /etc/varnish/default.vcl&lt;/i&gt;&lt;br /&gt;Use (apply) the vcl: &lt;i&gt;varnishadm -t localhost:6082 vcl.use load01&lt;/i&gt;&lt;br /&gt;More on this command: &lt;a href="http://www.varnish-cache.org/docs/2.1/reference/varnishadm.html" target="_blank"&gt;http://www.varnish-cache.org/docs/2.1/reference/varnishadm.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-8477876725419303190?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/8477876725419303190/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2011/03/top-4-varnish-commands.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/8477876725419303190'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/8477876725419303190'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2011/03/top-4-varnish-commands.html' title='Top 5 Varnish commands'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-6280563138001184131</id><published>2011-03-15T14:29:00.000-07:00</published><updated>2011-03-15T14:29:38.140-07:00</updated><title type='text'>Facebook iframe like box tracking with Clicky (or Google Analytics)</title><content type='html'>This is how to do GetClicky tracking for facebooks fbxml iframe like box and the fbxml like button.&lt;br /&gt;&lt;br /&gt;Without going into details about fb app id and such, this is a working method to implement the GetClicky tracking for facebook like boxes and buttons.&lt;br /&gt;&lt;br /&gt;Standard fb script:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;div id=&amp;quot;fb-root&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;//&amp;lt;![CDATA[&lt;br /&gt;window.fbAsyncInit = function() {&lt;br /&gt; FB.init({appId: '157019890979609', status: true, cookie: true,&lt;br /&gt; xfbml: true});&lt;br /&gt;});&lt;br /&gt;(function() {&lt;br /&gt;var e = document.createElement('script'); &lt;br /&gt;e.async = true;&lt;br /&gt;e.src = document.location.protocol +&lt;br /&gt;'//connect.facebook.net/sv_SE/all.js';&lt;br /&gt;document.getElementById('fb-root').appendChild(e);&lt;br /&gt;}());&lt;br /&gt;//]]&amp;gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The facebook "like" event hook:&lt;br /&gt;&lt;pre class="prettyprint"&gt;FB.Event.subscribe('edge.create', function(href, widget) {&lt;br /&gt;// do something here&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The clicky call:&lt;br /&gt;&lt;pre class="prettyprint"&gt;if(typeof(clicky) != &amp;quot;undefined&amp;quot;){&lt;br /&gt;clicky.log('page url','facebook like clicked','click');&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Combined:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;div id=&amp;quot;fb-root&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;//&amp;lt;![CDATA[&lt;br /&gt;window.fbAsyncInit = function() {&lt;br /&gt;FB.init({appId: '157019890979609', status: true, cookie: true,&lt;br /&gt;xfbml: true});&lt;br /&gt;FB.Event.subscribe('edge.create', function(href, widget) {&lt;br /&gt;if(typeof(clicky) != &amp;quot;undefined&amp;quot;){&lt;br /&gt;clicky.log('page url','facebook like clicked','click');&lt;br /&gt;}&lt;br /&gt;});&lt;br /&gt;};&lt;br /&gt;(function() {&lt;br /&gt;var e = document.createElement('script'); &lt;br /&gt;e.async = true;&lt;br /&gt;e.src = document.location.protocol +&lt;br /&gt;'//connect.facebook.net/sv_SE/all.js';&lt;br /&gt;document.getElementById('fb-root').appendChild(e);&lt;br /&gt;}());&lt;br /&gt;//]]&amp;gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Of course the same works for Google Analytics, just swap &lt;em&gt;clicky.log&lt;/em&gt; for &lt;em&gt;_gaq.push&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-6280563138001184131?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/6280563138001184131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2011/03/facebook-iframe-like-box-tracking-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/6280563138001184131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/6280563138001184131'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2011/03/facebook-iframe-like-box-tracking-with.html' title='Facebook iframe like box tracking with Clicky (or Google Analytics)'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-8930481820358275390</id><published>2011-03-14T15:29:00.000-07:00</published><updated>2011-03-16T05:56:40.674-07:00</updated><title type='text'>Custom Drupal RSS Feed with CCK</title><content type='html'>Ever needed to do a quick RSS feed one-off out of your Drupal site? This is how I do it.&lt;br /&gt;&lt;br /&gt;Setup: Drupal 6, CCK, Views enabled.&lt;br /&gt;&lt;br /&gt;First, create a new content type and name it &lt;em&gt;rssarticle&lt;/em&gt;. Add three text fields named &lt;em&gt;heading, text&lt;/em&gt; and &lt;em&gt;link&lt;/em&gt;. Save the content type.&lt;br /&gt;&lt;br /&gt;Now, we'll use Views as a query builder that will provide the SQL you will run to build the feed. Start out by setting up a &lt;em&gt;Node&lt;/em&gt; View, set Style to &lt;em&gt;unformatted&lt;/em&gt; and Row style to &lt;em&gt;Fields&lt;/em&gt;. Select the fields you want in your feed and move on to add a &lt;em&gt;node type&lt;/em&gt; filter to restrict the view to our newly added &lt;em&gt;rssarticle&lt;/em&gt; content type. Keep the rest of the settings untouched.&lt;br /&gt;&lt;br /&gt;Now, hit the Preview button. You should now see some nodes rendered and a Query field that displays the query like so:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;SELECT node.nid AS nid,&lt;br /&gt;   node_data_field_heading.field_heading_value AS &lt;br /&gt;   node_data_field_heading_field_heading_value,&lt;br /&gt;   node.type AS node_type,&lt;br /&gt;   node.vid AS node_vid,&lt;br /&gt;   node_data_field_heading.field_link_value AS &lt;br /&gt;   node_data_field_heading_field_link_value,&lt;br /&gt;   node_data_field_heading.field_text_value AS &lt;br /&gt;   node_data_field_heading_field_text_value&lt;br /&gt; FROM node node &lt;br /&gt; LEFT JOIN content_type_rssarticle node_data_field_heading ON &lt;br /&gt;   node.vid = node_data_field_heading.vid&lt;br /&gt; WHERE node.type in ('rssarticle')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Copy that SQL query and fire up your favorite php editor. Create a new php file by the name of &lt;em&gt;mycustomfeed.php&lt;/em&gt;. Start by adding the following lines.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;?php&lt;br /&gt;// Load necessary Drupal parts&lt;br /&gt;require_once './includes/bootstrap.inc';&lt;br /&gt;drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);&lt;br /&gt;&lt;br /&gt;// XML header&lt;br /&gt;echo('&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;');&lt;br /&gt;&lt;br /&gt;// Get nodes from db&lt;br /&gt;$results = db_query("");&lt;br /&gt;&lt;br /&gt;// Loop over the result&lt;br /&gt;while ($result = db_fetch_object($results)) {&lt;br /&gt; print $result-&amp;gt;nid;&lt;br /&gt;}&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now this won't do much on it's own, we need the SQL you just copied to go in here too. Complete the code with this, placing it within the &lt;em&gt;db_query("")&lt;/em&gt; statement.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;?php&lt;br /&gt;// Load necessary Drupal parts&lt;br /&gt;require_once './includes/bootstrap.inc';&lt;br /&gt;drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);&lt;br /&gt;&lt;br /&gt;// XML header&lt;br /&gt;echo('&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;');&lt;br /&gt;&lt;br /&gt;// Get nodes from db&lt;br /&gt;$results = db_query("SELECT node.nid AS nid, &lt;br /&gt;node_data_field_heading.field_heading_value AS &lt;br /&gt;node_data_field_heading_field_heading_value, node.type AS &lt;br /&gt;node_type, node.vid AS node_vid, &lt;br /&gt;node_data_field_heading.field_link_value AS &lt;br /&gt;node_data_field_heading_field_link_value, &lt;br /&gt;node_data_field_heading.field_text_value AS &lt;br /&gt;node_data_field_heading_field_text_value FROM node node &lt;br /&gt;LEFT JOIN content_type_rssarticle node_data_field_heading &lt;br /&gt;ON node.vid = node_data_field_heading.vid WHERE &lt;br /&gt;node.type in ('rssarticle')");&lt;br /&gt;&lt;br /&gt;// Loop over the result&lt;br /&gt;while ($result = db_fetch_object($results)) {&lt;br /&gt; print $result-&amp;gt;nid . "\n";&lt;br /&gt;}&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Upload the file to your Drupal root and run either from a browser or from the command line.&lt;br /&gt;&lt;br /&gt;It should output an xml header and list of NIDs. Check error logs for any warnings or sql errors and correct your code accordingly.&lt;br /&gt;&lt;br /&gt;Now that we have the query working it's time to finish the job, adding the xml parts required for a valid RSS 2.0 feed as well as adding a &lt;em&gt;LIMIT&lt;/em&gt; clause and performing some ISO date conversion magic.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;?php&lt;br /&gt;// Load necessary Drupal parts&lt;br /&gt;require_once './includes/bootstrap.inc';&lt;br /&gt;drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);&lt;br /&gt;&lt;br /&gt;// XML header&lt;br /&gt;echo('&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;');&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"&amp;gt;&lt;br /&gt;  &amp;lt;channel&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;My Custom Feed&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;description&amp;gt;This is My Custom Feed&amp;lt;/description&amp;gt;&lt;br /&gt;    &amp;lt;link&amp;gt;http://www.domain.com/mycustomfeed&amp;lt;/link&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt;// Get nodes from db&lt;br /&gt;$results = db_query("SELECT node.nid AS nid,&lt;br /&gt;node_data_field_heading.field_heading_value AS &lt;br /&gt;node_data_field_heading_field_heading_value, &lt;br /&gt;node.type AS node_type, node.vid AS node_vid, &lt;br /&gt;node_data_field_heading.field_link_value AS &lt;br /&gt;node_data_field_heading_field_link_value, &lt;br /&gt;node_data_field_heading.field_text_value AS &lt;br /&gt;node_data_field_heading_field_text_value,&lt;br /&gt;node.changed AS node_changed FROM node node &lt;br /&gt;LEFT JOIN content_type_rssarticle node_data_field_heading &lt;br /&gt;ON node.vid = node_data_field_heading.vid&lt;br /&gt;WHERE node.type in ('rssarticle') LIMIT 0,10");&lt;br /&gt;// Execute query and loop over the result&lt;br /&gt;while ($result = db_fetch_object($results)) {&lt;br /&gt;?&amp;gt;&lt;br /&gt;&amp;lt;item&amp;gt;&lt;br /&gt;&amp;lt;title&amp;gt;&lt;br /&gt;&amp;lt;?php echo $result-&amp;gt;node_data_field_heading_field_heading_value?&amp;gt;&lt;br /&gt;&amp;lt;/title&amp;gt;&lt;br /&gt;&amp;lt;description&amp;gt;&lt;br /&gt;&amp;lt;?php echo $result-&amp;gt;node_data_field_heading_field_text_value?&amp;gt;&lt;br /&gt;&amp;lt;/description&amp;gt;&lt;br /&gt;&amp;lt;link&amp;gt;&lt;br /&gt;&amp;lt;?php echo $result-&amp;gt;node_data_field_heading_field_link_value?&amp;gt;&lt;br /&gt;&amp;lt;/link&amp;gt;&lt;br /&gt;&amp;lt;author&amp;gt;noreply@domain.com (Corporate Inc)&amp;lt;/author&amp;gt;&lt;br /&gt;&amp;lt;dc:creator&amp;gt;Head.Honcho&amp;lt;/dc:creator&amp;gt;&lt;br /&gt;&amp;lt;category&amp;gt;CustomCategory&amp;lt;/category&amp;gt;&lt;br /&gt;&amp;lt;guid isPermaLink="false"&amp;gt;&amp;lt;?php echo $node-&amp;gt;nid?&amp;gt;&amp;lt;/guid&amp;gt;&lt;br /&gt;&amp;lt;pubDate&amp;gt;&lt;br /&gt;&amp;lt;?php echo date('D, d M Y H:i:s T',$result-&amp;gt;node_changed)?&amp;gt;&lt;br /&gt;&amp;lt;/pubDate&amp;gt;&lt;br /&gt;&amp;lt;/item&amp;gt;&lt;br /&gt;&amp;lt;?php } // End while loop ?&amp;gt;&lt;br /&gt;&amp;lt;/channel&amp;gt;&lt;br /&gt;&amp;lt;/rss&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For more on custom rss from drupal, check out my &lt;a href="http://christophercato.blogspot.com/2011/03/drupal-rss-2-with-imagecache-images-in.html"&gt;post on rss with imagecache images and media enclosures&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-8930481820358275390?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/8930481820358275390/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2011/03/custom-drupal-rss-feed-with-cck.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/8930481820358275390'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/8930481820358275390'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2011/03/custom-drupal-rss-feed-with-cck.html' title='Custom Drupal RSS Feed with CCK'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-2470432928441311871</id><published>2011-03-03T07:35:00.000-08:00</published><updated>2011-03-03T09:45:41.018-08:00</updated><title type='text'>Drupal RSS 2 with imagecache images in media enclosures</title><content type='html'>I got a new project to do. It involved publishing news clips to various sites via valid rss 2.0. The feed includes a headline, a link, a short text and an image. But, the image has to go in an enclosure like the following&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;enclosure url="http://domain.com/file.jpg" &lt;br /&gt;length="3737" &lt;br /&gt;type="image/jpeg"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The way I chose to do this was by setting up a view to handle the listings and then doing a custom template based on the dead simple Basic. I removed all trim and just added the outline of a valid rss 2.0 feed structure by creating my page.tpl.php, node-view-viewname.tpl.php and views-view.tpl.php where node-view-viewname is the template file that will print the actual item. Easy.&lt;br /&gt;&lt;br /&gt;Now here comes a problem... To make up for the never-ending creativity of our dear editors, we use ImageCache. Without it we would be lost in a world of image sizes and formats. If you are at all familiar with ImageCache you know that it's lazy and only generates an image if a request for an image fires a 404 error. This is fine for most purposes but since rss media enclosures require certain metadata about the file and it doesn't exist yet we need to force ImageCache to generate the image before we can fill the enclosure tag with the correct values.&lt;br /&gt;We can do this in template.php by adding the following function:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;/*&lt;br /&gt;* This function pregenerates ImageCache images&lt;br /&gt;* to give access to the file metadata&lt;br /&gt;* @param $vars&lt;br /&gt;*/&lt;br /&gt;function basic_attach_ic_metadata(&amp;amp;$vars) {&lt;br /&gt;$presetname = "100x100";&lt;br /&gt;$filepath = $vars['field_bild'][0]['filepath'];&lt;br /&gt;$preset = imagecache_preset_by_name($presetname);&lt;br /&gt;$dst = imagecache_create_path($presetname, $filepath);&lt;br /&gt;if (!file_exists($dst)) {&lt;br /&gt;imagecache_build_derivative($preset['actions'], $filepath, $dst);&lt;br /&gt;}&lt;br /&gt;$vars['genimage'] = $dst;&lt;br /&gt;}&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;Which in it's turn gets called from template.php like so:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;?php function basic_preprocess_node(&amp;amp;$vars, $hook) {&lt;br /&gt;  basic_attach_ic_metadata(&amp;amp;$vars);&lt;br /&gt;...&lt;br /&gt;}&amp;gt;&lt;br /&gt;&lt;/pre&gt;Now, when the node is prepared for display the variable $genimage will be set with the path to a generated image. This now available for your node template to consume and we can print the enclosure tag.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;enclosure url="http://domain.com/&amp;lt;?php echo $genimage;?&amp;gt;"&lt;br /&gt;length="&amp;lt;?php echo filesize($genimage);?&amp;gt;" &lt;br /&gt;type="image/jpeg"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;All done!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-2470432928441311871?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/2470432928441311871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2011/03/drupal-rss-2-with-imagecache-images-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/2470432928441311871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/2470432928441311871'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2011/03/drupal-rss-2-with-imagecache-images-in.html' title='Drupal RSS 2 with imagecache images in media enclosures'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5600017894340887470.post-8747608293733719883</id><published>2011-03-01T12:49:00.000-08:00</published><updated>2011-03-02T05:49:46.196-08:00</updated><title type='text'>External ads in Drupal</title><content type='html'>Allrighty. So someone is twisting your arm and trying to make you add blocks of ad network script tags on your perfect Drupal site? Polluting your DOM? No way, not on my shift! Here's how to keep it safe.&lt;br /&gt;&lt;br /&gt;First you need a basic understanding of a few concepts. I'll use Taxonomy, CCK, Views and some funky template preprocess magic all bound together by our beloved page.tpl.php. The external ads are presumably a bunch of script tags.&lt;br /&gt;&lt;br /&gt;Start out by creating a .php or .html file that hold each ad, place these somewhere within your drupal folder in a folder, why not name it "ads". Any cache-busting techniques should be done with php. Note the full URL:s.&lt;br /&gt;&lt;br /&gt;Now for some Drupal hands-on: Create a Vocabulary that will hold the tags that will tell Drupal which nodes get what ads. Name it "Customers". It should be a flat vocabulary.&lt;br /&gt;&lt;br /&gt;Next, create a content-type that will serve as the holder for the dreaded external ads. Name it "Customer". Add as many text fields as the total of your ad slots. The text fields will only hold URLs and should be plain text only. Save when done. (You can easily add more custom fields here that can be used for your nodes)&lt;br /&gt;&lt;br /&gt;Move on to Taxonomy-&amp;gt;Vocabularies. Configure the previously added vocab so that it is selected for your "regular" content-type so you can relate it with its Customer. Now, add as many terms to the vocabulary as you have customer sections, i.e. companya, companyb, companyc.&lt;br /&gt;&lt;br /&gt;Create a Customer node. Populate the fields with the URL:s from the first step. Fill the title field with one of your terms from Customers, for example "companya". Note: This needs to be unique.&lt;br /&gt;&lt;br /&gt;Now, edit one of your "regular" nodes. Add the term "companya" to the node and save it.&lt;br /&gt;&lt;br /&gt;Set up a view to load your Customer data by creating a new View, naming it "customerdata".  The View should be Unformatted and row style should be Node. Add Node:Title as an argument, accepting the default 404/hide view settings. Add a filter on node type and set it to Customer. Save it.&lt;br /&gt;&lt;br /&gt;The time has come for us to bring it all together and display the ads. This is when your template.php hacking skills come into play. Fire up an editor and open template.php for the theme you'll be using. You now need to create a function that will load the data from the Customer node and attach it to the node before rendering.&lt;br /&gt;You also need to make sure this gets called when the page is constructed. You might need it on the page level or the node level, it depends.&lt;br /&gt;&lt;br /&gt;template.php:&lt;br /&gt;&lt;script src="https://gist.github.com/850944.js?file=gistfile1.php"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;The last part is your page.tpl.php. To print your now safer external ads, construct something along the lines of&lt;br /&gt;&lt;pre&gt;echo "iframe src=" . $ad1 . "/iframe";&lt;/pre&gt;&lt;br /&gt;The same can be repeated for any custom field. The process is the same, add it to the Customer content-type, fill out the Customer node, tag your node(s) with the appropriate term and output it in your page.tpl.php.&lt;br /&gt;&lt;br /&gt;Upload template.php and page.tpl.php, flush your caches and load one of your tagged nodes. You should now see the ad being displayed. If not, check the html source for your iframe tag or flush your cache.&lt;br /&gt;&lt;br /&gt;This example is just a basic example and can be improved upon endlessly. A few of those being Rules integration, error checking and Views caching.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5600017894340887470-8747608293733719883?l=christophercato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://christophercato.blogspot.com/feeds/8747608293733719883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://christophercato.blogspot.com/2011/03/external-ads-in-drupal.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/8747608293733719883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5600017894340887470/posts/default/8747608293733719883'/><link rel='alternate' type='text/html' href='http://christophercato.blogspot.com/2011/03/external-ads-in-drupal.html' title='External ads in Drupal'/><author><name>Christopher Cato</name><uri>http://www.blogger.com/profile/01740225109829923234</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
