mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-14 01:02:57 -07:00
User management, migration and newsletter
This commit is contained in:
parent
11ecbf04f6
commit
42c437905e
26 changed files with 888 additions and 389 deletions
|
@ -25,39 +25,76 @@
|
|||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using PlexRequests.Core.SettingModels;
|
||||
using PlexRequests.Store;
|
||||
using Quartz;
|
||||
|
||||
namespace PlexRequests.Core.Migration.Migrations
|
||||
{
|
||||
[Migration(1950, "v1.9.5.0")]
|
||||
public class Version195 : BaseMigration, IMigration
|
||||
{
|
||||
public Version195(ISettingsService<PlexRequestSettings> plexRequestSettings, ISettingsService<NewletterSettings> news)
|
||||
public Version195(ISettingsService<PlexRequestSettings> plexRequestSettings, ISettingsService<NewletterSettings> news, ISettingsService<ScheduledJobsSettings> jobs)
|
||||
{
|
||||
PlexRequestSettings = plexRequestSettings;
|
||||
NewsletterSettings = news;
|
||||
Jobs = jobs;
|
||||
}
|
||||
public int Version => 1950;
|
||||
|
||||
private ISettingsService<PlexRequestSettings> PlexRequestSettings { get; }
|
||||
private ISettingsService<NewletterSettings> NewsletterSettings { get; }
|
||||
private ISettingsService<ScheduledJobsSettings> Jobs { get; }
|
||||
|
||||
public void Start(IDbConnection con)
|
||||
{
|
||||
var plex = PlexRequestSettings.GetSettings();
|
||||
UpdateApplicationSettings();
|
||||
UpdateDb(con);
|
||||
|
||||
UpdateSchema(con, Version);
|
||||
}
|
||||
|
||||
private void UpdateDb(IDbConnection con)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void UpdateApplicationSettings()
|
||||
{
|
||||
var plex = PlexRequestSettings.GetSettings();
|
||||
var jobSettings = Jobs.GetSettings();
|
||||
var newsLetter = NewsletterSettings.GetSettings();
|
||||
|
||||
newsLetter.SendToPlexUsers = true;
|
||||
UpdateScheduledSettings(jobSettings);
|
||||
|
||||
if (plex.SendRecentlyAddedEmail)
|
||||
{
|
||||
newsLetter.SendRecentlyAddedEmail = plex.SendRecentlyAddedEmail;
|
||||
plex.SendRecentlyAddedEmail = false;
|
||||
|
||||
PlexRequestSettings.SaveSettings(plex);
|
||||
NewsletterSettings.SaveSettings(newsLetter);
|
||||
}
|
||||
|
||||
UpdateSchema(con, Version);
|
||||
|
||||
NewsletterSettings.SaveSettings(newsLetter);
|
||||
Jobs.SaveSettings(jobSettings);
|
||||
}
|
||||
|
||||
private void UpdateScheduledSettings(ScheduledJobsSettings settings)
|
||||
{
|
||||
settings.PlexAvailabilityChecker = 60;
|
||||
settings.SickRageCacher = 60;
|
||||
settings.SonarrCacher = 60;
|
||||
settings.CouchPotatoCacher = 60;
|
||||
settings.StoreBackup = 24;
|
||||
settings.StoreCleanup = 24;
|
||||
settings.UserRequestLimitResetter = 12;
|
||||
settings.PlexEpisodeCacher = 12;
|
||||
|
||||
var cron = (Quartz.Impl.Triggers.CronTriggerImpl)CronScheduleBuilder.WeeklyOnDayAndHourAndMinute(DayOfWeek.Friday, 7, 0).Build();
|
||||
settings.RecentlyAddedCron = cron.CronExpressionString; // Weekly CRON at 7 am on Mondays
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,12 +31,24 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Common.Logging, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Common.Logging.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Data.Sqlite">
|
||||
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Ninject">
|
||||
<HintPath>..\packages\Ninject.3.2.0.0\lib\net45-full\Ninject.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Quartz, Version=2.3.3.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Quartz.2.3.3\lib\net40\Quartz.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
|
@ -66,6 +78,13 @@
|
|||
<Name>PlexRequests.Store</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="job_scheduling_data_2_0.xsd">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
|
11
PlexRequests.Core.Migration/app.config
Normal file
11
PlexRequests.Core.Migration/app.config
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
361
PlexRequests.Core.Migration/job_scheduling_data_2_0.xsd
Normal file
361
PlexRequests.Core.Migration/job_scheduling_data_2_0.xsd
Normal file
|
@ -0,0 +1,361 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
|
||||
targetNamespace="http://quartznet.sourceforge.net/JobSchedulingData"
|
||||
elementFormDefault="qualified"
|
||||
version="2.0">
|
||||
|
||||
<xs:element name="job-scheduling-data">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Root level node</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:element name="pre-processing-commands" type="pre-processing-commandsType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Commands to be executed before scheduling the jobs and triggers in this file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="processing-directives" type="processing-directivesType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Directives to be followed while scheduling the jobs and triggers in this file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="schedule" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:element name="job" type="job-detailType" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="trigger" type="triggerType" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="version" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Version of the XML Schema instance</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:complexType name="pre-processing-commandsType">
|
||||
<xs:sequence maxOccurs="unbounded">
|
||||
<xs:element name="delete-jobs-in-group" type="xs:string" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Delete all jobs, if any, in the identified group. "*" can be used to identify all groups. Will also result in deleting all triggers related to the jobs.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="delete-triggers-in-group" type="xs:string" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Delete all triggers, if any, in the identified group. "*" can be used to identify all groups. Will also result in deletion of related jobs that are non-durable.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="delete-job" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Delete the identified job if it exists (will also result in deleting all triggers related to it).</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="name" type="xs:string" />
|
||||
<xs:element name="group" type="xs:string" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="delete-trigger" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Delete the identified trigger if it exists (will also result in deletion of related jobs that are non-durable).</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="name" type="xs:string" />
|
||||
<xs:element name="group" type="xs:string" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="processing-directivesType">
|
||||
<xs:sequence>
|
||||
<xs:element name="overwrite-existing-data" type="xs:boolean" minOccurs="0" default="true">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Whether the existing scheduling data (with same identifiers) will be overwritten. If false, and ignore-duplicates is not false, and jobs or triggers with the same names already exist as those in the file, an error will occur.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="ignore-duplicates" type="xs:boolean" minOccurs="0" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If true (and overwrite-existing-data is false) then any job/triggers encountered in this file that have names that already exist in the scheduler will be ignored, and no error will be produced.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="schedule-trigger-relative-to-replaced-trigger" type="xs:boolean" minOccurs="0" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If true trigger's start time is calculated based on earlier run time instead of fixed value. Trigger's start time must be undefined for this to work.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="job-detailType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Define a JobDetail</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="name" type="xs:string" />
|
||||
<xs:element name="group" type="xs:string" minOccurs="0" />
|
||||
<xs:element name="description" type="xs:string" minOccurs="0" />
|
||||
<xs:element name="job-type" type="xs:string" />
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:element name="durable" type="xs:boolean" />
|
||||
<xs:element name="recover" type="xs:boolean" />
|
||||
</xs:sequence>
|
||||
<xs:element name="job-data-map" type="job-data-mapType" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="job-data-mapType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Define a JobDataMap</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="entry" type="entryType" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="entryType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Define a JobDataMap entry</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="key" type="xs:string" />
|
||||
<xs:element name="value" type="xs:string" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="triggerType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Define a Trigger</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:choice>
|
||||
<xs:element name="simple" type="simpleTriggerType" />
|
||||
<xs:element name="cron" type="cronTriggerType" />
|
||||
<xs:element name="calendar-interval" type="calendarIntervalTriggerType" />
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="abstractTriggerType" abstract="true">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Common Trigger definitions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="name" type="xs:string" />
|
||||
<xs:element name="group" type="xs:string" minOccurs="0" />
|
||||
<xs:element name="description" type="xs:string" minOccurs="0" />
|
||||
<xs:element name="job-name" type="xs:string" />
|
||||
<xs:element name="job-group" type="xs:string" minOccurs="0" />
|
||||
<xs:element name="priority" type="xs:nonNegativeInteger" minOccurs="0" />
|
||||
<xs:element name="calendar-name" type="xs:string" minOccurs="0" />
|
||||
<xs:element name="job-data-map" type="job-data-mapType" minOccurs="0" />
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:choice>
|
||||
<xs:element name="start-time" type="xs:dateTime" />
|
||||
<xs:element name="start-time-seconds-in-future" type="xs:nonNegativeInteger" />
|
||||
</xs:choice>
|
||||
<xs:element name="end-time" type="xs:dateTime" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="simpleTriggerType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Define a SimpleTrigger</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="abstractTriggerType">
|
||||
<xs:sequence>
|
||||
<xs:element name="misfire-instruction" type="simple-trigger-misfire-instructionType" minOccurs="0" />
|
||||
<xs:sequence minOccurs="0">
|
||||
<xs:element name="repeat-count" type="repeat-countType" />
|
||||
<xs:element name="repeat-interval" type="xs:nonNegativeInteger" />
|
||||
</xs:sequence>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="cronTriggerType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Define a CronTrigger</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="abstractTriggerType">
|
||||
<xs:sequence>
|
||||
<xs:element name="misfire-instruction" type="cron-trigger-misfire-instructionType" minOccurs="0" />
|
||||
<xs:element name="cron-expression" type="cron-expressionType" />
|
||||
<xs:element name="time-zone" type="xs:string" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="calendarIntervalTriggerType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Define a DateIntervalTrigger</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="abstractTriggerType">
|
||||
<xs:sequence>
|
||||
<xs:element name="misfire-instruction" type="date-interval-trigger-misfire-instructionType" minOccurs="0" />
|
||||
<xs:element name="repeat-interval" type="xs:nonNegativeInteger" />
|
||||
<xs:element name="repeat-interval-unit" type="interval-unitType" />
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="cron-expressionType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Cron expression (see JavaDoc for examples)
|
||||
|
||||
Special thanks to Chris Thatcher (thatcher@butterfly.net) for the regular expression!
|
||||
|
||||
Regular expressions are not my strong point but I believe this is complete,
|
||||
with the caveat that order for expressions like 3-0 is not legal but will pass,
|
||||
and month and day names must be capitalized.
|
||||
If you want to examine the correctness look for the [\s] to denote the
|
||||
seperation of individual regular expressions. This is how I break them up visually
|
||||
to examine them:
|
||||
|
||||
SECONDS:
|
||||
(
|
||||
((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)
|
||||
| (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))
|
||||
| ([\?])
|
||||
| ([\*])
|
||||
) [\s]
|
||||
MINUTES:
|
||||
(
|
||||
((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)
|
||||
| (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))
|
||||
| ([\?])
|
||||
| ([\*])
|
||||
) [\s]
|
||||
HOURS:
|
||||
(
|
||||
((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)
|
||||
| (([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))
|
||||
| ([\?])
|
||||
| ([\*])
|
||||
) [\s]
|
||||
DAY OF MONTH:
|
||||
(
|
||||
((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)
|
||||
| (([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)
|
||||
| (L(-[0-9])?)
|
||||
| (L(-[1-2][0-9])?)
|
||||
| (L(-[3][0-1])?)
|
||||
| (LW)
|
||||
| ([1-9]W)
|
||||
| ([1-3][0-9]W)
|
||||
| ([\?])
|
||||
| ([\*])
|
||||
)[\s]
|
||||
MONTH:
|
||||
(
|
||||
((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)
|
||||
| (([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))
|
||||
| (((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)
|
||||
| ((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))
|
||||
| ([\?])
|
||||
| ([\*])
|
||||
)[\s]
|
||||
DAY OF WEEK:
|
||||
(
|
||||
(([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)
|
||||
| ([1-7]/([1-7]))
|
||||
| (((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)
|
||||
| ((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)
|
||||
| (([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))(L|LW)?)
|
||||
| (([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)
|
||||
| ([\?])
|
||||
| ([\*])
|
||||
)
|
||||
YEAR (OPTIONAL):
|
||||
(
|
||||
[\s]?
|
||||
([\*])?
|
||||
| ((19[7-9][0-9])|(20[0-9][0-9]))?
|
||||
| (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?
|
||||
| ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?
|
||||
)
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern
|
||||
value="(((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)|(([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))|([\?])|([\*]))[\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L(-[0-9])?)|(L(-[1-2][0-9])?)|(L(-[3][0-1])?)|(LW)|([1-9]W)|([1-3][0-9]W)|([\?])|([\*]))[\s](((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)|(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\?])|([\*]))[\s]((([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)|([1-7]/([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L|LW)?)|(([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)|([\?])|([\*]))([\s]?(([\*])?|(19[7-9][0-9])|(20[0-9][0-9]))?| (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?| ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?)" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="repeat-countType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Number of times to repeat the Trigger (-1 for indefinite)</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:minInclusive value="-1" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
||||
<xs:simpleType name="simple-trigger-misfire-instructionType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Simple Trigger Misfire Instructions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="SmartPolicy" />
|
||||
<xs:pattern value="RescheduleNextWithExistingCount" />
|
||||
<xs:pattern value="RescheduleNextWithRemainingCount" />
|
||||
<xs:pattern value="RescheduleNowWithExistingRepeatCount" />
|
||||
<xs:pattern value="RescheduleNowWithRemainingRepeatCount" />
|
||||
<xs:pattern value="FireNow" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="cron-trigger-misfire-instructionType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Cron Trigger Misfire Instructions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="SmartPolicy" />
|
||||
<xs:pattern value="DoNothing" />
|
||||
<xs:pattern value="FireOnceNow" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="date-interval-trigger-misfire-instructionType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Date Interval Trigger Misfire Instructions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="SmartPolicy" />
|
||||
<xs:pattern value="DoNothing" />
|
||||
<xs:pattern value="FireOnceNow" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="interval-unitType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Interval Units</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="Day" />
|
||||
<xs:pattern value="Hour" />
|
||||
<xs:pattern value="Minute" />
|
||||
<xs:pattern value="Month" />
|
||||
<xs:pattern value="Second" />
|
||||
<xs:pattern value="Week" />
|
||||
<xs:pattern value="Year" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
</xs:schema>
|
6
PlexRequests.Core.Migration/packages.config
Normal file
6
PlexRequests.Core.Migration/packages.config
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Common.Logging" version="3.0.0" targetFramework="net45" />
|
||||
<package id="Common.Logging.Core" version="3.0.0" targetFramework="net45" />
|
||||
<package id="Quartz" version="2.3.3" targetFramework="net45" />
|
||||
</packages>
|
|
@ -26,13 +26,20 @@
|
|||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using PlexRequests.Core.Models;
|
||||
using PlexRequests.Core.Notification;
|
||||
using Newtonsoft.Json;
|
||||
using PlexRequests.Helpers;
|
||||
|
||||
namespace PlexRequests.Core.SettingModels
|
||||
{
|
||||
public class NewletterSettings : Settings
|
||||
{
|
||||
public bool SendRecentlyAddedEmail { get; set; }
|
||||
public bool SendToPlexUsers { get; set; }
|
||||
public string CustomUsers { get; set; }
|
||||
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> CustomUsersEmailAddresses => CustomUsers.SplitEmailsByDelimiter(';');
|
||||
}
|
||||
}
|
|
@ -24,23 +24,13 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace PlexRequests.Core.SettingModels
|
||||
{
|
||||
public class ScheduledJobsSettings : Settings
|
||||
{
|
||||
public ScheduledJobsSettings()
|
||||
{
|
||||
PlexAvailabilityChecker = 60;
|
||||
SickRageCacher = 60;
|
||||
SonarrCacher = 60;
|
||||
CouchPotatoCacher = 60;
|
||||
StoreBackup = 24;
|
||||
StoreCleanup = 24;
|
||||
UserRequestLimitResetter = 12;
|
||||
PlexEpisodeCacher = 12;
|
||||
RecentlyAdded = 168;
|
||||
}
|
||||
|
||||
public int PlexAvailabilityChecker { get; set; }
|
||||
public int SickRageCacher { get; set; }
|
||||
public int SonarrCacher { get; set; }
|
||||
|
@ -49,6 +39,8 @@ namespace PlexRequests.Core.SettingModels
|
|||
public int StoreCleanup { get; set; }
|
||||
public int UserRequestLimitResetter { get; set; }
|
||||
public int PlexEpisodeCacher { get; set; }
|
||||
[Obsolete("We use the CRON job now")]
|
||||
public int RecentlyAdded { get; set; }
|
||||
public string RecentlyAddedCron { get; set; }
|
||||
}
|
||||
}
|
|
@ -110,10 +110,9 @@ namespace PlexRequests.Core
|
|||
Salt = salt,
|
||||
Hash = PasswordHasher.ComputeHash(password, salt),
|
||||
Claims = ByteConverterHelper.ReturnBytes(claims),
|
||||
UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties())
|
||||
UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()),
|
||||
};
|
||||
Repo.Insert(userModel);
|
||||
|
||||
var userRecord = Repo.Get(userModel.UserGuid);
|
||||
|
||||
return new Guid(userRecord.UserGuid);
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -64,5 +66,36 @@ namespace PlexRequests.Helpers
|
|||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static IEnumerable<string> SplitEmailsByDelimiter(this string input, char delimiter)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
yield return string.Empty;
|
||||
}
|
||||
var startIndex = 0;
|
||||
var delimiterIndex = 0;
|
||||
|
||||
while (delimiterIndex >= 0)
|
||||
{
|
||||
delimiterIndex = input.IndexOf(delimiter, startIndex);
|
||||
string substring = input;
|
||||
if (delimiterIndex > 0)
|
||||
{
|
||||
substring = input.Substring(0, delimiterIndex).Trim();
|
||||
}
|
||||
|
||||
if (!substring.Contains("\"") || substring.IndexOf("\"") != substring.LastIndexOf("\""))
|
||||
{
|
||||
yield return substring;
|
||||
input = input.Substring(delimiterIndex + 1).Trim();
|
||||
startIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex = delimiterIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,10 @@ namespace PlexRequests.Helpers
|
|||
{
|
||||
public class UserClaims
|
||||
{
|
||||
public const string Admin = "Admin"; // Can do everything including creating new users and editing settings
|
||||
public const string PowerUser = "PowerUser"; // Can only manage the requests, approve etc.
|
||||
public const string User = "User"; // Can only request
|
||||
public const string Admin = nameof(Admin); // Can do everything including creating new users and editing settings
|
||||
public const string PowerUser = nameof(PowerUser); // Can only manage the requests, approve etc.
|
||||
public const string User = nameof(User); // Can only request
|
||||
public const string Newsletter = nameof(Newsletter); // Has newsletter feature enabled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,12 +33,12 @@ namespace PlexRequests.Services.Jobs
|
|||
{
|
||||
public abstract class HtmlTemplateGenerator
|
||||
{
|
||||
protected virtual void AddParagraph(ref StringBuilder stringBuilder, string text, int fontSize = 14, string fontWeight = "normal")
|
||||
protected virtual void AddParagraph(StringBuilder stringBuilder, string text, int fontSize = 14, string fontWeight = "normal")
|
||||
{
|
||||
stringBuilder.AppendFormat("<p style=\"font-family: sans-serif; font-size: {1}px; font-weight: {2}; margin: 0; Margin-bottom: 15px;\">{0}</p>", text, fontSize, fontWeight);
|
||||
}
|
||||
|
||||
protected virtual void AddImageInsideTable(ref StringBuilder sb, string url)
|
||||
protected virtual void AddImageInsideTable(StringBuilder sb, string url)
|
||||
{
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td align=\"center\">");
|
||||
|
@ -49,17 +49,17 @@ namespace PlexRequests.Services.Jobs
|
|||
sb.Append("</tr>");
|
||||
}
|
||||
|
||||
protected virtual void Href(ref StringBuilder sb, string url)
|
||||
protected virtual void Href(StringBuilder sb, string url)
|
||||
{
|
||||
sb.AppendFormat("<a href=\"{0}\">", url);
|
||||
}
|
||||
|
||||
protected virtual void EndTag(ref StringBuilder sb, string tag)
|
||||
protected virtual void EndTag(StringBuilder sb, string tag)
|
||||
{
|
||||
sb.AppendFormat("</{0}>", tag);
|
||||
}
|
||||
|
||||
protected virtual void Header(ref StringBuilder sb, int size, string text, string fontWeight = "normal")
|
||||
protected virtual void Header(StringBuilder sb, int size, string text, string fontWeight = "normal")
|
||||
{
|
||||
sb.AppendFormat(
|
||||
"<h{0} style=\"font-family: sans-serif; font-weight: {2}; margin: 0; Margin-bottom: 15px;\">{1}</h{0}>",
|
||||
|
|
|
@ -51,8 +51,7 @@ namespace PlexRequests.Services.Jobs
|
|||
public class RecentlyAdded : HtmlTemplateGenerator, IJob, IRecentlyAdded
|
||||
{
|
||||
public RecentlyAdded(IPlexApi api, ISettingsService<PlexSettings> plexSettings,
|
||||
ISettingsService<EmailNotificationSettings> email,
|
||||
ISettingsService<ScheduledJobsSettings> scheduledService, IJobRecord rec,
|
||||
ISettingsService<EmailNotificationSettings> email, IJobRecord rec,
|
||||
ISettingsService<NewletterSettings> newsletter,
|
||||
IPlexReadOnlyDatabase db)
|
||||
{
|
||||
|
@ -60,7 +59,6 @@ namespace PlexRequests.Services.Jobs
|
|||
Api = api;
|
||||
PlexSettings = plexSettings;
|
||||
EmailSettings = email;
|
||||
ScheduledJobsSettings = scheduledService;
|
||||
NewsletterSettings = newsletter;
|
||||
PlexDb = db;
|
||||
}
|
||||
|
@ -73,7 +71,6 @@ namespace PlexRequests.Services.Jobs
|
|||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private ISettingsService<EmailNotificationSettings> EmailSettings { get; }
|
||||
private ISettingsService<NewletterSettings> NewsletterSettings { get; }
|
||||
private ISettingsService<ScheduledJobsSettings> ScheduledJobsSettings { get; }
|
||||
private IJobRecord JobRecord { get; }
|
||||
private IPlexReadOnlyDatabase PlexDb { get; }
|
||||
|
||||
|
@ -88,19 +85,8 @@ namespace PlexRequests.Services.Jobs
|
|||
{
|
||||
return;
|
||||
}
|
||||
var jobs = JobRecord.GetJobs();
|
||||
var thisJob =
|
||||
jobs.FirstOrDefault(
|
||||
x => x.Name.Equals(JobNames.RecentlyAddedEmail, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
var jobSettings = ScheduledJobsSettings.GetSettings();
|
||||
|
||||
if (thisJob?.LastRun > DateTime.Now.AddHours(-jobSettings.RecentlyAdded))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Start();
|
||||
Start(settings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -114,10 +100,11 @@ namespace PlexRequests.Services.Jobs
|
|||
|
||||
public void Test()
|
||||
{
|
||||
Start(true);
|
||||
var settings = NewsletterSettings.GetSettings();
|
||||
Start(settings, true);
|
||||
}
|
||||
|
||||
private void Start(bool testEmail = false)
|
||||
private void Start(NewletterSettings newletterSettings, bool testEmail = false)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var plexSettings = PlexSettings.GetSettings();
|
||||
|
@ -129,16 +116,16 @@ namespace PlexRequests.Services.Jobs
|
|||
var recentlyAddedTv = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, tvSection.Key);
|
||||
var recentlyAddedMovies = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, movieSection.Key);
|
||||
|
||||
GenerateMovieHtml(recentlyAddedMovies, plexSettings, ref sb);
|
||||
GenerateTvHtml(recentlyAddedTv, plexSettings, ref sb);
|
||||
GenerateMovieHtml(recentlyAddedMovies, plexSettings, sb);
|
||||
GenerateTvHtml(recentlyAddedTv, plexSettings, sb);
|
||||
|
||||
var template = new RecentlyAddedTemplate();
|
||||
var html = template.LoadTemplate(sb.ToString());
|
||||
|
||||
Send(html, plexSettings, testEmail);
|
||||
Send(newletterSettings, html, plexSettings, testEmail);
|
||||
}
|
||||
|
||||
private void GenerateMovieHtml(RecentlyAddedModel movies, PlexSettings plexSettings, ref StringBuilder sb)
|
||||
private void GenerateMovieHtml(RecentlyAddedModel movies, PlexSettings plexSettings, StringBuilder sb)
|
||||
{
|
||||
sb.Append("<h1>New Movies:</h1><br/><br/>");
|
||||
sb.Append(
|
||||
|
@ -156,94 +143,41 @@ namespace PlexRequests.Services.Jobs
|
|||
var imdbId = PlexHelper.GetProviderIdFromPlexGuid(plexGUID);
|
||||
var info = _movieApi.GetMovieInformation(imdbId).Result;
|
||||
|
||||
AddImageInsideTable(ref sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}");
|
||||
AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}");
|
||||
|
||||
sb.Append("<tr>");
|
||||
sb.Append(
|
||||
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
||||
|
||||
Href(ref sb, $"https://www.imdb.com/title/{info.ImdbId}/");
|
||||
Header(ref sb, 3, $"{info.Title} {info.ReleaseDate?.ToString("yyyy") ?? string.Empty}");
|
||||
EndTag(ref sb, "a");
|
||||
Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/");
|
||||
Header(sb, 3, $"{info.Title} {info.ReleaseDate?.ToString("yyyy") ?? string.Empty}");
|
||||
EndTag(sb, "a");
|
||||
|
||||
if (info.Genres.Any())
|
||||
{
|
||||
AddParagraph(ref sb, $"Genre: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}");
|
||||
AddParagraph(sb,
|
||||
$"Genre: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}");
|
||||
}
|
||||
|
||||
AddParagraph(ref sb, info.Overview);
|
||||
|
||||
EndLoopHtml(ref sb);
|
||||
AddParagraph(sb, info.Overview);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
Log.Error("Exception when trying to process a Movie, either in getting the metadata from Plex OR getting the information from TheMovieDB, Plex GUID = {0}", plexGUID);
|
||||
Log.Error(
|
||||
"Exception when trying to process a Movie, either in getting the metadata from Plex OR getting the information from TheMovieDB, Plex GUID = {0}",
|
||||
plexGUID);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndLoopHtml(sb);
|
||||
}
|
||||
|
||||
}
|
||||
sb.Append("</table><br/><br/>");
|
||||
}
|
||||
|
||||
private void GenerateMovieHtml(IEnumerable<MetadataItems> movies, ref StringBuilder sb)
|
||||
{
|
||||
var items = movies as MetadataItems[] ?? movies.ToArray();
|
||||
if (!items.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
sb.Append("<h1>New Movies:</h1><br/><br/>");
|
||||
sb.Append(
|
||||
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
|
||||
foreach (var movie in items.OrderByDescending(x => x.added_at))
|
||||
{
|
||||
var plexGUID = string.Empty;
|
||||
try
|
||||
{
|
||||
plexGUID = movie.guid;
|
||||
|
||||
var imdbId = PlexHelper.GetProviderIdFromPlexGuid(plexGUID);
|
||||
|
||||
var info = _movieApi.GetMovieInformation(imdbId).Result; // TODO remove this and get the image info from Plex https://github.com/jakewaldron/PlexEmail/blob/master/scripts/plexEmail.py#L391
|
||||
|
||||
AddImageInsideTable(ref sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}");
|
||||
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
||||
|
||||
Href(ref sb, $"https://www.imdb.com/title/{info.ImdbId}/");
|
||||
var title = string.IsNullOrEmpty(movie.original_title)
|
||||
? $"{movie.title} {movie.originally_available_at:yyyy}"
|
||||
: $"{movie.original_title} AKA {movie.title} {movie.originally_available_at:yyyy}";
|
||||
|
||||
Header(ref sb, 3, title);
|
||||
EndTag(ref sb, "a");
|
||||
|
||||
if (!string.IsNullOrEmpty(movie.tagline))
|
||||
{
|
||||
AddParagraph(ref sb, movie.tagline);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(movie.tags_genre))
|
||||
{
|
||||
AddParagraph(ref sb, $"Genre: {PlexHelper.FormatGenres(movie.tags_genre)}");
|
||||
}
|
||||
|
||||
AddParagraph(ref sb, movie.summary);
|
||||
|
||||
EndLoopHtml(ref sb);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
Log.Error("Exception when trying to process a Movie, either in getting the metadata from Plex OR getting the information from TheMovieDB, Plex GUID = {0}", plexGUID);
|
||||
}
|
||||
|
||||
}
|
||||
sb.Append("</table><br/><br/>");
|
||||
}
|
||||
|
||||
private void GenerateTvHtml(RecentlyAddedModel tv, PlexSettings plexSettings, ref StringBuilder sb)
|
||||
private void GenerateTvHtml(RecentlyAddedModel tv, PlexSettings plexSettings, StringBuilder sb)
|
||||
{
|
||||
// TV
|
||||
sb.Append("<h1>New Episodes:</h1><br/><br/>");
|
||||
|
@ -267,117 +201,42 @@ namespace PlexRequests.Services.Jobs
|
|||
{
|
||||
banner = banner.Replace("http", "https"); // Always use the Https banners
|
||||
}
|
||||
AddImageInsideTable(sb, banner);
|
||||
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td align=\"center\">");
|
||||
sb.AppendFormat("<img src=\"{0}\" width=\"400px\" text-align=\"center\" />", banner);
|
||||
sb.Append("</td>");
|
||||
sb.Append("</tr>");
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
||||
sb.Append(
|
||||
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
||||
|
||||
var title = $"{t.grandparentTitle} - {t.title} {t.originallyAvailableAt?.Substring(0, 4)}";
|
||||
|
||||
sb.AppendFormat("<a href=\"https://www.imdb.com/title/{0}/\"><h3 style=\"font-family: sans-serif; font-weight: normal; margin: 0; Margin-bottom: 15px;\">{1}</p></a>",
|
||||
info.externals.imdb, title); // Only the year
|
||||
|
||||
|
||||
sb.AppendFormat("<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">Season: {0}, Episode: {1}</p>", t.parentIndex, t.index);
|
||||
|
||||
|
||||
sb.AppendFormat("<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">Genre: {0}</p>", string.Join(", ", info.genres.Select(x => x.ToString()).ToArray()));
|
||||
sb.AppendFormat("<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">{0}</p>",
|
||||
string.IsNullOrEmpty(t.summary) ? info.summary : t.summary); // Episode Summary
|
||||
|
||||
sb.Append("<td");
|
||||
sb.Append("<hr>");
|
||||
sb.Append("<br>");
|
||||
sb.Append("<br>");
|
||||
sb.Append("</tr>");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
sb.Append("<td");
|
||||
sb.Append("<hr>");
|
||||
sb.Append("<br>");
|
||||
sb.Append("<br>");
|
||||
sb.Append("</tr>");
|
||||
Log.Error(e);
|
||||
Log.Error("Exception when trying to process a TV Show, either in getting the metadata from Plex OR getting the information from TVMaze, Plex GUID = {0}", plexGUID);
|
||||
}
|
||||
}
|
||||
sb.Append("</table><br/><br/>");
|
||||
}
|
||||
|
||||
private void GenerateTvHtml(IEnumerable<MetadataItems> tv, ref StringBuilder sb)
|
||||
{
|
||||
var items = tv as MetadataItems[] ?? tv.ToArray();
|
||||
if (!items.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TV
|
||||
sb.Append("<h1>New Episodes:</h1><br/><br/>");
|
||||
sb.Append(
|
||||
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
|
||||
foreach (var t in items.OrderByDescending(x => x.added_at))
|
||||
{
|
||||
var plexGUID = string.Empty;
|
||||
try
|
||||
{
|
||||
|
||||
plexGUID = t.guid;
|
||||
var seasonInfo = PlexHelper.GetSeasonsAndEpisodesFromPlexGuid(plexGUID);
|
||||
|
||||
var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(plexGUID)));
|
||||
|
||||
var banner = info.image?.original;
|
||||
if (!string.IsNullOrEmpty(banner))
|
||||
{
|
||||
banner = banner.Replace("http", "https"); // Always use the Https banners
|
||||
}
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td align=\"center\">");
|
||||
sb.AppendFormat("<img src=\"{0}\" width=\"400px\" text-align=\"center\" />", banner);
|
||||
sb.Append("</td>");
|
||||
sb.Append("</tr>");
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
||||
|
||||
var title = !string.IsNullOrEmpty(t.SeriesTitle)
|
||||
? $"{t.SeriesTitle} - {t.title} {t.originally_available_at:yyyy}"
|
||||
: $"{t.title}";
|
||||
|
||||
sb.AppendFormat("<a href=\"https://www.imdb.com/title/{0}/\"><h3 style=\"font-family: sans-serif; font-weight: normal; margin: 0; Margin-bottom: 15px;\">{1}</p></a>",
|
||||
info.externals.imdb, title);
|
||||
|
||||
sb.AppendFormat("<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">Season: {0}, Episode: {1}</p>", seasonInfo.SeasonNumber, seasonInfo.EpisodeNumber);
|
||||
Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/");
|
||||
Header(sb, 3, title);
|
||||
EndTag(sb, "a");
|
||||
|
||||
AddParagraph(sb, $"Season: {t.parentIndex}, Episode: {t.index}");
|
||||
if (info.genres.Any())
|
||||
{
|
||||
sb.AppendFormat(
|
||||
"<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">Genre: {0}</p>",
|
||||
string.Join(", ", info.genres.Select(x => x.ToString()).ToArray()));
|
||||
AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}");
|
||||
}
|
||||
sb.AppendFormat("<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">{0}</p>",
|
||||
string.IsNullOrEmpty(t.summary) ? info.summary : t.summary); // Episode Summary
|
||||
|
||||
sb.Append("<td");
|
||||
sb.Append("<hr>");
|
||||
sb.Append("<br>");
|
||||
sb.Append("<br>");
|
||||
sb.Append("</tr>");
|
||||
AddParagraph(sb, string.IsNullOrEmpty(t.summary) ? info.summary : t.summary);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
Log.Error("Exception when trying to process a TV Show, either in getting the metadata from Plex OR getting the information from TVMaze, Plex GUID = {0}", plexGUID);
|
||||
Log.Error(
|
||||
"Exception when trying to process a TV Show, either in getting the metadata from Plex OR getting the information from TVMaze, Plex GUID = {0}",
|
||||
plexGUID);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EndLoopHtml(sb);
|
||||
}
|
||||
}
|
||||
sb.Append("</table><br/><br/>");
|
||||
}
|
||||
|
||||
private void Send(string html, PlexSettings plexSettings, bool testEmail = false)
|
||||
private void Send(NewletterSettings newletterSettings, string html, PlexSettings plexSettings, bool testEmail = false)
|
||||
{
|
||||
var settings = EmailSettings.GetSettings();
|
||||
|
||||
|
@ -394,6 +253,8 @@ namespace PlexRequests.Services.Jobs
|
|||
};
|
||||
|
||||
if (!testEmail)
|
||||
{
|
||||
if (newletterSettings.SendToPlexUsers)
|
||||
{
|
||||
var users = Api.GetUsers(plexSettings.PlexAuthToken);
|
||||
foreach (var user in users.User)
|
||||
|
@ -401,7 +262,16 @@ namespace PlexRequests.Services.Jobs
|
|||
message.Bcc.Add(new MailboxAddress(user.Username, user.Email));
|
||||
}
|
||||
}
|
||||
message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender)); // Include the admin
|
||||
|
||||
if (newletterSettings.CustomUsersEmailAddresses.Any())
|
||||
{
|
||||
foreach (var user in newletterSettings.CustomUsersEmailAddresses)
|
||||
{
|
||||
message.Bcc.Add(new MailboxAddress(user, user));
|
||||
}
|
||||
}
|
||||
}
|
||||
message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.RecipientEmail)); // Include the admin
|
||||
|
||||
message.From.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender));
|
||||
try
|
||||
|
@ -429,7 +299,7 @@ namespace PlexRequests.Services.Jobs
|
|||
}
|
||||
}
|
||||
|
||||
private void EndLoopHtml(ref StringBuilder sb)
|
||||
private void EndLoopHtml(StringBuilder sb)
|
||||
{
|
||||
sb.Append("<td");
|
||||
sb.Append("<hr>");
|
||||
|
|
|
@ -39,14 +39,14 @@ namespace PlexRequests.Store
|
|||
/// Creates the tables located in the SqlTables.sql file.
|
||||
/// </summary>
|
||||
/// <param name="connection">The connection.</param>
|
||||
public static void CreateTables(IDbConnection connection)
|
||||
public static void CreateTables(this IDbConnection connection)
|
||||
{
|
||||
connection.Open();
|
||||
connection.Execute(Sql.SqlTables);
|
||||
connection.Close();
|
||||
}
|
||||
|
||||
public static void DropTable(IDbConnection con, string tableName)
|
||||
public static void DropTable(this IDbConnection con, string tableName)
|
||||
{
|
||||
using (con)
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ namespace PlexRequests.Store
|
|||
}
|
||||
}
|
||||
|
||||
public static void AddColumn(IDbConnection connection, string tableName, string alterType, string newColumn, bool isNullable, string dataType)
|
||||
public static void AddColumn(this IDbConnection connection, string tableName, string alterType, string newColumn, bool isNullable, string dataType)
|
||||
{
|
||||
connection.Open();
|
||||
var result = connection.Query<TableInfo>($"PRAGMA table_info({tableName});");
|
||||
|
@ -77,7 +77,7 @@ namespace PlexRequests.Store
|
|||
connection.Close();
|
||||
}
|
||||
|
||||
public static void Vacuum(IDbConnection con)
|
||||
public static void Vacuum(this IDbConnection con)
|
||||
{
|
||||
using (con)
|
||||
{
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
|
||||
// Select a user to populate on the right side
|
||||
$scope.selectUser = function (id) {
|
||||
$scope.selectedUser = $scope.users.find(x => x.id === id);
|
||||
var user = $scope.users.filter(function (item) {
|
||||
return item.id === id;
|
||||
});
|
||||
$scope.selectedUser = user[0];
|
||||
}
|
||||
|
||||
// Get all users in the system
|
||||
|
@ -64,6 +67,15 @@
|
|||
});
|
||||
};
|
||||
|
||||
$scope.hasClaim = function(claim) {
|
||||
var claims = $scope.selectedUser.claimsArray;
|
||||
|
||||
var result = claims.some(function (item) {
|
||||
return item === claim.name;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
$scope.$watch('claims|filter:{selected:true}',
|
||||
function (nv) {
|
||||
$scope.selectedClaims = nv.map(function (claim) {
|
||||
|
@ -74,7 +86,7 @@
|
|||
|
||||
|
||||
$scope.updateUser = function () {
|
||||
|
||||
userManagementService.updateUser($scope.selectedUser.id, $scope.selectedUser.claimsItem);
|
||||
}
|
||||
|
||||
function getBaseUrl() {
|
||||
|
|
|
@ -24,10 +24,20 @@
|
|||
return $http.get('/usermanagement/claims');
|
||||
}
|
||||
|
||||
var updateUser = function (id, claims) {
|
||||
|
||||
return $http({
|
||||
url: '/usermanagement/updateUser',
|
||||
method: "POST",
|
||||
data: { id: id, claims: claims }
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
getUsers: getUsers,
|
||||
addUser: addUser,
|
||||
getClaims: getClaims
|
||||
getClaims: getClaims,
|
||||
updateUser: updateUser,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,9 @@ namespace PlexRequests.UI.Jobs
|
|||
|
||||
private IEnumerable<IJobDetail> CreateJobs()
|
||||
{
|
||||
var settingsService = Service.Resolve<ISettingsService<ScheduledJobsSettings>>();
|
||||
var s = settingsService.GetSettings();
|
||||
|
||||
var jobs = new List<IJobDetail>();
|
||||
|
||||
var jobList = new List<IJobDetail>
|
||||
|
@ -66,9 +69,13 @@ namespace PlexRequests.UI.Jobs
|
|||
JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(),
|
||||
JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(),
|
||||
JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build(),
|
||||
JobBuilder.Create<RecentlyAdded>().WithIdentity("RecentlyAddedModel", "Email").Build()
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(s.RecentlyAddedCron))
|
||||
{
|
||||
jobList.Add(JobBuilder.Create<RecentlyAdded>().WithIdentity("RecentlyAddedModel", "Email").Build());
|
||||
}
|
||||
|
||||
|
||||
jobs.AddRange(jobList);
|
||||
|
||||
|
@ -155,24 +162,34 @@ namespace PlexRequests.UI.Jobs
|
|||
var userRequestLimiter =
|
||||
TriggerBuilder.Create()
|
||||
.WithIdentity("UserRequestLimiter", "Request")
|
||||
.StartAt(DateTimeOffset.Now.AddMinutes(5)) // Everything has started on application start, lets wait 5 minutes
|
||||
.StartAt(DateBuilder.FutureDate(5, IntervalUnit.Minute))
|
||||
// Everything has started on application start, lets wait 5 minutes
|
||||
.WithSimpleSchedule(x => x.WithIntervalInHours(s.UserRequestLimitResetter).RepeatForever())
|
||||
.Build();
|
||||
|
||||
var plexEpCacher =
|
||||
TriggerBuilder.Create()
|
||||
.WithIdentity("PlexEpisodeCacher", "Cache")
|
||||
.StartAt(DateTimeOffset.Now.AddMinutes(5))
|
||||
.StartAt(DateBuilder.FutureDate(5, IntervalUnit.Minute))
|
||||
.WithSimpleSchedule(x => x.WithIntervalInHours(s.PlexEpisodeCacher).RepeatForever())
|
||||
.Build();
|
||||
|
||||
|
||||
var cronJob = string.IsNullOrEmpty(s.RecentlyAddedCron);
|
||||
if (!cronJob)
|
||||
{
|
||||
var rencentlyAdded =
|
||||
TriggerBuilder.Create()
|
||||
.WithIdentity("RecentlyAddedModel", "Email")
|
||||
.StartNow()
|
||||
.WithCronSchedule(s.RecentlyAddedCron)
|
||||
.WithSimpleSchedule(x => x.WithIntervalInHours(2).RepeatForever())
|
||||
.Build();
|
||||
|
||||
triggers.Add(rencentlyAdded);
|
||||
}
|
||||
|
||||
|
||||
|
||||
triggers.Add(plexAvailabilityChecker);
|
||||
triggers.Add(srCacher);
|
||||
|
@ -182,7 +199,6 @@ namespace PlexRequests.UI.Jobs
|
|||
triggers.Add(storeCleanup);
|
||||
triggers.Add(userRequestLimiter);
|
||||
triggers.Add(plexEpCacher);
|
||||
triggers.Add(rencentlyAdded);
|
||||
|
||||
return triggers;
|
||||
}
|
||||
|
|
|
@ -46,5 +46,6 @@ namespace PlexRequests.UI.Models
|
|||
public bool Video { get; set; }
|
||||
public double VoteAverage { get; set; }
|
||||
public int VoteCount { get; set; }
|
||||
public bool AlreadyInCp { get; set; }
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ namespace PlexRequests.UI.Models
|
|||
public string EmailAddress { get; set; }
|
||||
public UserManagementPlexInformation PlexInfo { get; set; }
|
||||
public string[] ClaimsArray { get; set; }
|
||||
public List<UserManagementUpdateModel.ClaimsModel> ClaimsItem { get; set; }
|
||||
}
|
||||
|
||||
public class UserManagementPlexInformation
|
||||
|
@ -57,5 +58,22 @@ namespace PlexRequests.UI.Models
|
|||
[JsonProperty("email")]
|
||||
public string EmailAddress { get; set; }
|
||||
}
|
||||
|
||||
public class UserManagementUpdateModel
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("claims")]
|
||||
public List<ClaimsModel> Claims { get; set; }
|
||||
|
||||
public class ClaimsModel
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("selected")]
|
||||
public bool Selected { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ using PlexRequests.Store.Models;
|
|||
using PlexRequests.Store.Repository;
|
||||
using PlexRequests.UI.Helpers;
|
||||
using PlexRequests.UI.Models;
|
||||
|
||||
using Quartz;
|
||||
using Action = PlexRequests.Helpers.Analytics.Action;
|
||||
|
||||
namespace PlexRequests.UI.Modules
|
||||
|
@ -210,7 +210,7 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
Post["/autoupdate"] = x => AutoUpdate();
|
||||
|
||||
Post["/testslacknotification", true] = async (x,ct) => await TestSlackNotification();
|
||||
Post["/testslacknotification", true] = async (x, ct) => await TestSlackNotification();
|
||||
|
||||
Get["/slacknotification"] = _ => SlackNotifications();
|
||||
Post["/slacknotification"] = _ => SaveSlackNotifications();
|
||||
|
@ -975,7 +975,8 @@ namespace PlexRequests.UI.Modules
|
|||
SonarrCacher = s.SonarrCacher,
|
||||
StoreBackup = s.StoreBackup,
|
||||
StoreCleanup = s.StoreCleanup,
|
||||
JobRecorder = jobsDict
|
||||
JobRecorder = jobsDict,
|
||||
RecentlyAddedCron = s.RecentlyAddedCron
|
||||
};
|
||||
return View["SchedulerSettings", model];
|
||||
}
|
||||
|
@ -986,6 +987,21 @@ namespace PlexRequests.UI.Modules
|
|||
Analytics.TrackEventAsync(Category.Admin, Action.Update, "Update ScheduledJobs", Username, CookieHelper.GetAnalyticClientId(Cookies));
|
||||
var settings = this.Bind<ScheduledJobsSettings>();
|
||||
|
||||
if (!string.IsNullOrEmpty(settings.RecentlyAddedCron))
|
||||
{
|
||||
// Validate CRON
|
||||
var isValid = CronExpression.IsValidExpression(settings.RecentlyAddedCron);
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message =
|
||||
$"CRON {settings.RecentlyAddedCron} is not valid. Please ensure you are using a valid CRON."
|
||||
});
|
||||
}
|
||||
}
|
||||
var result = await ScheduledJobSettings.SaveSettingsAsync(settings);
|
||||
|
||||
return Response.AsJson(result
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace PlexRequests.UI.Modules
|
|||
Get["/local/{id}"] = x => LocalDetails((Guid)x.id);
|
||||
Get["/plex/{id}", true] = async (x, ct) => await PlexDetails(x.id);
|
||||
Get["/claims"] = x => GetClaims();
|
||||
Post["/updateuser"] = x => UpdateUser();
|
||||
}
|
||||
|
||||
private ICustomUserMapper UserMapper { get; }
|
||||
|
@ -57,15 +58,35 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
var userProps = ByteConverterHelper.ReturnObject<UserProperties>(user.UserProperties);
|
||||
|
||||
model.Add(new UserManagementUsersViewModel
|
||||
var m = new UserManagementUsersViewModel
|
||||
{
|
||||
Id = user.UserGuid,
|
||||
Claims = claimsString,
|
||||
Username = user.UserName,
|
||||
Type = UserType.LocalUser,
|
||||
EmailAddress = userProps.EmailAddress,
|
||||
ClaimsArray = claims
|
||||
});
|
||||
ClaimsArray = claims,
|
||||
ClaimsItem = new List<UserManagementUpdateModel.ClaimsModel>()
|
||||
};
|
||||
|
||||
// Add all of the current claims
|
||||
foreach (var c in claims)
|
||||
{
|
||||
m.ClaimsItem.Add(new UserManagementUpdateModel.ClaimsModel { Name = c, Selected = true });
|
||||
}
|
||||
|
||||
var allClaims = UserMapper.GetAllClaims();
|
||||
|
||||
// Get me the current claims that the user does not have
|
||||
var missingClaims = allClaims.Except(claims);
|
||||
|
||||
// Add them into the view
|
||||
foreach (var missingClaim in missingClaims)
|
||||
{
|
||||
m.ClaimsItem.Add(new UserManagementUpdateModel.ClaimsModel { Name = missingClaim, Selected = false });
|
||||
}
|
||||
|
||||
model.Add(m);
|
||||
}
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
|
@ -121,6 +142,44 @@ namespace PlexRequests.UI.Modules
|
|||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user" });
|
||||
}
|
||||
|
||||
private Response UpdateUser()
|
||||
{
|
||||
var body = Request.Body.AsString();
|
||||
if (string.IsNullOrEmpty(body))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user, invalid JSON body" });
|
||||
}
|
||||
|
||||
var model = JsonConvert.DeserializeObject<UserManagementUpdateModel>(body);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.Id))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel
|
||||
{
|
||||
Result = true,
|
||||
Message = "Couldn't find the user"
|
||||
});
|
||||
}
|
||||
|
||||
var claims = new List<string>();
|
||||
|
||||
foreach (var c in model.Claims)
|
||||
{
|
||||
if (c.Selected)
|
||||
{
|
||||
claims.Add(c.Name);
|
||||
}
|
||||
}
|
||||
|
||||
var userFound = UserMapper.GetUser(new Guid(model.Id));
|
||||
|
||||
userFound.Claims = ByteConverterHelper.ReturnBytes(claims.ToArray());
|
||||
|
||||
var user = UserMapper.EditUser(userFound);
|
||||
|
||||
return Response.AsJson(user);
|
||||
}
|
||||
|
||||
private Response LocalDetails(Guid id)
|
||||
{
|
||||
var localUser = UserMapper.GetUser(id);
|
||||
|
|
|
@ -63,14 +63,25 @@ namespace PlexRequests.UI
|
|||
Debug.WriteLine("Added Contravariant Binder");
|
||||
kernel.Components.Add<IBindingResolver, ContravariantBindingResolver>();
|
||||
|
||||
Debug.WriteLine("Start the bootstrapper with the Kernel.ı");
|
||||
Debug.WriteLine("Start the bootstrapper with the Kernel.");
|
||||
app.UseNancy(options => options.Bootstrapper = new Bootstrapper(kernel));
|
||||
Debug.WriteLine("Finished bootstrapper");
|
||||
|
||||
|
||||
Debug.WriteLine("Migrating DB Now");
|
||||
var runner = kernel.Get<IMigrationRunner>();
|
||||
runner.MigrateToLatest();
|
||||
|
||||
|
||||
Debug.WriteLine("Settings up Scheduler");
|
||||
var scheduler = new Scheduler();
|
||||
scheduler.StartScheduler();
|
||||
|
||||
var runner = kernel.Get<IMigrationRunner>();
|
||||
runner.MigrateToLatest();
|
||||
//var c = kernel.Get<IRecentlyAdded>();
|
||||
//c.Test();
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
|
|
@ -16,13 +16,31 @@
|
|||
<small>Note: This will require you to setup your email notifications</small>
|
||||
@if (Model.SendRecentlyAddedEmail)
|
||||
{
|
||||
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail" checked="checked"><label for="SendRecentlyAddedEmail">Send out a weekly email of recently added content to all your Plex 'Friends'</label>
|
||||
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail" checked="checked"><label for="SendRecentlyAddedEmail">Enable the newsletter of recently added content</label>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail"><label for="SendRecentlyAddedEmail">Send out a weekly email of recently added content to all your Plex 'Friends'</label>
|
||||
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail"><label for="SendRecentlyAddedEmail">Enable the newsletter of recently added content</label>
|
||||
}
|
||||
|
||||
@if (Model.SendToPlexUsers)
|
||||
{
|
||||
<input type="checkbox" id="SendToPlexUsers" name="SendToPlexUsers" checked="checked"><label for="SendToPlexUsers">Send to all of your Plex 'Friends'</label>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="checkbox" id="SendToPlexUsers" name="SendToPlexUsers"><label for="SendToPlexUsers">Send to all of your Plex 'Friends'</label>
|
||||
}
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="StoreCleanup" class="control-label">A comma separated list of email addresses you want the newsletter to go to (For users that are not in your Plex Friends)</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom " placeholder="email@address.com;second@address.com" id="StoreCleanup" name="StoreCleanup" value="@Model.CustomUsers">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<button id="recentlyAddedBtn" class="btn btn-primary-outline">Send test email to Admin</button>
|
||||
</div>
|
||||
|
|
|
@ -79,10 +79,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<small>Please note, this uses a Quartz CRON job, you can build a CRON <a href="http://www.cronmaker.com/">Here</a></small>
|
||||
<div class="form-group">
|
||||
<label for="RecentlyAdded" class="control-label">Recently Added Email (hours)</label>
|
||||
<label for="RecentlyAddedCron" class="control-label">Recently Added Email (CRON)</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom " id="RecentlyAdded" name="RecentlyAdded" value="@Model.RecentlyAdded">
|
||||
<input type="text" class="form-control form-control-custom " id="RecentlyAddedCron" name="RecentlyAddedCron" value="@Model.RecentlyAddedCron">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -201,6 +201,7 @@
|
|||
{{/if_eq}}
|
||||
{{/if_eq}}
|
||||
{{/if_eq}}
|
||||
|
||||
{{#if_eq type "tv"}}
|
||||
{{#if_eq tvFullyAvailable true}}
|
||||
@*//TODO Not used yet*@
|
||||
|
|
|
@ -116,8 +116,8 @@
|
|||
|
||||
<strong>Modify Roles:</strong>
|
||||
<!--Load all claims-->
|
||||
<div class="checkbox" ng-repeat="claim in claims">
|
||||
<input id="claimCheckboxEdit_{{$id}}" class="checkbox-custom" name="selectedClaims[]" ng-checked="@*//TODO: Need to figure our how to preselect them*@" ng-model="claim.selected" type="checkbox" value="claim" />
|
||||
<div class="checkbox" ng-repeat="claim in selectedUser.claimsItem">
|
||||
<input id="claimCheckboxEdit_{{$id}}" class="checkbox-custom" name="selectedClaims[]" ng-checked="claim.selected" ng-model="claim.selected" type="checkbox" value="claim" />
|
||||
<label for="claimCheckboxEdit_{{$id}}">{{claim.name}}</label>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue