/// <summary>
		/// Initialized a singleton; after calling this, use Analytics.Track() for each event.
		/// </summary>
		/// <param name="apiSecret">The segment.com apiSecret</param>
		/// <param name="userInfo">Information about the user that you have previous collected</param>
		/// <param name="propertiesThatGoWithEveryEvent">A set of key-value pairs to send with *every* event</param>
		/// <param name="allowTracking">If false, this will not do any communication with segment.io</param>
		public Analytics(string apiSecret, UserInfo userInfo, Dictionary<string, string> propertiesThatGoWithEveryEvent, bool allowTracking = true)
		{
			if (_singleton != null)
			{
				throw new ApplicationException("You can only construct a single Analytics object.");
			}
			_singleton = this;
			_propertiesThatGoWithEveryEvent = propertiesThatGoWithEveryEvent;

			_userInfo = userInfo;

			AllowTracking = allowTracking;
			//UrlThatReturnsExternalIpAddress is a static and should really be set before this is called, so don't mess with it if the clien has given us a different url to us
			if (string.IsNullOrEmpty(UrlThatReturnsExternalIpAddress))
				UrlThatReturnsExternalIpAddress = "http://icanhazip.com";//went down: "http://ipecho.net/plain";

			if (!AllowTracking)
				return;


			//bring in settings from any previous version
			if (AnalyticsSettings.Default.NeedUpgrade)
			{
				//see http://stackoverflow.com/questions/3498561/net-applicationsettingsbase-should-i-call-upgrade-every-time-i-load
				AnalyticsSettings.Default.Upgrade();
				AnalyticsSettings.Default.NeedUpgrade = false;
				AnalyticsSettings.Default.Save();
			}

			const string UserConfigFileName = "user.config";

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{
				// Apparently a first-time install. Any chance we can migrate settings from another channel of this app?
				// We really want to use the same ID if possible to keep our statistics valid.

				// We need to get the company name and exe name of the main application, without introducing a dependency on
				// Windows.Forms, so we can't use the Windows.Forms.Application methods.
				var entryAssembly = Assembly.GetEntryAssembly(); // the main exe assembly
				var productExe = Path.GetFileNameWithoutExtension(entryAssembly.Location);
				AssemblyCompanyAttribute companyAttribute = AssemblyCompanyAttribute.GetCustomAttribute(entryAssembly, typeof(AssemblyCompanyAttribute)) as AssemblyCompanyAttribute;
				if (companyAttribute != null && !string.IsNullOrEmpty(productExe))
				{
					string companyName = companyAttribute.Company;
					var settingsLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
						companyName);
					// Coincidentally, 5 is a good length for Bloom...better heuristic later?
					// For example, we could
					// - look for the last capital letter, and truncate to there. BloomAlpha->Bloom; HearThisAlpha->HearThis; *HearThis->Hear; TEX->?TE; TEXAlpha->TEX; BloomBetaOne->*BloomBeta
					// - look for the first non-initial capital letter, and truncate from there on. BloomAlpha->Bloom, HearThisAlpha->*Hear; HearThis->*Hear; TEX->*T' TEXAlpha->TEX; BloomBetaOne->Bloom
					// - look for a non-initial capital letter following at least one LC letter. Similar except TEX->TEX, TEXAlpha->*TEXAlpha.
					// In general, truncating too much is better than too little; too much just makes us slow, while too little may make us miss useful results.
					// It's true that truncating too much (like TEX->TE) may cause us to fetch an analytics ID from the wrong program. But even this is harmless, AFAIK.
					var index = Math.Min(5, productExe.Length);
					var prefix = productExe.Substring(0, index);
					var pattern = prefix + "*";
					var possibleParentFolders = Directory.GetDirectories(settingsLocation, pattern);
					var possibleFolders = new List<string>();
					foreach (var folder in possibleParentFolders)
					{
						possibleFolders.AddRange(Directory.GetDirectories(folder).Where(f => File.Exists(Path.Combine(f, UserConfigFileName))));
					}

					possibleFolders.Sort((first, second) =>
					{
						if (first == second)
							return 0;
						var firstConfigPath = Path.Combine(first, UserConfigFileName);
						var secondConfigPath = Path.Combine(second, UserConfigFileName);
						// Reversing the arguments like this means that second comes before first if it has a LARGER mod time.
						// That is, we end up with the most recently modified user.config first.
						return new FileInfo(secondConfigPath).LastWriteTimeUtc.CompareTo(new FileInfo(firstConfigPath).LastWriteTimeUtc);
					});
					foreach (var folder in possibleFolders)
					{
						try
						{
							var doc = XDocument.Load(Path.Combine(folder, UserConfigFileName));
							var idSetting =
								doc.XPathSelectElement(
									"configuration/userSettings/DesktopAnalytics.AnalyticsSettings/setting[@name='IdForAnalytics']");
							if (idSetting == null)
								continue;
							string analyticsId = idSetting.Value;
							if (string.IsNullOrEmpty(analyticsId))
								continue;
							AnalyticsSettings.Default.IdForAnalytics = analyticsId;
							AnalyticsSettings.Default.FirstName = ExtractSetting(AnalyticsSettings.Default.FirstName, doc, "FirstName");
							AnalyticsSettings.Default.LastName = ExtractSetting(AnalyticsSettings.Default.LastName, doc, "LastName");
							AnalyticsSettings.Default.LastVersionLaunched = ExtractSetting(AnalyticsSettings.Default.LastVersionLaunched, doc, "LastVersionLaunched");
							AnalyticsSettings.Default.Email = ExtractSetting(AnalyticsSettings.Default.Email, doc, "Email");
							AnalyticsSettings.Default.Save();
							break;
						}
						catch (Exception)
						{
							// If anything goes wrong we just won't try to get our ID from this source.
						}
					}
				}
			}

			Segment.Analytics.Initialize(apiSecret);
			Segment.Analytics.Client.Failed += Client_Failed;
			Segment.Analytics.Client.Succeeded += Client_Succeeded;

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{

				AnalyticsSettings.Default.IdForAnalytics = Guid.NewGuid().ToString();
				AnalyticsSettings.Default.Save();
			}

			var context = new Context();
			context.Add("language", _userInfo.UILanguageCode);

			_options = new Options();
			_options.SetContext(context);

			UpdateSegmentIOInformationOnThisUser();
			ReportIpAddressOfThisMachineAsync(); //this will take a while and may fail, so just do it when/if we can
			string versionNumberWithBuild = "";
			try
			{
				versionNumberWithBuild = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString();
			}
			catch (NullReferenceException)
			{
				try
				{
					// GetEntryAssembly is null for MAF plugins
					versionNumberWithBuild = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
				}
				catch (NullReferenceException)
				{
					// This probably can't happen, but if it does, just roll with it.
				}
			}
			string versionNumber = versionNumberWithBuild.Split('.').Take(2).Aggregate((a, b) => a + "." + b);
			SetApplicationProperty("Version", versionNumber);
			SetApplicationProperty("FullVersion", versionNumberWithBuild);
			SetApplicationProperty("UserName", GetUserNameForEvent());
			SetApplicationProperty("Browser", GetOperatingSystemLabel());


			if (string.IsNullOrEmpty(AnalyticsSettings.Default.LastVersionLaunched))
			{
				//"Created" is a special property that segment.io understands and coverts to equivalents in various analytics services
				//So it's not as descriptive for us as "FirstLaunchOnSystem", but it will give the best experience on the analytics sites.
				TrackWithApplicationProperties("Created");
			}
			else if (AnalyticsSettings.Default.LastVersionLaunched != versionNumberWithBuild)
			{
				TrackWithApplicationProperties("Upgrade", new Properties
					{
						{"OldVersion", AnalyticsSettings.Default.LastVersionLaunched},
					});
			}

			//we want to record the launch event independent of whether we also recorded a special first launch
			// But that is done after we retrieve (or fail to retrieve) our external ip address.
			// See http://issues.bloomlibrary.org/youtrack/issue/BL-4011.

			AnalyticsSettings.Default.LastVersionLaunched = versionNumberWithBuild;
			AnalyticsSettings.Default.Save();

		}
