updated add series

pull/23/head
Keivan Beigi 12 years ago committed by kay.one
parent f3e601d4ed
commit 0531029ce7

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using NLog; using NLog;
using Nancy; using Nancy;
using NzbDrone.Api.Extentions;
namespace NzbDrone.Api.ErrorManagment namespace NzbDrone.Api.ErrorManagment
{ {
@ -17,15 +18,21 @@ namespace NzbDrone.Api.ErrorManagment
public Response HandleException(NancyContext context, Exception exception) public Response HandleException(NancyContext context, Exception exception)
{ {
var apiException = exception as ApiException; var apiException = exception as ApiException;
if (apiException != null) if (apiException != null)
{ {
_logger.WarnException("API Error", apiException); _logger.WarnException("API Error", apiException);
return apiException.ToErrorResponse(); return apiException.ToErrorResponse();
} }
_logger.ErrorException("Unexpected error", exception); _logger.ErrorException("Unexpected error", exception);
return null;
return new ErrorModel()
{
Message = exception.Message,
Description = exception.ToString()
}.AsResponse(HttpStatusCode.InternalServerError);
} }
} }
} }

@ -59,6 +59,9 @@
<Reference Include="AutoMapper"> <Reference Include="AutoMapper">
<HintPath>..\packages\AutoMapper.2.2.0\lib\net40\AutoMapper.dll</HintPath> <HintPath>..\packages\AutoMapper.2.2.0\lib\net40\AutoMapper.dll</HintPath>
</Reference> </Reference>
<Reference Include="FluentValidation">
<HintPath>..\packages\FluentValidation.3.4.6.0\lib\Net40\FluentValidation.dll</HintPath>
</Reference>
<Reference Include="Nancy"> <Reference Include="Nancy">
<HintPath>..\packages\Nancy.0.15.3\lib\net40\Nancy.dll</HintPath> <HintPath>..\packages\Nancy.0.15.3\lib\net40\Nancy.dll</HintPath>
</Reference> </Reference>
@ -93,6 +96,7 @@
<Compile Include="Extentions\Serializer.cs" /> <Compile Include="Extentions\Serializer.cs" />
<Compile Include="QualityProfiles\RootFolderModule.cs" /> <Compile Include="QualityProfiles\RootFolderModule.cs" />
<Compile Include="Series\SeriesModule.cs" /> <Compile Include="Series\SeriesModule.cs" />
<Compile Include="Series\SeriesLookupModule.cs" />
<Compile Include="ErrorManagment\ApiException.cs" /> <Compile Include="ErrorManagment\ApiException.cs" />
<Compile Include="Bootstrapper.cs" /> <Compile Include="Bootstrapper.cs" />
<Compile Include="ErrorManagment\ErrorHandler.cs" /> <Compile Include="ErrorManagment\ErrorHandler.cs" />

@ -0,0 +1,27 @@
using System.Linq;
using Nancy;
using NzbDrone.Api.Extentions;
using NzbDrone.Api.QualityType;
using NzbDrone.Core.Providers;
namespace NzbDrone.Api.Series
{
public class SeriesLookupModule : NzbDroneApiModule
{
private readonly TvDbProvider _tvDbProvider;
public SeriesLookupModule(TvDbProvider tvDbProvider)
: base("/Series/lookup")
{
_tvDbProvider = tvDbProvider;
Get["/"] = x => GetQualityType();
}
private Response GetQualityType()
{
var tvDbResults = _tvDbProvider.SearchSeries((string)Request.Query.term);
return tvDbResults.AsResponse();
}
}
}

@ -1,27 +1,58 @@
using System.Linq; using System.Linq;
using FluentValidation;
using Nancy; using Nancy;
using NzbDrone.Api.Extentions; using NzbDrone.Api.Extentions;
using NzbDrone.Api.QualityType; using NzbDrone.Common;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
namespace NzbDrone.Api.Series namespace NzbDrone.Api.Series
{ {
public class SeriesModule : NzbDroneApiModule public class SeriesModule : NzbDroneApiModule
{ {
private readonly TvDbProvider _tvDbProvider; private readonly SeriesProvider _seriesProvider;
private readonly JobProvider _jobProvider;
public SeriesModule(TvDbProvider tvDbProvider) public SeriesModule(SeriesProvider seriesProvider, JobProvider jobProvider)
: base("/Series") : base("/Series")
{ {
_tvDbProvider = tvDbProvider; _seriesProvider = seriesProvider;
Get["/lookup"] = x => GetQualityType(); _jobProvider = jobProvider;
Post["/"] = x => AddSeries();
} }
private Response GetQualityType() private Response AddSeries()
{ {
var tvDbResults = _tvDbProvider.SearchSeries((string)Request.Query.term); var request = Request.Body.FromJson<Core.Repository.Series>();
return tvDbResults.AsResponse();
_seriesProvider.AddSeries("", request.Path, request.SeriesId, request.QualityProfileId, null);
_jobProvider.QueueJob(typeof(ImportNewSeriesJob));
return new Response { StatusCode = HttpStatusCode.Created };
}
}
public class SeriesValidator : AbstractValidator<Core.Repository.Series>
{
private readonly DiskProvider _diskProvider;
public SeriesValidator(DiskProvider diskProvider)
{
_diskProvider = diskProvider;
} }
public SeriesValidator()
{
RuleSet("POST", () =>
{
RuleFor(s => s.SeriesId).GreaterThan(0);
RuleFor(s => s.Path).NotEmpty().Must(_diskProvider.FolderExists);
RuleFor(s => s.QualityProfileId).GreaterThan(0);
});
}
} }
} }

@ -2,6 +2,7 @@
<packages> <packages>
<package id="Autofac" version="2.6.3.862" targetFramework="net40" /> <package id="Autofac" version="2.6.3.862" targetFramework="net40" />
<package id="AutoMapper" version="2.2.0" targetFramework="net40" /> <package id="AutoMapper" version="2.2.0" targetFramework="net40" />
<package id="FluentValidation" version="3.4.6.0" targetFramework="net40" />
<package id="Nancy" version="0.15.3" targetFramework="net40" /> <package id="Nancy" version="0.15.3" targetFramework="net40" />
<package id="Nancy.Bootstrappers.Autofac" version="0.15.3" targetFramework="net40" /> <package id="Nancy.Bootstrappers.Autofac" version="0.15.3" targetFramework="net40" />
<package id="Nancy.Hosting.Aspnet" version="0.15.3" targetFramework="net40" /> <package id="Nancy.Hosting.Aspnet" version="0.15.3" targetFramework="net40" />

@ -390,6 +390,7 @@
<Content Include="_backboneApp\JsLibraries\backbone.js" /> <Content Include="_backboneApp\JsLibraries\backbone.js" />
<Content Include="_backboneApp\JsLibraries\backbone.marionette.js" /> <Content Include="_backboneApp\JsLibraries\backbone.marionette.js" />
<Content Include="_backboneApp\AddSeries\addSeriesLayoutTemplate.html" /> <Content Include="_backboneApp\AddSeries\addSeriesLayoutTemplate.html" />
<Content Include="_backboneApp\Series\SeriesModel.js" />
<Content Include="_backboneApp\Shared\ErrorModel.js" /> <Content Include="_backboneApp\Shared\ErrorModel.js" />
<Content Include="_backboneApp\Shared\ErrorTemplate.html" /> <Content Include="_backboneApp\Shared\ErrorTemplate.html" />
<Content Include="_backboneApp\Shared\ErrorView.js" /> <Content Include="_backboneApp\Shared\ErrorView.js" />

