mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-25 02:30:20 +02:00
Series/Index wired up to backbone, just need to add DT
This commit is contained in:
parent
84fbfb5d48
commit
796f63680a
@ -57,7 +57,8 @@ public static void InitializeAutomapper()
|
||||
|
||||
//Series
|
||||
Mapper.CreateMap<Core.Repository.Series, SeriesModel>()
|
||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.SeriesId));
|
||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.SeriesId))
|
||||
.ForMember(dest => dest.CustomStartDate, opt => opt.ResolveUsing<NullableDatetimeToString>().FromMember(src => src.CustomStartDate));
|
||||
//.ForMember(dest => dest.BacklogSetting, opt => opt.MapFrom(src => Convert.ToInt32(src.BacklogSetting)));
|
||||
}
|
||||
|
||||
|
@ -95,6 +95,7 @@
|
||||
<Compile Include="Directories\DirectoryModule.cs" />
|
||||
<Compile Include="Extentions\NancyJsonSerializer.cs" />
|
||||
<Compile Include="Extentions\Serializer.cs" />
|
||||
<Compile Include="Resolvers\NullableDatetimeToString.cs" />
|
||||
<Compile Include="RootFolders\RootFolderModule.cs" />
|
||||
<Compile Include="Series\SeriesModel.cs" />
|
||||
<Compile Include="Series\SeriesModule.cs" />
|
||||
|
20
NzbDrone.Api/Resolvers/NullableDatetimeToString.cs
Normal file
20
NzbDrone.Api/Resolvers/NullableDatetimeToString.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using NzbDrone.Api.QualityProfiles;
|
||||
using NzbDrone.Core.Repository.Quality;
|
||||
|
||||
namespace NzbDrone.Api.Resolvers
|
||||
{
|
||||
public class NullableDatetimeToString : ValueResolver<DateTime?, String>
|
||||
{
|
||||
protected override String ResolveCore(DateTime? source)
|
||||
{
|
||||
if(!source.HasValue)
|
||||
return String.Empty;
|
||||
|
||||
return source.Value.ToString("yyyy-MM-dd");
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Api.QualityProfiles;
|
||||
using NzbDrone.Core.Model;
|
||||
|
||||
namespace NzbDrone.Api.Series
|
||||
@ -12,6 +13,7 @@ public class SeriesModel
|
||||
|
||||
//Todo: Sorters should be done completely on the client
|
||||
//Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing?
|
||||
//Todo: We should get the entire QualityProfile instead of ID and Name separately
|
||||
|
||||
//View Only
|
||||
public String Title { get; set; }
|
||||
@ -41,6 +43,6 @@ public class SeriesModel
|
||||
public Boolean SeasonFolder { get; set; }
|
||||
public Boolean Monitored { get; set; }
|
||||
public BacklogSettingType BacklogSetting { get; set; }
|
||||
public DateTime? CustomStartDate { get; set; }
|
||||
public String CustomStartDate { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using FluentValidation;
|
||||
@ -7,6 +9,7 @@
|
||||
using NzbDrone.Api.QualityProfiles;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Core.Jobs;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Providers.Core;
|
||||
|
||||
@ -25,7 +28,10 @@ public SeriesModule(SeriesProvider seriesProvider, JobProvider jobProvider)
|
||||
Get["/"] = x => AllSeries();
|
||||
Get["/{id}"] = x => GetSeries((int)x.id);
|
||||
Post["/"] = x => AddSeries();
|
||||
Delete["/{id}"] = x => DeleteSeries((int)x.id);
|
||||
Put["/"] = x => UpdateSeries();
|
||||
|
||||
//Todo: Backbone failing and not sending the id properly... wtf
|
||||
Delete["/"] = x => DeleteSeries(0);
|
||||
}
|
||||
|
||||
private Response AllSeries()
|
||||
@ -59,9 +65,43 @@ private Response AddSeries()
|
||||
return new Response { StatusCode = HttpStatusCode.Created };
|
||||
}
|
||||
|
||||
private Response UpdateSeries()
|
||||
{
|
||||
var request = Request.Body.FromJson<SeriesModel>();
|
||||
|
||||
var series = _seriesProvider.GetSeries(request.Id);
|
||||
|
||||
series.Monitored = request.Monitored;
|
||||
series.SeasonFolder = request.SeasonFolder;
|
||||
series.QualityProfileId = request.QualityProfileId;
|
||||
|
||||
var oldPath = series.Path;
|
||||
|
||||
series.Path = request.Path;
|
||||
series.BacklogSetting = (BacklogSettingType)request.BacklogSetting;
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(request.CustomStartDate))
|
||||
series.CustomStartDate = DateTime.Parse(request.CustomStartDate, null, DateTimeStyles.RoundtripKind);
|
||||
|
||||
else
|
||||
series.CustomStartDate = null;
|
||||
|
||||
_seriesProvider.UpdateSeries(series);
|
||||
|
||||
if (oldPath != series.Path)
|
||||
_jobProvider.QueueJob(typeof(DiskScanJob), new { SeriesId = series.SeriesId });
|
||||
|
||||
_seriesProvider.UpdateSeries(series);
|
||||
|
||||
return request.AsResponse();
|
||||
}
|
||||
|
||||
private Response DeleteSeries(int id)
|
||||
{
|
||||
//_seriesProvider.DeleteSeries(id);
|
||||
var seriesId = Convert.ToInt32(Request.Headers["id"].FirstOrDefault());
|
||||
var deleteFiles = Convert.ToBoolean(Request.Headers["deleteFiles"].FirstOrDefault());
|
||||
|
||||
_jobProvider.QueueJob(typeof(DeleteSeriesJob), new {SeriesId = seriesId, DeleteFiles = deleteFiles});
|
||||
return new Response { StatusCode = HttpStatusCode.OK };
|
||||
}
|
||||
}
|
||||
|
@ -409,6 +409,8 @@
|
||||
<Content Include="_backboneApp\JsLibraries\backbone.js" />
|
||||
<Content Include="_backboneApp\JsLibraries\backbone.marionette.js" />
|
||||
<Content Include="_backboneApp\AddSeries\addSeriesLayoutTemplate.html" />
|
||||
<Content Include="_backboneApp\Series\EditSeriesTemplate.html" />
|
||||
<Content Include="_backboneApp\Series\EditSeriesView.js" />
|
||||
<Content Include="_backboneApp\Shared\ModalRegion.js" />
|
||||
<Content Include="_backboneApp\Series\DeleteSeriesTemplate.html" />
|
||||
<Content Include="_backboneApp\Series\Index\SeriesCollectionTemplate.html" />
|
||||
|
@ -16,8 +16,6 @@ NzbDrone.AddSeries.AddNewSeriesView = Backbone.Marionette.Layout.extend({
|
||||
|
||||
collection: new NzbDrone.AddSeries.SearchResultCollection(),
|
||||
|
||||
|
||||
|
||||
initialize: function (options) {
|
||||
if (options.rootFolders === undefined) {
|
||||
throw 'rootFolder arg. is required.';
|
||||
@ -27,9 +25,8 @@ NzbDrone.AddSeries.AddNewSeriesView = Backbone.Marionette.Layout.extend({
|
||||
throw 'qualityProfiles arg. is required.';
|
||||
}
|
||||
|
||||
|
||||
this.rootFoldersCollection = options.rootFolders;
|
||||
this.qualityProfilesCollection = options.qualityProfiles;
|
||||
this.qualityProfileCollection = options.qualityProfiles;
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
@ -44,7 +41,6 @@ NzbDrone.AddSeries.AddNewSeriesView = Backbone.Marionette.Layout.extend({
|
||||
});
|
||||
|
||||
this.resultView = new NzbDrone.AddSeries.SearchResultView({ collection: this.collection });
|
||||
|
||||
},
|
||||
|
||||
search: function (context) {
|
||||
@ -80,7 +76,7 @@ NzbDrone.AddSeries.AddNewSeriesView = Backbone.Marionette.Layout.extend({
|
||||
resultUpdated: function (options, context) {
|
||||
_.each(options.models, function (model) {
|
||||
model.set('rootFolders', context.rootFoldersCollection);
|
||||
model.set('qualityProfiles', context.qualityProfilesCollection);
|
||||
model.set('qualityProfiles', context.qualityProfileCollection);
|
||||
});
|
||||
|
||||
context.searchResult.show(context.resultView);
|
||||
|
@ -31,6 +31,7 @@ NzbDrone.AddSeries.SearchItemView = Backbone.Marionette.ItemView.extend({
|
||||
var quality = this.ui.qualityProfile.val();
|
||||
var rootFolderId = this.ui.rootFolder.val();
|
||||
|
||||
//Todo: This wiil create an invalid path on linux...
|
||||
var rootPath = this.model.get('rootFolders').get(rootFolderId).get('path');
|
||||
var path = rootPath + "\\" + title;
|
||||
|
||||
|
@ -26,8 +26,8 @@
|
||||
top: 10%;
|
||||
left: 50%;
|
||||
z-index: @zindexModal;
|
||||
width: 560px;
|
||||
margin-left: -280px;
|
||||
width: 660px;
|
||||
margin-left: -330px;
|
||||
background-color: @white;
|
||||
border: 1px solid #999;
|
||||
border: 1px solid rgba(0,0,0,.3);
|
||||
@ -60,7 +60,7 @@
|
||||
.modal-body {
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
max-height: 400px;
|
||||
max-height: 450px;
|
||||
padding: 15px;
|
||||
}
|
||||
// Remove bottom margin if need be
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
body {
|
||||
background: #191919 url(images/background.jpg) no-repeat right top;
|
||||
font-size: 13px;
|
||||
font-size: 14px;
|
||||
color: #3C3C3C;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
@ -84,6 +84,7 @@ body {
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
/* Progress Bar Text */
|
||||
.progress {
|
||||
width: 125px;
|
||||
position: relative;
|
||||
@ -106,4 +107,23 @@ body {
|
||||
width: 125px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Todo: Should move this to somehting modal/form specific */
|
||||
|
||||
label.checkbox {
|
||||
font-size: 14px;
|
||||
line-height: normal;
|
||||
color: #595959;
|
||||
}
|
||||
|
||||
label, .form-horizontal input, .form-horizontal select {
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
label.control-label {
|
||||
font-size: 16px;
|
||||
line-height: 16px;
|
||||
font-weight: bold;
|
||||
}
|
@ -1,10 +1,16 @@
|
||||
<div class="modal-header">
|
||||
<h2>Delete {{title}}</h2>
|
||||
<h3>Delete: {{title}}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete '{{title}}'? </p>
|
||||
<p>Are you sure you want to delete '{{title}}'?</p>
|
||||
<div class="series-delete-files">
|
||||
<label class="checkbox">
|
||||
<input id="delete-from-disk" type="checkbox" value="true">
|
||||
Delete all files from disk?
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">cancel</button>
|
||||
<button class="btn btn-danger x-confirm-delete">Ok</button>
|
||||
<button class="btn btn-danger x-confirm-delete">delete</button>
|
||||
</div>
|
@ -21,7 +21,8 @@ NzbDrone.Series.DeleteSeriesView = Backbone.Marionette.ItemView.extend({
|
||||
},
|
||||
|
||||
removeSeries: function () {
|
||||
this.model.destroy({ wait: true });
|
||||
//Todo: why the fuck doesn't destroy send the ID?
|
||||
this.model.destroy({ wait: true, headers: { id: this.model.get('id'), deleteFiles: $('#delete-from-disk').prop('checked') } });
|
||||
this.model.collection.remove(this.model);
|
||||
this.$el.parent().modal('hide');
|
||||
},
|
||||
|
73
NzbDrone.Web/_backboneApp/Series/EditSeriesTemplate.html
Normal file
73
NzbDrone.Web/_backboneApp/Series/EditSeriesTemplate.html
Normal file
@ -0,0 +1,73 @@
|
||||
<div class="modal-header">
|
||||
<h3>Edit: {{title}}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputMonitored">Monitored</label>
|
||||
<div class="controls">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id ="inputMonitored" name="monitored">
|
||||
Should NzbDrone download episodes for this series?
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputSeasonFolder">Use Season Folder</label>
|
||||
<div class="controls">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id ="inputSeasonFolder" name="seasonFolder">
|
||||
Should downloaded episodes be stored in season folders?
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputQualityProfile">Quality Profile</label>
|
||||
<div class="controls">
|
||||
|
||||
<select class="x-quality-profile" name="qualityProfileId">
|
||||
{{#each qualityProfiles.models}}
|
||||
<option value="{{id}}">{{attributes.name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<span class="help-inline">Which Quality Profile should NzbDrone use to download episodes?</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputPath">Path</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="inputPath" placeholder="Path" name="path">
|
||||
<span class="help-inline">Where should NzbDrone store episodes for this series?</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputBacklogSetting">Backlog Setting</label>
|
||||
<div class="controls">
|
||||
<select id="inputBacklogSetting" class="inputClass x-backlog-setting" name="backlogSetting">
|
||||
<option value="0">Inherit</option>
|
||||
<option value="1">Enable</option>
|
||||
<option value="2">Disable</option>
|
||||
</select>
|
||||
<span class="help-inline">Should NzbDrone search for missing episodes every 30 days?</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputCustomStartDate">Custom Start Date</label>
|
||||
<div class="controls">
|
||||
<input type="date" id="inputCustomStartDate" name="customStartDate">
|
||||
<span class="help-inline">Should NzbDrone only download episodes after your preferred start date?</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-danger pull-left x-remove" >delete</button>
|
||||
<button class="btn" data-dismiss="modal">cancel</button>
|
||||
<button class="btn btn-primary x-save">save</button>
|
||||
</div>
|
52
NzbDrone.Web/_backboneApp/Series/EditSeriesView.js
Normal file
52
NzbDrone.Web/_backboneApp/Series/EditSeriesView.js
Normal file
@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
/*global NzbDrone, Backbone*/
|
||||
/// <reference path="../app.js" />
|
||||
/// <reference path="SeriesModel.js" />
|
||||
/// <reference path="DeleteSeriesView.js" />
|
||||
/// <reference path="../Quality/qualityProfileCollection.js" />
|
||||
|
||||
NzbDrone.Series.EditSeriesView = Backbone.Marionette.ItemView.extend({
|
||||
template: 'Series/EditSeriesTemplate',
|
||||
tagName: 'div',
|
||||
className: "modal",
|
||||
|
||||
ui: {
|
||||
progressbar: '.progress .bar',
|
||||
qualityProfile: '.x-quality-profile',
|
||||
backlogSettings: '.x-backlog-setting',
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-save': 'saveSeries',
|
||||
'click .x-remove': 'removeSeries'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.qualityProfileCollection = options.qualityProfiles;
|
||||
this.model.set('qualityProfiles', this.qualityProfileCollection);
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
NzbDrone.ModelBinder.bind(this.model, this.el);
|
||||
},
|
||||
|
||||
qualityProfileCollection: new NzbDrone.Quality.QualityProfileCollection(),
|
||||
|
||||
saveSeries: function () {
|
||||
//Todo: Get qualityProfile + backlog setting from UI
|
||||
var qualityProfile = this.ui.qualityProfile.val();
|
||||
var qualityProfileText = this.ui.qualityProfile.children('option:selected').text();
|
||||
var backlogSetting = this.ui.backlogSettings.val();
|
||||
|
||||
this.model.set({ qualityProfileId: qualityProfile, backlogSetting: backlogSetting, qualityProfileName: qualityProfileText });
|
||||
|
||||
this.model.save();
|
||||
this.trigger('saved');
|
||||
this.$el.parent().modal('hide');
|
||||
},
|
||||
|
||||
removeSeries: function () {
|
||||
var view = new NzbDrone.Series.DeleteSeriesView({ model: this.model });
|
||||
NzbDrone.modalRegion.show(view);
|
||||
}
|
||||
});
|
@ -8,8 +8,7 @@ NzbDrone.Series.IndexLayout = Backbone.Marionette.Layout.extend({
|
||||
route: 'Series/index',
|
||||
|
||||
ui: {
|
||||
edit: '.edit-series',
|
||||
delele: '.delete-series'
|
||||
|
||||
},
|
||||
|
||||
regions: {
|
||||
|
@ -7,4 +7,7 @@
|
||||
<td>
|
||||
{{{formatProgress episodeFileCount episodeCount}}}
|
||||
<td>
|
||||
<td>Edit/<i class="icon-remove x-remove" title="Delete Series"></i></td>
|
||||
<td>
|
||||
<i class="icon-cog x-edit" title="Edit Series"></i>
|
||||
<i class="icon-remove x-remove" title="Delete Series"></i>
|
||||
</td>
|
||||
|
@ -3,7 +3,9 @@
|
||||
/// <reference path="../../app.js" />
|
||||
/// <reference path="../SeriesModel.js" />
|
||||
/// <reference path="../SeriesCollection.js" />
|
||||
/// <reference path="../EditSeriesView.js" />
|
||||
/// <reference path="../DeleteSeriesView.js" />
|
||||
/// <reference path="../../Quality/qualityProfileCollection.js" />
|
||||
|
||||
NzbDrone.Series.Index.SeriesItemView = Backbone.Marionette.ItemView.extend({
|
||||
template: 'Series/Index/SeriesItemTemplate',
|
||||
@ -14,25 +16,45 @@ NzbDrone.Series.Index.SeriesItemView = Backbone.Marionette.ItemView.extend({
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-remove': 'removeSeries',
|
||||
'click .x-edit': 'editSeries',
|
||||
'click .x-remove': 'removeSeries'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.qualityProfileCollection = options.qualityProfiles;
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
NzbDrone.ModelBinder.bind(this.model, this.el);
|
||||
},
|
||||
|
||||
removeSeries: function () {
|
||||
//this.model.destroy({ wait: true });
|
||||
//this.model.collection.remove(this.model);
|
||||
qualityProfileCollection: new NzbDrone.Quality.QualityProfileCollection(),
|
||||
|
||||
editSeries: function () {
|
||||
var view = new NzbDrone.Series.EditSeriesView({ model: this.model, qualityProfiles: this.qualityProfileCollection });
|
||||
view.on('saved', this.render, this);
|
||||
NzbDrone.modalRegion.show(view);
|
||||
},
|
||||
|
||||
removeSeries: function () {
|
||||
var view = new NzbDrone.Series.DeleteSeriesView({ model: this.model });
|
||||
NzbDrone.modalRegion.show(view);
|
||||
},
|
||||
onSave: function() {
|
||||
alert("saved!");
|
||||
}
|
||||
});
|
||||
|
||||
NzbDrone.Series.Index.SeriesCollectionView = Backbone.Marionette.CompositeView.extend({
|
||||
itemView: NzbDrone.Series.Index.SeriesItemView,
|
||||
itemViewOptions: {},
|
||||
template: 'Series/Index/SeriesCollectionTemplate',
|
||||
tagName: 'table',
|
||||
className: 'table table-hover',
|
||||
qualityProfileCollection: new NzbDrone.Quality.QualityProfileCollection(),
|
||||
|
||||
initialize: function() {
|
||||
this.qualityProfileCollection.fetch();
|
||||
this.itemViewOptions = { qualityProfiles: this.qualityProfileCollection };
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user