<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0"> <channel><title>Aaron Boundy</title> <link>http://www.arboundy.com</link> <description>Aaron Boundy - Infrastructure / Sharepoint Solutions</description> <lastBuildDate>Sat, 12 May 2012 11:56:51 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.0.1</generator> <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/AaronBoundy" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="aaronboundy" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><title>Install VMware Tools on Suse 11 SP1 Guest</title><link>http://www.arboundy.com/2012/05/install-vmware-tools-on-suse-11-sp1-guest/</link> <comments>http://www.arboundy.com/2012/05/install-vmware-tools-on-suse-11-sp1-guest/#comments</comments> <pubDate>Sat, 12 May 2012 11:49:30 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[ESX]]></category> <category><![CDATA[VMware]]></category> <category><![CDATA[vSphere]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1368</guid> <description><![CDATA[Simple how-to steps in uninstalling open-vm-tools (if present) then installing the official VMware Tools on a Suse 11 guest.]]></description> <content:encoded><![CDATA[<p>When commissioning a Suse 11 guest it may install a version of open-vm-tools which often performs its role adequately. However recently I found I have some live data store migration issues with guests that only had open-vm-tools.</p><p>Your version may be slightly different but for my guests, to uninstall open-vm-tools I needed to run</p><pre class="qoate-code">
rpm -e vmware-open-vm-tools-kmod-8.0.4-480973.sles11sp1.x86_64
rpm -e vmware-open-vm-tools-common-8.0.4-480973.sles11sp1.x86_64
</pre><p>in order, ie kmod needed to be removed prior to common.</p><p>It was now onto installing the official VMware Tools &#8211; which isnt as easy as with Windows guests.</p><p>VMware Tools has gcc and the kernel headers as a prerequisite, so the first step is to connect the installation DVD (eg SLES-11-SP1-DVD-x86_64-GM-DVD1.iso) to the guest via the vSphere console.</p><p>Mount to your standard location, if you are not sure the below command will create a mount point and mount the DVD in one go;</p><pre class="qoate-code">
mkdir /mnt/cdrom &#038;&#038; mount /dev/cdrom /mnt/cdrom
</pre><p>From there you can run yast;</p><pre class="qoate-code">
yast2
</pre><p>and select Software, then Software Management;</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/05/Selecting-Software-Management.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/05/Selecting-Software-Management-300x163.png" alt="" title="Selecting Software Management" width="300" height="163" class="aligncenter size-medium wp-image-1369" /></a></p><p>Tab down to the Search Phrase and enter &#8216;gcc&#8217;. The tab over to the right side, highlight it press [ENTER] &#8211; this will select it to be installed;</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/05/Search-for-gcc.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/05/Search-for-gcc-300x163.png" alt="" title="Search for gcc" width="300" height="163" class="aligncenter size-medium wp-image-1370" /></a></p><p>Tab back to the Search Phrase and enter &#8216;kernel&#8217;. Tab over to the right side and highlight kernel-default-devel and press [ENTER] &#8211; this will select it and some dependencies to be installed;</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/05/Search-for-kernel.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/05/Search-for-kernel-300x163.png" alt="" title="Search for kernel" width="300" height="163" class="aligncenter size-medium wp-image-1371" /></a></p><p>Tab back around the page until you get to Accept and hit [ENTER]. You will be presented with a confirmation box, when happy press [ENTER];</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/05/Confirmation.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/05/Confirmation-300x163.png" alt="" title="Confirmation" width="300" height="163" class="aligncenter size-medium wp-image-1372" /></a></p><p>Yast will now go ahead and install gcc and the kernel headers, with should only take a minute or so;</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/05/Installing-gcc-and-kernel.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/05/Installing-gcc-and-kernel-300x163.png" alt="" title="Installing gcc and kernel" width="300" height="163" class="aligncenter size-medium wp-image-1373" /></a></p><p>Once it is complete you can next / ok your way out and close yast. If you want you can confirm the installations via;</p><pre class="qoate-code">
rpm -q gcc
rpm -q kernel-defaullt-dev
</pre><p>With prerequisites done its now onto VMware Tools. First unmount the installation DVD:</p><pre class="qoate-code">
umount /mnt/cdrom
</pre><p>Via the vSphere console for that guest select VM, Install VMware Tools this will attach an ISO that has the installation files in it onto the the guest, but it wont mount it so enter;</p><pre class="qoate-code">
mount /dev/cdrom /mnt/cdrom
</pre><p>There are two versions of the installation, and RPM package and a .tar.gz which needs to be compiled, which is the way I went. First, extract the .tar.gz into your temp folder via;</p><pre class="qoate-code">
cp /mnt/cdrom/VMwareTools-*.tar.gz /tmp
cd /tmp
tar zxpf /mnt/cdrom/VMwareTools-*.tar.gz
</pre><p>After the extraction is complete, go to the created vmware-tools-distrib folder and run the installation script  vmware-install.pl;</p><pre class="qoate-code">
cd vmware-tools-distrib
/vmware-install.pl
</pre><p>Now its pretty much follow the prompts;</p><pre class="qoate-code">
In which directory do you want to install the binary files?
[/usr/bin]
&#13;
What is the directory that contains the init directories (rc0.d/ to rc6.d/)?
[/etc/init.d]
&#13;
What is the directory that contains the init scripts?
[/etc/init.d]
&#13;
In which directory do you want to install the daemon files?
[/usr/sbin]
&#13;
In which directory do you want to install the library files?
[/usr/lib/vmware-tools]
&#13;
The path "/usr/lib/vmware-tools" does not exist currently. This program is
going to create it, including needed parent directories. Is this what you want?
[yes]
&#13;
In which directory do you want to install the documentation files?
[/usr/share/doc/vmware-tools]
&#13;
The path "/usr/share/doc/vmware-tools" does not exist currently. This program
is going to create it, including needed parent directories. Is this what you
want? [yes]
&#13;
The path "/usr/share/doc/vmware-tools" does not exist currently. This program
is going to create it, including needed parent directories. Is this what you
want? [yes]
&#13;
The installation of VMware Tools for Linux completed
successfully. You can decide to remove this software from your system at any
time by invoking the following command: "/usr/bin/vmware-uninstall-tools.pl".
</pre><p>VMware Tools is installed, now it needs to be compiled against your machine (hence the gcc and header prerequisites. The installation will offer to kick this off for you;</p><pre class="qoate-code">
Before running VMware Tools for the first time, you need to configure it by
invoking the following command: "/usr/bin/vmware-config-tools.pl". Do you want
this program to invoke the command for you now? [yes]
&#13;
What is the location of the directory of C header files that match your running
kernel? [/lib/modules/2.6.32.12-0.7-default/build/include]
</pre><p>Stuff will happen;</p><pre class="qoate-code">
Building the vmmemctl module.
&#13;
Using 2.6.x kernel build system.
make: Entering directory `/tmp/vmware-config0/vmmemctl-only'
make -C /lib/modules/2.6.32.12-0.7-default/build/include/..
SUBDIRS=$PWD SRCROOT=$PWD/. \
         MODULEBUILDDIR= modules
make[1]: Entering directory `/usr/src/linux-2.6.32.12-0.7-obj/x86_64/default'
make -C ../../../linux-2.6.32.12-0.7
O=/usr/src/linux-2.6.32.12-0.7-obj/x86_64/default/. modules
...
...
</pre><p>and then it will ask</p><pre class="qoate-code">
None of the pre-built vmhgfs modules for VMware Tools is suitable for your
running kernel.  Do you want this program to try to build the vmhgfs module for
your system (you need to have a C compiler installed on your system)? [yes]
</pre><p>There will be four of these for vmhgfs, vmxnet, vmblock and vmci &#8211; after confirming each there will be a blurb about the compiling process.</p><p>After vmci it prompts;</p><pre class="qoate-code">
The communication service is used in addition to the standard communication
between the guest and the host.  The rest of the software provided by VMware
Tools is designed to work independently of this feature.
If you wish to have the VMCI feature, you can install the driver by running
vmware-config-tools.pl again after making sure that gcc, binutils, make and the
kernel sources for your running kernel are installed on your machine. These
packages are available on your distribution's installation CD.
[ Press Enter key to continue ]
</pre><p>After continuing you will be asked to confirm two more compiles for vmxnet3 then pvscsi, and then you pretty much done;</p><pre class="qoate-code">
Creating a new initrd boot image for the kernel.
&#13;
Kernel image:   /boot/vmlinuz-2.6.32.12-0.7-default
Initrd image:   /boot/initrd-2.6.32.12-0.7-default
Root device:    /dev/sda2 (mounted on / as ext3)
Resume device:  /dev/sda1
Kernel Modules: vmxnet hwmon thermal_sys thermal scsi_mod
scsi_transport_spi mptbase mptscsih mptspi libata ata_piix ata_generic
ide-core piix ide-pci-generic processor fan jbd mbcache ext3 edd
crc-t10dif sd_mod
Features:       block resume.userspace resume.kernel
Bootsplash:     SLES (800x600)
28276 blocks
No X install found.
&#13;
  Checking acpi hot plug                                              done
Starting VMware Tools services in the virtual machine:
  Switching to guest configuration:                                   done
  Paravirtual SCSI module:                                            done
  Guest memory manager:                                               done
  Guest operating system daemon:                                      done
&#13;
The configuration of VMware Tools for Linux for this running
kernel completed successfully.
&#13;
You must restart your X session before any mouse or graphics changes take
effect.
&#13;
You can now run VMware Tools by invoking the following command:
"/usr/bin/vmware-toolbox" during an X server session.
&#13;
To enable advanced X features (e.g., guest resolution fit, drag and drop, and
file and text copy/paste), you will need to do one (or more) of the following:
1. Manually start /usr/bin/vmware-user
2. Log out and log back into your desktop session; and,
3. Restart your X session.
</pre><p>And that&#8217;s it &#8211; you&#8217;ve swapped out open-vm-tools for the official VMware Tools.</p> <img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/GPn4wVigMlA" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/05/install-vmware-tools-on-suse-11-sp1-guest/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>SEO, Panda and Above the Fold</title><link>http://www.arboundy.com/2012/05/seo-panda-and-above-the-fold/</link> <comments>http://www.arboundy.com/2012/05/seo-panda-and-above-the-fold/#comments</comments> <pubDate>Mon, 07 May 2012 15:02:20 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[Internet]]></category> <category><![CDATA[SEO]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1363</guid> <description><![CDATA[Some time ago I wrote a post titled <a
href="http://www.arboundy.com/2011/01/grumpy-old-man-sick-of-seo-internet-advertising/">“Grump Old Man Sick of SEO</a>” which was a naïve complaint about over the top SEO (black hat) and excessive advertising. It looks like recent changes to Google's algorithm are targeting those very things.]]></description> <content:encoded><![CDATA[<p>Some time ago I wrote a post titled <a
href="http://www.arboundy.com/2011/01/grumpy-old-man-sick-of-seo-internet-advertising/">“Grump Old Man Sick of SEO</a>” which was a naïve complaint about over the top SEO (black hat) and excessive advertising. It was in reaction to a site which was a maze to read and every second word was an advertisement;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/05/Stacking-Ads-Above-the-Fold.png" alt="" title="Stacking Ads Above the Fold" width="326" height="231" class="aligncenter size-full wp-image-1364" /></p><p>Well it appears Google have made another series of algorithm changes aimed at detecting these type of sites and reduce the ranking in the results.</p><p>In January Google announced the <a
href="http://insidesearch.blogspot.com.au/2012/01/page-layout-algorithm-improvement.html">Page Layout Algorithm</a> improvement stating;</p><blockquote><p>…we’ve heard complaints from users that if they click on a result and it’s difficult to find the actual content, they aren’t happy with the experience. Rather than scrolling down the page past a slew of ads, users want to see content right away.</p></blockquote><p>The claim given this adjustment, sites with high ads and low content above-the-fold “<strong><em>may not rank as highly going forward</em></strong>”.</p><p>Above-the-fold is typically recognized as the first 600pixels deep of a webpage, eg for my one of my pages;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/05/My-Site-Above-the-Fold.png" alt="" title="My Site Above the Fold" width="216" height="172" class="aligncenter size-full wp-image-1365" /></p><p>This adjustment apparently detects pages with stacked ads / low content in this “first seen” part of the page. Google claim it won’t affect a lot of sites, but it does appear to be a warning.</p><p>There is some great information and research of the impact of Above the Fold by <a
href="http://www.useit.com/jakob/">Jakob Nielsen</a> in his post “<a
href="http://www.useit.com/alertbox/scrolling-attention.html">Scrolling and Attention</a>”. Of course there is the counter view, such as <a
href="http://lefft.com/">Paddy Donnelly’s</a> &#8220;<a
href="http://iampaddy.com/lifebelow600/">Life Below 600</a>&#8220;.</p><p>In February Google announced a significant algorithm change known as <a
href="http://googleblog.blogspot.com.au/2011/02/finding-more-high-quality-sites-in.html">Panda</a> which by their own calculation affects nearly 12% of all queries. Initially applicable to US queries only, in <a
href="http://insidesearch.blogspot.com.au/2012/04/another-step-to-reward-high-quality.html">April</a> it was expanded to include all languages and aims to reduce the ranking of site of low quality, low value, copied content and sites “<strong><em>that are just not very useful</em></strong>”.</p><p>Typically not many specifics have been given, Google resist making too many statements to prevent reverse engineering, but it’s interesting to note in their April announcement it specifically gave the example of those hated hot word ads.</p><p>So some significant changes which are targeting some of the sites I mentioned in my Grumpy SEO post – I think the layout algorithm will be the most interesting to watch, as a number of sites, even popular ones have this type of design.</p> <img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/hUsBOW6Aaj8" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/05/seo-panda-and-above-the-fold/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Common Queries Against Google Analytics in C#</title><link>http://www.arboundy.com/2012/04/common-queries-against-google-analytics-in-c/</link> <comments>http://www.arboundy.com/2012/04/common-queries-against-google-analytics-in-c/#comments</comments> <pubDate>Sat, 28 Apr 2012 13:20:45 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[C#]]></category> <category><![CDATA[SEO]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1341</guid> <description><![CDATA[I’m looking at stripping the entire data from Google Analytics to use in some ideas and the type of data you want all depends on the content of your data query so I thought I’d list some of the most common data query types to help any others looking to do something similar.]]></description> <content:encoded><![CDATA[<div
class="info_box"> This is a follow up to a previous post you should check out first <a
href="http://www.arboundy.com/2012/04/getting-started-with-google-analytics-in-c/">here</a>.</div><p></p><p>Yesterday I wrote about <a
href="http://www.arboundy.com/2012/04/getting-started-with-google-analytics-in-c/">getting started with Google Analytics in C#</a> to get a simple list of visits in the last 7 days;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/04/PageViews.png" alt="" title="PageViews" width="239" height="147" class="aligncenter size-full wp-image-1326" /></p><p>The type of data you want all depends on the content of your data query, in that example</p><pre class="qoate-code">
DataQuery PageViewQuery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:date",
    Metrics = "ga:pageviews",
    Sort = "ga:date",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
</pre><p>I must admit I&#8217;m looking at stripping the entire data from Google Analytics to use in some ideas, so I thought I&#8217;d list some of the most common data query types to help any others looking to do something similar.</p><p>By the way &#8211; the output of any times (for instance ga:timeOnSite) I have left as the raw value of number of seconds. If you want to convert that to something more human readable you can user something like;</p><pre class="qoate-code">
TimeSpan timeOnSite  = TimeSpan.FromSeconds(value);
Console.WriteLine(String.Format("{0}:{1}", timeOnSite.Hours, timeOnSite.Minutes));
</pre><h3>Visits By Country</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:country",
    Metrics = "ga:visits",
    Sort = "-ga:visits",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(String.Format("{0}\t{1}",
    Entry.Title.Text.Split('=')[1],
    Entry.Metrics[0].Value));
</pre><h3>Visitors and Pageviews Over Time</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Metrics = "ga:visits,ga:pageviews",
    Sort = "-ga:visits",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(String.Format("Visits: {0}", Entry.Metrics[0].Value));
Console.WriteLine(String.Format("Pageviews: {0}", Entry.Metrics[1].Value));
</pre><h3>New vs Returning Visits</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:visitorType",
    Metrics = "ga:visits",
    Sort = "ga:visits",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(String.Format("{0}\t{1}",
    Entry.Title.Text.Split('=')[1],
    Entry.Metrics[0].Value));
</pre><h3>Browser and Operating System</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:operatingSystem,ga:operatingSystemVersion,ga:browser,ga:browserVersion",
    Metrics = "ga:visits",
    Sort = "-ga:visits",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(String.Format("{0}{1}using {2}{3}\t{4}",
    values[0].Split('=')[1], values[1].Split('=')[1],
    values[2].Split('=')[1], values[3].Split('=')[1],
    Entry.Metrics[0].Value));
</pre><h3>Time on Site</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Metrics = "ga:visits,ga:timeOnSite",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(String.Format("\tVisits: {0}", Entry.Metrics[0].Value));
Console.WriteLine(String.Format("\tTimeOnSite: {0}", Entry.Metrics[1].Value));
</pre><h3>Referring Sites</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:source",
    Metrics = "ga:pageviews,ga:timeOnSite,ga:exits",
    Filters = "ga:medium==referral",
    Sort = "-ga:pageviews",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(Entry.Title.Text.Split('=')[1]);
Console.WriteLine(String.Format("\tPageViews: {0}", Entry.Metrics[0].Value));
Console.WriteLine(String.Format("\tTimeOnSite: {0}", Entry.Metrics[1].Value));
Console.WriteLine(String.Format("\texits: {0}", Entry.Metrics[1].Value));
</pre><h3>Keywords</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:keyword",
    Metrics = "ga:visits",
    Sort = "-ga:visits",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(String.Format("{0}\t{1}",
    Entry.Title.Text.Split('=')[1],
    Entry.Metrics[0].Value));
</pre><h3>Search Engines</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:source",
    Metrics = "ga:pageviews,ga:timeOnSite,ga:exits",
    Filters = "ga:medium==cpa,ga:medium==cpc,ga:medium==cpm,ga:medium==cpp,ga:medium==cpv,ga:medium==organic,ga:medium==ppc",
    Sort = "-ga:pageviews",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(Entry.Title.Text.Split('=')[1]);
Console.WriteLine(String.Format("\tPageViews: {0}", Entry.Metrics[0].Value));
Console.WriteLine(String.Format("\tTimeOnSite: {0}", Entry.Metrics[1].Value));
Console.WriteLine(String.Format("\texits: {0}", Entry.Metrics[1].Value));
</pre><h3>Search Engines &#8211; Organic Search</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:source",
    Metrics = "ga:pageviews,ga:timeOnSite,ga:exits",
    Filters = "ga:medium==organic",
    Sort = "-ga:pageviews",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(Entry.Title.Text.Split('=')[1]);
Console.WriteLine(String.Format("\tPageViews: {0}", Entry.Metrics[0].Value));
Console.WriteLine(String.Format("\tTimeOnSite: {0}", Entry.Metrics[1].Value));
Console.WriteLine(String.Format("\texits: {0}", Entry.Metrics[1].Value));
</pre><h3>Search Engines &#8211; Paid Search</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:source",
    Metrics = "ga:pageviews,ga:timeOnSite,ga:exits",
    Filters = "ga:medium==cpa,ga:medium==cpc,ga:medium==cpm,ga:medium==cpp,ga:medium==cpv,ga:medium==ppc",
    Sort = "-ga:pageviews",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
Console.WriteLine(Entry.Title.Text.Split('=')[1]);
Console.WriteLine(String.Format("\tPageViews: {0}", Entry.Metrics[0].Value));
Console.WriteLine(String.Format("\tTimeOnSite: {0}", Entry.Metrics[1].Value));
Console.WriteLine(String.Format("\texits: {0}", Entry.Metrics[1].Value));
</pre><h3>Top Content</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:pagePath",
    Metrics = "ga:pageviews,ga:uniquePageviews,ga:timeOnPage,ga:bounces,ga:entrances,ga:exits",
    Sort = "-ga:pageviews",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(String.Format("{0}\t{1}",
    Entry.Title.Text.Split('=')[1],
    Entry.Metrics[0].Value));
</pre><h3>Top Landing Pages</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:landingPagePath",
    Metrics = "ga:entrances,ga:bounces",
    Sort = "-ga:entrances",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(Entry.Title.Text.Split('=')[1]);
Console.WriteLine(String.Format("\tEntrances: {0}", Entry.Metrics[0].Value));
Console.WriteLine(String.Format("\tBounces: {0}", Entry.Metrics[1].Value));
</pre><h3>Top Exit Pages</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:exitPagePath",
    Metrics = "ga:exits,ga:pageviews",
    Sort = "-ga:exits",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(Entry.Title.Text.Split('=')[1]);
Console.WriteLine(String.Format("\tExits: {0}", Entry.Metrics[0].Value));
Console.WriteLine(String.Format("\tPageViews: {0}", Entry.Metrics[1].Value));
</pre><h3>Site Search &#8211; Search Terms</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:searchKeyword",
    Metrics = "ga:searchUniques",
    Sort = "-ga:earchUniques",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(String.Format("{0}\t{1}",
    Entry.Title.Text.Split('=')[1],
    Entry.Metrics[0].Value));
</pre><h3>Mobile Traffic</h3><pre class="qoate-code">
DataQuery dataquery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:browser,ga:operatingSystem,ga:source",
    Metrics = "ga:visits,ga:pageviews,ga:timeOnSite",
    Segment = "gaid::-11",
    Sort = "ga:operatingSystem",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
...
Console.WriteLine(Entry.Title.Text);
Console.WriteLine(String.Format("\tVisits: {0}", Entry.Metrics[0].Value));
Console.WriteLine(String.Format("\tPageviews: {0}", Entry.Metrics[1].Value));
Console.WriteLine(String.Format("\tTimeOnSite: {0}", Entry.Metrics[1].Value));
</pre><img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/EH5F1ZuNDSI" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/04/common-queries-against-google-analytics-in-c/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Getting Started with Google Analytics in C#</title><link>http://www.arboundy.com/2012/04/getting-started-with-google-analytics-in-c/</link> <comments>http://www.arboundy.com/2012/04/getting-started-with-google-analytics-in-c/#comments</comments> <pubDate>Fri, 27 Apr 2012 15:39:50 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[C#]]></category> <category><![CDATA[SEO]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1322</guid> <description><![CDATA[A simple demonstration of how you can get your Google Analytics statistics using Google.GData.Analytics.dl in .Net / C#]]></description> <content:encoded><![CDATA[<p>Now here is a fun one.</p><p>Everyone uses Google Analytics it has amazingly valuable information that you not only want to analyse but often publish on your website or application &#8211; and it isnt that hard to tap into it via C#.</p><p>The first thing you need are Google Analytics C# libraries. Id like to say they are available at the project site (<a
href="http://google-gdata.googlecode.com">http://google-gdata.googlecode.com</a>) but unfortunately the links are broken. After a bit of hunting I found them in the SVN repository at <a
href="http://google-gdata.googlecode.com/svn/trunk/clients/cs/lib/Release/">http://google-gdata.googlecode.com/svn/trunk/clients/cs/lib/Release/</a>.</p><p>You want to get all the dll&#8217;s;</p><p>Google.GData.AccessControl.dll, Google.GData.Analytics.dll, Google.GData.Apps.dll, Google.GData.Blogger.dll, Google.GData.Calendar.dll, Google.GData.Client.dll, Google.GData.Contacts.dll, Google.GData.ContentForShopping.dll, Google.GData.Documents.dll, Google.GData.Extensions.dll, Google.GData.Photos.dll, Google.GData.Spreadsheets.dll, Google.GData.WebmasterTools.dll, Google.GData.YouTube.dll</p><p>- I have also zipped them and included them at the bottom of the page.</p><p>Depending on what you want to do later you may also want to get NewtonSoft.Json.dll which is an amazing Json to C# serializer by Jason Newton-King (<a
href="http://james.newtonking.com/pages/json-net.aspx">http://james.newtonking.com/pages/json-net.aspx</a>) which I use for <a
href="bf3mobile.com">bf3mobile.com</a> &#8211; if you have a ton of json data it is honey from heaven.</p><p>This simple project is also going to use Google.GData.Client.dll and Google.GData.Analytics.dll &#8211; and its a little console app to find your pageview count per day for the last seven days.</p><p>Firstly lets declare the properties;</p><pre class="qoate-code">
public static string username { get; set; }
&#13;
public static string password { get; set;}
&#13;
public static AnalyticsService Service { get; set; }
&#13;
public static string AccountsFeedUrl { get; set; }
&#13;
public static string DataFeedUrl { get; set; }
</pre><p>Now normally when you want to interigate the Analytics data for a specific website you need three things &#8211; your username, password and the profile ID of the website &#8211; which looks something like ga:12345678 &#8211; not something you often have handy &#8211; so the first part of the code is going to search all your websites by name.</p><p>You get to your list of websites via the accounts feed url &#8220;https://www.google.com/analytics/feeds/accounts/default&#8221;;</p><pre class="qoate-code">
//set your username and password
username = "JohnCitizen@gmail.com";
password = "Password123";
&#13;
//set the accounts feed url
AccountsFeedUrl = "https://www.google.com/analytics/feeds/accounts/default";
&#13;
//bind to the google analytics service
Service = new AnalyticsService("AnalyticsSampleApp");
Service.setUserCredentials(username, password);
</pre><p>Now that you have bound to Google Analytics you create an open search;</p><pre class="qoate-code">
AccountQuery AccountsQuery = new AccountQuery(AccountsFeedUrl);
AccountFeed AccountsFeed = Service.Query(AccountsQuery);
</pre><p>The results come back a series of AtomEntry entries, so you can simply roll through them looking for your website;</p><pre class="qoate-code">
List&lt;AtomEntry&gt; Accounts = AccountsFeed.Entries.ToList();
foreach(AtomEntry Account in Accounts)
    if(Account.Title.Text.Equals("www.arboundy.com"))
    {
        GetPageViews(Account);
    }
</pre><p>and call your method, in this example GetPageViews.</p><p>To get data about your site you use the data feed url  &#8220;https://www.google.com/analytics/feeds/data&#8221; and this is where you need the site&#8217;s profile ID, which you can get from your now found AtomEntry;</p><pre class="qoate-code">
string ProfileID = Account.Id.AbsoluteUri.Substring(47);
DataFeedUrl = "https://www.google.com/analytics/feeds/data";
</pre><p>Next you have to create a DataQuery with the parameters defining your search;</p><pre class="qoate-code">
DataQuery PageViewQuery = new DataQuery(DataFeedUrl)
{
    Ids = ProfileID,
    Dimensions = "ga:date",
    Metrics = "ga:pageviews",
    Sort = "ga:date",
    GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
    GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
};
</pre><div
class="info_box"> Be sure to check out my other post list all the common data queries <a
href="http://www.arboundy.com/2012/04/common-queries-against-google-analytics-in-c/">here</a>.</div><p></p><p>All fairly readable &#8211; we are look for ga:pageviews sorted by ga:date from GAStartDate to the GAEndDate (both in yyyy-MM-dd). Naturally your could sort by ga:pageviews instead. (Note the sort is by default ascending, if you want to sort descending you put a minus in front, ie: Sort = &#8220;-ga:date&#8221;)</p><p>Armed with your query you get the results;</p><pre class="qoate-code">
DataFeed Results = Service.Query(PageViewQuery);
</pre><p>The results come back one per dimension, which we defined as ga:date &#8211; so there will be seven entries. If we didnt define the dimension, there would be only one result &#8211; the sum of all the results &#8211; ie the sum of all seven days.</p><p>Each result comes back as an AtomEntry &#8211; and they are fairly complicated. Ends out to make easy sense of them you cast the as a DataEntry;</p><pre class="qoate-code">
foreach (AtomEntry Result in Results.Entries)
{
    DataEntry Entry = (DataEntry)Result;
</pre><p>Your title is your Entry.Title.Text which will come our something like ga:date=20120421 and your value will be Entry.Metrics[0].Value &#8211; so with a simple loop;</p><pre class="qoate-code">
foreach (AtomEntry Result in Results.Entries)
{
    DataEntry Entry = (DataEntry)Result;
    Console.WriteLine(String.Format("{0}\t{1}",
        Entry.Title.Text.Split('=')[1],
        Entry.Metrics[0].Value));
};
</pre><p>you get</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/04/PageViews.png" alt="" title="PageViews" width="239" height="147" class="aligncenter size-full wp-image-1326" /></p><p>Not bad. As said a copy of the libraries is below plus the full code.</p><div
class="download_box"> <a
href="http://www.arboundy.com/wp-content/uploads/2012/04/Google.GData_.Libraries.zip">Cached copy of Google GData Libraries from my site (ref note below)</a></div><p>.</p><div
class="warning_box"> Please note you should always attempt to download from the official links, I provide this cached copy purely to assist those having difficulty downloading it and am not responsible for any errors or loss caused by its usage</div><p>.</p><pre class="qoate-code">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Google.GData.Analytics;
using Google.GData.Client;
&#13;
namespace Google.Analytics
{
    class Program
    {
        public static string username { get; set; }
&#13;
        public static string password { get; set;}
&#13;
        public static AnalyticsService Service { get; set; }
&#13;
        public static string AccountsFeedUrl { get; set; }
&#13;
        public static string DataFeedUrl { get; set; }
&#13;
        static void Main(string[] args)
        {
            username = "JohnCitizen@gmail.com";
            password = "Password123";
            AccountsFeedUrl =
               "https://www.google.com/analytics/feeds/accounts/default";
&#13;
            Service = new AnalyticsService("AnalyticsSampleApp");
            Service.setUserCredentials(username, password);
&#13;
            AccountQuery AccountsQuery = new AccountQuery(AccountsFeedUrl);
            AccountFeed AccountsFeed = Service.Query(AccountsQuery);
&#13;
            List&lt;AtomEntry&gt; Accounts = AccountsFeed.Entries.ToList();
            foreach (AtomEntry Account in Accounts)
                if (Account.Title.Text.Equals("www.arboundy.com"))
                {
                    GetPageViews(Account);
                }
        }
&#13;
        private static void GetPageViews(AtomEntry Account)
        {
            string ProfileID = Account.Id.AbsoluteUri.Substring(47);
            DataFeedUrl = "https://www.google.com/analytics/feeds/data";
&#13;
            DataQuery PageViewQuery = new DataQuery(DataFeedUrl)
            {
                Ids = ProfileID,
                Dimensions = "ga:date",
                Metrics = "ga:pageviews",
                Sort = "ga:date",
                GAStartDate = (DateTime.Now).AddDays(-7).ToString("yyyy-MM-dd"),
                GAEndDate = (DateTime.Now).ToString("yyyy-MM-dd")
            };
&#13;
            DataFeed Results = Service.Query(PageViewQuery);
&#13;
            foreach (AtomEntry Result in Results.Entries)
            {
                DataEntry Entry = (DataEntry)Result;
&#13;
                Console.WriteLine(String.Format("{0}\t{1}",
                    Entry.Title.Text.Split('=')[1],
                    Entry.Metrics[0].Value));
            }
        }
    }
}
</pre><img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/R6Joid02Km4" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/04/getting-started-with-google-analytics-in-c/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Why VMware ESX / vSphere is your Friend</title><link>http://www.arboundy.com/2012/04/why-vmware-esx-vsphere-is-your-friend/</link> <comments>http://www.arboundy.com/2012/04/why-vmware-esx-vsphere-is-your-friend/#comments</comments> <pubDate>Thu, 12 Apr 2012 11:50:34 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[ESX]]></category> <category><![CDATA[Infrastructure]]></category> <category><![CDATA[Technology]]></category> <category><![CDATA[VMware]]></category> <category><![CDATA[vSphere]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1250</guid> <description><![CDATA[I can tell you that due to issues we have bashed our ESX Cluster like an old shoe. We have had hardware black screen of deaths, hard reboots, crashed LUN's, filled LUN's, disappearing LUN's, disappearing fibre cards, disappearing NIC's - the lot. The servers have been opened and reopened, components on the desk like an server massacre and people wearing three anti static straps just for luck.]]></description> <content:encoded><![CDATA[<p>I must admit when four years ago we looked at replacing our stock server next to server next to server environment with clustered VMware ESX I was hesitant. Sure the technology worked in a Development / Lab but running your LIVE servers virtualized &#8211; all those eggs in one basket!?!? I was quickly converted, and in the last few months have become a preacher.</p><p>I have been working on the project of transitioning from the old ESX infrastructure to the new infrastructure, a project which includes everything from replacing the SAN&#8217;s, Hosts, upgrading ESX, changing from a Fibre to a Ethernet fabric &#8211; the works.</p><p>We had a plan, and no this isn&#8217;t a story of how bad that plan went. Every issue we have experienced was during nominated maintenance windows and we had the ultimate fail-over of restoring &#8211; this isn&#8217;t an incompetence story.</p><p>Anyway, we had a plan, a series of tasks and test that would see us transition from the old to the new, with a period of running both at the same time as the guests were migrated;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/04/PlanA.png" alt="" title="PlanA" width="345" height="83" class="aligncenter size-full wp-image-1251" /></p><p>This forecast and acknowledged risks during the migration stage. But due to factors including the Global Financial Crisis and hardware incompatibilities we had to extend the life of the legacy infrastructure much further than we liked and had delays in commissioning the new infrastructure, thus extending our risk period;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/04/PlanB.png" alt="" title="PlanB" width="344" height="83" class="aligncenter size-full wp-image-1252" /></p><p>I can tell you that due to issues we have bashed our ESX Cluster like an old shoe. We have had hardware black screen of deaths, hard reboots, crashed LUN&#8217;s, filled LUN&#8217;s, disappearing LUN&#8217;s, disappearing fibre cards, disappearing NIC&#8217;s &#8211; the lot. The servers have been opened and reopened, components on the desk like an server massacre and people wearing three anti static straps just for luck.</p><p>And VMware has pulled through every time.</p><p>Every time something went pop Im there with my recovery plan thinking &#8220;the Windows guests are toast&#8221;, we get it together and VMware just says &#8220;Cheers!&#8221; and everything comes online.</p><p>Ive become a broken record in the server room, constantly say how I cant believe it pulled it back together. Im almost willing to claim that I could abuse its parents and chuck a cup of water at it and VMware will just recover with a smile.</p><p>Sure VMware creates a new capacity, performance and management style &#8211; but if you treat it with respect &#8211; Im at the point of saying it can do anything.</p> <img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/uRmgvkvA08E" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/04/why-vmware-esx-vsphere-is-your-friend/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Now that’s a Cluster</title><link>http://www.arboundy.com/2012/03/now-thats-a-cluster/</link> <comments>http://www.arboundy.com/2012/03/now-thats-a-cluster/#comments</comments> <pubDate>Fri, 30 Mar 2012 20:53:20 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[Internet]]></category> <category><![CDATA[Monitoring]]></category> <category><![CDATA[Technology]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1265</guid> <description><![CDATA[At the time of writing they had 748 hosts with 10,382 CPU's and 15 Terrabytes of RAM while serving a few Gigabytes per second of outgoing data and a couple Gigabytes of incoming data...]]></description> <content:encoded><![CDATA[<p><img
src="http://www.arboundy.com/wp-content/uploads/2012/04/NowThatsAKnife.png" alt="" title="NowThatsAKnife" width="203" height="202" class="alignleft size-full wp-image-1267" />Some time ago I wrote about <a
href="http://www.arboundy.com/2011/06/wikimedia-strategic-plan-the-future-of-wikipedia/">Wikimedia&#8217;s Strategic Plan – A collaborative vision for the movement through 2015</a> which talked about (amongst many things) how they wanted to increase the total number of people served to 1 billion and the number of Wikipedia articles they offer to 50 million.</p><p>The other night I was doing some research of load balancers and http accelerators which let me to to HAProxy (<a
href="http://haproxy.1wt.eu">http://haproxy.1wt.eu</a>) which led me to the cluster monitoring solution Ganglia (<a
href="http://ganglia.sourceforge.net/">http://ganglia.sourceforge.net</a>) which led me to <a
href="http://ganglia.wikimedia.org/latest/?r=hour&#038;cs=&#038;ce=&#038;m=&#038;s=by+name&#038;c=&#038;tab=m&#038;vn=">Wikimedia&#8217;s Live Grid Report</a> (don&#8217;t you just love the internet?).</p><p>Its available for anyone too look at and shows the utilisation of their cluster. At the time of writing they had 748 hosts with 10,382 CPU&#8217;s and 15 Terrabytes of RAM while serving a few Gigabytes per second of outgoing data and a couple Gigabytes of incoming data&#8230;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/04/Wikipedia-Load.png" alt="" title="Wikipedia Load" width="298" height="218" class="aligncenter size-full wp-image-1269" /></p><p>and it reminded me how there is a whole different world out there most of us don&#8217;t get to see.</p><p>I mean I get happy walking around 60+ soloed but 750 clustered &#8211; that would be impressive.</p><p>If you hunt around you can even see the individual hosts and sure they have got some 24&#215;2.66 GHz 11Gb RAM 250Gb HD units but the majority appear to be nice little 8&#215;2.XX GHz 8Gb RAM 72Gb HD ones &#8211; that Google type of many small low cost units clustered together &#8230;</p><p><a
href="http://news.cnet.com/8301-1001_3-10209580-92.html"><img
src="http://www.arboundy.com/wp-content/uploads/2012/04/Google-Server-300x172.png" alt="" title="Google Server" width="300" height="172" class="aligncenter size-medium wp-image-1271" /></a></p><p>meaning individual failures, even many individual failures have minimal impact to the overall environment.</p><p>Looking through the cluster stats and configs made me quite jealous &#8211; I want one!</p> <img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/XJ853fOF50A" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/03/now-thats-a-cluster/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Developing an iPhone Web App in C# – Part 2</title><link>http://www.arboundy.com/2012/02/developing-an-iphone-web-app-in-csharp-part/</link> <comments>http://www.arboundy.com/2012/02/developing-an-iphone-web-app-in-csharp-part/#comments</comments> <pubDate>Sun, 05 Feb 2012 12:50:21 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[.Net]]></category> <category><![CDATA[C#]]></category> <category><![CDATA[iPad]]></category> <category><![CDATA[iPhone]]></category> <category><![CDATA[JavaScript]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1180</guid> <description><![CDATA[Part 2 of Developing an iPhone Web App in C# move to creating an installation footer, hiding the address bar, and creating a Home Screen icon..]]></description> <content:encoded><![CDATA[<p>Please use the page tabs to navigate through the article.<a
name="TOP"></a><br
/></p><div
id="ctabs"><ul
class="nav"><li
class="Hiding_the_Address_Bar"><a
href="#Hiding_the_Address_Bar" class="current">Hiding the Address Bar</a></li><li
class="Creating_an_Add_to_Home_Screen_Footer"><a
href="#Creating_an_Add_to_Home_Screen_Footer" class="">Creating an Add to Home Screen Footer</a></li><li
class="Creating_an_Custom_Home_Screen_Icon"><a
href="#Creating_an_Custom_Home_Screen_Icon" class="">Creating an Custom Home Screen Icon</a></li></ul><p></p><div
class="list-wrap"><div
id="Hiding_the_Address_Bar" class=""><p>In <a
href="http://www.arboundy.com/2012/02/developing-an-iphone-web-app-in-csharp-part-1/">part 1</a> we creates an aspx page which detected whether the user&#8217;s device was either a PC, iPad, iPhone or Andriod, and if iPhone presented a tailored experience. We also locked this experience by disabling the zoom and drag features using metatgs and javascript.</p><p>So far the behind code is</p><pre class="qoate-code">
protected void Page_Load(object sender, EventArgs e)
{
    Set_Env();
}
&#13;
private void Set_Env()
{
    if (Request.UserAgent.Contains("iPhone"))
        Set_Env_iPhone();
    else if (Request.UserAgent.Contains("iPad"))
        Label1.Text = "iPad";
    else if (Request.UserAgent.Contains("Andriod"))
        Label1.Text = "Andriod";
    else Label1.Text = "PC";
}
&#13;
private void Set_Env_iPhone()
{
    Main.Height = 712;
    Main.Width = 640;
&#13;
    Main.BackColor = System.Drawing.Color.Black;
    Footer.BackColor = System.Drawing.Color.OliveDrab;
&#13;
    //add viewport metatag
    //&lt;meta name="viewport" content="initial-scale=0.5, user-scalable=no" /&gt;
&#13;
    System.Web.UI.HtmlControls.HtmlMeta mneta1 =
        new System.Web.UI.HtmlControls.HtmlMeta
        {
            Name = "viewport",
            Content = "initial-scale=0.5, user-scalable=no"
        };
    this.Header.Controls.Add(mneta1);
}
</pre><p>and it renders on the iPhone like;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Begin-Part2.png" alt="" title="Begin Part2" width="193" height="287" class="aligncenter size-full wp-image-1182" /></p><p>Now I want to include the footer. So far Main is set to a height of 712 pixels and the scale is set to 0.5, so if I want to include I have to reduce the Main.Height to 662 pixels and set the Footer.Height to 50 pixels;</p><pre class="qoate-code">
Main.Height = 622;
Main.Width = 640;
&#13;
Footer.Height = 90;
Footer.Width = 640;
</pre><p>and I have added a margin = 0 to the body to tightened everything up</p><pre class="qoate-code">
&lt;body style="margin: 0" ontouchmove="BlockOnTouchMove(event);"&gt;
</pre><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Now-with-Footer1.png" alt="" title="Now with Footer" width="192" height="287" class="aligncenter size-full wp-image-1186" /></p><p>Not happy with the green but I can fiddle with that later. I am also not happy with the address bar across the top, it will make convincing the user to install the &#8220;app&#8221; harder.</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Address-Bar.png" alt="" title="Address Bar" width="192" height="39" class="aligncenter size-full wp-image-1188" /></p><h3>Hiding the Address Bar</h3><p>There are a number of ways that you can hide the address bar but probably the easiest is by adding an onload to your body;</p><pre class="qoate-code">
onload="setTimeout(function() { window.scrollTo(0, 1) }, 100);"
</pre><p>Now when I did this the first time absolutely nothing happened. Whilst trying to figure it out I found that if I put a br below my footer I had some movement;</p><pre class="qoate-code">
&lt;asp:UpdatePanel ID="UpdatePanel1" runat="server"&gt;
    &lt;ContentTemplate&gt;
        &lt;asp:Panel ID="Main" runat="server"&gt;
            &lt;asp:Label ID="Label1" runat="server" Text="Label"&gt;&lt;/asp:Label&gt;
        &lt;/asp:Panel&gt;
        &lt;asp:Panel ID="Footer" runat="server"&gt;
        &lt;/asp:Panel&gt;
        &lt;br/&gt;
    &lt;/ContentTemplate&gt;
&lt;/asp:UpdatePanel&gt;
</pre><p>If I added a lot of br&#8217;s I had a lot of movement;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Hiding-Address-Bar.png" alt="" title="Hiding Address Bar" width="519" height="266" class="aligncenter size-full wp-image-1191" /></p><p>It appears that to hide the addess panel your pages height must be greater than the viewport size for it to disappear. I began fiddling with Main.Height and found that an extra 78 pixels was the perfect amount for my page;</p><pre class="qoate-code">
Main.Height = 662 + 78; //extra for address bar
</pre><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Address-Bar-Hidden.png" alt="" title="Address Bar Hidden" width="194" height="288" class="aligncenter size-full wp-image-1195" /></p><p><a
href="#TOP">Click here to go to the top of the page and select the next tab to continue.!</a></p></div><div
id="Creating_an_Add_to_Home_Screen_Footer" class="hide"><p>We all know that you can bookmark or add a short-cut to a website on the home screen of the iPhone. However if this is done with certain metatags in place the iPhone turns the application into a web app, creating a whole different user experience including offline capabilities and improved performance through caching.</p><p>I want to use the initial footer of my page to prompt the user to Add to Home Screen. To do this I am going to change the footer colour so that it is the same as the button bar&#8217;s so it blends it and looks like it is part of the iPhone rather than the website. Using the Converting a <a
href="http://www.arboundy.com/code/c/converting-a-hexadecimal-color-to-a-system.drawing.color-object/">Hexidecimal Color to System.Drawing.Color</a> I have written about previously (<a
href="http://www.arboundy.com/code/c/converting-a-hexadecimal-color-to-a-system.drawing.color-object/">link</a>);</p><pre class="qoate-code">
Footer.BackColor = HTMLHexToColor("#A9B6C7");
</pre><p>Which doesnt look too bad;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Blank-Footer.png" alt="" title="Blank Footer" width="194" height="288" class="aligncenter size-full wp-image-1199" /></p><p>Now I want to add the below image to the footer;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Install-Instructions1.png" alt="" title="Install Instructions" width="320" height="42" class="aligncenter size-full wp-image-1203" /></p><pre class="qoate-code">
&lt;asp:Panel ID="Footer" runat="server"&gt;
    &lt;div id="div_install_footer" text-align: center"&gt;
        &lt;img src="img/InstallInstructions.png" width='620' /&gt;
    &lt;/div&gt;
&lt;/asp:Panel&gt;
</pre><p>results in;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/With-Install-Instructions.png" alt="" title="With Install Instructions" width="193" height="287" class="aligncenter size-full wp-image-1204" /></p><p>Of course if the user does that now we have two problems, the icon that will be created and used for the shortcut will be a screenshot of the webpage (and its not that thrilling at the moment) and the title will be &#8220;untitled&#8221;.</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Adding-to-Home-Screen.png" alt="" title="Adding to Home Screen" width="385" height="282" class="aligncenter size-full wp-image-1206" /></p><p><a
href="#TOP">Click here to go to the top of the page and select the next tab to continue.!</a></p></div><div
id="Creating_an_Custom_Home_Screen_Icon" class="hide"><p>The title of your icon is preset to the title of the web page. As I want the same page to cater for all experiences the title may change &#8211; On a PC it may be &#8220;Welcome to bf3mobile.com&#8221; whereas on an iPhone I want it short and snappy; &#8220;BF3 Stats&#8221;.</p><p>To set the title of your page dynamically you can use a protected string when the page is loaded;</p><pre class="qoate-code">
&lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="bfmobile002._Default" %&gt;
&#13;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head runat="server"&gt;
&#13;
    &lt;title&gt;&lt;%= this.Title %&gt;&lt;/title&gt;
&#13;
&lt;/head&gt;
&lt;script type="text/javascript"&gt;
    //block the moving / draging
    function BlockOnTouchMove(event) {
        event.preventDefault();
    }
&lt;/script&gt;
</pre><pre class="qoate-code">
private void Set_Env_iPhone()
{
    Main.Height = 662 + 78; //extra for address bar
    ...
    ...
&#13;
    //set the page title
    this.Title = "BF3 Stats";
&#13;
    ...
    ...
}
</pre><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Pre-Filled-in-Title.png" alt="" title="Pre Filled in Title" width="193" height="140" class="aligncenter size-full wp-image-1215" /></p><h3>Option One</h3><p>There are two ways to specify a custom icon for your Home shortcut. The first option is the easiest, simply create a 128 x 128 pixel image call apple-touch-icon.png and place it in the root of your website, similar to favicon.ico;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/apple-touch-icon.png" alt="" title="apple-touch-icon" width="128" height="128" class="aligncenter size-full wp-image-1216" /></p><p> The iPhone detects the presence of the icon and uses it;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Using-your-Custom-Icon.png" alt="" title="Using your Custom Icon" width="389" height="286" class="aligncenter size-full wp-image-1217" /></p><p>Ive deliberately created the most basic icon possible to show how the iPhone kindly adds the gradient effect for you, making even the simplest icon look pretty good.</p><h3>Option Two</h3><p>If for some reason the above option is not available (maybe you dont have root access to your site) you can have any 128 x 128 pixel png file as your icon and specify it in your pages head;</p><pre class="qoate-code">
&lt;link rel=&quot;apple-touch-icon&quot; href=&quot;path/myicon.png&quot;/&gt;
</pre><h3>End Part 2</h3><p>Well that&#8217;s the end of Part 2. Next we will be see what differences occur when opening the site from the URL versus the Home shortcut and how we have to cater for them.</p><p></p></div></p></div></div><p></p> <img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/hKtapFH1t2w" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/02/developing-an-iphone-web-app-in-csharp-part/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Developing an iPhone Web App in C# – Part 1</title><link>http://www.arboundy.com/2012/02/developing-an-iphone-web-app-in-csharp-part-1/</link> <comments>http://www.arboundy.com/2012/02/developing-an-iphone-web-app-in-csharp-part-1/#comments</comments> <pubDate>Sat, 04 Feb 2012 18:28:53 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[.Net]]></category> <category><![CDATA[C#]]></category> <category><![CDATA[iPad]]></category> <category><![CDATA[iPhone]]></category> <category><![CDATA[JavaScript]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1109</guid> <description><![CDATA[I want to extend my websites to include a tailored experience for PC versus iPhone, iPad and Andriod - however I want to leverage my C# skills. Part 1 of Developing an iPhone Web App in C# begins with creating the base aspx page, understanding how iPhone's Safari renders its viewport, and controlling the iPhone experience by preventing zoom and dragging with code behind.]]></description> <content:encoded><![CDATA[<p>Please use the page tabs to navigate through the article.<a
name="TOP"></a><br
/></p><div
id="ctabs"><ul
class="nav"><li
class="Intro"><a
href="#Intro" class="current">Intro</a></li><li
class="How_iPhone_Renders_Web_Pages_and_Modifying_in_Code_Behind"><a
href="#How_iPhone_Renders_Web_Pages_and_Modifying_in_Code_Behind" class="">How iPhone Renders Web Pages and Modifying in Code Behind</a></li><li
class="Creating_an_Custom_Home_Screen_Icon"><a
href="#Creating_an_Custom_Home_Screen_Icon" class="">Creating an Custom Home Screen Icon</a></li></ul><p></p><div
class="list-wrap"><div
id="Intro" class=""><p>I developed <a
href="http://bf3mobile.com">bf3mobile.com</a> as a bit of fun and to get an initiation into developing web sites / apps for mobile devices, iPhone in particular. It works and renders ok but it doesnt have the appearance and feel of a true app &#8211; in reality it is a webpage that iPhone / Andriod renders to look ok on the screen.</p><p>It ends out you can use a series of features and tricks to create a more powerful experience and roll your website into and effective web app on the iPhone. What I want to do is use <a
href="http://bf3mobile.com">bf3mobile.com</a> to create a template that caters for all variants of the incoming user &#8211; all with one url, C# and javascript.</p><p>The diagram below shows the workflow I envisage;</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/02/Workflow.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Workflow.png" alt="" title="Workflow" width="345" height="487" class="aligncenter size-full wp-image-1171" /></a></p><p>In this workflow the user access the site on any device through the same url, http://bf3mobile.com. The site detects if the user is opening it on an a PC, Andriod, iPad or iPhone device and renders the specifically designed experience for it.</p><p>For iPad and iPhone, it also provides the ability of bookmarking the site, effectively turning it into a web app, something that is detected the next time the site is opened to further enhance the experience. The solution will have to be completely AJAX driven, as any formal postback&#8217;s or links out to other pages would break the web app experience &#8211; more on this later.</p><h3>Getting Started</h3><p>So lets look at the basic default.aspx;</p><pre class="qoate-code">
&lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="bfmobile002._Default" %&gt;
&#13;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head runat="server"&gt;
    &lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form id="form1" runat="server"&gt;
    &lt;asp:ScriptManager ID="ScriptManager1" runat="server"&gt;
    &lt;/asp:ScriptManager&gt;
    &lt;div&gt;
        &lt;asp:UpdatePanel ID="UpdatePanel1" runat="server"&gt;
            &lt;ContentTemplate&gt;
                &lt;asp:Panel ID="Main" runat="server"&gt;
                    &lt;asp:Label ID="Label1" runat="server" Text="Label"&gt;&lt;/asp:Label&gt;
                &lt;/asp:Panel&gt;
                &lt;asp:Panel ID="Footer" runat="server"&gt;
                &lt;/asp:Panel&gt;
            &lt;/ContentTemplate&gt;
        &lt;/asp:UpdatePanel&gt;
    &lt;/div&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre><p>It is my intention that other than for some Javascript and CSS this pretty much be the entire code for the main page. All the activity will be managed in the Main panel, either through the addition of elements or injecting of user controls. Label1 is just a tester that I have in there temporarily.</p><h3>Basic PC, iPad, iPhone and Android detection</h3><p>The first requirement is the detection of the device for the incoming request. The easiest (though not 100% fool proof) way of doing this is through Request.User agent;</p><pre class="qoate-code">
protected void Page_Load(object sender, EventArgs e)
{
    Set_Env();
}
&#13;
private void Set_Env()
{
    if (Request.UserAgent.Contains("iPhone"))
        Label1.Text = "iPhone";
    else if (Request.UserAgent.Contains("iPad"))
        Label1.Text = "iPad";
    else if (Request.UserAgent.Contains("Andriod"))
        Label1.Text = "Andriod";
    else Label1.Text = "PC";
}
</pre><p>Basic, but lets see if it works. If I open it in Chrome;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/PC-Detected1.png" alt="" title="PC Detected" width="323" height="114" class="aligncenter size-full wp-image-1175" /></p><p>All good, now on the iPhone;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/iPhone-Detected.png" alt="" title="iPhone Detected" width="268" height="346" class="aligncenter size-full wp-image-1139" /></p><p>and yes that tiny word says iPhone &#8211; zoomed in its much clearer. This exposes our first problem, how iPhone&#8217;s render pages in Safari.</p><p><a
href="#TOP">Click here to go to the top of the page and select the next tab to continue.!</a></p></div><div
id="How_iPhone_Renders_Web_Pages_and_Modifying_in_Code_Behind" class="hide"><p>Without any additional information iPhone&#8217;s Safari will take the incoming web site and do its best to render it on the screen (known as viewport) by scaling the site down. It does this very well, and from my testing it appears to have different scales for different objects (pictures versus text) but I havent confirmed that.</p><p>So a standard page like my site will be scaled down a size like below;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/My-Site-In-iPhone1.png" alt="" title="My Site In iPhone" width="194" height="287" class="aligncenter size-full wp-image-1147" /></p><p>and luckily for me it doesn&#8217;t look too bad.</p><p>For my first version of <a
href="http://bf3mobile.com">bf3mobile.com</a> I relied too much on this ability. When I first started building it I used normal size fonts etc thinking the iPhone would scale up but I ended up with it rendering really small on the sreen. So then I started building it big as I could see it was being scaled down and to get it to look good I would jimmy the layout.</p><p>I ended up with a page that on a PC has 45px font because proportionately it shrank down nice on the iPhone;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/45px-Font.png" alt="" title="45px Font" width="495" height="128" class="aligncenter size-full wp-image-1149" /></p><p>It ends out you can tell iPhone what to scale the site as. So when you consider that the iPhone&#8217;s Safari screen size is 320 pixels wide by 356 pixels high you can build a page that is 160 x 178 pixels and tell it to scale by 2;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Scale-By-2.png" alt="" title="Scale By 2" width="369" height="286" class="aligncenter size-full wp-image-1151" /></p><p>and similarly you can do the opposite of having a page that is 640 x 712 pixels and scaling it by 0.5.</p><p>You do this by setting a viewport metatag in your page. This tag is only used by Safari on your iPhone / iPad;</p><pre class="qoate-code">
&lt;meta name="viewport" content="initial-scale=0.5" /&gt;
</pre><p>In my app I want to run the thin entirely server side, so to inject this metatag at run time it involves my Set_Env() method;</p><pre class="qoate-code">
private void Set_Env()
{
    if (Request.UserAgent.Contains("iPhone"))
        Set_Env_iPhone();
    else if (Request.UserAgent.Contains("iPad"))
        Label1.Text = "iPad";
    else if (Request.UserAgent.Contains("Andriod"))
        Label1.Text = "Andriod";
    else Label1.Text = "PC";
}
&#13;
private void Set_Env_iPhone()
{
    Main.Height = 712;
    Main.Width = 640;
&#13;
    Main.BackColor = System.Drawing.Color.Black;
&#13;
    //add viewport metatag
    //&lt;meta name="viewport" content="initial-scale=0.5" /&gt;
&#13;
    System.Web.UI.HtmlControls.HtmlMeta meta1 =
        new System.Web.UI.HtmlControls.HtmlMeta
        {
            Name = "viewport",
            Content = "initial-scale=0.5"
        };
    this.Header.Controls.Add(meta1);
}
</pre><p>Now, when the page is opened and it is detected as being from an iPhone it will set my main panel to 712 x 640 and injected the metatag that tells the iPhone to scale by 0.5. The key here is the</p><pre class="qoate-code">
System.Web.UI.HtmlControls.HtmlMeta meta1 =
    new System.Web.UI.HtmlControls.HtmlMeta
    {
        Name = "viewport",
        Content = "initial-scale=0.5"
    };
this.Header.Controls.Add(meta1);
</pre><p>line, which allows the addition of the metatag a run time (you could use this for title and keywords too).</p><p>So how does it look running on a PC versus an iPhone?</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/02/Sale-by-0.5.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Sale-by-0.5.png" alt="" title="Sale by 0.5" width="357" height="286" class="aligncenter size-full wp-image-1153" /></a></p><p>and how is the rendered code different?</p><p>For the PC version is;</p><pre class="qoate-code">
&lt;head&gt;
    &lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form name="form1" method="post" action="default.aspx" id="form1"&gt;
&lt;div&gt;
&lt;input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /&gt;
...
</pre><p>but for the iPhone version it is;</p><pre class="qoate-code">
&lt;head&gt;
    &lt;title&gt;&lt;/title&gt;
    &lt;meta name="viewport" content="initial-scale=0.5" /&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form name="form1" method="post" action="Default.aspx" id="form1"&gt;
&lt;div&gt;
&lt;input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /&gt;
...
</pre><p>This is almost perfect &#8211; same code but two different renderings.</p><p>But its not quite perfect, because the user can still zoom or drag the viewport around;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Zoom-and-Scroll.png" alt="" title="Zoom and Scroll" width="388" height="287" class="aligncenter size-full wp-image-1156" /></p><p>and that makes it look unprofessional (at least for what I want).</p><h3>How to prevent Zoom</h3><p>Well it ends out that you can add further values to the viewport metatag to prevent zoom;</p><pre class="qoate-code">
&lt;meta name="viewport" content="initial-scale=0.5, user-scalable=no" /&gt;
</pre><p>So if I modify the Set_Env_iPhone() method to;</p><pre class="qoate-code">
//add viewport metatag
//&lt;meta name="viewport" content="initial-scale=0.5, user-scalable=no" /&gt;
&#13;
System.Web.UI.HtmlControls.HtmlMeta meta1 =
    new System.Web.UI.HtmlControls.HtmlMeta
    {
        Name = "viewport",
        Content = "initial-scale=0.5, user-scalable=no"
    };
this.Header.Controls.Add(meta1);
</pre><p>the page source becomes;</p><pre class="qoate-code">
&lt;head&gt;
    &lt;title&gt;&lt;/title&gt;
    &lt;meta name="viewport" content="initial-scale=0.5", user-scalable=no /&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form name="form1" method="post" action="Default.aspx" id="form1"&gt;
&lt;div&gt;
&lt;input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /&gt;
...
</pre><p>and the zoom function is disabled.</p><h3>How to prevent Drag</h3><p>I havent found a metatag value that prevents drag. Instead it appears that there are a serious of events that can be intercepted by Javascript. One of these is ontouchmove. Adding the Javascript</p><pre class="qoate-code">
&lt;script type="text/javascript"&gt;
    //block the moving / draging
    function BlockOnTouchMove(event) {
        event.preventDefault();
    }
&lt;/script&gt;
</pre><p>and binding it to your body</p><pre class="qoate-code">
&lt;body ontouchmove="BlockOnTouchMove(event);"&gt;
</pre><p>like so</p><pre class="qoate-code">
&lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="bfmobile002._Default" %&gt;
&#13;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head runat="server"&gt;
    &lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;script type="text/javascript"&gt;
    //block the moving / draging
    function BlockOnTouchMove(event) {
        event.preventDefault();
    }
&lt;/script&gt;
&lt;body ontouchmove="BlockOnTouchMove(event);"&gt;
    &lt;form id="form1" runat="server"&gt;
    &lt;asp:ScriptManager ID="ScriptManager1" runat="server"&gt;
    &lt;/asp:ScriptManager&gt;
    &lt;div&gt;
        &lt;asp:UpdatePanel ID="UpdatePanel1" runat="server"&gt;
            &lt;ContentTemplate&gt;
                &lt;asp:Panel ID="Main" runat="server"&gt;
                    &lt;asp:Label ID="Label1" runat="server" Text="Label"&gt;&lt;/asp:Label&gt;
                &lt;/asp:Panel&gt;
                &lt;asp:Panel ID="Footer" runat="server"&gt;
                &lt;/asp:Panel&gt;
            &lt;/ContentTemplate&gt;
        &lt;/asp:UpdatePanel&gt;
    &lt;/div&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre><p>disables the user&#8217;s ability to move the viewport.</p><h3>End Part 1</h3><p>I want to do this in sections otherwise it just becomes too long, so this is a good time to end part one. So far the one set of code detects if the device is a PC, iPad, iPhone or Andriod, and for iPhone it renders a tailored experience which is locked in. Part 2 will be tomorrow and will further explorer the modifying of the iPhone experience and the creating enabling the user to save your site as a Web App.</p></div><div
id="Creating_an_Custom_Home_Screen_Icon" class="hide"><p>The title of your icon is preset to the title of the web page. As I want the same page to cater for all experiences the title may change &#8211; On a PC it may be &#8220;Welcome to bf3mobile.com&#8221; whereas on an iPhone I want it short and snappy; &#8220;BF3 Stats&#8221;.</p><p>To set the title of your page dynamically you can use a protected string when the page is loaded;</p><pre class="qoate-code">
&lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="bfmobile002._Default" %&gt;
&#13;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head runat="server"&gt;
&#13;
    &lt;title&gt;&lt;%= this.Title %&gt;&lt;/title&gt;
&#13;
&lt;/head&gt;
&lt;script type="text/javascript"&gt;
    //block the moving / draging
    function BlockOnTouchMove(event) {
        event.preventDefault();
    }
&lt;/script&gt;
</pre><pre class="qoate-code">
private void Set_Env_iPhone()
{
    Main.Height = 662 + 78; //extra for address bar
    ...
    ...
&#13;
    //set the page title
    this.Title = "BF3 Stats";
&#13;
    ...
    ...
}
</pre><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Pre-Filled-in-Title.png" alt="" title="Pre Filled in Title" width="193" height="140" class="aligncenter size-full wp-image-1215" /></p><h3>Option One</h3><p>There are two ways to specify a custom icon for your Home shortcut. The first option is the easiest, simply create a 128 x 128 pixel image call apple-touch-icon.png and place it in the root of your website, similar to favicon.ico;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/apple-touch-icon.png" alt="" title="apple-touch-icon" width="128" height="128" class="aligncenter size-full wp-image-1216" /></p><p> The iPhone detects the presence of the icon and uses it;</p><p><img
src="http://www.arboundy.com/wp-content/uploads/2012/02/Using-your-Custom-Icon.png" alt="" title="Using your Custom Icon" width="389" height="286" class="aligncenter size-full wp-image-1217" /></p><p>Ive deliberately created the most basic icon possible to show how the iPhone kindly adds the gradient effect for you, making even the simplest icon look pretty good.</p><h3>Option Two</h3><p>If for some reason the above option is not available (maybe you dont have root access to your site) you can have any 128 x 128 pixel png file as your icon and specify it in your pages head;</p><pre class="qoate-code">
&lt;link rel=&quot;apple-touch-icon&quot; href=&quot;path/myicon.png&quot;/&gt;
</pre><h3>End Part 2</h3><p>Well that&#8217;s the end of Part 2. Next we will be see what differences occur when opening the site from the URL versus the Home shortcut and how we have to cater for them.</p><p></p></div></p></div></div><p></p> <img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/nqmLmpUBHak" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/02/developing-an-iphone-web-app-in-csharp-part-1/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Extract all Emails from Active Directory to File</title><link>http://www.arboundy.com/2012/02/extract-all-emails-from-active-directory-to-file/</link> <comments>http://www.arboundy.com/2012/02/extract-all-emails-from-active-directory-to-file/#comments</comments> <pubDate>Sat, 04 Feb 2012 11:54:46 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[Active Directory]]></category> <category><![CDATA[C#]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1054</guid> <description><![CDATA[Often you want to get all the valid email addresses that have been specified in an Active Directory domain. Most commonly I use this to create a incoming whitelist on an SMTP server in a DMZ. Extracting this list via C# is pretty easy these days. First you need to connect to the domain, and ...]]></description> <content:encoded><![CDATA[<p>Often you want to get all the valid email addresses that have been specified in an Active Directory domain. Most commonly I use this to create a incoming whitelist on an SMTP server in a DMZ.</p><p>Extracting this list via C# is pretty easy these days.</p><p>First you need to connect to the domain, and to do this you need to add a reference to System.DirectoryServices. From there you create a DirectoryEntry to the root of your domain;</p><pre class="qoate-code">
DirectoryEntry root = new DirectoryEntry("LDAP://example.com");
</pre><p>From there you can recursively go through domain tree in a number of ways, including a DomainSearcher. For this we will keep it simple with a recursive loop;</p><pre class="qoate-code">
ListEmailAddress(root);
</pre><pre class="qoate-code">
private static void ListEmailAddress(DirectoryEntry Root)
{
    try
    {
        foreach(DirectoryEntry Child in Root.Children)
        {
            if (Child.SchemaClassName.Equals("user"))
            {
                ListSMTPAddresses(Child);
            }
            ListEmailAddress(Child);
        }
    }
    catch
    {}
}
</pre><p>Quite simply; for each node in the tree (starting with root) for each child node it checks if it is a user, if so it calls the ListSMTPAddresses(Child) method, then it chucks the child back into the method again. This recursively goes through all the OU&#8217;s in the tree finding each user.</p><p>The ListSMTPAddresses(Child) method is simply;</p><pre class="qoate-code">
private static void ListSMTPAddresses(DirectoryEntry Child)
{
    try
    {
        foreach(string SMTPAddress in Child.Properties["proxyaddresses"])
        {
            if (SMTPAddress.ToLower().Contains("smtp:"))
            {
                string Address = SMTPAddress.ToLower().Substring(5);
                if (Address.Contains("systemmailbox"))
                {
                    if (!EmailAddresses.Contains(Address))
                         EmailAddresses.Add(Address);
                }
            }
        }
    }
    catch
    {}
}
</pre><p>Child.Properties["proxyaddresses"] is a little different than some of the property values as it is an list of all the email addresses associated with an acount &#8211; so we have to do the foreach loop. However most other values can be read by a simple;</p><pre class="qoate-code">
string x = Child.Properties["propertyname"].Value;
</pre><p>Once done the the results are written to a file;</p><pre class="qoate-code">
private static void WriteFile()
{
    ///sort and clean
    EmailAddresses.Sort();
&#13;
    StringBuilder txt = new StringBuilder();
    foreach(string EmailAddress in EmailAddresses)
        txt.AppendLine(EmailAddress);
&#13;
    //write
    TextWriter writer = new StreamWriter("SMTPAddresses.txt");
    writer.Write(txt.ToString());
    writer.Close();
    writer.Dispose();
}
</pre><p>By modifying this code you can get to nearly every property for a user oject in Active Directory by changing the property name.</p><p>Richard Mueller of rlmueller.net has spreadsheet of property names and the definitions at http://www.rlmueller.net/UserAttributes.htm.</p><div
class="download_box"> <a
href="http://www.arboundy.com/wp-content/uploads/2012/01/SMTP.Addresses.Extract.cs_.txt">Download SMTP.Addresses.Extract.cs</a></div><p>.</p> <img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/BLm7QLedIxY" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/02/extract-all-emails-from-active-directory-to-file/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>bf3mobile.com – Your Battlefield 3 Stats on Phone</title><link>http://www.arboundy.com/2012/01/bf3mobile.com-your-battlefield-3-stats-on-phone/</link> <comments>http://www.arboundy.com/2012/01/bf3mobile.com-your-battlefield-3-stats-on-phone/#comments</comments> <pubDate>Mon, 30 Jan 2012 12:58:40 +0000</pubDate> <dc:creator>aaron</dc:creator> <category><![CDATA[Blog]]></category> <category><![CDATA[.Net]]></category> <category><![CDATA[C#]]></category> <category><![CDATA[JavaScript]]></category> <guid isPermaLink="false">http://www.arboundy.com/?p=1072</guid> <description><![CDATA[Been having some fun over the last few days creating a web <a
href="http://bf3mobile.com">site</a> that allows Battlefield 3 players to view their soldier statistics from any web enabled iphone or andriod handset.]]></description> <content:encoded><![CDATA[<p>Been having some fun over the last few days creating a web <a
href="http://bf3mobile.com">site</a> that allows Battlefield 3 players to view their soldier statistics from any web enabled iphone or andriod handset.</p><p>I&#8217;m yet to start learning Objective-C so I went the standard website way, and with the iphone interface and some tricks you can achieve a fairly impressive look quote quickly.</p><p>Making it look like an app was important, and if you get the javascript / css just right you can get pretty close;</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/01/bf3mobile.com-LogonScreen.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/01/bf3mobile.com-LogonScreen-200x300.png" alt="" title="bf3mobile.com-LogonScreen" width="200" height="300" class="aligncenter size-medium wp-image-1073" /></a></p><p>As you can see from the logon screen above. The site remembers soldiers you have previously looked up and places the in the drop down list, and with the iphone / safari rendering it comes out clean;</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/01/bf3mobile.com-DropDownList.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/01/bf3mobile.com-DropDownList-200x300.png" alt="" title="bf3mobile.com-DropDownList" width="200" height="300" class="aligncenter size-medium wp-image-1075" /></a></p><p>The trickiest part was getting the stats, screen scraping may have been possible but would have been too time consuming. Luckily after a bit of sleuthing I found a json feed and was able to convert it to a stats page;</p><p><a
href="http://www.arboundy.com/wp-content/uploads/2012/01/bf3mobile.com-StatsPage.png"><img
src="http://www.arboundy.com/wp-content/uploads/2012/01/bf3mobile.com-StatsPage-200x300.png" alt="" title="bf3mobile.com-StatsPage" width="200" height="300" class="aligncenter size-medium wp-image-1076" /></a></p><p>To keep the source guessing I punch the stats request through a series of randomised proxies with fake spoofed user agents &#8211; but don&#8217;t tell any one.</p><p>I&#8217;ve probably missed the boat with this one, there are a number of iphone and andriod apps out there which do similar things, and EA/Dice have been claiming an official app will be out soon (though they have been saying that for months) &#8211; but it still was a fun exercise on how to get the data and how to render it.</p><p>If you are a Battlefield 3 fan be sure to check it out on your mobile at <a
href="http://bf3mobile.com">bf3mobile.com</a>, if not you can always test it using one of my retired soldier&#8217;s name &#8211; arb_anikan.</p><p>I want to add a lot more features, but I&#8217;ll probably get too tempted to rebuild it in Objective-C and see what its like to publish your own app.</p> <img src="http://feeds.feedburner.com/~r/AaronBoundy/~4/vfDcrQauwBw" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://www.arboundy.com/2012/01/bf3mobile.com-your-battlefield-3-stats-on-phone/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>

