Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

RSS Feed Aggregator and Blogging Smart Client

, 16 Aug 2005
RSS Feed aggregator and blogging Smart Client which uses Enterprise Library, Updater Application Block, lots of XML hacks and desktop tricks. A comprehensive guide to real life hurdles of Smart Client development.
rssfeeder_src.zip
RSSFeeder
RSSBlogAPI
RSSBlogAPI.csproj.user
Web References
CommunityServer
blogservice.disco
blogservice.wsdl
Reference.map
DotText
Reference.map
simpleblogservice.disco
simpleblogservice.wsdl
RSSCommon
Helper
PropertyEditor
RSSCommon.csproj.user
RSSFeeder
Controls
docs
dropshadow.png
My Pic 7.jpg
WIndows98.jpg
Helpers
Resources
E-mail.ico
rss.ico
RSSFeeder.mdb
RSSFeeder.csproj.user
RSSStarter.exe
RSSFeederResources
PublicQueue.ico
RSSFeeder.mdb
RSSFeederResources.csproj.user
RSSFeederSetup
Messages.ico
RSSFeederSetup.vdproj
Test
RSSStarter
DummyForm.frm
DummyForm.frx
MainModule.bas
RSSStarter.exe
RSSStarter.vbp
RSSStarter.vbw
RSSTests
App.ico
RSSTests.csproj.user
Thirdparty
AxInterop.DHTMLEDLib.dll
AxInterop.SHDocVw.dll
Eyefinder.dll
GotDotNet.Exslt.dll
HttpDownloader.dll
Interop.DHTMLEDLib.dll
Interop.SHDocVw.dll
Interop.WindowsInstaller.dll
Microsoft.ApplicationBlocks.Updater.ActivationProcessors.dll
Microsoft.ApplicationBlocks.Updater.dll
Microsoft.ApplicationBlocks.Updater.Downloaders.dll
Microsoft.Practices.EnterpriseLibrary.Caching.dll
Microsoft.Practices.EnterpriseLibrary.Common.dll
Microsoft.Practices.EnterpriseLibrary.Configuration.dll
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.dll
Microsoft.Practices.EnterpriseLibrary.Logging.dll
Microsoft.Practices.EnterpriseLibrary.Security.Cache.CachingStore.dll
Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.dll
Microsoft.Practices.EnterpriseLibrary.Security.dll
NotifyIconBalloon.dll
SandBar.dll
SandDock.dll
SgmlReaderDll.dll
// Copyright � 2005 by Omar Al Zabir. All rights are reserved.
// 
// If you like this code then feel free to go ahead and use it.
// The only thing I ask is that you don't remove or alter my copyright notice.
//
// Your use of this software is entirely at your own risk. I make no claims or
// warrantees about the reliability or fitness of this code for any particular purpose.
// If you make changes or additions to this code please mark your code as being yours.
// 
// website http://www.oazabir.com, email OmarAlZabir@gmail.com, msn oazabir@hotmail.com

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;

using NotifyIcon;
namespace RSSFeeder
{
	using RSSCommon;
	using Helpers;
	/// <summary>
	/// Summary description for RSSServer.
	/// </summary>
	public class RSSServer : System.Windows.Forms.Form
	{
		#region Designer

		private System.Windows.Forms.PictureBox pictureBox1;
		private System.Windows.Forms.Label label1;
		private System.Windows.Forms.Button blogpaperButton;
		private System.Windows.Forms.Button browseFeedsButton;
		private System.Windows.Forms.Button synchronizeButton;
		private System.Windows.Forms.Button exitButton;
		private System.Windows.Forms.Label updaterStatusLabel;
		private System.Windows.Forms.Label statusLabel;
		private System.Windows.Forms.Button hideButton;
		private System.Windows.Forms.Timer syncTimer;
		private System.Windows.Forms.Button weblogButton;
		private System.ComponentModel.IContainer components;