@ -66,8 +66,8 @@ NzbDrone.AddSeries.AddNewSeriesView = Backbone.Marionette.Layout.extend({
resultUpdated: function (options, context) { resultUpdated: function (options, context) {
_.each(options.models, function (model) { _.each(options.models, function (model) {
model.set('rootFolders', context.rootFoldersCollection.models); model.set('rootFolders', context.rootFoldersCollection);
model.set('qualityProfiles', context.qualityProfilesCollection.models); model.set('qualityProfiles', context.qualityProfilesCollection);
}); });
context.searchResult.show(context.resultView); context.searchResult.show(context.resultView);

@ -4,17 +4,17 @@
</div> </div>
<div id="{{id}}" class="accordion-body collapse"> <div id="{{id}}" class="accordion-body collapse">
<div class="accordion-inner"> <div class="accordion-inner">
<select class="root-dir-input span7"> <select class="span7 x-root-folder">
{{#each rootFolders}} {{#each rootFolders.models}}
<option value="{{id}}">{{attributes.path}}</option> <option value="{{id}}">{{attributes.path}}</option>
{{/each}} {{/each}}
</select> </select>
<select class="quality-profile-input span2"> <select class="span2 x-quality-profile">
{{#each qualityProfiles}} {{#each qualityProfiles.models}}
<option value="{{id}}">{{attributes.name}}</option> <option value="{{id}}">{{attributes.name}}</option>
{{/each}} {{/each}}
</select> </select>
<div class="btn btn-success pull-right icon-plus"> <div class="btn btn-success pull-right icon-plus x-add">
</div> </div>
</div> </div>
<div name="overview"></div> <div name="overview"></div>

@ -1,17 +1,47 @@
/// <reference path="../../app.js" /> /// <reference path="../../app.js" />
/// <reference path="../SearchResultModel.js" /> /// <reference path="../SearchResultModel.js" />
/// <reference path="../../Series/SeriesModel.js" />
/// <reference path="../SearchResultCollection.js" /> /// <reference path="../SearchResultCollection.js" />
NzbDrone.AddSeries.SearchItemView = Backbone.Marionette.ItemView.extend({ NzbDrone.AddSeries.SearchItemView = Backbone.Marionette.ItemView.extend({
template: "AddSeries/AddNewSeries/SearchResultTemplate", template: "AddSeries/AddNewSeries/SearchResultTemplate",
className: 'search-item accordion-group', className: 'search-item accordion-group',
ui: {
qualityProfile: '.x-quality-profile',
rootFolder: '.x-root-folder'
},
events: {
'click .x-add': 'add'
},
onRender: function () { onRender: function () {
this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'change', this.render);
//this.listenTo(this.model.get('rootFolders'), 'reset', this.render); },
//this.listenTo(this.model.get('qualityProfiles'), 'reset', this.render);
add: function () {
var seriesId = this.model.get('id');
var title = this.model.get('seriesName');
var quality = this.ui.qualityProfile.val();
var rootFolderId = this.ui.rootFolder.val();
var rootPath = this.model.get('rootFolders').get(rootFolderId).get('path');
var path = rootPath + "\\" + title;
var model = new NzbDrone.Series.SeriesModel({
seriesId: seriesId,
title: title,
qualityProfileId: quality,
path: path
});
model.save();
} }
}); });
NzbDrone.AddSeries.SearchResultView = Backbone.Marionette.CollectionView.extend({ NzbDrone.AddSeries.SearchResultView = Backbone.Marionette.CollectionView.extend({
@ -23,5 +53,7 @@ NzbDrone.AddSeries.SearchResultView = Backbone.Marionette.CollectionView.extend(
initialize: function () { initialize: function () {
this.listenTo(this.collection, 'reset', this.render); this.listenTo(this.collection, 'reset', this.render);
}, },
}); });

@ -1,8 +1,10 @@
/// <reference path="../app.js" /> /// <reference path="../app.js" />
/// <reference path="SearchResultModel.js" /> /// <reference path="SearchResultModel.js" />
"use strict";
NzbDrone.AddSeries.SearchResultCollection = Backbone.Collection.extend({ NzbDrone.AddSeries.SearchResultCollection = Backbone.Collection.extend({
url: NzbDrone.Constants.ApiRoot + '/series/lookup', url: NzbDrone.Constants.ApiRoot + '/series/lookup',
model: NzbDrone.AddSeries.SearchResultModel, model: NzbDrone.AddSeries.SearchResultModel
}); });

@ -9,7 +9,7 @@ _.extend(Marionette.TemplateCache.prototype, {
console.log("Loading template '" + templateId + "'"); console.log("Loading template '" + templateId + "'");
jQuery.ajax({ $.ajax({
url: '_backboneApp//' + templateId + '.html', url: '_backboneApp//' + templateId + '.html',
cache:false, cache:false,
async: false async: false

@ -0,0 +1,10 @@
NzbDrone.Series.SeriesModel = Backbone.Model.extend({
url: NzbDrone.Constants.ApiRoot + '/series'
});
NzbDrone.Series.SeriesCollection = Backbone.Collection.extend({
model: NzbDrone.Series.SeriesModel,
url: NzbDrone.Constants.ApiRoot + '/series',
});

@ -9,6 +9,13 @@ NzbDrone.Shared.ErrorCollection = Backbone.Collection.extend({
NzbDrone.Shared.ErrorModel = Backbone.Model.extend({ NzbDrone.Shared.ErrorModel = Backbone.Model.extend({
mutators: {
pre: function () {
return this.get('message').lines().lenght > 1;
}
},
defaults: { defaults: {
"title": "NO_TITLE", "title": "NO_TITLE",
"message": "NO_MESSAGE", "message": "NO_MESSAGE",

@ -1,4 +1,9 @@
<div class="alert alert-error"> <div class="alert alert-error">
<button type="button" class="close" data-dismiss="alert">×</button> <button type="button" class="close" data-dismiss="alert">×</button>
<i class="icon-warning-sign" /><strong>{{title}}</strong> {{message}} <i class="icon-warning-sign" /><strong>{{title}}</strong>
{{#if preFormatted}}
<pre> {{message}}</pre>
{{else}}
{{message}}
{{/if}}
</div> </div>

@ -18,6 +18,7 @@ if (typeof console == "undefined") {
} }
NzbDrone = new Backbone.Marionette.Application(); NzbDrone = new Backbone.Marionette.Application();
NzbDrone.Series = NzbDrone.module("Series");
NzbDrone.AddSeries = NzbDrone.module("AddSeries"); NzbDrone.AddSeries = NzbDrone.module("AddSeries");
NzbDrone.Quality = NzbDrone.module("Quality"); NzbDrone.Quality = NzbDrone.module("Quality");
NzbDrone.Shared = NzbDrone.module("Shared"); NzbDrone.Shared = NzbDrone.module("Shared");
@ -36,14 +37,14 @@ NzbDrone.Constants = {
}; };
NzbDrone.Events = { NzbDrone.Events = {
DisplayInMainRegion: "DisplayInMainRegion", DisplayInMainRegion: "DisplayInMainRegion"
}; };
NzbDrone.Routes = { NzbDrone.Routes = {
Series: { Series: {
Add: 'series/add', Add: 'series/add'
}, }
}; };
NzbDrone.Controller = Backbone.Marionette.Controller.extend({ NzbDrone.Controller = Backbone.Marionette.Controller.extend({
@ -55,7 +56,7 @@ NzbDrone.Controller = Backbone.Marionette.Controller.extend({
notFound: function () { notFound: function () {
alert('route not found'); alert('route not found');
}, }
}); });
@ -78,7 +79,7 @@ NzbDrone.addInitializer(function (options) {
NzbDrone.addRegions({ NzbDrone.addRegions({
mainRegion: "#main-region", mainRegion: "#main-region",
errorRegion: "#error-region", errorRegion: "#error-region"
}); });
NzbDrone.Router = new NzbDrone.Router(); NzbDrone.Router = new NzbDrone.Router();

@ -1,3 +1,89 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/Environment/Editor/MatchingBraceHighlighting/Position/@EntryValue">BOTH_SIDES</s:String> <s:String x:Key="/Default/Environment/Editor/MatchingBraceHighlighting/Position/@EntryValue">BOTH_SIDES</s:String>
<s:String x:Key="/Default/Environment/Editor/MatchingBraceHighlighting/Style/@EntryValue">OUTLINE</s:String></wpf:ResourceDictionary> <s:String x:Key="/Default/Environment/Editor/MatchingBraceHighlighting/Style/@EntryValue">OUTLINE</s:String>
<s:Boolean x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=5C7F3FB135E52A44B9447C48B2EEEE92/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=5C7F3FB135E52A44B9447C48B2EEEE92/AbsolutePath/@EntryValue">C:\Dropbox\Git\NzbDrone\NzbDrone.sln.DotSettings</s:String>
<s:Boolean x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=File5C7F3FB135E52A44B9447C48B2EEEE92/@KeyIndexDefined">True</s:Boolean>
<s:Double x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=File5C7F3FB135E52A44B9447C48B2EEEE92/RelativePriority/@EntryValue">1</s:Double>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/QuickList/=26E712D4B91E2E49A0E92C0AFE6FF57E/Entry/=38860059D7978D4DAF1997C7CBC46A78/EntryName/@EntryValue">Backbone model</s:String>
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/QuickList/=26E712D4B91E2E49A0E92C0AFE6FF57E/Entry/=38860059D7978D4DAF1997C7CBC46A78/Position/@EntryValue">5</s:Int64>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/@KeyIndexDefined">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Applicability/=File/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/CustomProperties/=Extension/@EntryIndexedValue">js</s:String>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/CustomProperties/=FileName/@EntryIndexedValue">Model</s:String>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/CustomProperties/=ValidateFileName/@EntryIndexedValue">True</s:String>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Description/@EntryValue">Backbone Model</s:String>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Field/=ModelName/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Field/=ModelName/Expression/@EntryValue">getFileNameWithoutExtension()</s:String>
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Field/=ModelName/Order/@EntryValue">0</s:Int64>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Field/=resource/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Field/=resource/Expression/@EntryValue">getFileNameWithoutExtension()</s:String>
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Field/=resource/InitialRange/@EntryValue">-1</s:Int64>
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Field/=resource/Order/@EntryValue">1</s:Int64>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Reformat/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Scope/=0A12C11AC0ACCA4E921B6B2CEE180C57/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Scope/=0A12C11AC0ACCA4E921B6B2CEE180C57/Type/@EntryValue">InAnyWebProject</s:String>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/ShortenQualifiedReferences/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=4F6A0CA32FE60746A73308F7E39A63C1/Text/@EntryValue">NzbDrone.$ModelName$Model = Backbone.Model.extend({&#xD;
&#xD;
});&#xD;
&#xD;
&#xD;
$ModelName$Collection = Backbone.Collection.extend({&#xD;
&#xD;
model: NzbDrone.$ModelName$Model,&#xD;
url: NzbDrone.Constants.ApiRoot + '/$resource$',&#xD;
&#xD;
});&#xD;
</s:String>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/@KeyIndexDefined">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/Description/@EntryValue">Backbone Model</s:String>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/Field/=ModelName/@KeyIndexDefined">True</s:Boolean>
<s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/Field/=ModelName/Order/@EntryValue">0</s:Int64>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/Reformat/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/Scope/=FFA15E6CFCBE90499C572A859225B012/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/Scope/=FFA15E6CFCBE90499C572A859225B012/Type/@EntryValue">InJavaScriptFile</s:String>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/Shortcut/@EntryValue">model</s:String>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/ShortenQualifiedReferences/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=686B0D0C738CD1449F9389FEB5A34944/Text/@EntryValue">$ModelName$ = Backbone.M</s:String>
</wpf:ResourceDictionary>
Loading…
Cancel
Save