From b76a1abda578b8ff64bad2997b036b0fc43e264f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 3 Nov 2016 03:14:14 -0400 Subject: move classes to portable server lib --- .../Notifications/Notifications.cs | 547 +++++++++++++++++++++ .../Notifications/WebSocketNotifier.cs | 54 ++ 2 files changed, 601 insertions(+) create mode 100644 Emby.Server.Implementations/Notifications/Notifications.cs create mode 100644 Emby.Server.Implementations/Notifications/WebSocketNotifier.cs (limited to 'Emby.Server.Implementations/Notifications') diff --git a/Emby.Server.Implementations/Notifications/Notifications.cs b/Emby.Server.Implementations/Notifications/Notifications.cs new file mode 100644 index 000000000..2d441c18c --- /dev/null +++ b/Emby.Server.Implementations/Notifications/Notifications.cs @@ -0,0 +1,547 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Common.Updates; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Notifications; +using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Updates; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Model.Threading; + +namespace Emby.Server.Implementations.Notifications +{ + /// + /// Creates notifications for various system events + /// + public class Notifications : IServerEntryPoint + { + private readonly IInstallationManager _installationManager; + private readonly IUserManager _userManager; + private readonly ILogger _logger; + + private readonly ITaskManager _taskManager; + private readonly INotificationManager _notificationManager; + + private readonly ILibraryManager _libraryManager; + private readonly ISessionManager _sessionManager; + private readonly IServerApplicationHost _appHost; + private readonly ITimerFactory _timerFactory; + + private ITimer LibraryUpdateTimer { get; set; } + private readonly object _libraryChangedSyncLock = new object(); + + private readonly IConfigurationManager _config; + private readonly IDeviceManager _deviceManager; + + public Notifications(IInstallationManager installationManager, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager, ITimerFactory timerFactory) + { + _installationManager = installationManager; + _userManager = userManager; + _logger = logger; + _taskManager = taskManager; + _notificationManager = notificationManager; + _libraryManager = libraryManager; + _sessionManager = sessionManager; + _appHost = appHost; + _config = config; + _deviceManager = deviceManager; + _timerFactory = timerFactory; + } + + public void Run() + { + _installationManager.PluginInstalled += _installationManager_PluginInstalled; + _installationManager.PluginUpdated += _installationManager_PluginUpdated; + _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed; + _installationManager.PluginUninstalled += _installationManager_PluginUninstalled; + + _taskManager.TaskCompleted += _taskManager_TaskCompleted; + + _userManager.UserCreated += _userManager_UserCreated; + _libraryManager.ItemAdded += _libraryManager_ItemAdded; + _sessionManager.PlaybackStart += _sessionManager_PlaybackStart; + _sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped; + _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged; + _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged; + _appHost.ApplicationUpdated += _appHost_ApplicationUpdated; + _deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded; + + _userManager.UserLockedOut += _userManager_UserLockedOut; + } + + async void _userManager_UserLockedOut(object sender, GenericEventArgs e) + { + var type = NotificationType.UserLockedOut.ToString(); + + var notification = new NotificationRequest + { + NotificationType = type + }; + + notification.Variables["UserName"] = e.Argument.Name; + + await SendNotification(notification).ConfigureAwait(false); + } + + async void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs e) + { + var type = NotificationType.CameraImageUploaded.ToString(); + + var notification = new NotificationRequest + { + NotificationType = type + }; + + notification.Variables["DeviceName"] = e.Argument.Device.Name; + + await SendNotification(notification).ConfigureAwait(false); + } + + async void _appHost_ApplicationUpdated(object sender, GenericEventArgs e) + { + var type = NotificationType.ApplicationUpdateInstalled.ToString(); + + var notification = new NotificationRequest + { + NotificationType = type, + Url = e.Argument.infoUrl + }; + + notification.Variables["Version"] = e.Argument.versionStr; + notification.Variables["ReleaseNotes"] = e.Argument.description; + + await SendNotification(notification).ConfigureAwait(false); + } + + async void _installationManager_PluginUpdated(object sender, GenericEventArgs> e) + { + var type = NotificationType.PluginUpdateInstalled.ToString(); + + var installationInfo = e.Argument.Item1; + + var notification = new NotificationRequest + { + Description = e.Argument.Item2.description, + NotificationType = type + }; + + notification.Variables["Name"] = installationInfo.Name; + notification.Variables["Version"] = installationInfo.Version.ToString(); + notification.Variables["ReleaseNotes"] = e.Argument.Item2.description; + + await SendNotification(notification).ConfigureAwait(false); + } + + async void _installationManager_PluginInstalled(object sender, GenericEventArgs e) + { + var type = NotificationType.PluginInstalled.ToString(); + + var installationInfo = e.Argument; + + var notification = new NotificationRequest + { + Description = installationInfo.description, + NotificationType = type + }; + + notification.Variables["Name"] = installationInfo.name; + notification.Variables["Version"] = installationInfo.versionStr; + + await SendNotification(notification).ConfigureAwait(false); + } + + async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e) + { + // This notification is for users who can't auto-update (aka running as service) + if (!_appHost.HasUpdateAvailable || _appHost.CanSelfUpdate) + { + return; + } + + var type = NotificationType.ApplicationUpdateAvailable.ToString(); + + var notification = new NotificationRequest + { + Description = "Please see emby.media for details.", + NotificationType = type + }; + + await SendNotification(notification).ConfigureAwait(false); + } + + async void _appHost_HasPendingRestartChanged(object sender, EventArgs e) + { + if (!_appHost.HasPendingRestart) + { + return; + } + + var type = NotificationType.ServerRestartRequired.ToString(); + + var notification = new NotificationRequest + { + NotificationType = type + }; + + await SendNotification(notification).ConfigureAwait(false); + } + + private NotificationOptions GetOptions() + { + return _config.GetConfiguration("notifications"); + } + + void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e) + { + var item = e.MediaInfo; + + if (item == null) + { + _logger.Warn("PlaybackStart reported with null media info."); + return; + } + + var video = e.Item as Video; + if (video != null && video.IsThemeMedia) + { + return; + } + + var type = GetPlaybackNotificationType(item.MediaType); + + SendPlaybackNotification(type, e); + } + + void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e) + { + var item = e.MediaInfo; + + if (item == null) + { + _logger.Warn("PlaybackStopped reported with null media info."); + return; + } + + var video = e.Item as Video; + if (video != null && video.IsThemeMedia) + { + return; + } + + var type = GetPlaybackStoppedNotificationType(item.MediaType); + + SendPlaybackNotification(type, e); + } + + private async void SendPlaybackNotification(string type, PlaybackProgressEventArgs e) + { + var user = e.Users.FirstOrDefault(); + + if (user != null && !GetOptions().IsEnabledToMonitorUser(type, user.Id.ToString("N"))) + { + return; + } + + var item = e.MediaInfo; + + if ( item.IsThemeMedia) + { + // Don't report theme song or local trailer playback + return; + } + + var notification = new NotificationRequest + { + NotificationType = type + }; + + if (e.Item != null) + { + notification.Variables["ItemName"] = GetItemName(e.Item); + } + else + { + notification.Variables["ItemName"] = item.Name; + } + + notification.Variables["UserName"] = user == null ? "Unknown user" : user.Name; + notification.Variables["AppName"] = e.ClientName; + notification.Variables["DeviceName"] = e.DeviceName; + + await SendNotification(notification).ConfigureAwait(false); + } + + private string GetPlaybackNotificationType(string mediaType) + { + if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.AudioPlayback.ToString(); + } + if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.GamePlayback.ToString(); + } + if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.VideoPlayback.ToString(); + } + + return null; + } + + private string GetPlaybackStoppedNotificationType(string mediaType) + { + if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.AudioPlaybackStopped.ToString(); + } + if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.GamePlaybackStopped.ToString(); + } + if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.VideoPlaybackStopped.ToString(); + } + + return null; + } + + private readonly List _itemsAdded = new List(); + void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) + { + if (!FilterItem(e.Item)) + { + return; + } + + lock (_libraryChangedSyncLock) + { + if (LibraryUpdateTimer == null) + { + LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, 5000, + Timeout.Infinite); + } + else + { + LibraryUpdateTimer.Change(5000, Timeout.Infinite); + } + + _itemsAdded.Add(e.Item); + } + } + + private bool FilterItem(BaseItem item) + { + if (item.IsFolder) + { + return false; + } + + if (item.LocationType == LocationType.Virtual) + { + return false; + } + + if (item is IItemByName) + { + return false; + } + + return item.SourceType == SourceType.Library; + } + + private async void LibraryUpdateTimerCallback(object state) + { + List items; + + lock (_libraryChangedSyncLock) + { + items = _itemsAdded.ToList(); + _itemsAdded.Clear(); + DisposeLibraryUpdateTimer(); + } + + items = items.Take(10).ToList(); + + foreach (var item in items) + { + var notification = new NotificationRequest + { + NotificationType = NotificationType.NewLibraryContent.ToString() + }; + + notification.Variables["Name"] = GetItemName(item); + + await SendNotification(notification).ConfigureAwait(false); + } + } + + public static string GetItemName(BaseItem item) + { + var name = item.Name; + var episode = item as Episode; + if (episode != null) + { + if (episode.IndexNumber.HasValue) + { + name = string.Format("Ep{0} - {1}", episode.IndexNumber.Value.ToString(CultureInfo.InvariantCulture), name); + } + if (episode.ParentIndexNumber.HasValue) + { + name = string.Format("S{0}, {1}", episode.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture), name); + } + } + + var hasSeries = item as IHasSeries; + + if (hasSeries != null) + { + name = hasSeries.SeriesName + " - " + name; + } + + var hasArtist = item as IHasArtist; + if (hasArtist != null) + { + var artists = hasArtist.AllArtists; + + if (artists.Count > 0) + { + name = hasArtist.AllArtists[0] + " - " + name; + } + } + + return name; + } + + async void _userManager_UserCreated(object sender, GenericEventArgs e) + { + var notification = new NotificationRequest + { + UserIds = new List { e.Argument.Id.ToString("N") }, + Name = "Welcome to Emby!", + Description = "Check back here for more notifications." + }; + + await SendNotification(notification).ConfigureAwait(false); + } + + async void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e) + { + var result = e.Result; + + if (result.Status == TaskCompletionStatus.Failed) + { + var type = NotificationType.TaskFailed.ToString(); + + var notification = new NotificationRequest + { + Description = result.ErrorMessage, + Level = NotificationLevel.Error, + NotificationType = type + }; + + notification.Variables["Name"] = result.Name; + notification.Variables["ErrorMessage"] = result.ErrorMessage; + + await SendNotification(notification).ConfigureAwait(false); + } + } + + async void _installationManager_PluginUninstalled(object sender, GenericEventArgs e) + { + var type = NotificationType.PluginUninstalled.ToString(); + + var plugin = e.Argument; + + var notification = new NotificationRequest + { + NotificationType = type + }; + + notification.Variables["Name"] = plugin.Name; + notification.Variables["Version"] = plugin.Version.ToString(); + + await SendNotification(notification).ConfigureAwait(false); + } + + async void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e) + { + var installationInfo = e.InstallationInfo; + + var type = NotificationType.InstallationFailed.ToString(); + + var notification = new NotificationRequest + { + Level = NotificationLevel.Error, + Description = e.Exception.Message, + NotificationType = type + }; + + notification.Variables["Name"] = installationInfo.Name; + notification.Variables["Version"] = installationInfo.Version; + + await SendNotification(notification).ConfigureAwait(false); + } + + private async Task SendNotification(NotificationRequest notification) + { + try + { + await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending notification", ex); + } + } + + public void Dispose() + { + DisposeLibraryUpdateTimer(); + + _installationManager.PluginInstalled -= _installationManager_PluginInstalled; + _installationManager.PluginUpdated -= _installationManager_PluginUpdated; + _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed; + _installationManager.PluginUninstalled -= _installationManager_PluginUninstalled; + + _taskManager.TaskCompleted -= _taskManager_TaskCompleted; + + _userManager.UserCreated -= _userManager_UserCreated; + _libraryManager.ItemAdded -= _libraryManager_ItemAdded; + _sessionManager.PlaybackStart -= _sessionManager_PlaybackStart; + + _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged; + _appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged; + _appHost.ApplicationUpdated -= _appHost_ApplicationUpdated; + + _deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded; + _userManager.UserLockedOut -= _userManager_UserLockedOut; + } + + private void DisposeLibraryUpdateTimer() + { + if (LibraryUpdateTimer != null) + { + LibraryUpdateTimer.Dispose(); + LibraryUpdateTimer = null; + } + } + } +} diff --git a/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs b/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs new file mode 100644 index 000000000..8b3367217 --- /dev/null +++ b/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs @@ -0,0 +1,54 @@ +using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Controller.Plugins; +using System.Linq; + +namespace Emby.Server.Implementations.Notifications +{ + /// + /// Notifies clients anytime a notification is added or udpated + /// + 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.NotificationsMarkedRead += _notificationsRepo_NotificationsMarkedRead; + } + + void _notificationsRepo_NotificationsMarkedRead(object sender, NotificationReadEventArgs e) + { + var list = e.IdList.ToList(); + + list.Add(e.UserId); + list.Add(e.IsRead.ToString().ToLower()); + + var msg = string.Join("|", list.ToArray()); + + _serverManager.SendWebSocketMessage("NotificationsMarkedRead", 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; + } + } +} -- cgit v1.2.3 From 0d5d91b4c4575271d16fe507a328fbcbb1510acd Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 3 Nov 2016 03:35:00 -0400 Subject: move notification classes --- .../Emby.Server.Implementations.csproj | 5 + .../Notifications/CoreNotificationTypes.cs | 198 ++++++++++++++ .../IConfigurableNotificationService.cs | 8 + .../Notifications/InternalNotificationService.cs | 61 +++++ .../NotificationConfigurationFactory.cs | 21 ++ .../Notifications/NotificationManager.cs | 296 +++++++++++++++++++++ .../MediaBrowser.Server.Implementations.csproj | 5 - .../Notifications/CoreNotificationTypes.cs | 198 -------------- .../IConfigurableNotificationService.cs | 8 - .../Notifications/InternalNotificationService.cs | 61 ----- .../NotificationConfigurationFactory.cs | 21 -- .../Notifications/NotificationManager.cs | 296 --------------------- .../ApplicationHost.cs | 1 + 13 files changed, 590 insertions(+), 589 deletions(-) create mode 100644 Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs create mode 100644 Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs create mode 100644 Emby.Server.Implementations/Notifications/InternalNotificationService.cs create mode 100644 Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs create mode 100644 Emby.Server.Implementations/Notifications/NotificationManager.cs delete mode 100644 MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs delete mode 100644 MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs delete mode 100644 MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs delete mode 100644 MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs delete mode 100644 MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs (limited to 'Emby.Server.Implementations/Notifications') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 567c9b99e..18e357679 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -115,6 +115,11 @@ + + + + + diff --git a/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs b/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs new file mode 100644 index 000000000..f9fb98f85 --- /dev/null +++ b/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs @@ -0,0 +1,198 @@ +using MediaBrowser.Controller; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Notifications; +using System; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Model.Globalization; + +namespace Emby.Server.Implementations.Notifications +{ + public class CoreNotificationTypes : INotificationTypeFactory + { + private readonly ILocalizationManager _localization; + private readonly IServerApplicationHost _appHost; + + public CoreNotificationTypes(ILocalizationManager localization, IServerApplicationHost appHost) + { + _localization = localization; + _appHost = appHost; + } + + public IEnumerable GetNotificationTypes() + { + var knownTypes = new List + { + new NotificationTypeInfo + { + Type = NotificationType.ApplicationUpdateInstalled.ToString(), + DefaultDescription = "{ReleaseNotes}", + DefaultTitle = "A new version of Emby Server has been installed.", + Variables = new List{"Version"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.InstallationFailed.ToString(), + DefaultTitle = "{Name} installation failed.", + Variables = new List{"Name", "Version"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.PluginInstalled.ToString(), + DefaultTitle = "{Name} was installed.", + Variables = new List{"Name", "Version"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.PluginError.ToString(), + DefaultTitle = "{Name} has encountered an error.", + DefaultDescription = "{ErrorMessage}", + Variables = new List{"Name", "ErrorMessage"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.PluginUninstalled.ToString(), + DefaultTitle = "{Name} was uninstalled.", + Variables = new List{"Name", "Version"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.PluginUpdateInstalled.ToString(), + DefaultTitle = "{Name} was updated.", + DefaultDescription = "{ReleaseNotes}", + Variables = new List{"Name", "ReleaseNotes", "Version"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.ServerRestartRequired.ToString(), + DefaultTitle = "Please restart Emby Server to finish updating." + }, + + new NotificationTypeInfo + { + Type = NotificationType.TaskFailed.ToString(), + DefaultTitle = "{Name} failed.", + DefaultDescription = "{ErrorMessage}", + Variables = new List{"Name", "ErrorMessage"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.NewLibraryContent.ToString(), + DefaultTitle = "{Name} has been added to your media library.", + Variables = new List{"Name"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.AudioPlayback.ToString(), + DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.", + Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.GamePlayback.ToString(), + DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.", + Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.VideoPlayback.ToString(), + DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.", + Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.AudioPlaybackStopped.ToString(), + DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.", + Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.GamePlaybackStopped.ToString(), + DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.", + Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.VideoPlaybackStopped.ToString(), + DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.", + Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.CameraImageUploaded.ToString(), + DefaultTitle = "A new camera image has been uploaded from {DeviceName}.", + Variables = new List{"DeviceName"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.UserLockedOut.ToString(), + DefaultTitle = "{UserName} has been locked out.", + Variables = new List{"UserName"} + } + }; + + if (!_appHost.CanSelfUpdate) + { + knownTypes.Add(new NotificationTypeInfo + { + Type = NotificationType.ApplicationUpdateAvailable.ToString(), + DefaultTitle = "A new version of Emby Server is available for download." + }); + } + + foreach (var type in knownTypes) + { + Update(type); + } + + var systemName = _localization.GetLocalizedString("CategorySystem"); + + return knownTypes.OrderByDescending(i => string.Equals(i.Category, systemName, StringComparison.OrdinalIgnoreCase)) + .ThenBy(i => i.Category) + .ThenBy(i => i.Name); + } + + private void Update(NotificationTypeInfo note) + { + note.Name = _localization.GetLocalizedString("NotificationOption" + note.Type) ?? note.Type; + + note.IsBasedOnUserEvent = note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1; + + if (note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("CategoryUser"); + } + else if (note.Type.IndexOf("Plugin", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("CategoryPlugin"); + } + else if (note.Type.IndexOf("CameraImageUploaded", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("CategorySync"); + } + else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("CategoryUser"); + } + else + { + note.Category = _localization.GetLocalizedString("CategorySystem"); + } + } + } +} diff --git a/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs b/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs new file mode 100644 index 000000000..d74667c48 --- /dev/null +++ b/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs @@ -0,0 +1,8 @@ +namespace Emby.Server.Implementations.Notifications +{ + public interface IConfigurableNotificationService + { + bool IsHidden { get; } + bool IsEnabled(string notificationType); + } +} diff --git a/Emby.Server.Implementations/Notifications/InternalNotificationService.cs b/Emby.Server.Implementations/Notifications/InternalNotificationService.cs new file mode 100644 index 000000000..61c564f18 --- /dev/null +++ b/Emby.Server.Implementations/Notifications/InternalNotificationService.cs @@ -0,0 +1,61 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Notifications; +using System.Threading; +using System.Threading.Tasks; +using System; + +namespace Emby.Server.Implementations.Notifications +{ + public class InternalNotificationService : INotificationService, IConfigurableNotificationService + { + private readonly INotificationsRepository _repo; + + public InternalNotificationService(INotificationsRepository repo) + { + _repo = repo; + } + + public string Name + { + get { return "Dashboard Notifications"; } + } + + public Task SendNotification(UserNotification request, CancellationToken cancellationToken) + { + return _repo.AddNotification(new Notification + { + Date = request.Date, + Description = request.Description, + Level = request.Level, + Name = request.Name, + Url = request.Url, + UserId = request.User.Id.ToString("N") + + }, cancellationToken); + } + + public bool IsEnabledForUser(User user) + { + return user.Policy.IsAdministrator; + } + + public bool IsHidden + { + get { return true; } + } + + public bool IsEnabled(string notificationType) + { + if (notificationType.IndexOf("playback", StringComparison.OrdinalIgnoreCase) != -1) + { + return false; + } + if (notificationType.IndexOf("newlibrarycontent", StringComparison.OrdinalIgnoreCase) != -1) + { + return false; + } + return true; + } + } +} diff --git a/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs b/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs new file mode 100644 index 000000000..a7c5b1233 --- /dev/null +++ b/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs @@ -0,0 +1,21 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Notifications; +using System.Collections.Generic; + +namespace Emby.Server.Implementations.Notifications +{ + public class NotificationConfigurationFactory : IConfigurationFactory + { + public IEnumerable GetConfigurations() + { + return new List + { + new ConfigurationStore + { + Key = "notifications", + ConfigurationType = typeof (NotificationOptions) + } + }; + } + } +} diff --git a/Emby.Server.Implementations/Notifications/NotificationManager.cs b/Emby.Server.Implementations/Notifications/NotificationManager.cs new file mode 100644 index 000000000..db7980497 --- /dev/null +++ b/Emby.Server.Implementations/Notifications/NotificationManager.cs @@ -0,0 +1,296 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Notifications; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Notifications; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Extensions; + +namespace Emby.Server.Implementations.Notifications +{ + public class NotificationManager : INotificationManager + { + private readonly ILogger _logger; + private readonly IUserManager _userManager; + private readonly IServerConfigurationManager _config; + + private INotificationService[] _services; + private INotificationTypeFactory[] _typeFactories; + + public NotificationManager(ILogManager logManager, IUserManager userManager, IServerConfigurationManager config) + { + _userManager = userManager; + _config = config; + _logger = logManager.GetLogger(GetType().Name); + } + + private NotificationOptions GetConfiguration() + { + return _config.GetConfiguration("notifications"); + } + + public Task SendNotification(NotificationRequest request, CancellationToken cancellationToken) + { + var notificationType = request.NotificationType; + + var options = string.IsNullOrWhiteSpace(notificationType) ? + null : + GetConfiguration().GetOptions(notificationType); + + var users = GetUserIds(request, options) + .Select(i => _userManager.GetUserById(i)); + + var title = GetTitle(request, options); + var description = GetDescription(request, options); + + var tasks = _services.Where(i => IsEnabled(i, notificationType)) + .Select(i => SendNotification(request, i, users, title, description, cancellationToken)); + + return Task.WhenAll(tasks); + } + + private Task SendNotification(NotificationRequest request, + INotificationService service, + IEnumerable users, + string title, + string description, + CancellationToken cancellationToken) + { + users = users.Where(i => IsEnabledForUser(service, i)) + .ToList(); + + var tasks = users.Select(i => SendNotification(request, service, title, description, i, cancellationToken)); + + return Task.WhenAll(tasks); + + } + + private IEnumerable GetUserIds(NotificationRequest request, NotificationOption options) + { + if (request.SendToUserMode.HasValue) + { + switch (request.SendToUserMode.Value) + { + case SendToUserType.Admins: + return _userManager.Users.Where(i => i.Policy.IsAdministrator) + .Select(i => i.Id.ToString("N")); + case SendToUserType.All: + return _userManager.Users.Select(i => i.Id.ToString("N")); + case SendToUserType.Custom: + return request.UserIds; + default: + throw new ArgumentException("Unrecognized SendToUserMode: " + request.SendToUserMode.Value); + } + } + + if (options != null && !string.IsNullOrWhiteSpace(request.NotificationType)) + { + var config = GetConfiguration(); + + return _userManager.Users + .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Policy)) + .Select(i => i.Id.ToString("N")); + } + + return request.UserIds; + } + + private async Task SendNotification(NotificationRequest request, + INotificationService service, + string title, + string description, + User user, + CancellationToken cancellationToken) + { + var notification = new UserNotification + { + Date = request.Date, + Description = description, + Level = request.Level, + Name = title, + Url = request.Url, + User = user + }; + + _logger.Debug("Sending notification via {0} to user {1}", service.Name, user.Name); + + try + { + await service.SendNotification(notification, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error sending notification to {0}", ex, service.Name); + } + } + + private string GetTitle(NotificationRequest request, NotificationOption options) + { + var title = request.Name; + + // If empty, grab from options + if (string.IsNullOrEmpty(title)) + { + if (!string.IsNullOrEmpty(request.NotificationType)) + { + if (options != null) + { + title = options.Title; + } + } + } + + // If still empty, grab default + if (string.IsNullOrEmpty(title)) + { + if (!string.IsNullOrEmpty(request.NotificationType)) + { + var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase)); + + if (info != null) + { + title = info.DefaultTitle; + } + } + } + + title = title ?? string.Empty; + + foreach (var pair in request.Variables) + { + var token = "{" + pair.Key + "}"; + + title = title.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase); + } + + return title; + } + + private string GetDescription(NotificationRequest request, NotificationOption options) + { + var text = request.Description; + + // If empty, grab from options + if (string.IsNullOrEmpty(text)) + { + if (!string.IsNullOrEmpty(request.NotificationType)) + { + if (options != null) + { + text = options.Description; + } + } + } + + // If still empty, grab default + if (string.IsNullOrEmpty(text)) + { + if (!string.IsNullOrEmpty(request.NotificationType)) + { + var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase)); + + if (info != null) + { + text = info.DefaultDescription; + } + } + } + + text = text ?? string.Empty; + + foreach (var pair in request.Variables) + { + var token = "{" + pair.Key + "}"; + + text = text.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase); + } + + return text; + } + + private bool IsEnabledForUser(INotificationService service, User user) + { + try + { + return service.IsEnabledForUser(user); + } + catch (Exception ex) + { + _logger.ErrorException("Error in IsEnabledForUser", ex); + return false; + } + } + + private bool IsEnabled(INotificationService service, string notificationType) + { + if (string.IsNullOrEmpty(notificationType)) + { + return true; + } + + var configurable = service as IConfigurableNotificationService; + + if (configurable != null) + { + return configurable.IsEnabled(notificationType); + } + + return GetConfiguration().IsServiceEnabled(service.Name, notificationType); + } + + public void AddParts(IEnumerable services, IEnumerable notificationTypeFactories) + { + _services = services.ToArray(); + _typeFactories = notificationTypeFactories.ToArray(); + } + + public IEnumerable GetNotificationTypes() + { + var list = _typeFactories.Select(i => + { + try + { + return i.GetNotificationTypes().ToList(); + } + catch (Exception ex) + { + _logger.ErrorException("Error in GetNotificationTypes", ex); + return new List(); + } + + }).SelectMany(i => i).ToList(); + + var config = GetConfiguration(); + + foreach (var i in list) + { + i.Enabled = config.IsEnabled(i.Type); + } + + return list; + } + + public IEnumerable GetNotificationServices() + { + return _services.Where(i => + { + var configurable = i as IConfigurableNotificationService; + + return configurable == null || !configurable.IsHidden; + + }).Select(i => new NotificationServiceInfo + { + Name = i.Name, + Id = i.Name.GetMD5().ToString("N") + + }).OrderBy(i => i.Name); + } + } +} diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 3fb3ca883..95285d604 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -212,7 +212,6 @@ - @@ -230,10 +229,6 @@ - - - - diff --git a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs deleted file mode 100644 index 0f2629f58..000000000 --- a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs +++ /dev/null @@ -1,198 +0,0 @@ -using MediaBrowser.Controller; -using MediaBrowser.Controller.Notifications; -using MediaBrowser.Model.Notifications; -using System; -using System.Collections.Generic; -using System.Linq; -using MediaBrowser.Model.Globalization; - -namespace MediaBrowser.Server.Implementations.Notifications -{ - public class CoreNotificationTypes : INotificationTypeFactory - { - private readonly ILocalizationManager _localization; - private readonly IServerApplicationHost _appHost; - - public CoreNotificationTypes(ILocalizationManager localization, IServerApplicationHost appHost) - { - _localization = localization; - _appHost = appHost; - } - - public IEnumerable GetNotificationTypes() - { - var knownTypes = new List - { - new NotificationTypeInfo - { - Type = NotificationType.ApplicationUpdateInstalled.ToString(), - DefaultDescription = "{ReleaseNotes}", - DefaultTitle = "A new version of Emby Server has been installed.", - Variables = new List{"Version"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.InstallationFailed.ToString(), - DefaultTitle = "{Name} installation failed.", - Variables = new List{"Name", "Version"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.PluginInstalled.ToString(), - DefaultTitle = "{Name} was installed.", - Variables = new List{"Name", "Version"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.PluginError.ToString(), - DefaultTitle = "{Name} has encountered an error.", - DefaultDescription = "{ErrorMessage}", - Variables = new List{"Name", "ErrorMessage"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.PluginUninstalled.ToString(), - DefaultTitle = "{Name} was uninstalled.", - Variables = new List{"Name", "Version"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.PluginUpdateInstalled.ToString(), - DefaultTitle = "{Name} was updated.", - DefaultDescription = "{ReleaseNotes}", - Variables = new List{"Name", "ReleaseNotes", "Version"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.ServerRestartRequired.ToString(), - DefaultTitle = "Please restart Emby Server to finish updating." - }, - - new NotificationTypeInfo - { - Type = NotificationType.TaskFailed.ToString(), - DefaultTitle = "{Name} failed.", - DefaultDescription = "{ErrorMessage}", - Variables = new List{"Name", "ErrorMessage"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.NewLibraryContent.ToString(), - DefaultTitle = "{Name} has been added to your media library.", - Variables = new List{"Name"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.AudioPlayback.ToString(), - DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.", - Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.GamePlayback.ToString(), - DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.", - Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.VideoPlayback.ToString(), - DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.", - Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.AudioPlaybackStopped.ToString(), - DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.", - Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.GamePlaybackStopped.ToString(), - DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.", - Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.VideoPlaybackStopped.ToString(), - DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.", - Variables = new List{"UserName", "ItemName", "DeviceName", "AppName"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.CameraImageUploaded.ToString(), - DefaultTitle = "A new camera image has been uploaded from {DeviceName}.", - Variables = new List{"DeviceName"} - }, - - new NotificationTypeInfo - { - Type = NotificationType.UserLockedOut.ToString(), - DefaultTitle = "{UserName} has been locked out.", - Variables = new List{"UserName"} - } - }; - - if (!_appHost.CanSelfUpdate) - { - knownTypes.Add(new NotificationTypeInfo - { - Type = NotificationType.ApplicationUpdateAvailable.ToString(), - DefaultTitle = "A new version of Emby Server is available for download." - }); - } - - foreach (var type in knownTypes) - { - Update(type); - } - - var systemName = _localization.GetLocalizedString("CategorySystem"); - - return knownTypes.OrderByDescending(i => string.Equals(i.Category, systemName, StringComparison.OrdinalIgnoreCase)) - .ThenBy(i => i.Category) - .ThenBy(i => i.Name); - } - - private void Update(NotificationTypeInfo note) - { - note.Name = _localization.GetLocalizedString("NotificationOption" + note.Type) ?? note.Type; - - note.IsBasedOnUserEvent = note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1; - - if (note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1) - { - note.Category = _localization.GetLocalizedString("CategoryUser"); - } - else if (note.Type.IndexOf("Plugin", StringComparison.OrdinalIgnoreCase) != -1) - { - note.Category = _localization.GetLocalizedString("CategoryPlugin"); - } - else if (note.Type.IndexOf("CameraImageUploaded", StringComparison.OrdinalIgnoreCase) != -1) - { - note.Category = _localization.GetLocalizedString("CategorySync"); - } - else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1) - { - note.Category = _localization.GetLocalizedString("CategoryUser"); - } - else - { - note.Category = _localization.GetLocalizedString("CategorySystem"); - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs b/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs deleted file mode 100644 index cdfd0f640..000000000 --- a/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MediaBrowser.Server.Implementations.Notifications -{ - public interface IConfigurableNotificationService - { - bool IsHidden { get; } - bool IsEnabled(string notificationType); - } -} diff --git a/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs b/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs deleted file mode 100644 index 4a625f0fb..000000000 --- a/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs +++ /dev/null @@ -1,61 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Notifications; -using MediaBrowser.Model.Notifications; -using System.Threading; -using System.Threading.Tasks; -using System; - -namespace MediaBrowser.Server.Implementations.Notifications -{ - public class InternalNotificationService : INotificationService, IConfigurableNotificationService - { - private readonly INotificationsRepository _repo; - - public InternalNotificationService(INotificationsRepository repo) - { - _repo = repo; - } - - public string Name - { - get { return "Dashboard Notifications"; } - } - - public Task SendNotification(UserNotification request, CancellationToken cancellationToken) - { - return _repo.AddNotification(new Notification - { - Date = request.Date, - Description = request.Description, - Level = request.Level, - Name = request.Name, - Url = request.Url, - UserId = request.User.Id.ToString("N") - - }, cancellationToken); - } - - public bool IsEnabledForUser(User user) - { - return user.Policy.IsAdministrator; - } - - public bool IsHidden - { - get { return true; } - } - - public bool IsEnabled(string notificationType) - { - if (notificationType.IndexOf("playback", StringComparison.OrdinalIgnoreCase) != -1) - { - return false; - } - if (notificationType.IndexOf("newlibrarycontent", StringComparison.OrdinalIgnoreCase) != -1) - { - return false; - } - return true; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs b/MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs deleted file mode 100644 index a336eba0e..000000000 --- a/MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs +++ /dev/null @@ -1,21 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Notifications; -using System.Collections.Generic; - -namespace MediaBrowser.Server.Implementations.Notifications -{ - public class NotificationConfigurationFactory : IConfigurationFactory - { - public IEnumerable GetConfigurations() - { - return new List - { - new ConfigurationStore - { - Key = "notifications", - ConfigurationType = typeof (NotificationOptions) - } - }; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs b/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs deleted file mode 100644 index f19ff8a5f..000000000 --- a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs +++ /dev/null @@ -1,296 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Notifications; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Notifications; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Extensions; - -namespace MediaBrowser.Server.Implementations.Notifications -{ - public class NotificationManager : INotificationManager - { - private readonly ILogger _logger; - private readonly IUserManager _userManager; - private readonly IServerConfigurationManager _config; - - private INotificationService[] _services; - private INotificationTypeFactory[] _typeFactories; - - public NotificationManager(ILogManager logManager, IUserManager userManager, IServerConfigurationManager config) - { - _userManager = userManager; - _config = config; - _logger = logManager.GetLogger(GetType().Name); - } - - private NotificationOptions GetConfiguration() - { - return _config.GetConfiguration("notifications"); - } - - public Task SendNotification(NotificationRequest request, CancellationToken cancellationToken) - { - var notificationType = request.NotificationType; - - var options = string.IsNullOrWhiteSpace(notificationType) ? - null : - GetConfiguration().GetOptions(notificationType); - - var users = GetUserIds(request, options) - .Select(i => _userManager.GetUserById(i)); - - var title = GetTitle(request, options); - var description = GetDescription(request, options); - - var tasks = _services.Where(i => IsEnabled(i, notificationType)) - .Select(i => SendNotification(request, i, users, title, description, cancellationToken)); - - return Task.WhenAll(tasks); - } - - private Task SendNotification(NotificationRequest request, - INotificationService service, - IEnumerable users, - string title, - string description, - CancellationToken cancellationToken) - { - users = users.Where(i => IsEnabledForUser(service, i)) - .ToList(); - - var tasks = users.Select(i => SendNotification(request, service, title, description, i, cancellationToken)); - - return Task.WhenAll(tasks); - - } - - private IEnumerable GetUserIds(NotificationRequest request, NotificationOption options) - { - if (request.SendToUserMode.HasValue) - { - switch (request.SendToUserMode.Value) - { - case SendToUserType.Admins: - return _userManager.Users.Where(i => i.Policy.IsAdministrator) - .Select(i => i.Id.ToString("N")); - case SendToUserType.All: - return _userManager.Users.Select(i => i.Id.ToString("N")); - case SendToUserType.Custom: - return request.UserIds; - default: - throw new ArgumentException("Unrecognized SendToUserMode: " + request.SendToUserMode.Value); - } - } - - if (options != null && !string.IsNullOrWhiteSpace(request.NotificationType)) - { - var config = GetConfiguration(); - - return _userManager.Users - .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Policy)) - .Select(i => i.Id.ToString("N")); - } - - return request.UserIds; - } - - private async Task SendNotification(NotificationRequest request, - INotificationService service, - string title, - string description, - User user, - CancellationToken cancellationToken) - { - var notification = new UserNotification - { - Date = request.Date, - Description = description, - Level = request.Level, - Name = title, - Url = request.Url, - User = user - }; - - _logger.Debug("Sending notification via {0} to user {1}", service.Name, user.Name); - - try - { - await service.SendNotification(notification, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error sending notification to {0}", ex, service.Name); - } - } - - private string GetTitle(NotificationRequest request, NotificationOption options) - { - var title = request.Name; - - // If empty, grab from options - if (string.IsNullOrEmpty(title)) - { - if (!string.IsNullOrEmpty(request.NotificationType)) - { - if (options != null) - { - title = options.Title; - } - } - } - - // If still empty, grab default - if (string.IsNullOrEmpty(title)) - { - if (!string.IsNullOrEmpty(request.NotificationType)) - { - var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase)); - - if (info != null) - { - title = info.DefaultTitle; - } - } - } - - title = title ?? string.Empty; - - foreach (var pair in request.Variables) - { - var token = "{" + pair.Key + "}"; - - title = title.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase); - } - - return title; - } - - private string GetDescription(NotificationRequest request, NotificationOption options) - { - var text = request.Description; - - // If empty, grab from options - if (string.IsNullOrEmpty(text)) - { - if (!string.IsNullOrEmpty(request.NotificationType)) - { - if (options != null) - { - text = options.Description; - } - } - } - - // If still empty, grab default - if (string.IsNullOrEmpty(text)) - { - if (!string.IsNullOrEmpty(request.NotificationType)) - { - var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase)); - - if (info != null) - { - text = info.DefaultDescription; - } - } - } - - text = text ?? string.Empty; - - foreach (var pair in request.Variables) - { - var token = "{" + pair.Key + "}"; - - text = text.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase); - } - - return text; - } - - private bool IsEnabledForUser(INotificationService service, User user) - { - try - { - return service.IsEnabledForUser(user); - } - catch (Exception ex) - { - _logger.ErrorException("Error in IsEnabledForUser", ex); - return false; - } - } - - private bool IsEnabled(INotificationService service, string notificationType) - { - if (string.IsNullOrEmpty(notificationType)) - { - return true; - } - - var configurable = service as IConfigurableNotificationService; - - if (configurable != null) - { - return configurable.IsEnabled(notificationType); - } - - return GetConfiguration().IsServiceEnabled(service.Name, notificationType); - } - - public void AddParts(IEnumerable services, IEnumerable notificationTypeFactories) - { - _services = services.ToArray(); - _typeFactories = notificationTypeFactories.ToArray(); - } - - public IEnumerable GetNotificationTypes() - { - var list = _typeFactories.Select(i => - { - try - { - return i.GetNotificationTypes().ToList(); - } - catch (Exception ex) - { - _logger.ErrorException("Error in GetNotificationTypes", ex); - return new List(); - } - - }).SelectMany(i => i).ToList(); - - var config = GetConfiguration(); - - foreach (var i in list) - { - i.Enabled = config.IsEnabled(i.Type); - } - - return list; - } - - public IEnumerable GetNotificationServices() - { - return _services.Where(i => - { - var configurable = i as IConfigurableNotificationService; - - return configurable == null || !configurable.IsHidden; - - }).Select(i => new NotificationServiceInfo - { - Name = i.Name, - Id = i.Name.GetMD5().ToString("N") - - }).OrderBy(i => i.Name); - } - } -} diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 7c8945ff2..294e780cc 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -107,6 +107,7 @@ using Emby.Server.Implementations.Devices; using Emby.Server.Implementations.Dto; using Emby.Server.Implementations.FileOrganization; using Emby.Server.Implementations.Library; +using Emby.Server.Implementations.Notifications; using Emby.Server.Implementations.Persistence; using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.TV; -- cgit v1.2.3