diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-07-06 17:23:32 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-07-06 17:23:32 -0400 |
| commit | 53450bd514eec97d58eb18b8a01feab36475826b (patch) | |
| tree | 5cd1b4013852619b0e108a8f2442ab893a924441 /MediaBrowser.Server.Implementations | |
| parent | b3054a6a2216e36cc37279a1fc0f4c14e6668c8f (diff) | |
added a notifications service
Diffstat (limited to 'MediaBrowser.Server.Implementations')
8 files changed, 764 insertions, 66 deletions
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs new file mode 100644 index 000000000..65567d3a8 --- /dev/null +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs @@ -0,0 +1,173 @@ +using MediaBrowser.Common.Events; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Common.Updates; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Notifications; +using System; +using System.Linq; +using System.Threading; +using MediaBrowser.Model.Tasks; + +namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications +{ + /// <summary> + /// Creates notifications for various system events + /// </summary> + public class Notifications : IServerEntryPoint + { + private readonly INotificationsRepository _notificationsRepo; + private readonly IInstallationManager _installationManager; + private readonly IUserManager _userManager; + private readonly ILogger _logger; + + private readonly ITaskManager _taskManager; + + public Notifications(IInstallationManager installationManager, INotificationsRepository notificationsRepo, IUserManager userManager, ILogger logger, ITaskManager taskManager) + { + _installationManager = installationManager; + _notificationsRepo = notificationsRepo; + _userManager = userManager; + _logger = logger; + _taskManager = taskManager; + } + + public void Run() + { + _installationManager.PackageInstallationCompleted += _installationManager_PackageInstallationCompleted; + _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed; + _installationManager.PluginUninstalled += _installationManager_PluginUninstalled; + + _taskManager.TaskCompleted += _taskManager_TaskCompleted; + } + + async void _taskManager_TaskCompleted(object sender, GenericEventArgs<TaskResult> e) + { + var result = e.Argument; + + if (result.Status == TaskCompletionStatus.Failed) + { + foreach (var user in _userManager + .Users + .Where(i => i.Configuration.IsAdministrator) + .ToList()) + { + var notification = new Notification + { + UserId = user.Id, + Category = "ScheduledTaskFailed", + Name = result.Name + " failed", + RelatedId = result.Name, + Description = result.ErrorMessage, + Level = NotificationLevel.Error + }; + + try + { + await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error adding notification", ex); + } + } + } + } + + async void _installationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e) + { + var plugin = e.Argument; + + foreach (var user in _userManager + .Users + .Where(i => i.Configuration.IsAdministrator) + .ToList()) + { + var notification = new Notification + { + UserId = user.Id, + Category = "PluginUninstalled", + Name = plugin.Name + " has been uninstalled", + RelatedId = plugin.Id.ToString() + }; + + try + { + await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error adding notification", ex); + } + } + } + + async void _installationManager_PackageInstallationCompleted(object sender, InstallationEventArgs e) + { + var installationInfo = e.InstallationInfo; + + foreach (var user in _userManager + .Users + .Where(i => i.Configuration.IsAdministrator) + .ToList()) + { + var notification = new Notification + { + UserId = user.Id, + Category = "PackageInstallationCompleted", + Name = installationInfo.Name + " " + installationInfo.Version + " was installed", + RelatedId = installationInfo.Name, + Description = e.PackageVersionInfo.description + }; + + try + { + await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error adding notification", ex); + } + } + } + + async void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e) + { + var installationInfo = e.InstallationInfo; + + foreach (var user in _userManager + .Users + .Where(i => i.Configuration.IsAdministrator) + .ToList()) + { + var notification = new Notification + { + UserId = user.Id, + Category = "PackageInstallationFailed", + Level = NotificationLevel.Error, + Name = installationInfo.Name + " " + installationInfo.Version + " installation failed", + RelatedId = installationInfo.Name, + Description = e.Exception.Message + }; + + try + { + await _notificationsRepo.AddNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error adding notification", ex); + } + } + } + + public void Dispose() + { + _installationManager.PackageInstallationCompleted -= _installationManager_PackageInstallationCompleted; + _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed; + } + } +} diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs new file mode 100644 index 000000000..2264cc524 --- /dev/null +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs @@ -0,0 +1,63 @@ +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Controller.Plugins; +using System.Linq; + +namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications +{ + /// <summary> + /// Sends out messages anytime a notification is added or udpated + /// </summary> + public class WebSocketNotifier : IServerEntryPoint + { + private readonly INotificationsRepository _notificationsRepo; + + private readonly IServerManager _serverManager; + + public WebSocketNotifier(INotificationsRepository notificationsRepo, IServerManager serverManager) + { + _notificationsRepo = notificationsRepo; + _serverManager = serverManager; + } + + public void Run() + { + _notificationsRepo.NotificationAdded += _notificationsRepo_NotificationAdded; + _notificationsRepo.NotificationUpdated += _notificationsRepo_NotificationUpdated; + + _notificationsRepo.NotificationsMarkedRead += _notificationsRepo_NotificationsMarkedRead; + } + + void _notificationsRepo_NotificationsMarkedRead(object sender, NotificationReadEventArgs e) + { + var list = e.IdList.Select(i => i.ToString("N")).ToList(); + + list.Add(e.UserId.ToString("N")); + list.Add(e.IsRead.ToString().ToLower()); + + var msg = string.Join("|", list.ToArray()); + + _serverManager.SendWebSocketMessage("NotificationsMarkedRead", msg); + } + + void _notificationsRepo_NotificationUpdated(object sender, NotificationUpdateEventArgs e) + { + var msg = e.Notification.UserId + "|" + e.Notification.Id; + + _serverManager.SendWebSocketMessage("NotificationUpdated", msg); + } + + void _notificationsRepo_NotificationAdded(object sender, NotificationUpdateEventArgs e) + { + var msg = e.Notification.UserId + "|" + e.Notification.Id; + + _serverManager.SendWebSocketMessage("NotificationAdded", msg); + } + + public void Dispose() + { + _notificationsRepo.NotificationAdded -= _notificationsRepo_NotificationAdded; + _notificationsRepo.NotificationUpdated -= _notificationsRepo_NotificationUpdated; + } + } +} diff --git a/MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs b/MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs index b4bffc077..6325944f1 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/WebSocketEvents.cs @@ -73,64 +73,44 @@ namespace MediaBrowser.Server.Implementations.EntryPoints _appHost.HasPendingRestartChanged += kernel_HasPendingRestartChanged; _installationManager.PluginUninstalled += InstallationManager_PluginUninstalled; - _installationManager.PackageInstalling += installationManager_PackageInstalling; - _installationManager.PackageInstallationCancelled += installationManager_PackageInstallationCancelled; - _installationManager.PackageInstallationCompleted += installationManager_PackageInstallationCompleted; - _installationManager.PackageInstallationFailed += installationManager_PackageInstallationFailed; + _installationManager.PackageInstalling += _installationManager_PackageInstalling; + _installationManager.PackageInstallationCancelled += _installationManager_PackageInstallationCancelled; + _installationManager.PackageInstallationCompleted += _installationManager_PackageInstallationCompleted; + _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed; _taskManager.TaskExecuting += _taskManager_TaskExecuting; _taskManager.TaskCompleted += _taskManager_TaskCompleted; } - void _taskManager_TaskCompleted(object sender, GenericEventArgs<TaskResult> e) + void _installationManager_PackageInstalling(object sender, InstallationEventArgs e) { - _serverManager.SendWebSocketMessage("ScheduledTaskEnded", e.Argument); + _serverManager.SendWebSocketMessage("PackageInstalling", e.InstallationInfo); } - void _taskManager_TaskExecuting(object sender, EventArgs e) + void _installationManager_PackageInstallationCancelled(object sender, InstallationEventArgs e) { - var task = (IScheduledTask)sender; - _serverManager.SendWebSocketMessage("ScheduledTaskStarted", task.Name); + _serverManager.SendWebSocketMessage("PackageInstallationCancelled", e.InstallationInfo); } - /// <summary> - /// Installations the manager_ package installation failed. - /// </summary> - /// <param name="sender">The sender.</param> - /// <param name="e">The e.</param> - void installationManager_PackageInstallationFailed(object sender, GenericEventArgs<InstallationInfo> e) + void _installationManager_PackageInstallationCompleted(object sender, InstallationEventArgs e) { - _serverManager.SendWebSocketMessage("PackageInstallationFailed", e.Argument); + _serverManager.SendWebSocketMessage("PackageInstallationCompleted", e.InstallationInfo); } - /// <summary> - /// Installations the manager_ package installation completed. - /// </summary> - /// <param name="sender">The sender.</param> - /// <param name="e">The e.</param> - void installationManager_PackageInstallationCompleted(object sender, GenericEventArgs<InstallationInfo> e) + void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e) { - _serverManager.SendWebSocketMessage("PackageInstallationCompleted", e.Argument); + _serverManager.SendWebSocketMessage("PackageInstallationFailed", e.InstallationInfo); } - /// <summary> - /// Installations the manager_ package installation cancelled. - /// </summary> - /// <param name="sender">The sender.</param> - /// <param name="e">The e.</param> - void installationManager_PackageInstallationCancelled(object sender, GenericEventArgs<InstallationInfo> e) + void _taskManager_TaskCompleted(object sender, GenericEventArgs<TaskResult> e) { - _serverManager.SendWebSocketMessage("PackageInstallationCancelled", e.Argument); + _serverManager.SendWebSocketMessage("ScheduledTaskEnded", e.Argument); } - /// <summary> - /// Installations the manager_ package installing. - /// </summary> - /// <param name="sender">The sender.</param> - /// <param name="e">The e.</param> - void installationManager_PackageInstalling(object sender, GenericEventArgs<InstallationInfo> e) + void _taskManager_TaskExecuting(object sender, EventArgs e) { - _serverManager.SendWebSocketMessage("PackageInstalling", e.Argument); + var task = (IScheduledTask)sender; + _serverManager.SendWebSocketMessage("ScheduledTaskStarted", task.Name); } /// <summary> @@ -195,10 +175,10 @@ namespace MediaBrowser.Server.Implementations.EntryPoints _userManager.UserUpdated -= userManager_UserUpdated; _installationManager.PluginUninstalled -= InstallationManager_PluginUninstalled; - _installationManager.PackageInstalling -= installationManager_PackageInstalling; - _installationManager.PackageInstallationCancelled -= installationManager_PackageInstallationCancelled; - _installationManager.PackageInstallationCompleted -= installationManager_PackageInstallationCompleted; - _installationManager.PackageInstallationFailed -= installationManager_PackageInstallationFailed; + _installationManager.PackageInstalling -= _installationManager_PackageInstalling; + _installationManager.PackageInstallationCancelled -= _installationManager_PackageInstallationCancelled; + _installationManager.PackageInstallationCompleted -= _installationManager_PackageInstallationCompleted; + _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed; _appHost.HasPendingRestartChanged -= kernel_HasPendingRestartChanged; } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 34dc8ff52..625db7e4b 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -111,6 +111,8 @@ <Compile Include="Configuration\ServerConfigurationManager.cs" /> <Compile Include="EntryPoints\LibraryChangedNotifier.cs" /> <Compile Include="EntryPoints\LoadRegistrations.cs" /> + <Compile Include="EntryPoints\Notifications\Notifier.cs" /> + <Compile Include="EntryPoints\Notifications\WebSocketNotifier.cs" /> <Compile Include="EntryPoints\RefreshUsersMetadata.cs" /> <Compile Include="EntryPoints\WebSocketEvents.cs" /> <Compile Include="HttpServer\HttpResultFactory.cs" /> @@ -144,6 +146,7 @@ <Compile Include="MediaEncoder\MediaEncoder.cs" /> <Compile Include="Persistence\SqliteChapterRepository.cs" /> <Compile Include="Persistence\SqliteExtensions.cs" /> + <Compile Include="Persistence\SqliteNotificationsRepository.cs" /> <Compile Include="Persistence\TypeMapper.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Providers\ImageSaver.cs" /> diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs index c472241e3..7db8a5a6f 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs @@ -1,10 +1,8 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.Data; -using System.IO; using System.Threading; using System.Threading.Tasks; @@ -16,30 +14,20 @@ namespace MediaBrowser.Server.Implementations.Persistence private readonly ILogger _logger; - /// <summary> - /// The _app paths - /// </summary> - private readonly IApplicationPaths _appPaths; - private IDbCommand _deleteChaptersCommand; private IDbCommand _saveChapterCommand; /// <summary> /// Initializes a new instance of the <see cref="SqliteItemRepository" /> class. /// </summary> - /// <param name="appPaths">The app paths.</param> + /// <param name="connection">The connection.</param> /// <param name="logManager">The log manager.</param> /// <exception cref="System.ArgumentNullException">appPaths /// or /// jsonSerializer</exception> - public SqliteChapterRepository(IApplicationPaths appPaths, ILogManager logManager) + public SqliteChapterRepository(IDbConnection connection, ILogManager logManager) { - if (appPaths == null) - { - throw new ArgumentNullException("appPaths"); - } - - _appPaths = appPaths; + _connection = connection; _logger = logManager.GetLogger(GetType().Name); } @@ -48,12 +36,8 @@ namespace MediaBrowser.Server.Implementations.Persistence /// Opens the connection to the database /// </summary> /// <returns>Task.</returns> - public async Task Initialize() + public void Initialize() { - var dbFile = Path.Combine(_appPaths.DataPath, "chapters.db"); - - _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false); - string[] queries = { "create table if not exists chapters (ItemId GUID, ChapterIndex INT, StartPositionTicks BIGINT, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))", diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index e081a78e3..a90835c6a 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -90,7 +90,11 @@ namespace MediaBrowser.Server.Implementations.Persistence _logger = logManager.GetLogger(GetType().Name); - _chapterRepository = new SqliteChapterRepository(appPaths, logManager); + var chapterDbFile = Path.Combine(_appPaths.DataPath, "chapters.db"); + + var chapterConnection = SqliteExtensions.ConnectToDb(chapterDbFile).Result; + + _chapterRepository = new SqliteChapterRepository(chapterConnection, logManager); } /// <summary> @@ -119,7 +123,7 @@ namespace MediaBrowser.Server.Implementations.Persistence PrepareStatements(); - await _chapterRepository.Initialize().ConfigureAwait(false); + _chapterRepository.Initialize(); } /// <summary> diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs new file mode 100644 index 000000000..7b15467e0 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs @@ -0,0 +1,490 @@ +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Notifications; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Persistence +{ + public class SqliteNotificationsRepository : INotificationsRepository + { + private readonly IDbConnection _connection; + private readonly ILogger _logger; + + private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); + + public SqliteNotificationsRepository(IDbConnection connection, ILogManager logManager) + { + _connection = connection; + + _logger = logManager.GetLogger(GetType().Name); + } + + public event EventHandler<NotificationUpdateEventArgs> NotificationAdded; + public event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead; + public event EventHandler<NotificationUpdateEventArgs> NotificationUpdated; + + private IDbCommand _replaceNotificationCommand; + private IDbCommand _markReadCommand; + + public void Initialize() + { + string[] queries = { + + "create table if not exists Notifications (Id GUID NOT NULL, UserId GUID NOT NULL, Date DATETIME NOT NULL, Name TEXT NOT NULL, Description TEXT, Url TEXT, Level TEXT NOT NULL, IsRead BOOLEAN NOT NULL, Category TEXT NOT NULL, RelatedId TEXT, PRIMARY KEY (Id, UserId))", + "create index if not exists idx_Notifications on Notifications(Id, UserId)", + + //pragmas + "pragma temp_store = memory" + }; + + _connection.RunQueries(queries, _logger); + + PrepareStatements(); + } + + private void PrepareStatements() + { + _replaceNotificationCommand = _connection.CreateCommand(); + _replaceNotificationCommand.CommandText = "replace into Notifications (Id, UserId, Date, Name, Description, Url, Level, IsRead, Category, RelatedId) values (@Id, @UserId, @Date, @Name, @Description, @Url, @Level, @IsRead, @Category, @RelatedId)"; + + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Id"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@UserId"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Date"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Name"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Description"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Url"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Level"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@Category"); + _replaceNotificationCommand.Parameters.Add(_replaceNotificationCommand, "@RelatedId"); + + _markReadCommand = _connection.CreateCommand(); + _markReadCommand.CommandText = "update Notifications set IsRead=@IsRead where Id=@Id and UserId=@UserId"; + + _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@UserId"); + _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead"); + _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@Id"); + } + + /// <summary> + /// Gets the notifications. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>NotificationResult.</returns> + public NotificationResult GetNotifications(NotificationQuery query) + { + var whereClause = string.Empty; + + var result = new NotificationResult(); + + using (var cmd = _connection.CreateCommand()) + { + if (query.IsRead.HasValue || query.UserId.HasValue) + { + var clauses = new List<string>(); + + if (query.IsRead.HasValue) + { + clauses.Add("IsRead=@IsRead"); + cmd.Parameters.Add(cmd, "@IsRead", DbType.Boolean).Value = query.IsRead.Value; + } + + if (query.UserId.HasValue) + { + clauses.Add("UserId=@UserId"); + cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.UserId.Value; + } + + whereClause = " where " + string.Join(" And ", clauses.ToArray()); + } + + cmd.CommandText = string.Format("select count(Id) from Notifications{0};select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId from Notifications{0} order by IsRead asc, Date desc", whereClause); + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) + { + if (reader.Read()) + { + result.TotalRecordCount = reader.GetInt32(0); + } + + if (reader.NextResult()) + { + var notifications = GetNotifications(reader); + + if (query.StartIndex.HasValue) + { + notifications = notifications.Skip(query.StartIndex.Value); + } + + if (query.Limit.HasValue) + { + notifications = notifications.Take(query.Limit.Value); + } + + result.Notifications = notifications.ToArray(); + } + } + + return result; + } + } + + public NotificationsSummary GetNotificationsSummary(Guid userId) + { + var result = new NotificationsSummary(); + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select Level from Notifications where UserId=@UserId and IsRead=@IsRead"; + + cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = userId; + cmd.Parameters.Add(cmd, "@IsRead", DbType.Boolean).Value = false; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) + { + var levels = new List<NotificationLevel>(); + + while (reader.Read()) + { + levels.Add(GetLevel(reader, 0)); + } + + result.UnreadCount = levels.Count; + + if (levels.Count > 0) + { + result.MaxUnreadNotificationLevel = levels.Max(); + } + } + + return result; + } + } + + /// <summary> + /// Gets the notifications. + /// </summary> + /// <param name="reader">The reader.</param> + /// <returns>IEnumerable{Notification}.</returns> + private IEnumerable<Notification> GetNotifications(IDataReader reader) + { + while (reader.Read()) + { + yield return GetNotification(reader); + } + } + + private Notification GetNotification(IDataReader reader) + { + var notification = new Notification + { + Id = reader.GetGuid(0), + UserId = reader.GetGuid(1), + Date = reader.GetDateTime(2).ToUniversalTime(), + Name = reader.GetString(3) + }; + + if (!reader.IsDBNull(4)) + { + notification.Description = reader.GetString(4); + } + + if (!reader.IsDBNull(5)) + { + notification.Url = reader.GetString(5); + } + + notification.Level = GetLevel(reader, 6); + notification.IsRead = reader.GetBoolean(7); + + notification.Category = reader.GetString(8); + + if (!reader.IsDBNull(9)) + { + notification.RelatedId = reader.GetString(9); + } + + return notification; + } + + /// <summary> + /// Gets the notification. + /// </summary> + /// <param name="id">The id.</param> + /// <param name="userId">The user id.</param> + /// <returns>Notification.</returns> + /// <exception cref="System.ArgumentNullException"> + /// id + /// or + /// userId + /// </exception> + public Notification GetNotification(Guid id, Guid userId) + { + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + if (userId == Guid.Empty) + { + throw new ArgumentNullException("userId"); + } + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId where Id=@Id And UserId = @UserId"; + + cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = id; + cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = userId; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) + { + if (reader.Read()) + { + return GetNotification(reader); + } + } + return null; + } + } + + /// <summary> + /// Gets the level. + /// </summary> + /// <param name="reader">The reader.</param> + /// <param name="index">The index.</param> + /// <returns>NotificationLevel.</returns> + private NotificationLevel GetLevel(IDataReader reader, int index) + { + NotificationLevel level; + + var val = reader.GetString(index); + + Enum.TryParse(val, true, out level); + + return level; + } + + /// <summary> + /// Adds the notification. + /// </summary> + /// <param name="notification">The notification.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public async Task AddNotification(Notification notification, CancellationToken cancellationToken) + { + await ReplaceNotification(notification, cancellationToken).ConfigureAwait(false); + + if (NotificationAdded != null) + { + try + { + NotificationAdded(this, new NotificationUpdateEventArgs + { + Notification = notification + }); + } + catch (Exception ex) + { + _logger.ErrorException("Error in NotificationAdded event handler", ex); + } + } + } + + /// <summary> + /// Updates the notification. + /// </summary> + /// <param name="notification">The notification.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public async Task UpdateNotification(Notification notification, CancellationToken cancellationToken) + { + await ReplaceNotification(notification, cancellationToken).ConfigureAwait(false); + + if (NotificationUpdated != null) + { + try + { + NotificationUpdated(this, new NotificationUpdateEventArgs + { + Notification = notification + }); + } + catch (Exception ex) + { + _logger.ErrorException("Error in NotificationUpdated event handler", ex); + } + } + } + + /// <summary> + /// Replaces the notification. + /// </summary> + /// <param name="notification">The notification.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + private async Task ReplaceNotification(Notification notification, CancellationToken cancellationToken) + { + if (notification.Id == Guid.Empty) + { + throw new ArgumentException("The notification must have an id"); + } + if (notification.UserId == Guid.Empty) + { + throw new ArgumentException("The notification must have a user id"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + IDbTransaction transaction = null; + + try + { + transaction = _connection.BeginTransaction(); + + _replaceNotificationCommand.GetParameter(0).Value = notification.Id; + _replaceNotificationCommand.GetParameter(1).Value = notification.UserId; + _replaceNotificationCommand.GetParameter(2).Value = notification.Date.ToUniversalTime(); + _replaceNotificationCommand.GetParameter(3).Value = notification.Name; + _replaceNotificationCommand.GetParameter(4).Value = notification.Description; + _replaceNotificationCommand.GetParameter(5).Value = notification.Url; + _replaceNotificationCommand.GetParameter(6).Value = notification.Level.ToString(); + _replaceNotificationCommand.GetParameter(7).Value = notification.IsRead; + _replaceNotificationCommand.GetParameter(8).Value = notification.Category; + _replaceNotificationCommand.GetParameter(9).Value = notification.RelatedId; + + _replaceNotificationCommand.Transaction = transaction; + + _replaceNotificationCommand.ExecuteNonQuery(); + + transaction.Commit(); + } + catch (OperationCanceledException) + { + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + catch (Exception e) + { + _logger.ErrorException("Failed to save notification:", e); + + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + finally + { + if (transaction != null) + { + transaction.Dispose(); + } + + _writeLock.Release(); + } + } + + /// <summary> + /// Marks the read. + /// </summary> + /// <param name="notificationIdList">The notification id list.</param> + /// <param name="userId">The user id.</param> + /// <param name="isRead">if set to <c>true</c> [is read].</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public async Task MarkRead(IEnumerable<Guid> notificationIdList, Guid userId, bool isRead, CancellationToken cancellationToken) + { + var idArray = notificationIdList.ToArray(); + + await MarkReadInternal(idArray, userId, isRead, cancellationToken).ConfigureAwait(false); + + if (NotificationsMarkedRead != null) + { + try + { + NotificationsMarkedRead(this, new NotificationReadEventArgs + { + IdList = idArray.ToArray(), + IsRead = isRead, + UserId = userId + }); + } + catch (Exception ex) + { + _logger.ErrorException("Error in NotificationsMarkedRead event handler", ex); + } + } + } + + private async Task MarkReadInternal(IEnumerable<Guid> notificationIdList, Guid userId, bool isRead, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + IDbTransaction transaction = null; + + try + { + cancellationToken.ThrowIfCancellationRequested(); + + transaction = _connection.BeginTransaction(); + + _markReadCommand.GetParameter(0).Value = userId; + _markReadCommand.GetParameter(1).Value = isRead; + + foreach (var id in notificationIdList) + { + _markReadCommand.GetParameter(2).Value = id; + + _markReadCommand.Transaction = transaction; + + _markReadCommand.ExecuteNonQuery(); + } + + transaction.Commit(); + } + catch (OperationCanceledException) + { + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + catch (Exception e) + { + _logger.ErrorException("Failed to save notification:", e); + + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + finally + { + if (transaction != null) + { + transaction.Dispose(); + } + + _writeLock.Release(); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs index 48b877108..547c870eb 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.Updates; -using MediaBrowser.Controller; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using System; @@ -46,7 +45,9 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks // 1:30am new DailyTrigger { TimeOfDay = TimeSpan.FromHours(1.5) }, - new IntervalTrigger { Interval = TimeSpan.FromHours(2)} + new IntervalTrigger { Interval = TimeSpan.FromHours(3)}, + + new StartupTrigger() }; } |
