Yesterday it was a question on Community how to implement Goals in a multisite solution: https://community.sitecore.net/developers/f/9/t/1913
And if I write few blog posts about multisite I said to write also a blogpost about implementing goals in a multisite solution.
My site structure is :

My goals structure is :

Step 1 :
Modify site definition like. I added a new property to sites “goalsFolder”
<site name="countrythree" patch:before="site[@name='website']" hostName="countrythree.sitecoredemo.com" inherits="sitecoremvc-base" rootPath="/sitecore/content/countrythree" startItem="/" dictionaryDomain="{D8BC2E0C-36C5-4128-8F29-352B55E86676}" language="en" goalsFolder="countrythree" />
<site name="countrytwo" patch:before="site[@name='website']" hostName="countrytwo.sitecoredemo.com" inherits="sitecoremvc-base" rootPath="/sitecore/content/countrytwo" startItem="/" dictionaryDomain="{D8BC2E0C-36C5-4128-8F29-352B55E86676}" language="en" goalsFolder="countrytwo" />
<site name="countryone" patch:before="site[@name='website']" hostName="countryone.sitecoredemo.com" inherits="sitecoremvc-base" rootPath="/sitecore/content/countryone" startItem="/" dictionaryDomain="{D8BC2E0C-36C5-4128-8F29-352B55E86676}" language="de-de" goalsFolder="countrytwo" />
<site name="sitecoremvc-base" patch:before="site[@name='website']" hostName="*" enableTracking="true" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="50MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="25MB" filteredItemsCacheSize="10MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" cacheRenderingParameters="true" renderingParametersCacheSize="10MB" />
Step2:
Change type of command with name “anylitics:opengoals” from Sitecore.Analytics.config
into:
<command name="analytics:opengoals" type="Sitecore.Analytics.OpenGoals,SitecoreDemo" patch:source="Sitecore.Analytics.config"/>
Step3:
Create a new class OpenGoals:
using Sitecore;
using System;
namespace SitecoreDemo.Analytics
{
[Serializable, UsedImplicitly]
public class OpenGoals : OpenTrackingField
{
// Methods
protected override string GetUrl()
{
return "/sitecore/shell/~/xaml/Sitecore.Shell.Applications.Analytics.TrackingField.Goals.aspx";
}
}
}
4. Create a new xaml file name it Goals.xaml and add it to folder: /Sitecore/Shell/Override
<?xml version="1.0" encoding="UTF-8" ?>
<xamlControls xmlns:x="http://www.sitecore.net/xaml" xmlns:ajax="http://www.sitecore.net/ajax" xmlns:rest="http://www.sitecore.net/rest" xmlns:r="http://www.sitecore.net/renderings" xmlns:xmlcontrol="http://www.sitecore.net/xmlcontrols" xmlns:p="http://schemas.sitecore.net/Visual-Studio-Intellisense" xmlns:asp="http://www.sitecore.net/microsoft/webcontrols" xmlns:html="http://www.sitecore.net/microsoft/htmlcontrols" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<Sitecore.Shell.Applications.Analytics.TrackingField.Goals x:inherits="SitecoreDemo.Analytics.GoalsPage,SitecoreDemo">
<Sitecore.Controls.DialogPage runat="server" Header="Goals" Text="Select the goals that you want to associate with the selected item.">
<AjaxScriptManager runat="server"/>
<ContinuationManager runat="server" />
<Script runat="server" Src="/sitecore/Shell/Applications/Analytics/TrackingField/TrackingField.js" />
<Style runat="server">
#GoalsList table tr > td {
padding: 0 0 10px 0;
}
</Style>
<table width="100%" height="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td height="100%">
<GridPanel Width="100%" Height="100%" runat="server" Background="White">
<Border runat="server" GridPanel.Style="height:100%" Height="100%">
<Scrollbox id="GoalsList" runat="server" Height="100%" Padding="0px"/>
</Border>
</GridPanel>
</td>
</tr>
</table>
</Sitecore.Controls.DialogPage>
</Sitecore.Shell.Applications.Analytics.TrackingField.Goals>
</xamlControls>
5. Create a new class GoalsPage
using Sitecore;
using Sitecore.Analytics;
using Sitecore.Analytics.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Extensions.XElementExtensions;
using Sitecore.Shell.Applications.Analytics.TrackingField;
using Sitecore.Web.UI.HtmlControls;
using Sitecore.Web.UI.Sheer;
using Sitecore.Xml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
using System.Xml.Linq;
using SitecoreDemo.Util;
namespace SitecoreDemo.Analytics
{
[UsedImplicitly]
public class GoalsPage : TrackingFieldPageBase
{
// Methods
protected override void OK_Click()
{
Packet packet = new Packet("tracking", new string[0]);
base.AddIgnoreFlag(packet);
base.AddExistingProfiles(packet);
base.AddExistingCampaigns(packet);
if (HttpContext.Current != null)
{
List<PageEventItem> pageEventDefinitions = new List<PageEventItem>(from item in Tracker.DefinitionItems.PageEvents.Concat<PageEventItem>(Tracker.DefinitionItems.Goals)
where item.IsDeployed
select item);
base.AddExistingNoneGoals(packet, pageEventDefinitions);
CheckBoxList checkBoxList = this.GoalsList.Controls[0] as CheckBoxList;
if (checkBoxList != null)
{
TrackingFieldPageBase.GetEvents(packet, pageEventDefinitions, checkBoxList);
}
SheerResponse.SetDialogValue(packet.ToString());
base.OK_Click();
}
}
protected override void OnLoad(EventArgs e)
{
Assert.ArgumentNotNull(e, "e");
Assert.CanRunApplication("Content Editor/Ribbons/Chunks/Analytics - Attributes/Goals");
base.OnLoad(e);
}
protected override void Render(XDocument doc)
{
Assert.ArgumentNotNull(doc, "doc");
this.RenderGoals(doc);
}
private void RenderGoals(XDocument doc)
{
Assert.ArgumentNotNull(doc, "doc");
List<string> selected = new List<string>();
foreach (XElement element in doc.Descendants("event"))
{
selected.Add(element.GetAttributeValue("name"));
}
CheckBoxList child = new CheckBoxList {
ID = "GoalsCheckBoxList"
};
this.GoalsList.Controls.Add(child);
System.Web.UI.Page page = TrackingFieldPageBase.GetPage();
var site = ItemUtil.GetSiteContextForItem(ItemUtil.GetItem(Sitecore.Context.Request.QueryString["id"]));
var goalsFolder = site.Properties["goalsFolder"];
if ((page != null) && !page.IsPostBack)
{
IOrderedEnumerable<PageEventItem> query;
if (!string.IsNullOrEmpty(goalsFolder) && Sitecore.Context.ContentDatabase.GetItem(Tracker.DefinitionItems.Goals.Path+"/"+goalsFolder)!=null)
{
query = from e in Tracker.DefinitionItems.AllPageEvents
where e.InnerItem.Parent.Key.Equals(goalsFolder, StringComparison.InvariantCultureIgnoreCase) && (e.IsDeployed && e.IsGoal) && !e.IsSystem
orderby e.DisplayName
select e;
}
else
{
query = from e in Tracker.DefinitionItems.AllPageEvents
where(e.IsDeployed && e.IsGoal) && !e.IsSystem
orderby e.DisplayName
select e;
}
TrackingFieldPageBase.RenderCheckBoxList(child, query, selected);
}
}
// Properties
[UsedImplicitly]
protected Scrollbox GoalsList { get; set; }
}
}
Step6. Create a new class OpenTrackingField
using Sitecore;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Shell.Framework.Commands;
using Sitecore.Text;
using Sitecore.Web;
using Sitecore.Web.UI.Sheer;
using Sitecore.Web.UI.WebControls;
using System;
using Sitecore.StringExtensions;
namespace SitecoreDemo.Analytics
{
[Serializable, UsedImplicitly]
public class OpenTrackingField : Sitecore.Shell.Applications.Analytics.TrackingField.OpenTrackingField
{
CommandContext context;
// Methods
public override void Execute(CommandContext context)
{
this.context = context;
base.Execute(context);
}
protected virtual string GetUrl()
{
return "/sitecore/shell/~/xaml/Sitecore.Shell.Applications.Analytics.TrackingField.aspx";
}
[UsedImplicitly]
protected void Run(ClientPipelineArgs args)
{
Assert.ArgumentNotNull(args, "args");
Item item = base.DeserializeItems(args.Parameters["items"])[0];
if (SheerResponse.CheckModified())
{
string str = args.Parameters["fieldid"];
if (string.IsNullOrEmpty(str))
{
str = "__Tracking";
}
if (args.IsPostBack)
{
if (args.HasResult)
{
using (new StatisticDisabler(StatisticDisablerState.ForItemsWithoutVersionOnly))
{
item.Editing.BeginEdit();
item[str] = args.Result;
item.Editing.EndEdit();
}
if (AjaxScriptManager.Current != null)
{
AjaxScriptManager.Current.Dispatch("analytics:trackingchanged");
}
else
{
Context.ClientPage.SendMessage(this, "analytics:trackingchanged");
Context.ClientPage.SendMessage(this, "item:refresh(id={0})".FormatWith(new object[] { item.ID.ToString() }));
}
}
}
else if (item.Appearance.ReadOnly)
{
SheerResponse.Alert("You cannot edit the '{0}' item because it is protected.", new string[] { item.DisplayName });
}
else if (!item.Access.CanWrite())
{
SheerResponse.Alert("You cannot edit this item because you do not have write access to it.", new string[0]);
}
else
{
UrlString urlString = new UrlString(this.GetUrl());
urlString.Add("id", context.Items[0].ID.ToShortID().ToString());
UrlHandle handle = new UrlHandle();
handle["tracking"] = item[str];
handle.Add(urlString);
this.ShowDialog(urlString.ToString());
args.WaitForPostBack();
}
}
}
}
}
Step7.
I am on countryone site and I want to add a new goal to a page.
Is showing me just the goals from countryone folder.

This code was tested on Sitecore 8.1.