| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614 | 
							- using MediaBrowser.Common.Extensions;
 
- using MediaBrowser.Common.IO;
 
- using MediaBrowser.Common.Net;
 
- using MediaBrowser.Common.ScheduledTasks;
 
- using MediaBrowser.Controller;
 
- using MediaBrowser.Controller.Configuration;
 
- using MediaBrowser.Controller.Dto;
 
- using MediaBrowser.Controller.Plugins;
 
- using MediaBrowser.Controller.Session;
 
- using MediaBrowser.Model.Logging;
 
- using MediaBrowser.Model.Tasks;
 
- using ServiceStack.ServiceHost;
 
- using System;
 
- using System.Collections.Generic;
 
- using System.Diagnostics;
 
- using System.IO;
 
- using System.Linq;
 
- using System.Reflection;
 
- using System.Text;
 
- using System.Threading.Tasks;
 
- namespace MediaBrowser.WebDashboard.Api
 
- {
 
-     /// <summary>
 
-     /// Class GetDashboardConfigurationPages
 
-     /// </summary>
 
-     [Route("/dashboard/ConfigurationPages", "GET")]
 
-     [Restrict(VisibilityTo = EndpointAttributes.None)]
 
-     public class GetDashboardConfigurationPages : IReturn<List<ConfigurationPageInfo>>
 
-     {
 
-         /// <summary>
 
-         /// Gets or sets the type of the page.
 
-         /// </summary>
 
-         /// <value>The type of the page.</value>
 
-         public ConfigurationPageType? PageType { get; set; }
 
-     }
 
-     /// <summary>
 
-     /// Class GetDashboardConfigurationPage
 
-     /// </summary>
 
-     [Route("/dashboard/ConfigurationPage", "GET")]
 
-     [Restrict(VisibilityTo = EndpointAttributes.None)]
 
-     public class GetDashboardConfigurationPage
 
-     {
 
-         /// <summary>
 
-         /// Gets or sets the name.
 
-         /// </summary>
 
-         /// <value>The name.</value>
 
-         public string Name { get; set; }
 
-     }
 
-     /// <summary>
 
-     /// Class GetDashboardResource
 
-     /// </summary>
 
-     [Route("/dashboard/{ResourceName*}", "GET")]
 
-     [Restrict(VisibilityTo = EndpointAttributes.None)]
 
-     public class GetDashboardResource
 
-     {
 
-         /// <summary>
 
-         /// Gets or sets the name.
 
-         /// </summary>
 
-         /// <value>The name.</value>
 
-         public string ResourceName { get; set; }
 
-         /// <summary>
 
-         /// Gets or sets the V.
 
-         /// </summary>
 
-         /// <value>The V.</value>
 
-         public string V { get; set; }
 
-     }
 
-     /// <summary>
 
-     /// Class GetDashboardInfo
 
-     /// </summary>
 
-     [Route("/dashboard/dashboardInfo", "GET")]
 
-     [Restrict(VisibilityTo = EndpointAttributes.None)]
 
-     public class GetDashboardInfo : IReturn<DashboardInfo>
 
-     {
 
-     }
 
-     /// <summary>
 
-     /// Class DashboardService
 
-     /// </summary>
 
-     public class DashboardService : IRestfulService, IHasResultFactory
 
-     {
 
-         /// <summary>
 
-         /// Gets or sets the logger.
 
-         /// </summary>
 
-         /// <value>The logger.</value>
 
-         public ILogger Logger { get; set; }
 
-         /// <summary>
 
-         /// Gets or sets the HTTP result factory.
 
-         /// </summary>
 
-         /// <value>The HTTP result factory.</value>
 
-         public IHttpResultFactory ResultFactory { get; set; }
 
-         /// <summary>
 
-         /// Gets or sets the request context.
 
-         /// </summary>
 
-         /// <value>The request context.</value>
 
-         public IRequestContext RequestContext { get; set; }
 
-         /// <summary>
 
-         /// Gets or sets the task manager.
 
-         /// </summary>
 
-         /// <value>The task manager.</value>
 
-         private readonly ITaskManager _taskManager;
 
-         /// <summary>
 
-         /// The _app host
 
-         /// </summary>
 
-         private readonly IServerApplicationHost _appHost;
 
-         /// <summary>
 
-         /// The _server configuration manager
 
-         /// </summary>
 
-         private readonly IServerConfigurationManager _serverConfigurationManager;
 
-         private readonly ISessionManager _sessionManager;
 
-         /// <summary>
 
-         /// Initializes a new instance of the <see cref="DashboardService" /> class.
 
-         /// </summary>
 
-         /// <param name="taskManager">The task manager.</param>
 
-         /// <param name="appHost">The app host.</param>
 
-         /// <param name="serverConfigurationManager">The server configuration manager.</param>
 
-         /// <param name="sessionManager">The session manager.</param>
 
-         public DashboardService(ITaskManager taskManager, IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, ISessionManager sessionManager)
 
-         {
 
-             _taskManager = taskManager;
 
-             _appHost = appHost;
 
-             _serverConfigurationManager = serverConfigurationManager;
 
-             _sessionManager = sessionManager;
 
-         }
 
-         /// <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(Process.GetCurrentProcess().MainModule.FileName);
 
-                 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('/', '\\'));
 
-         }
 