Example #2
0
		/// <summary>
		/// Sets the context of this analytics call. Context contains information about the environment
		/// such as the app, the user agent, ip, etc ..
		/// </summary>
		/// <returns>This Options object for chaining.</returns>
		/// <param name="context">The visitor's context.</param>
		public Options SetContext (Context context)
		{
			this.Context = context;
			return this;
		}
Example #3
0
		/// <summary>
		/// Options object that allows the specification of a timestamp, 
		/// an anonymousId, a context, or target integrations.
		/// </summary>
		public Options ()
		{
			this.Integrations = new Dict ();
			this.Context = new Context ();
		}
Example #4
0
		/// <summary>
		/// Initialized a singleton; after calling this, use Analytics.Track() for each event.
		/// </summary>
		/// <param name="apiSecret">The segment.com apiSecret</param>
		/// <param name="userInfo">Information about the user that you have previous collected</param>
		/// <param name="propertiesThatGoWithEveryEvent">A set of key-value pairs to send with *every* event</param>
		/// <param name="allowTracking">If false, this will not do any communication with segment.io</param>
		public Analytics(string apiSecret, UserInfo userInfo, Dictionary<string, string> propertiesThatGoWithEveryEvent, bool allowTracking = true)
		{
			if (_singleton != null)
			{
				throw new ApplicationException("You can only construct a single Analytics object.");
			}
			_singleton = this;
			_propertiesThatGoWithEveryEvent = propertiesThatGoWithEveryEvent;

			_userInfo = userInfo;

			AllowTracking = allowTracking;
			//UrlThatReturnsExternalIpAddress is a static and should really be set before this is called, so don't mess with it if the client has given us a different url to us
			if (string.IsNullOrEmpty(UrlThatReturnsExternalIpAddress))
				UrlThatReturnsExternalIpAddress = "http://icanhazip.com"; //went down: "http://ipecho.net/plain";

			if (!AllowTracking)
				return;


			//bring in settings from any previous version
			if (AnalyticsSettings.Default.NeedUpgrade)
			{
				//see http://stackoverflow.com/questions/3498561/net-applicationsettingsbase-should-i-call-upgrade-every-time-i-load
				AnalyticsSettings.Default.Upgrade();
				AnalyticsSettings.Default.NeedUpgrade = false;
				AnalyticsSettings.Default.Save();
			}

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{
				// Apparently a first-time install. Any chance we can migrate settings from another channel of this app?
				// We really want to use the same ID if possible to keep our statistics valid.
				try
				{
					AttemptToGetUserIdSettingsFromDifferentChannel();
				}
				catch (Exception)
				{
					// Oh, well, we tried.
				}
			}

			Segment.Analytics.Initialize(apiSecret);
			Segment.Analytics.Client.Failed += Client_Failed;
			Segment.Analytics.Client.Succeeded += Client_Succeeded;

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{

				AnalyticsSettings.Default.IdForAnalytics = Guid.NewGuid().ToString();
				AnalyticsSettings.Default.Save();
			}

			var context = new Context();
			context.Add("language", _userInfo.UILanguageCode);

			_options = new Options();
			_options.SetContext(context);

			UpdateSegmentIOInformationOnThisUser();
			ReportIpAddressOfThisMachineAsync(); //this will take a while and may fail, so just do it when/if we can
			string versionNumberWithBuild = "";
			try
			{
				versionNumberWithBuild = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString();
			}
			catch (NullReferenceException)
			{
				try
				{
					// GetEntryAssembly is null for MAF plugins
					versionNumberWithBuild = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
				}
				catch (NullReferenceException)
				{
					// This probably can't happen, but if it does, just roll with it.
				}
			}
			string versionNumber = versionNumberWithBuild.Split('.').Take(2).Aggregate((a, b) => a + "." + b);
			SetApplicationProperty("Version", versionNumber);
			SetApplicationProperty("FullVersion", versionNumberWithBuild);
			SetApplicationProperty("UserName", GetUserNameForEvent());
			SetApplicationProperty("Browser", GetOperatingSystemLabel());


			if (string.IsNullOrEmpty(AnalyticsSettings.Default.LastVersionLaunched))
			{
				//"Created" is a special property that segment.io understands and coverts to equivalents in various analytics services
				//So it's not as descriptive for us as "FirstLaunchOnSystem", but it will give the best experience on the analytics sites.
				TrackWithApplicationProperties("Created");
			}
			else if (AnalyticsSettings.Default.LastVersionLaunched != versionNumberWithBuild)
			{
				TrackWithApplicationProperties("Upgrade", new Properties
				{
					{"OldVersion", AnalyticsSettings.Default.LastVersionLaunched},
				});
			}

			// We want to record the launch event independent of whether we also recorded a special first launch
			// But that is done after we retrieve (or fail to retrieve) our external ip address.
			// See http://issues.bloomlibrary.org/youtrack/issue/BL-4011.

			AnalyticsSettings.Default.LastVersionLaunched = versionNumberWithBuild;
			AnalyticsSettings.Default.Save();

		}
