|  | @@ -14,10 +14,7 @@ using System;
 | 
	
		
			
				|  |  |  using System.Collections.Generic;
 | 
	
		
			
				|  |  |  using System.IO;
 | 
	
		
			
				|  |  |  using System.Linq;
 | 
	
		
			
				|  |  | -using System.Text;
 | 
	
		
			
				|  |  |  using System.Threading.Tasks;
 | 
	
		
			
				|  |  | -using WebMarkupMin.Core.Minifiers;
 | 
	
		
			
				|  |  | -using WebMarkupMin.Core.Settings;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace MediaBrowser.WebDashboard.Api
 | 
	
		
			
				|  |  |  {
 | 
	
	
		
			
				|  | @@ -49,6 +46,12 @@ namespace MediaBrowser.WebDashboard.Api
 | 
	
		
			
				|  |  |          public string Name { get; set; }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    [Route("/web/Package", "GET")]
 | 
	
		
			
				|  |  | +    [Route("/dashboard/Package", "GET")]
 | 
	
		
			
				|  |  | +    public class GetDashboardPackage
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /// <summary>
 | 
	
		
			
				|  |  |      /// Class GetDashboardResource
 | 
	
		
			
				|  |  |      /// </summary>
 | 
	
	
		
			
				|  | @@ -120,35 +123,6 @@ namespace MediaBrowser.WebDashboard.Api
 | 
	
		
			
				|  |  |              _jsonSerializer = jsonSerializer;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the dashboard UI path.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <value>The dashboard UI path.</value>
 | 
	
		
			
				|  |  | -        public string DashboardUIPath
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            get
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                if (!string.IsNullOrEmpty(_serverConfigurationManager.Configuration.DashboardSourcePath))
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    return _serverConfigurationManager.Configuration.DashboardSourcePath;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                var runningDirectory = Path.GetDirectoryName(_serverConfigurationManager.ApplicationPaths.ApplicationPath);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                return Path.Combine(runningDirectory, "dashboard-ui");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the dashboard resource path.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="virtualPath">The virtual path.</param>
 | 
	
		
			
				|  |  | -        /// <returns>System.String.</returns>
 | 
	
		
			
				|  |  | -        private string GetDashboardResourcePath(string virtualPath)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            return Path.Combine(DashboardUIPath, virtualPath.Replace('/', Path.DirectorySeparatorChar));
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Gets the specified request.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
	
		
			
				|  | @@ -158,7 +132,7 @@ namespace MediaBrowser.WebDashboard.Api
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              var page = ServerEntryPoint.Instance.PluginConfigurationPages.First(p => p.Name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => ModifyHtml(page.GetHtmlStream(), null));
 | 
	
		
			
				|  |  | +            return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml(page.GetHtmlStream(), null));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -228,7 +202,7 @@ namespace MediaBrowser.WebDashboard.Api
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      Request.Response.Redirect("wizardstart.html");
 | 
	
		
			
				|  |  |                      return null;
 | 
	
		
			
				|  |  | -                }    
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              path = path.Replace("scripts/jquery.mobile-1.4.4.min.map", "thirdparty/jquerymobile-1.4.4/jquery.mobile-1.4.4.min.map", StringComparison.OrdinalIgnoreCase);
 | 
	
	
		
			
				|  | @@ -241,7 +215,7 @@ namespace MediaBrowser.WebDashboard.Api
 | 
	
		
			
				|  |  |                  !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) &&
 | 
	
		
			
				|  |  |                  !contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                return ResultFactory.GetResult(GetResourceStream(path, isHtml, localizationCulture).Result, contentType);
 | 
	
		
			
				|  |  | +                return ResultFactory.GetResult(GetResourceStream(path, localizationCulture).Result, contentType);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              TimeSpan? cacheDuration = null;
 | 
	
	
		
			
				|  | @@ -257,7 +231,7 @@ namespace MediaBrowser.WebDashboard.Api
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              var cacheKey = (assembly.Version + (localizationCulture ?? string.Empty) + path).GetMD5();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            return ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, isHtml, localizationCulture));
 | 
	
		
			
				|  |  | +            return ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, localizationCulture));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private string GetLocalizationCulture()
 | 
	
	
		
			
				|  | @@ -269,47 +243,17 @@ namespace MediaBrowser.WebDashboard.Api
 | 
	
		
			
				|  |  |          /// Gets the resource stream.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          /// <param name="path">The path.</param>
 | 
	
		
			
				|  |  | -        /// <param name="isHtml">if set to <c>true</c> [is HTML].</param>
 | 
	
		
			
				|  |  |          /// <param name="localizationCulture">The localization culture.</param>
 | 
	
		
			
				|  |  |          /// <returns>Task{Stream}.</returns>
 | 
	
		
			
				|  |  | -        private async Task<Stream> GetResourceStream(string path, bool isHtml, string localizationCulture)
 | 
	
		
			
				|  |  | +        private Task<Stream> GetResourceStream(string path, string localizationCulture)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            Stream resourceStream;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if (path.Equals("scripts/all.js", StringComparison.OrdinalIgnoreCase))
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                resourceStream = await GetAllJavascript().ConfigureAwait(false);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            else if (path.Equals("css/all.css", StringComparison.OrdinalIgnoreCase))
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                resourceStream = await GetAllCss().ConfigureAwait(false);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            else
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                resourceStream = GetRawResourceStream(path);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if (resourceStream != null)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                // Don't apply any caching for html pages
 | 
	
		
			
				|  |  | -                // jQuery ajax doesn't seem to handle if-modified-since correctly
 | 
	
		
			
				|  |  | -                if (isHtml)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    resourceStream = await ModifyHtml(resourceStream, localizationCulture).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            return resourceStream;
 | 
	
		
			
				|  |  | +            return GetPackageCreator()
 | 
	
		
			
				|  |  | +                .GetResource(path, localizationCulture, _appHost.ApplicationVersion.ToString());
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the raw resource stream.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="path">The path.</param>
 | 
	
		
			
				|  |  | -        /// <returns>Task{Stream}.</returns>
 | 
	
		
			
				|  |  | -        private Stream GetRawResourceStream(string path)
 | 
	
		
			
				|  |  | +        private PackageCreator GetPackageCreator()
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            return _fileSystem.GetFileStream(GetDashboardResourcePath(path), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
 | 
	
		
			
				|  |  | +            return new PackageCreator(_fileSystem, _localization, Logger, _serverConfigurationManager, _jsonSerializer);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -322,466 +266,67 @@ namespace MediaBrowser.WebDashboard.Api
 | 
	
		
			
				|  |  |              return Path.GetExtension(path).EndsWith("html", StringComparison.OrdinalIgnoreCase);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Modifies the HTML by adding common meta tags, css and js.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="sourceStream">The source stream.</param>
 | 
	
		
			
				|  |  | -        /// <param name="userId">The user identifier.</param>
 | 
	
		
			
				|  |  | -        /// <param name="localizationCulture">The localization culture.</param>
 | 
	
		
			
				|  |  | -        /// <returns>Task{Stream}.</returns>
 | 
	
		
			
				|  |  | -        private async Task<Stream> ModifyHtml(Stream sourceStream, string localizationCulture)
 | 
	
		
			
				|  |  | +        public async Task<object> Get(GetDashboardPackage request)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            using (sourceStream)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                string html;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                using (var memoryStream = new MemoryStream())
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    html = Encoding.UTF8.GetString(memoryStream.ToArray());
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    if (!string.IsNullOrWhiteSpace(localizationCulture))
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        var lang = localizationCulture.Split('-').FirstOrDefault();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                        html = _localization.LocalizeDocument(html, localizationCulture, GetLocalizationToken);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                        html = html.Replace("<html>", "<html lang=\"" + lang + "\">");
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    //try
 | 
	
		
			
				|  |  | -                    //{
 | 
	
		
			
				|  |  | -                    //    var minifier = new HtmlMinifier(new HtmlMinificationSettings(true));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    //    html = minifier.Minify(html).MinifiedContent;
 | 
	
		
			
				|  |  | -                    //}
 | 
	
		
			
				|  |  | -                    //catch (Exception ex)
 | 
	
		
			
				|  |  | -                    //{
 | 
	
		
			
				|  |  | -                    //    Logger.ErrorException("Error minifying html", ex);
 | 
	
		
			
				|  |  | -                    //}
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                var version = GetType().Assembly.GetName().Version;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                html = html.Replace("<head>", "<head>" + GetMetaTags() + GetCommonCss(version) + GetCommonJavascript(version));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                var bytes = Encoding.UTF8.GetBytes(html);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                return new MemoryStream(bytes);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private string GetLocalizationToken(string phrase)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            return "${" + phrase + "}";
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the meta tags.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <returns>System.String.</returns>
 | 
	
		
			
				|  |  | -        private static string GetMetaTags()
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var sb = new StringBuilder();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            sb.Append("<meta http-equiv=\"X-UA-Compatibility\" content=\"IE=Edge\">");
 | 
	
		
			
				|  |  | -            sb.Append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\">");
 | 
	
		
			
				|  |  | -            //sb.Append("<meta name=\"apple-mobile-web-app-capable\" content=\"yes\">");
 | 
	
		
			
				|  |  | -            sb.Append("<meta name=\"mobile-web-app-capable\" content=\"yes\">");
 | 
	
		
			
				|  |  | -            sb.Append("<meta name=\"application-name\" content=\"Media Browser\">");
 | 
	
		
			
				|  |  | -            //sb.Append("<meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\">");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            sb.Append("<link rel=\"icon\" sizes=\"114x114\" href=\"css/images/touchicon114.png\" />");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // http://developer.apple.com/library/ios/#DOCUMENTATION/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
 | 
	
		
			
				|  |  | -            sb.Append("<link rel=\"apple-touch-icon\" href=\"css/images/touchicon.png\" />");
 | 
	
		
			
				|  |  | -            sb.Append("<link rel=\"apple-touch-icon\" sizes=\"72x72\" href=\"css/images/touchicon72.png\" />");
 | 
	
		
			
				|  |  | -            sb.Append("<link rel=\"apple-touch-icon\" sizes=\"114x114\" href=\"css/images/touchicon114.png\" />");
 | 
	
		
			
				|  |  | -            sb.Append("<link rel=\"apple-touch-startup-image\" href=\"css/images/iossplash.png\" />");
 | 
	
		
			
				|  |  | -            sb.Append("<link rel=\"shortcut icon\" href=\"css/images/favicon.ico\" />");
 | 
	
		
			
				|  |  | +            var path = Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath,
 | 
	
		
			
				|  |  | +                "webclient-dump");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            return sb.ToString();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the common CSS.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="version">The version.</param>
 | 
	
		
			
				|  |  | -        /// <returns>System.String.</returns>
 | 
	
		
			
				|  |  | -        private static string GetCommonCss(Version version)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var versionString = "?v=" + version;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var files = new[]
 | 
	
		
			
				|  |  | -                            {
 | 
	
		
			
				|  |  | -                                "thirdparty/jquerymobile-1.4.4/jquery.mobile-1.4.4.min.css",
 | 
	
		
			
				|  |  | -                                "thirdparty/swipebox-master/css/swipebox.min.css" + versionString,
 | 
	
		
			
				|  |  | -                                "css/all.css" + versionString
 | 
	
		
			
				|  |  | -                            };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var tags = files.Select(s => string.Format("<link rel=\"stylesheet\" href=\"{0}\" />", s)).ToArray();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            return string.Join(string.Empty, tags);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the common javascript.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="version">The version.</param>
 | 
	
		
			
				|  |  | -        /// <returns>System.String.</returns>
 | 
	
		
			
				|  |  | -        private static string GetCommonJavascript(Version version)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var builder = new StringBuilder();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var versionString = "?v=" + version;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var files = new[]
 | 
	
		
			
				|  |  | -                            {
 | 
	
		
			
				|  |  | -                                "scripts/all.js" + versionString,
 | 
	
		
			
				|  |  | -                                "thirdparty/jstree1.0/jquery.jstree.min.js",
 | 
	
		
			
				|  |  | -                                "thirdparty/swipebox-master/js/jquery.swipebox.min.js" + versionString
 | 
	
		
			
				|  |  | -            };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var tags = files.Select(s => string.Format("<script src=\"{0}\"></script>", s)).ToArray();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            builder.Append(string.Join(string.Empty, tags));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            return builder.ToString();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets a stream containing all concatenated javascript
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <returns>Task{Stream}.</returns>
 | 
	
		
			
				|  |  | -        private async Task<Stream> GetAllJavascript()
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var memoryStream = new MemoryStream();
 | 
	
		
			
				|  |  | -            var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // jQuery + jQuery mobile
 | 
	
		
			
				|  |  | -            await AppendResource(memoryStream, "thirdparty/jquery-2.1.1.min.js", newLineBytes).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -            await AppendResource(memoryStream, "thirdparty/jquerymobile-1.4.4/jquery.mobile-1.4.4.min.js", newLineBytes).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            await AppendResource(memoryStream, "thirdparty/jquery.unveil-custom.js", newLineBytes).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // This script produces errors in older versions of safari
 | 
	
		
			
				|  |  | -            if ((Request.UserAgent ?? string.Empty).IndexOf("chrome/", StringComparison.OrdinalIgnoreCase) != -1)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                await AppendResource(memoryStream, "thirdparty/cast_sender.js", newLineBytes).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | -            await AppendLocalization(memoryStream).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -            await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Write the version string for the dashboard comparison function
 | 
	
		
			
				|  |  | -            var versionString = string.Format("window.dashboardVersion='{0}';", _appHost.ApplicationVersion);
 | 
	
		
			
				|  |  | -            var versionBytes = Encoding.UTF8.GetBytes(versionString);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            await memoryStream.WriteAsync(versionBytes, 0, versionBytes.Length).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -            await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var builder = new StringBuilder();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            foreach (var file in new[]
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                "thirdparty/apiclient/sha1.js",
 | 
	
		
			
				|  |  | -                "thirdparty/apiclient/mediabrowser.apiclient.js",
 | 
	
		
			
				|  |  | -                "thirdparty/apiclient/connectionmanager.js"
 | 
	
		
			
				|  |  | -            })
 | 
	
		
			
				|  |  | +            try
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                using (var fs = _fileSystem.GetFileStream(GetDashboardResourcePath(file), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    using (var streamReader = new StreamReader(fs))
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        var text = await streamReader.ReadToEndAsync().ConfigureAwait(false);
 | 
	
		
			
				|  |  | -                        builder.Append(text);
 | 
	
		
			
				|  |  | -                        builder.Append(Environment.NewLine);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +                Directory.Delete(path, true);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            foreach (var file in GetScriptFiles())
 | 
	
		
			
				|  |  | +            catch (IOException)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                var path = GetDashboardResourcePath("scripts/" + file);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    using (var streamReader = new StreamReader(fs))
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        var text = await streamReader.ReadToEndAsync().ConfigureAwait(false);
 | 
	
		
			
				|  |  | -                        builder.Append(text);
 | 
	
		
			
				|  |  | -                        builder.Append(Environment.NewLine);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var js = builder.ToString();
 | 
	
		
			
				|  |  | +            var creator = GetPackageCreator();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            try
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                var result = new CrockfordJsMinifier().Minify(js, false, Encoding.UTF8);
 | 
	
		
			
				|  |  | +            CopyDirectory(creator.DashboardUIPath, path);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                js = result.MinifiedContent;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            catch (Exception ex)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                Logger.ErrorException("Error minifying javascript", ex);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +            var culture = "en-US";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var bytes = Encoding.UTF8.GetBytes(js);
 | 
	
		
			
				|  |  | -            await memoryStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +            await DumpHtml(creator.DashboardUIPath, path, culture, _appHost.ApplicationVersion.ToString());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            memoryStream.Position = 0;
 | 
	
		
			
				|  |  | -            return memoryStream;
 | 
	
		
			
				|  |  | +            return "";
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private IEnumerable<string> GetScriptFiles()
 | 
	
		
			
				|  |  | +        private async Task DumpHtml(string source, string destination, string culture, string appVersion)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            return new[]
 | 
	
		
			
				|  |  | -                            {
 | 
	
		
			
				|  |  | -                                "extensions.js",
 | 
	
		
			
				|  |  | -                                "site.js",
 | 
	
		
			
				|  |  | -                                "librarybrowser.js",
 | 
	
		
			
				|  |  | -                                "librarylist.js",
 | 
	
		
			
				|  |  | -                                "editorsidebar.js",
 | 
	
		
			
				|  |  | -                                "librarymenu.js",
 | 
	
		
			
				|  |  | -                                "mediacontroller.js",
 | 
	
		
			
				|  |  | -                                "chromecast.js",
 | 
	
		
			
				|  |  | -                                "backdrops.js",
 | 
	
		
			
				|  |  | -                                "sync.js",
 | 
	
		
			
				|  |  | -                                "playlistmanager.js",
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                "mediaplayer.js",
 | 
	
		
			
				|  |  | -                                "mediaplayer-video.js",
 | 
	
		
			
				|  |  | -                                "nowplayingbar.js",
 | 
	
		
			
				|  |  | -                                "nowplayingpage.js",
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                "ratingdialog.js",
 | 
	
		
			
				|  |  | -                                "aboutpage.js",
 | 
	
		
			
				|  |  | -                                "alphapicker.js",
 | 
	
		
			
				|  |  | -                                "addpluginpage.js",
 | 
	
		
			
				|  |  | -                                "advancedconfigurationpage.js",
 | 
	
		
			
				|  |  | -                                "metadataadvanced.js",
 | 
	
		
			
				|  |  | -                                "autoorganizetv.js",
 | 
	
		
			
				|  |  | -                                "autoorganizelog.js",
 | 
	
		
			
				|  |  | -                                "channels.js",
 | 
	
		
			
				|  |  | -                                "channelslatest.js",
 | 
	
		
			
				|  |  | -                                "channelitems.js",
 | 
	
		
			
				|  |  | -                                "channelsettings.js",
 | 
	
		
			
				|  |  | -                                "dashboardgeneral.js",
 | 
	
		
			
				|  |  | -                                "dashboardpage.js",
 | 
	
		
			
				|  |  | -                                "dashboardsync.js",
 | 
	
		
			
				|  |  | -                                "device.js",
 | 
	
		
			
				|  |  | -                                "devices.js",
 | 
	
		
			
				|  |  | -                                "devicesupload.js",
 | 
	
		
			
				|  |  | -                                "directorybrowser.js",
 | 
	
		
			
				|  |  | -                                "dlnaprofile.js",
 | 
	
		
			
				|  |  | -                                "dlnaprofiles.js",
 | 
	
		
			
				|  |  | -                                "dlnasettings.js",
 | 
	
		
			
				|  |  | -                                "dlnaserversettings.js",
 | 
	
		
			
				|  |  | -                                "editcollectionitems.js",
 | 
	
		
			
				|  |  | -                                "edititemmetadata.js",
 | 
	
		
			
				|  |  | -                                "edititemimages.js",
 | 
	
		
			
				|  |  | -                                "edititemsubtitles.js",
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                "playbackconfiguration.js",
 | 
	
		
			
				|  |  | -                                "cinemamodeconfiguration.js",
 | 
	
		
			
				|  |  | -                                "encodingsettings.js",
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                "externalplayer.js",
 | 
	
		
			
				|  |  | -                                "favorites.js",
 | 
	
		
			
				|  |  | -                                "gamesrecommendedpage.js",
 | 
	
		
			
				|  |  | -                                "gamesystemspage.js",
 | 
	
		
			
				|  |  | -                                "gamespage.js",
 | 
	
		
			
				|  |  | -                                "gamegenrepage.js",
 | 
	
		
			
				|  |  | -                                "gamestudiospage.js",
 | 
	
		
			
				|  |  | -                                "homelatest.js",
 | 
	
		
			
				|  |  | -                                "indexpage.js",
 | 
	
		
			
				|  |  | -                                "itembynamedetailpage.js",
 | 
	
		
			
				|  |  | -                                "itemdetailpage.js",
 | 
	
		
			
				|  |  | -                                "itemgallery.js",
 | 
	
		
			
				|  |  | -                                "itemlistpage.js",
 | 
	
		
			
				|  |  | -                                "librarypathmapping.js",
 | 
	
		
			
				|  |  | -                                "reports.js",
 | 
	
		
			
				|  |  | -                                "librarysettings.js",
 | 
	
		
			
				|  |  | -                                "livetvchannel.js",
 | 
	
		
			
				|  |  | -                                "livetvchannels.js",
 | 
	
		
			
				|  |  | -                                "livetvguide.js",
 | 
	
		
			
				|  |  | -                                "livetvnewrecording.js",
 | 
	
		
			
				|  |  | -                                "livetvprogram.js",
 | 
	
		
			
				|  |  | -                                "livetvrecording.js",
 | 
	
		
			
				|  |  | -                                "livetvrecordinglist.js",
 | 
	
		
			
				|  |  | -                                "livetvrecordings.js",
 | 
	
		
			
				|  |  | -                                "livetvtimer.js",
 | 
	
		
			
				|  |  | -                                "livetvseriestimer.js",
 | 
	
		
			
				|  |  | -                                "livetvseriestimers.js",
 | 
	
		
			
				|  |  | -                                "livetvsettings.js",
 | 
	
		
			
				|  |  | -                                "livetvsuggested.js",
 | 
	
		
			
				|  |  | -                                "livetvstatus.js",
 | 
	
		
			
				|  |  | -                                "livetvtimers.js",
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                "loginpage.js",
 | 
	
		
			
				|  |  | -                                "logpage.js",
 | 
	
		
			
				|  |  | -                                "medialibrarypage.js",
 | 
	
		
			
				|  |  | -                                "metadataconfigurationpage.js",
 | 
	
		
			
				|  |  | -                                "metadataimagespage.js",
 | 
	
		
			
				|  |  | -                                "metadatasubtitles.js",
 | 
	
		
			
				|  |  | -                                "metadatakodi.js",
 | 
	
		
			
				|  |  | -                                "moviegenres.js",
 | 
	
		
			
				|  |  | -                                "moviecollections.js",
 | 
	
		
			
				|  |  | -                                "movies.js",
 | 
	
		
			
				|  |  | -                                "movieslatest.js",
 | 
	
		
			
				|  |  | -                                "moviepeople.js",
 | 
	
		
			
				|  |  | -                                "moviesrecommended.js",
 | 
	
		
			
				|  |  | -                                "moviestudios.js",
 | 
	
		
			
				|  |  | -                                "movietrailers.js",
 | 
	
		
			
				|  |  | -                                "musicalbums.js",
 | 
	
		
			
				|  |  | -                                "musicalbumartists.js",
 | 
	
		
			
				|  |  | -                                "musicartists.js",
 | 
	
		
			
				|  |  | -                                "musicgenres.js",
 | 
	
		
			
				|  |  | -                                "musicrecommended.js",
 | 
	
		
			
				|  |  | -                                "musicvideos.js",
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                "mypreferencesdisplay.js",
 | 
	
		
			
				|  |  | -                                "mypreferenceslanguages.js",
 | 
	
		
			
				|  |  | -                                "mypreferenceswebclient.js",
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                "notifications.js",
 | 
	
		
			
				|  |  | -                                "notificationlist.js",
 | 
	
		
			
				|  |  | -                                "notificationsetting.js",
 | 
	
		
			
				|  |  | -                                "notificationsettings.js",
 | 
	
		
			
				|  |  | -                                "playlist.js",
 | 
	
		
			
				|  |  | -                                "playlists.js",
 | 
	
		
			
				|  |  | -                                "playlistedit.js",
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                "plugincatalogpage.js",
 | 
	
		
			
				|  |  | -                                "pluginspage.js",
 | 
	
		
			
				|  |  | -                                "remotecontrol.js",
 | 
	
		
			
				|  |  | -                                "scheduledtaskpage.js",
 | 
	
		
			
				|  |  | -                                "scheduledtaskspage.js",
 | 
	
		
			
				|  |  | -                                "search.js",
 | 
	
		
			
				|  |  | -                                "serversecurity.js",
 | 
	
		
			
				|  |  | -                                "songs.js",
 | 
	
		
			
				|  |  | -                                "supporterkeypage.js",
 | 
	
		
			
				|  |  | -                                "supporterpage.js",
 | 
	
		
			
				|  |  | -                                "episodes.js",
 | 
	
		
			
				|  |  | -                                "thememediaplayer.js",
 | 
	
		
			
				|  |  | -                                "tvgenres.js",
 | 
	
		
			
				|  |  | -                                "tvlatest.js",
 | 
	
		
			
				|  |  | -                                "tvpeople.js",
 | 
	
		
			
				|  |  | -                                "tvrecommended.js",
 | 
	
		
			
				|  |  | -                                "tvshows.js",
 | 
	
		
			
				|  |  | -                                "tvstudios.js",
 | 
	
		
			
				|  |  | -                                "tvupcoming.js",
 | 
	
		
			
				|  |  | -                                "useredit.js",
 | 
	
		
			
				|  |  | -                                "userpassword.js",
 | 
	
		
			
				|  |  | -                                "myprofile.js",
 | 
	
		
			
				|  |  | -                                "userprofilespage.js",
 | 
	
		
			
				|  |  | -                                "userparentalcontrol.js",
 | 
	
		
			
				|  |  | -                                "userlibraryaccess.js",
 | 
	
		
			
				|  |  | -                                "wizardfinishpage.js",
 | 
	
		
			
				|  |  | -                                "wizardservice.js",
 | 
	
		
			
				|  |  | -                                "wizardstartpage.js",
 | 
	
		
			
				|  |  | -                                "wizardsettings.js",
 | 
	
		
			
				|  |  | -                                "wizarduserpage.js"
 | 
	
		
			
				|  |  | -                            };
 | 
	
		
			
				|  |  | +            foreach (var file in Directory.GetFiles(source, "*.html", SearchOption.TopDirectoryOnly))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                await DumpHtmlFile(file, destination, culture, appVersion).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private async Task AppendLocalization(Stream stream)
 | 
	
		
			
				|  |  | +        private async Task DumpHtmlFile(string file, string destination, string culture, string appVersion)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            var js = "window.localizationGlossary=" + _jsonSerializer.SerializeToString(_localization.GetJavaScriptLocalizationDictionary(GetLocalizationCulture()));
 | 
	
		
			
				|  |  | +            var filename = Path.GetFileName(file);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var bytes = Encoding.UTF8.GetBytes(js);
 | 
	
		
			
				|  |  | -            await stream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +            var targetPath = Path.Combine(destination, filename);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets all CSS.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <returns>Task{Stream}.</returns>
 | 
	
		
			
				|  |  | -        private async Task<Stream> GetAllCss()
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var files = new[]
 | 
	
		
			
				|  |  | -                                  {
 | 
	
		
			
				|  |  | -                                      "site.css",
 | 
	
		
			
				|  |  | -                                      "chromecast.css",
 | 
	
		
			
				|  |  | -                                      "mediaplayer.css",
 | 
	
		
			
				|  |  | -                                      "mediaplayer-video.css",
 | 
	
		
			
				|  |  | -                                      "librarymenu.css",
 | 
	
		
			
				|  |  | -                                      "librarybrowser.css",
 | 
	
		
			
				|  |  | -                                      "detailtable.css",
 | 
	
		
			
				|  |  | -                                      "card.css",
 | 
	
		
			
				|  |  | -                                      "tileitem.css",
 | 
	
		
			
				|  |  | -                                      "metadataeditor.css",
 | 
	
		
			
				|  |  | -                                      "notifications.css",
 | 
	
		
			
				|  |  | -                                      "search.css",
 | 
	
		
			
				|  |  | -                                      "pluginupdates.css",
 | 
	
		
			
				|  |  | -                                      "remotecontrol.css",
 | 
	
		
			
				|  |  | -                                      "userimage.css",
 | 
	
		
			
				|  |  | -                                      "livetv.css",
 | 
	
		
			
				|  |  | -                                      "nowplaying.css",
 | 
	
		
			
				|  |  | -                                      "icons.css"
 | 
	
		
			
				|  |  | -                                  };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var builder = new StringBuilder();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            foreach (var file in files)
 | 
	
		
			
				|  |  | +            using (var stream = await GetPackageCreator().GetResource(filename, culture, appVersion).ConfigureAwait(false))
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                var path = GetDashboardResourcePath("css/" + file);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
 | 
	
		
			
				|  |  | +                using (var fs = _fileSystem.GetFileStream(targetPath, FileMode.Create, FileAccess.Write, FileShare.Read))
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    using (var streamReader = new StreamReader(fs))
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        var text = await streamReader.ReadToEndAsync().ConfigureAwait(false);
 | 
	
		
			
				|  |  | -                        builder.Append(text);
 | 
	
		
			
				|  |  | -                        builder.Append(Environment.NewLine);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | +                    stream.CopyTo(fs);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var css = builder.ToString();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //try
 | 
	
		
			
				|  |  | -            //{
 | 
	
		
			
				|  |  | -            //    var result = new KristensenCssMinifier().Minify(builder.ToString(), false, Encoding.UTF8);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //    css = result.MinifiedContent;
 | 
	
		
			
				|  |  | -            //}
 | 
	
		
			
				|  |  | -            //catch (Exception ex)
 | 
	
		
			
				|  |  | -            //{
 | 
	
		
			
				|  |  | -            //    Logger.ErrorException("Error minifying css", ex);
 | 
	
		
			
				|  |  | -            //}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(css));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            memoryStream.Position = 0;
 | 
	
		
			
				|  |  | -            return memoryStream;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Appends the resource.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="outputStream">The output stream.</param>
 | 
	
		
			
				|  |  | -        /// <param name="path">The path.</param>
 | 
	
		
			
				|  |  | -        /// <param name="newLineBytes">The new line bytes.</param>
 | 
	
		
			
				|  |  | -        /// <returns>Task.</returns>
 | 
	
		
			
				|  |  | -        private async Task AppendResource(Stream outputStream, string path, byte[] newLineBytes)
 | 
	
		
			
				|  |  | +        private void CopyDirectory(string source, string destination)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            path = GetDashboardResourcePath(path);
 | 
	
		
			
				|  |  | +            Directory.CreateDirectory(destination);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                using (var streamReader = new StreamReader(fs))
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    var text = await streamReader.ReadToEndAsync().ConfigureAwait(false);
 | 
	
		
			
				|  |  | -                    var bytes = Encoding.UTF8.GetBytes(text);
 | 
	
		
			
				|  |  | -                    await outputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +            //Now Create all of the directories
 | 
	
		
			
				|  |  | +            foreach (string dirPath in Directory.GetDirectories(source, "*",
 | 
	
		
			
				|  |  | +                SearchOption.AllDirectories))
 | 
	
		
			
				|  |  | +                Directory.CreateDirectory(dirPath.Replace(source, destination));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            await outputStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +            //Copy all the files & Replaces any files with the same name
 | 
	
		
			
				|  |  | +            foreach (string newPath in Directory.GetFiles(source, "*.*",
 | 
	
		
			
				|  |  | +                SearchOption.AllDirectories))
 | 
	
		
			
				|  |  | +                File.Copy(newPath, newPath.Replace(source, destination), true);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 |