-         /// <summary>
 
-         /// Gets the specified request.
 
-         /// </summary>
 
-         /// <param name="request">The request.</param>
 
-         /// <returns>System.Object.</returns>
 
-         public object Get(GetDashboardInfo request)
 
-         {
 
-             var result = GetDashboardInfo(_appHost, _taskManager, _sessionManager);
 
-             return ResultFactory.GetOptimizedResult(RequestContext, result);
 
-         }
 
-         /// <summary>
 
-         /// Gets the dashboard info.
 
-         /// </summary>
 
-         /// <param name="appHost">The app host.</param>
 
-         /// <param name="taskManager">The task manager.</param>
 
-         /// <param name="connectionManager">The connection manager.</param>
 
-         /// <returns>DashboardInfo.</returns>
 
-         public static DashboardInfo GetDashboardInfo(IServerApplicationHost appHost,
 
-             ITaskManager taskManager,
 
-             ISessionManager connectionManager)
 
-         {
 
-             var connections = connectionManager.Sessions.Where(i => i.IsActive).ToArray();
 
-             return new DashboardInfo
 
-             {
 
-                 SystemInfo = appHost.GetSystemInfo(),
 
-                 RunningTasks = taskManager.ScheduledTasks.Where(i => i.State == TaskState.Running || i.State == TaskState.Cancelling)
 
-                                      .Select(ScheduledTaskHelpers.GetTaskInfo)
 
-                                      .ToArray(),
 
-                 ApplicationUpdateTaskId = taskManager.ScheduledTasks.First(t => t.ScheduledTask.GetType().Name.Equals("SystemUpdateTask", StringComparison.OrdinalIgnoreCase)).Id,
 
-                 ActiveConnections = connections.Select(SessionInfoDtoBuilder.GetSessionInfoDto).ToArray()
 
-             };
 
-         }
 
-         /// <summary>
 
-         /// Gets the specified request.
 
-         /// </summary>
 
-         /// <param name="request">The request.</param>
 
-         /// <returns>System.Object.</returns>
 
-         public object Get(GetDashboardConfigurationPage request)
 
-         {
 
-             var page = ServerEntryPoint.Instance.PluginConfigurationPages.First(p => p.Name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
 
-             return ResultFactory.GetStaticResult(RequestContext, page.Plugin.Version.ToString().GetMD5(), page.Plugin.AssemblyDateLastModified, null, MimeTypes.GetMimeType("page.html"), () => ModifyHtml(page.GetHtmlStream()));
 
-         }
 
-         /// <summary>
 
-         /// Gets the specified request.
 
-         /// </summary>
 
-         /// <param name="request">The request.</param>
 
-         /// <returns>System.Object.</returns>
 
-         public object Get(GetDashboardConfigurationPages request)
 
-         {
 
-             const string unavilableMessage = "The server is still loading. Please try again momentarily.";
 
-             var instance = ServerEntryPoint.Instance;
 
-             if (instance == null)
 
-             {
 
-                 throw new InvalidOperationException(unavilableMessage);
 
-             }
 
-             var pages = instance.PluginConfigurationPages;
 
-             if (pages == null)
 
-             {
 
-                 throw new InvalidOperationException(unavilableMessage);
 
-             }
 
-             if (request.PageType.HasValue)
 
-             {
 
-                 pages = pages.Where(p => p.ConfigurationPageType == request.PageType.Value);
 
-             }
 
-             return ResultFactory.GetOptimizedResult(RequestContext, pages.Select(p => new ConfigurationPageInfo(p)).ToList());
 
-         }
 
-         /// <summary>
 
-         /// Gets the specified request.
 
-         /// </summary>
 
-         /// <param name="request">The request.</param>
 
-         /// <returns>System.Object.</returns>
 
-         public object Get(GetDashboardResource request)
 
-         {
 
-             var path = request.ResourceName;
 
-             var contentType = MimeTypes.GetMimeType(path);
 
-             // Don't cache if not configured to do so
 
-             // But always cache images to simulate production
 
-             if (!_serverConfigurationManager.Configuration.EnableDashboardResponseCaching && !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
 
-             {
 
-                 return ResultFactory.GetResult(GetResourceStream(path).Result, contentType);
 
-             }
 
-             TimeSpan? cacheDuration = null;
 
-             // Cache images unconditionally - updates to image files will require new filename
 
-             // If there's a version number in the query string we can cache this unconditionally
 
-             if (contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) || !string.IsNullOrEmpty(request.V))
 
-             {
 
-                 cacheDuration = TimeSpan.FromDays(365);
 
-             }
 
-             var assembly = GetType().Assembly.GetName();
 
-             var cacheKey = (assembly.Version + path).GetMD5();
 
-             return ResultFactory.GetStaticResult(RequestContext, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path));
 
-         }
 