Example #5
0
		/// <summary>
		/// Options object that allows the specification of a timestamp, 
		/// an anonymousId, a context, or target integrations.
		/// </summary>
		public Options ()
		{
			Integrations = new Dict ();
			Context = new Context ();
		}
	    public Analytics(string apiSecret, UserInfo userInfo, bool allowTracking=true)
	    {
	        _userInfo = userInfo;

			AllowTracking = allowTracking;
	        //UrlThatReturnsExternalIpAddress is a static and should really be set before this is called, so don't mess with it if the clien has given us a different url to us
            if(string.IsNullOrEmpty(UrlThatReturnsExternalIpAddress)) 
                UrlThatReturnsExternalIpAddress = "http://icanhazip.com";//went down: "http://ipecho.net/plain";

			if (!AllowTracking)
				return;

			//bring in settings from any previous version
			if (AnalyticsSettings.Default.NeedUpgrade)
			{
				//see http://stackoverflow.com/questions/3498561/net-applicationsettingsbase-should-i-call-upgrade-every-time-i-load
				AnalyticsSettings.Default.Upgrade();
				AnalyticsSettings.Default.NeedUpgrade = false;
				AnalyticsSettings.Default.Save();
			}

			Segment.Analytics.Initialize(apiSecret);
			Segment.Analytics.Client.Failed += Client_Failed;
			Segment.Analytics.Client.Succeeded += Client_Succeeded;

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.IdForAnalytics))
			{

				AnalyticsSettings.Default.IdForAnalytics = Guid.NewGuid().ToString();
				AnalyticsSettings.Default.Save();
			}

			var context = new Context();
			context.Add("language", _userInfo.UILanguageCode);

			_options = new Options();
			_options.SetContext(context);

            UpdateSegmentIOInformationOnThisUser();
            ReportIpAddressOfThisMachineAsync(); //this will take a while and may fail, so just do it when/if we can

		    try
		    {
				_applicationVersion = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString();
		    }
		    catch (NullReferenceException)
		    {
			    try
			    {
					// GetEntryAssembly is null for MAF plugins
					_applicationVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
			    }
				catch (NullReferenceException)
			    {
				    // This probably can't happen, but if it does, just roll with it.
			    }
		    }

			if (string.IsNullOrEmpty(AnalyticsSettings.Default.LastVersionLaunched))
			{
				//"Created" is a special property that segment.io understands and coverts to equivalents in various analytics services
				//So it's not as descriptive for us as "FirstLaunchOnSystem", but it will give the best experience on the analytics sites.
				TrackWithDefaultProperties("Created");
			}
			else if (AnalyticsSettings.Default.LastVersionLaunched != _applicationVersion)
			{
				TrackWithDefaultProperties("Upgrade", new Properties
					{
						{"OldVersion", AnalyticsSettings.Default.LastVersionLaunched},
					});
			}
			
			//we want to record the launch event independent of whether we also recorded a special first launch

			TrackWithDefaultProperties("Launch");
			
			AnalyticsSettings.Default.LastVersionLaunched = _applicationVersion;
			AnalyticsSettings.Default.Save();

		}