/// <summary>
        /// Gets the hash for the specified file.
        /// </summary>
        /// <param name="contextManager">Provides the current SharePoint context</param>
        /// <param name="logger">To log messages to the migrator</param>
        /// <param name="prefixedServerRelativeUrl">Server relative URL of the file; a prefixed URL is allowed.</param>
        /// <returns>The stored hash, or an empty array if there is no stored hash</returns>
        public byte[] GetFileHash(IContextManager contextManager, IUpgradeLog logger, string prefixedServerRelativeUrl)
        {
            if (_hashValues == null)
            {
                InitHashValues(contextManager, logger);
            }

            var clientContext     = contextManager.CurrentContext;
            var serverRelativeUrl = SPUrlUtility.ResolveServerRelativeUrl(clientContext.Site, clientContext.Web, prefixedServerRelativeUrl);

            var webRelativeUrl = serverRelativeUrl.Substring(clientContext.Web.ServerRelativeUrl.Length);

            if (webRelativeUrl.StartsWith("/"))
            {
                webRelativeUrl = webRelativeUrl.Substring(1);
            }
            var hash       = new List <byte>();
            var hashString = "";

            if (_hashValues.TryGetValue(webRelativeUrl, out hashString))
            {
                var parts = hashString.Split('-');
                foreach (var part in parts)
                {
                    hash.Add(byte.Parse(part, System.Globalization.NumberStyles.HexNumber));
                }
            }
            return(hash.ToArray());
        }
        /// <summary>
        /// Stores the hash for the specified file.
        /// </summary>
        /// <param name="contextManager">Provides the current SharePoint context</param>
        /// <param name="logger">To log messages to the migrator</param>
        /// <param name="prefixedServerRelativeUrl">Server relative URL of the file; a prefixed URL is allowed</param>
        /// <param name="hash">Hash value to store</param>
        public void StoreFileHash(IContextManager contextManager, IUpgradeLog logger, string prefixedServerRelativeUrl, byte[] hash)
        {
            if (_hashValues == null)
            {
                throw new InvalidOperationException("Must have been initialized first (by calling GetFileHash)");
            }

            var clientContext     = contextManager.CurrentContext;
            var serverRelativeUrl = SPUrlUtility.ResolveServerRelativeUrl(clientContext.Site, clientContext.Web, prefixedServerRelativeUrl);

            var webRelativeUrl = serverRelativeUrl.Substring(clientContext.Web.ServerRelativeUrl.Length);

            if (webRelativeUrl.StartsWith("/"))
            {
                webRelativeUrl = webRelativeUrl.Substring(1);
            }
            var hashString = BitConverter.ToString(hash);

            _hashValues[webRelativeUrl] = hashString;
            var propertyValue = JsonConvert.SerializeObject(_hashValues.ToList());

            clientContext.Web.AllProperties[_propertyBagKey] = propertyValue;
            clientContext.Web.Update();
            clientContext.ExecuteQuery();
        }
        /// <summary>
        /// Replaces ~sitecollection/ and ~site/ references
        /// </summary>
        public string Process(IContextManager contextManager, IUpgradeLog logger, string contents)
        {
            var context = contextManager.CurrentContext;

            var webUrl = SPUrlUtility.ResolveServerRelativeUrl(context, "~site/");

            if (!webUrl.EndsWith("/"))
            {
                webUrl += "/";
            }
            var siteUrl = SPUrlUtility.ResolveServerRelativeUrl(context, "~sitecollection/");

            if (!siteUrl.EndsWith("/"))
            {
                siteUrl += "/";
            }
            var result = tokenRegex.Replace(contents,
                                            match => (match.Value == "~site/") ? webUrl : siteUrl);

            return(result);
        }