-         /// <summary>
 
-         /// Gets the resource stream.
 
-         /// </summary>
 
-         /// <param name="path">The path.</param>
 
-         /// <returns>Task{Stream}.</returns>
 
-         private async Task<Stream> GetResourceStream(string path)
 
-         {
 
-             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)
 
-             {
 
-                 var isHtml = IsHtml(path);
 
-                 // 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).ConfigureAwait(false);
 
-                 }
 
-             }
 
-             return resourceStream;
 
-         }
 
-         /// <summary>
 
-         /// Gets the raw resource stream.
 
-         /// </summary>
 
-         /// <param name="path">The path.</param>
 
-         /// <returns>Task{Stream}.</returns>
 
-         private Stream GetRawResourceStream(string path)
 
-         {
 
-             return new FileStream(GetDashboardResourcePath(path), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, true);
 
-         }
 
-         /// <summary>
 
-         /// Determines whether the specified path is HTML.
 
-         /// </summary>
 
-         /// <param name="path">The path.</param>
 
-         /// <returns><c>true</c> if the specified path is HTML; otherwise, <c>false</c>.</returns>
 
-         private bool IsHtml(string path)
 
-         {
 
-             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>
 
-         /// <returns>Task{Stream}.</returns>
 
-         internal async Task<Stream> ModifyHtml(Stream sourceStream)
 
-         {
 
-             string html;
 
-             using (var memoryStream = new MemoryStream())
 
-             {
 
-                 await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
 
-                 html = Encoding.UTF8.GetString(memoryStream.ToArray());
 
-             }
 
-             var version = GetType().Assembly.GetName().Version;
 
-             html = html.Replace("<head>", "<head>" + GetMetaTags() + GetCommonCss(version) + GetCommonJavascript(version));
 
-             var bytes = Encoding.UTF8.GetBytes(html);
 
-             sourceStream.Dispose();
 
-             return new MemoryStream(bytes);
 
-         }
 
-         /// <summary>
 
-         /// Gets the meta tags.
 
-         /// </summary>
 
-         /// <returns>System.String.</returns>
 
-         private static string GetMetaTags()
 
-         {
 
-             var sb = new StringBuilder();
 
-             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=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\">");
 
-             // 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=\"favicon.ico\" />");
 
-             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[]
 
-                             {
 
-                                 "http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css",
 
-                                 "thirdparty/jqm-icon-pack-3.0/font-awesome/jqm-icon-pack-3.0.0-fa.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 versionString = "?v=" + version;
 
-             var files = new[]
 
-                             {
 
-                                 "http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js", 
 
-                                 "http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js",
 
-                                 "scripts/all.js" + versionString,
 
-                                 "thirdparty/jstree1.0fix2/jquery.jstree.js"
 
-             };
 
-             var tags = files.Select(s => string.Format("<script src=\"{0}\"></script>", s)).ToArray();
 
-             return string.Join(string.Empty, tags);
 
-         }
 
-         /// <summary>
 
-         /// Gets a stream containing all concatenated javascript
 
-         /// </summary>
 
-         /// <returns>Task{Stream}.</returns>
 
-         private async Task<Stream> GetAllJavascript()
 
-         {
 
-             var assembly = GetType().Assembly;
 
-             var scriptFiles = new[]
 
-                                   {
 
-                                       "extensions.js",
 
-                                       "site.js",
 
-                                       "librarybrowser.js",
 
-                                       "aboutpage.js",
 
-                                       "allusersettings.js",
 
-                                       "alphapicker.js",
 
-                                       "addpluginpage.js",
 
-                                       "advancedconfigurationpage.js",
 
-                                       "advancedmetadataconfigurationpage.js",
 
-                                       "boxsets.js",
 
-                                       "clientsettings.js",
 
-                                       "dashboardpage.js",
 
-                                       "directorybrowser.js",
 
-                                       "edititemmetadata.js",
 
-                                       "edititempeople.js",
 
-                                       "edititemimages.js",
 
-                                       "edituserpage.js",
 
-                                       "favoritetv.js",
 
-                                       "gamesrecommendedpage.js",
 
-                                       "gamesystemspage.js",
 
-                                       "gamespage.js",
 
-                                       "gamegenrepage.js",
 
-                                       "gamestudiospage.js",
 
-                                       "indexpage.js",
 
-                                       "itembynamedetailpage.js",
 
-                                       "itemdetailpage.js",
 
-                                       "itemgallery.js",
 
-                                       "itemlistpage.js",
 
-                                       "librarysettings.js",
 
-                                       "loginpage.js",
 
-                                       "logpage.js",
 
-                                       "medialibrarypage.js",
 
-                                       "mediaplayer.js",
 
-                                       "metadataconfigurationpage.js",
 
-                                       "metadataimagespage.js",
 
-                                       "moviegenres.js",
 
-                                       "movies.js",
 
-                                       "moviepeople.js",
 
-                                       "moviesrecommended.js",
 
-                                       "moviestudios.js",
 
-                                       "movietrailers.js",
 
-                                       "musicalbums.js",
 
-                                       "musicartists.js",
 
-                                       "musicgenres.js",
 
-                                       "musicrecommended.js",
 
-                                       "musicvideos.js",
 
-                                       "notifications.js",
 
-                                       "playlist.js",
 
-                                       "plugincatalogpage.js",
 
-                                       "pluginspage.js",
 
-                                       "pluginupdatespage.js",
 
-                                       "remotecontrol.js",
 
-                                       "scheduledtaskpage.js",
 
-                                       "scheduledtaskspage.js",
 
-                                       "search.js",
 
-                                       "songs.js",
 
-                                       "supporterkeypage.js",
 
-                                       "supporterpage.js",
 
-                                       "tvgenres.js",
 
-                                       "tvnextup.js",
 
-                                       "tvpeople.js",
 
-                                       "tvrecommended.js",
 
-                                       "tvshows.js",
 
-                                       "tvstudios.js",
 
-                                       "updatepasswordpage.js",
 
-                                       "userimagepage.js",
 
-                                       "userprofilespage.js",
 
-                                       "wizardfinishpage.js",
 
-                                       "wizardstartpage.js",
 
-                                       "wizarduserpage.js"
 
-                                   };
 
-             var memoryStream = new MemoryStream();
 
-             var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine);
 
-             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);
 
-             await AppendResource(memoryStream, "thirdparty/autoNumeric.js", newLineBytes).ConfigureAwait(false);
 
-             await AppendResource(memoryStream, "thirdparty/html5slider.js", newLineBytes).ConfigureAwait(false);
 
-             await AppendResource(assembly, memoryStream, "MediaBrowser.WebDashboard.ApiClient.js", newLineBytes).ConfigureAwait(false);
 
-             foreach (var file in scriptFiles)
 
-             {
 
-                 await AppendResource(memoryStream, "scripts/" + file, newLineBytes).ConfigureAwait(false);
 
-             }
 
-             memoryStream.Position = 0;
 
-             return memoryStream;
 
-         }
 
-         /// <summary>
 
-         /// Gets all CSS.
 
-         /// </summary>
 
-         /// <returns>Task{Stream}.</returns>
 
-         private async Task<Stream> GetAllCss()
 
-         {
 
-             var files = new[]
 
-                                   {
 
-                                       "site.css",
 
-                                       "librarybrowser.css",
 
-                                       "detailtable.css",
 
-                                       "posteritem.css",
 
-                                       "tileitem.css",
 
-                                       "metadataeditor.css",
 
-                                       "notifications.css",
 
-                                       "search.css",
 
-                                       "pluginupdates.css",
 
-                                       "remotecontrol.css",
 
-                                       "userimage.css"
 
-                                   };
 
-             var memoryStream = new MemoryStream();
 
-             var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine);
 
-             foreach (var file in files)
 
-             {
 
-                 await AppendResource(memoryStream, "css/" + file, newLineBytes).ConfigureAwait(false);
 
-             }
 
-             memoryStream.Position = 0;
 
-             return memoryStream;
 
-         }
 
-         /// <summary>
 
-         /// Appends the resource.
 
-         /// </summary>
 
-         /// <param name="assembly">The assembly.</param>
 
-         /// <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(Assembly assembly, Stream outputStream, string path, byte[] newLineBytes)
 
-         {
 
-             using (var stream = assembly.GetManifestResourceStream(path))
 
-             {
 
-                 await stream.CopyToAsync(outputStream).ConfigureAwait(false);
 
-                 await outputStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
 
-             }
 
-         }
 
-         /// <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)
 
-         {
 
-             path = GetDashboardResourcePath(path);
 
-             using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, 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);
 
-                 }
 
-             }
 
-             await outputStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
 
-         }
 
-     }
 
- }
 
 
  |