		public RSSServer()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();

			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(RSSServer));
			this.pictureBox1 = new System.Windows.Forms.PictureBox();
			this.label1 = new System.Windows.Forms.Label();
			this.blogpaperButton = new System.Windows.Forms.Button();
			this.browseFeedsButton = new System.Windows.Forms.Button();
			this.synchronizeButton = new System.Windows.Forms.Button();
			this.exitButton = new System.Windows.Forms.Button();
			this.updaterStatusLabel = new System.Windows.Forms.Label();
			this.statusLabel = new System.Windows.Forms.Label();
			this.hideButton = new System.Windows.Forms.Button();
			this.syncTimer = new System.Windows.Forms.Timer(this.components);
			this.weblogButton = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// pictureBox1
			// 
			this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
			this.pictureBox1.Location = new System.Drawing.Point(8, 8);
			this.pictureBox1.Name = "pictureBox1";
			this.pictureBox1.Size = new System.Drawing.Size(31, 14);
			this.pictureBox1.TabIndex = 0;
			this.pictureBox1.TabStop = false;
			// 
			// label1
			// 
			this.label1.BackColor = System.Drawing.Color.Transparent;
			this.label1.Location = new System.Drawing.Point(48, 8);
			this.label1.Name = "label1";
			this.label1.Size = new System.Drawing.Size(208, 16);
			this.label1.TabIndex = 1;
			this.label1.Text = "Feeder";
			// 
			// blogpaperButton
			// 
			this.blogpaperButton.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
			this.blogpaperButton.Location = new System.Drawing.Point(4, 53);
			this.blogpaperButton.Name = "blogpaperButton";
			this.blogpaperButton.Size = new System.Drawing.Size(96, 23);
			this.blogpaperButton.TabIndex = 2;
			this.blogpaperButton.Text = "Read Blogpaper";
			this.blogpaperButton.Click += new System.EventHandler(this.readFeedsMenuItem_Click);
			// 
			// browseFeedsButton
			// 
			this.browseFeedsButton.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
			this.browseFeedsButton.Location = new System.Drawing.Point(104, 53);
			this.browseFeedsButton.Name = "browseFeedsButton";
			this.browseFeedsButton.Size = new System.Drawing.Size(88, 23);
			this.browseFeedsButton.TabIndex = 3;
			this.browseFeedsButton.Text = "Browse Feeds";
			this.browseFeedsButton.Click += new System.EventHandler(this.manageFeedsMenuItem_Click);
			// 
			// synchronizeButton
			// 
			this.synchronizeButton.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
			this.synchronizeButton.Location = new System.Drawing.Point(196, 53);
			this.synchronizeButton.Name = "synchronizeButton";
			this.synchronizeButton.Size = new System.Drawing.Size(83, 23);
			this.synchronizeButton.TabIndex = 4;
			this.synchronizeButton.Text = "Send/Receive";
			this.synchronizeButton.Click += new System.EventHandler(this.downloadFeedsMenuItem_Click);
			// 
			// exitButton
			// 
			this.exitButton.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
			this.exitButton.Location = new System.Drawing.Point(357, 53);
			this.exitButton.Name = "exitButton";
			this.exitButton.Size = new System.Drawing.Size(56, 23);
			this.exitButton.TabIndex = 6;
			this.exitButton.Text = "Exit";
			this.exitButton.Click += new System.EventHandler(this.exitMenuItem_Click);
			// 
			// updaterStatusLabel
			// 
			this.updaterStatusLabel.BackColor = System.Drawing.Color.Transparent;
			this.updaterStatusLabel.ForeColor = System.Drawing.Color.DimGray;
			this.updaterStatusLabel.Location = new System.Drawing.Point(8, 24);
			this.updaterStatusLabel.Name = "updaterStatusLabel";
			this.updaterStatusLabel.Size = new System.Drawing.Size(400, 32);
			this.updaterStatusLabel.TabIndex = 6;
			// 
			// statusLabel
			// 
			this.statusLabel.BackColor = System.Drawing.Color.Transparent;
			this.statusLabel.Location = new System.Drawing.Point(96, 8);
			this.statusLabel.Name = "statusLabel";
			this.statusLabel.Size = new System.Drawing.Size(280, 16);
			this.statusLabel.TabIndex = 7;
			// 
			// hideButton
			// 
			this.hideButton.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
			this.hideButton.Location = new System.Drawing.Point(376, 0);
			this.hideButton.Name = "hideButton";
			this.hideButton.Size = new System.Drawing.Size(40, 23);
			this.hideButton.TabIndex = 7;
			this.hideButton.Text = "Hide";
			this.hideButton.Click += new System.EventHandler(this.hideButton_Click);
			// 
			// syncTimer
			// 
			this.syncTimer.Enabled = true;
			this.syncTimer.Interval = 300000;
			this.syncTimer.Tick += new System.EventHandler(this.syncTimer_Tick);
			// 
			// weblogButton
			// 
			this.weblogButton.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
			this.weblogButton.Location = new System.Drawing.Point(284, 53);
			this.weblogButton.Name = "weblogButton";
			this.weblogButton.Size = new System.Drawing.Size(68, 23);
			this.weblogButton.TabIndex = 5;
			this.weblogButton.Text = "Weblog";
			this.weblogButton.Click += new System.EventHandler(this.postButton_Click);
			// 
			// RSSServer
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 14);
			this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
			this.ClientSize = new System.Drawing.Size(416, 78);
			this.ControlBox = false;
			this.Controls.Add(this.weblogButton);
			this.Controls.Add(this.hideButton);
			this.Controls.Add(this.exitButton);
			this.Controls.Add(this.synchronizeButton);
			this.Controls.Add(this.browseFeedsButton);
			this.Controls.Add(this.blogpaperButton);
			this.Controls.Add(this.label1);
			this.Controls.Add(this.pictureBox1);
			this.Controls.Add(this.updaterStatusLabel);
			this.Controls.Add(this.statusLabel);
			this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
			this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
			this.MaximizeBox = false;
			this.MinimizeBox = false;
			this.Name = "RSSServer";
			this.ShowInTaskbar = false;
			this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
			this.Closing += new System.ComponentModel.CancelEventHandler(this.RSSServer_Closing);
			this.Load += new System.EventHandler(this.RSSServer_Load);
			this.Activated += new System.EventHandler(this.RSSServer_Activated);
			this.Deactivate += new System.EventHandler(this.RSSServer_Deactivate);
			this.ResumeLayout(false);

		}
		#endregion

		#endregion

		#region Private Methods

		//private WebLogMenuHelper _WebLogMenuHelper;

		public static BalloonTip TrayNotifyIcon;
		private static ContextMenu TrayContextMenu;

		private void ShowMe()
		{
			UIHelper.PositionForm( this, Configuration.Instance.PopupWindowPosition );
			
			base.TopMost = true;
			base.Show();
			base.Activate();
			base.TopMost = false;

			RSSHelper.ShowDownloadForm();
		}
		private void RSSServer_Load(object sender, System.EventArgs e)
		{
			this.LoadStatistics();
			
			// Move myself to a corner
			UIHelper.PositionForm( this, Configuration.Instance.PopupWindowPosition );
			
			// Check for updates
			UpdaterHelper.Instance.Start(this, new ProgressEventHandler( ProgressHandler ) );

			this.SetupNotifyIcon();

		}

		/// <summary>
		/// Setup the tray icon and the context menu
		/// </summary>
		private void SetupNotifyIcon()
		{
			TrayContextMenu = new ContextMenu();
			TrayNotifyIcon = new BalloonTip();
			
			MenuItem showMenuitem = new MenuItem( "&Show", new System.EventHandler(this.mnuShow_Click) );
			showMenuitem.DefaultItem = true;
			TrayContextMenu.MenuItems.Add(showMenuitem);

			TrayContextMenu.MenuItems.Add("&Daily Blogpaper", new System.EventHandler(this.dailyBlogPaperMenu_Click));
			TrayContextMenu.MenuItems.Add("&Browse Feeds", new System.EventHandler(this.browseFeedsMenu_Click));
			TrayContextMenu.MenuItems.Add("&Web Log", new System.EventHandler(this.webLogMenu_Click));
			
			TrayContextMenu.MenuItems.Add("-");
			TrayContextMenu.MenuItems.Add("&Synchronize", new System.EventHandler(this.synchronizeMenu_Click));
		

			// Silent mode menu
			MenuItem silentModeMenuItem = TrayContextMenu.MenuItems.Add("&Silent Mode", new System.EventHandler(this.silentModeMenuItem_Click));
			silentModeMenuItem.Checked = Configuration.Instance.SilentMode;
			// No baloon popup menu
			MenuItem noBaloonPopupMenuItem = TrayContextMenu.MenuItems.Add("&No Baloon Popup", new System.EventHandler(this.noBaloonPopupMenuItem_Click));
			noBaloonPopupMenuItem.Checked = Configuration.Instance.NoBaloonPopup;
			
			TrayContextMenu.MenuItems.Add("-");
			TrayContextMenu.MenuItems.Add("&Exit", new System.EventHandler(this.mnuExit_Click));

			// Show Notify Icon
			TrayNotifyIcon.Text = this.Text;				// Help text for MouseLeave on Icon
			TrayNotifyIcon.Icon = this.Icon;				// Icon for NotifyIcon
			TrayNotifyIcon.Form = this;						// Form to restore when DoubleClick on Icon
			TrayNotifyIcon.ContextMenu = TrayContextMenu;	// ContextMenu for RightClick on Icon
			TrayNotifyIcon.Visible = true;					// Show icon in TaskBar
			TrayNotifyIcon.Click += new EventHandler(notifyIcon_Click);
			
			// Show Balloon Tip
			if( Configuration.Instance.SilentMode && !Configuration.Instance.NoBaloonPopup ) 
				TrayNotifyIcon.ShowBalloon("RSS Feeder", "RSS Feeder Started", BalloonTip.NotifyInfoFlags.Info, 1000);

			this.components.Add( TrayNotifyIcon );
			this.components.Add( TrayContextMenu );
		}

		
		private void silentModeMenuItem_Click(object sender, System.EventArgs e)
		{
			MenuItem silentModeMenuItem = sender as MenuItem;
			silentModeMenuItem.Checked = !silentModeMenuItem.Checked;
			Configuration.Instance.SilentMode = silentModeMenuItem.Checked;

			this.HideMe();
		}

		private void mnuShow_Click(object sender, System.EventArgs e)
		{
			this.ShowMe();
		}

		private void mnuExit_Click(object sender, System.EventArgs e)
		{
			this.Close();
		}

		private void dailyBlogPaperMenu_Click(object sender, System.EventArgs e)
		{
			this.ShowBlogpaper();
		}

		private void browseFeedsMenu_Click(object sender, System.EventArgs e)
		{
			this.ShowManageFeeds();
		}

		private void webLogMenu_Click(object sender, System.EventArgs e)
		{
			this.ShowWebLog();
		}

		private void synchronizeMenu_Click(object sender, System.EventArgs e)
		{
			this.DownloadAllFeeds();
		}

		private void noBaloonPopupMenuItem_Click(object sender, System.EventArgs e)
		{
			MenuItem item = sender as MenuItem;
			item.Checked = !item.Checked;
			Configuration.Instance.NoBaloonPopup = item.Checked;
		}

		private void ProgressHandler( object sender, ProgressEventArgs e )
		{
			if( base.InvokeRequired )
			{				
				this.Invoke( new ProgressEventHandler( ProgressHandler ), new object [] { sender, e } );
			}
			else
			{
				updaterStatusLabel.Text = "Auto Update: " + e.Message;

				// The value indicates whether the message should be shown by force on screen
				if( e.Value > 0 )
					this.ShowMe();
			}
		}

		private void LoadStatistics()
		{
			int channelCount, totalUnreadCount;
			DatabaseHelper.GetStatistics( out channelCount, out totalUnreadCount );

			statusLabel.Text = string.Format( statusLabel.Text, channelCount, totalUnreadCount );
		}

		/// <summary>
		/// Download channels which are due now
		/// </summary>
		private void DownloadPendingFeeds()
		{
			IList allChannels = DatabaseHelper.GetChannels();
			IList dueChannels = new ArrayList();

			// Find which channels are due for downloading
			foreach( Channel channel in allChannels ) 
				if( channel.NextUpdate <= DateTime.Now ) dueChannels.Add( channel );

			if( dueChannels.Count > 0 )
				RSSHelper.DownloadChannels( this, dueChannels, new EventHandler( DownloadComplete ) );
		}

		private void ManageFeeds()
		{			
			Cursor = Cursors.WaitCursor;

			EntryPoint.ShowMainForm( 0 );

			Cursor = Cursors.Default;
		}

		private void Exit()
		{
			TrayNotifyIcon.Remove();
			TrayNotifyIcon.Dispose();

			exitButton.Text = "Exiting...";
			this.Refresh();
			
			// Stop download
			RSSHelper.StopDownload();

			// Close the mainform 
			EntryPoint.CloseMainForm();

			// Close newspaper
			EntryPoint.CloseNewspaper();

			EntryPoint.CloseWebLogManagerForm();

			// Check for updates
			UpdaterHelper.Instance.Stop();

			// Shutdown error report
			ErrorReportForm.Shutdown();
		}

		private void ShowBlogpaper()
		{
			blogpaperButton.Tag = blogpaperButton.Text;
			blogpaperButton.Text = "Loading...";
			this.Refresh();

			EntryPoint.ShowNewspaper(0);		
	
			blogpaperButton.Text = (string)blogpaperButton.Tag;
		}

		private void ShowDownloadFeeds(bool pending)
		{
			synchronizeButton.Tag = synchronizeButton.Text;
			synchronizeButton.Text = "Downloading...";
			this.Refresh();
			
			if( pending )
				this.DownloadPendingFeeds();
			else
				this.DownloadAllFeeds();

			this.HideMe();
			synchronizeButton.Text = (string)synchronizeButton.Tag;
		}

		private void ShowManageFeeds()
		{
			browseFeedsButton.Tag = browseFeedsButton.Text;
			browseFeedsButton.Text = "Loading...";
			this.Refresh();

			this.ManageFeeds();

			browseFeedsButton.Text = (string)browseFeedsButton.Tag;
			this.HideMe();
		}

		private void ShowOptions()
		{
			using( PropertyView view = new PropertyView() )
			{
				view.SelectedObject = Configuration.Instance;
				view.ShowDialog( this );
			}			
		}
		
		private void DownloadAllFeeds()
		{
			IList allChannels = DatabaseHelper.GetChannels();
			if( allChannels.Count > 0 )
				RSSHelper.DownloadChannels( this, allChannels, new EventHandler( DownloadComplete ) );
		}

		private void PostPendingItems()
		{
			// Just call the synchronizer with no channels
			RSSHelper.DownloadChannels( this, new ArrayList(), new EventHandler( DownloadComplete ) ) ;
		}

		private void HideMe()
		{
			this.Hide();
			MemoryHelper.ReduceMemory();
		}

		private void ShowWebLog()
		{
			this.weblogButton.Tag = this.weblogButton.Text;
			this.weblogButton.Text = "Loading...";
			this.Refresh();

			EntryPoint.ShowWebLogManagerForm();

			this.weblogButton.Text = this.weblogButton.Tag as string;
		}

		#endregion

		#region Control Events
		
		private void optionsMenuItem_Click(object sender, System.EventArgs e)
		{
			this.ShowOptions();
		}

		private void RSSServer_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			this.Exit();
		}

		private void closeLink_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
		{
			this.Close();
		}
		
		private void synchronizeButton_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
		{
			this.ShowDownloadFeeds(false);	
		}

		private void DownloadComplete( object sender, EventArgs e )
		{
			// Resume timer
			syncTimer.Start();

			// Update statistics
			this.LoadStatistics();
		}

		private void synchronizeButton_Resize(object sender, System.EventArgs e)
		{
			UIHelper.PositionForm( this, Configuration.Instance.PopupWindowPosition );
		}

		private void browseFeedsButton_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
		{
			this.ShowManageFeeds();
		}

		private void notifyIcon_Click(object sender, System.EventArgs e)
		{
			this.ShowMe();
		}

		private void syncTimer_Tick(object sender, System.EventArgs e)
		{
			syncTimer.Stop();
			
			this.ShowDownloadFeeds(true);
			
			ErrorReportForm.SendErrorReport();

			syncTimer.Start();
		}

		private void downloadFeedsMenuItem_Click(object sender, System.EventArgs e)
		{
			this.Hide();
			this.ShowDownloadFeeds(false);
		}

		private void readFeedsMenuItem_Click(object sender, System.EventArgs e)
		{
			this.ShowBlogpaper();
		}

		private void exitMenuItem_Click(object sender, System.EventArgs e)
		{
			this.Close();
		}

		private void RSSServer_Deactivate(object sender, System.EventArgs e)
		{
			this.HideMe();
		}

		private void blogpaperButton_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
		{
			this.ShowBlogpaper();
		}


		private void manageFeedsMenuItem_Click(object sender, System.EventArgs e)
		{
			this.ShowManageFeeds();
		}		
		
		private void hideButton_Click(object sender, System.EventArgs e)
		{
			this.Hide();
		}

		private void postButton_Click(object sender, System.EventArgs e)
		{
			this.ShowWebLog();
		}

		
//		private void newWebLogMenuItem_Click(object sender, System.EventArgs e)
//		{
//			this._WebLogMenuHelper.NewWebLog();		
//		}

		private void deliveryWebLogMenuItem_Click(object sender, System.EventArgs e)
		{
			this.PostPendingItems();
		}
		
//		private void webLogsMenuItem_Click(object sender, System.EventArgs e)
//		{
//			if( 0 == webLogsMenuItem.MenuItems.Count )
//				this._WebLogMenuHelper.NewWebLog();
//		}

		private bool _FirstTimeLoad = true;
		private void RSSServer_Activated(object sender, System.EventArgs e)
		{
			if( this._FirstTimeLoad && Configuration.Instance.SilentMode ) this.HideMe();

			this._FirstTimeLoad = false;
		}

		#endregion

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author


| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 16 Aug 2005
Article Copyright 2005 by Omar Al Zabir
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid