mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-15 01:32:55 -07:00
Fixed a bug in the Login and added a unit test to cover that.
Added a button to approve an individual request. Fixed some minor bugs in the request screen
This commit is contained in:
parent
e6bd71a359
commit
452ad07ba0
17 changed files with 238 additions and 25 deletions
37
PlexRequests.Api.Interfaces/ICouchPotatoApi.cs
Normal file
37
PlexRequests.Api.Interfaces/ICouchPotatoApi.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
#region Copyright
|
||||
// /************************************************************************
|
||||
// Copyright (c) 2016 Jamie Rees
|
||||
// File: ICouchPotatoApi.cs
|
||||
// Created By: Jamie Rees
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace PlexRequests.Api.Interfaces
|
||||
{
|
||||
public interface ICouchPotatoApi
|
||||
{
|
||||
bool AddMovie(string imdbid, string apiKey, string title, Uri baseUrl);
|
||||
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
using System;
|
||||
|
||||
using PlexRequests.Api.Models;
|
||||
using PlexRequests.Api.Models.Plex;
|
||||
|
||||
namespace PlexRequests.Api.Interfaces
|
||||
{
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="IApiRequest.cs" />
|
||||
<Compile Include="ICouchPotatoApi.cs" />
|
||||
<Compile Include="IPlexApi.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -24,9 +24,10 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PlexRequests.Api.Models
|
||||
namespace PlexRequests.Api.Models.Plex
|
||||
{
|
||||
public class PlexAuthentication
|
||||
{
|
||||
|
|
|
@ -24,9 +24,10 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace PlexRequests.Api.Models
|
||||
namespace PlexRequests.Api.Models.Plex
|
||||
{
|
||||
[XmlRoot(ElementName = "errors")]
|
||||
public class PlexError
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace PlexRequests.Api.Models
|
||||
namespace PlexRequests.Api.Models.Plex
|
||||
{
|
||||
[XmlRoot(ElementName = "Server")]
|
||||
public class Server
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>PlexRequests.Api.Models</RootNamespace>
|
||||
<AssemblyName>PlexRequests.Api.Models</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
|
|
@ -36,7 +36,6 @@ using Newtonsoft.Json.Linq;
|
|||
using NLog;
|
||||
|
||||
using PlexRequests.Api.Interfaces;
|
||||
using PlexRequests.Api.Models;
|
||||
|
||||
using RestSharp;
|
||||
|
||||
|
|
|
@ -29,12 +29,12 @@ using System;
|
|||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using NLog;
|
||||
|
||||
using PlexRequests.Api.Interfaces;
|
||||
using RestSharp;
|
||||
|
||||
namespace PlexRequests.Api
|
||||
{
|
||||
public class CouchPotatoApi
|
||||
public class CouchPotatoApi : ICouchPotatoApi
|
||||
{
|
||||
public CouchPotatoApi()
|
||||
{
|
||||
|
|
|
@ -28,6 +28,7 @@ using System;
|
|||
|
||||
using PlexRequests.Api.Interfaces;
|
||||
using PlexRequests.Api.Models;
|
||||
using PlexRequests.Api.Models.Plex;
|
||||
using PlexRequests.Helpers;
|
||||
|
||||
using RestSharp;
|
||||
|
|
|
@ -37,6 +37,7 @@ using NUnit.Framework;
|
|||
|
||||
using PlexRequests.Api.Interfaces;
|
||||
using PlexRequests.Api.Models;
|
||||
using PlexRequests.Api.Models.Plex;
|
||||
using PlexRequests.Core;
|
||||
using PlexRequests.Core.SettingModels;
|
||||
using PlexRequests.UI.Models;
|
||||
|
@ -91,6 +92,39 @@ namespace PlexRequests.UI.Tests
|
|||
PlexMock.Verify(x => x.GetUsers(It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LoginWithoutAuthenticationWithEmptyUsername()
|
||||
{
|
||||
var expectedSettings = new AuthenticationSettings { UserAuthentication = false, PlexAuthToken = "abc" };
|
||||
AuthMock.Setup(x => x.GetSettings()).Returns(expectedSettings);
|
||||
|
||||
var bootstrapper = new ConfigurableBootstrapper(with =>
|
||||
{
|
||||
with.Module<UserLoginModule>();
|
||||
with.Dependency(AuthMock.Object);
|
||||
with.Dependency(PlexMock.Object);
|
||||
with.RootPathProvider<TestRootPathProvider>();
|
||||
});
|
||||
|
||||
bootstrapper.WithSession(new Dictionary<string, object>());
|
||||
|
||||
var browser = new Browser(bootstrapper);
|
||||
var result = browser.Post("/userlogin", with =>
|
||||
{
|
||||
with.HttpRequest();
|
||||
with.Header("Accept", "application/json");
|
||||
with.FormValue("Username", string.Empty);
|
||||
});
|
||||
|
||||
Assert.That(HttpStatusCode.OK, Is.EqualTo(result.StatusCode));
|
||||
|
||||
var body = JsonConvert.DeserializeObject<JsonResponseModel>(result.Body.AsString());
|
||||
Assert.That(body.Result, Is.EqualTo(false));
|
||||
AuthMock.Verify(x => x.GetSettings(), Times.Never);
|
||||
PlexMock.Verify(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
PlexMock.Verify(x => x.GetUsers(It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LoginWithUsernameSuccessfully()
|
||||
{
|
||||
|
|
|
@ -76,6 +76,10 @@ namespace PlexRequests.UI
|
|||
container.Register<IConfigurationReader, ConfigurationReader>();
|
||||
container.Register<IIntervals, UpdateInterval>();
|
||||
|
||||
container.Register<ICouchPotatoApi, CouchPotatoApi>();
|
||||
|
||||
|
||||
|
||||
|
||||
container.Register<IPlexApi, PlexApi>();
|
||||
base.ConfigureRequestContainer(container, context);
|
||||
|
|
|
@ -13,6 +13,7 @@ var tvimer = 0;
|
|||
movieLoad();
|
||||
tvLoad();
|
||||
|
||||
// Approve all
|
||||
$('#approveAll').click(function () {
|
||||
$.ajax({
|
||||
type: 'post',
|
||||
|
@ -127,6 +128,34 @@ $(document).on("click", ".delete", function (e) {
|
|||
|
||||
});
|
||||
|
||||
// Approve single request
|
||||
$(document).on("click", ".approve", function (e) {
|
||||
e.preventDefault();
|
||||
var buttonId = e.target.id;
|
||||
var $form = $('#approve' + buttonId);
|
||||
|
||||
$.ajax({
|
||||
type: $form.prop('method'),
|
||||
url: $form.prop('action'),
|
||||
data: $form.serialize(),
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
|
||||
if (checkJsonResponse(response)) {
|
||||
generateNotify("Success! Request Approved.", "success");
|
||||
|
||||
$("button[custom-button='" + buttonId + "']").remove();
|
||||
$("#" + buttonId + "notapproved").prop("class", "fa fa-check");
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
console.log(e);
|
||||
generateNotify("Something went wrong!", "danger");
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Clear issues
|
||||
$(document).on("click", ".clear", function (e) {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -5,7 +5,13 @@
|
|||
message: message
|
||||
}, {
|
||||
// settings
|
||||
type: type
|
||||
type: type,
|
||||
animate: {
|
||||
enter: 'animated bounceInDown',
|
||||
exit: 'animated bounceOutUp'
|
||||
},
|
||||
newest_on_top: true
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,10 @@ using Nancy;
|
|||
using Nancy.Security;
|
||||
|
||||
using NLog;
|
||||
|
||||
using PlexRequests.Api;
|
||||
using PlexRequests.Api.Interfaces;
|
||||
using PlexRequests.Core;
|
||||
using PlexRequests.Core.SettingModels;
|
||||
using PlexRequests.Store;
|
||||
using PlexRequests.UI.Models;
|
||||
|
||||
|
@ -41,11 +44,13 @@ namespace PlexRequests.UI.Modules
|
|||
public class ApprovalModule : BaseModule
|
||||
{
|
||||
|
||||
public ApprovalModule(IRepository<RequestedModel> service) : base("approval")
|
||||
public ApprovalModule(IRepository<RequestedModel> service, ISettingsService<CouchPotatoSettings> cpService, ICouchPotatoApi cpApi) : base("approval")
|
||||
{
|
||||
this.RequiresAuthentication();
|
||||
|
||||
Service = service;
|
||||
CpService = cpService;
|
||||
CpApi = cpApi;
|
||||
|
||||
Post["/approve"] = parameters => Approve((int)Request.Form.requestid);
|
||||
Post["/approveall"] = x => ApproveAll();
|
||||
|
@ -54,6 +59,8 @@ namespace PlexRequests.UI.Modules
|
|||
private IRepository<RequestedModel> Service { get; set; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private ISettingsService<CouchPotatoSettings> CpService { get; }
|
||||
private ICouchPotatoApi CpApi { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Approves the specified request identifier.
|
||||
|
@ -62,6 +69,10 @@ namespace PlexRequests.UI.Modules
|
|||
/// <returns></returns>
|
||||
private Response Approve(int requestId)
|
||||
{
|
||||
if (!Context.CurrentUser.IsAuthenticated())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "You are not an Admin, so you cannot approve any requests." });
|
||||
}
|
||||
// Get the request from the DB
|
||||
var request = Service.Get(requestId);
|
||||
|
||||
|
@ -71,15 +82,59 @@ namespace PlexRequests.UI.Modules
|
|||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no requests to approve. Please refresh." });
|
||||
}
|
||||
|
||||
switch (request.Type)
|
||||
{
|
||||
case RequestType.Movie:
|
||||
return RequestMovieAndUpdateStatus(request);
|
||||
case RequestType.TvShow:
|
||||
return RequestTvAndUpdateStatus(request);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(request));
|
||||
}
|
||||
}
|
||||
|
||||
private Response RequestTvAndUpdateStatus(RequestedModel request)
|
||||
{
|
||||
// TODO
|
||||
return Response.AsJson(new JsonResponseModel());
|
||||
}
|
||||
|
||||
private Response RequestMovieAndUpdateStatus(RequestedModel request)
|
||||
{
|
||||
if (!Context.CurrentUser.IsAuthenticated())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "You are not an Admin, so you cannot approve any requests." });
|
||||
}
|
||||
|
||||
var cpSettings = CpService.GetSettings();
|
||||
var cp = new CouchPotatoApi();
|
||||
Log.Info("Adding movie to CP : {0}", request.Title);
|
||||
var result = cp.AddMovie(request.ImdbId, cpSettings.ApiKey, request.Title, cpSettings.FullUri);
|
||||
Log.Trace("Adding movie to CP result {0}", result);
|
||||
if (result)
|
||||
{
|
||||
// Approve it
|
||||
request.Approved = true;
|
||||
|
||||
// Update the record
|
||||
var result = Service.Update(request);
|
||||
var inserted = Service.Update(request);
|
||||
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false, Message = "We could not approve this request. Please try again or check the logs." });
|
||||
return Response.AsJson(inserted
|
||||
? new JsonResponseModel {Result = true}
|
||||
: new JsonResponseModel
|
||||
{
|
||||
Result = false,
|
||||
Message = "We could not approve this request. Please try again or check the logs."
|
||||
});
|
||||
}
|
||||
return
|
||||
Response.AsJson(
|
||||
new
|
||||
{
|
||||
Result = false,
|
||||
Message =
|
||||
"Something went wrong adding the movie to CouchPotato! Please check your settings."
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -88,19 +143,37 @@ namespace PlexRequests.UI.Modules
|
|||
/// <returns></returns>
|
||||
private Response ApproveAll()
|
||||
{
|
||||
var requests = Service.GetAll();
|
||||
var requests = Service.GetAll().Where(x => x.Approved == false);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no requests to approve. Please refresh." });
|
||||
}
|
||||
|
||||
var cpSettings = CpService.GetSettings();
|
||||
|
||||
|
||||
var updatedRequests = new List<RequestedModel>();
|
||||
foreach (var r in requestedModels)
|
||||
{
|
||||
if (r.Type == RequestType.Movie)
|
||||
{
|
||||
var result = SendMovie(cpSettings, r, CpApi);
|
||||
if (result)
|
||||
{
|
||||
r.Approved = true;
|
||||
updatedRequests.Add(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Could not approve send the movie {0} to couch potato!", r.Title);
|
||||
}
|
||||
}
|
||||
if (r.Type == RequestType.TvShow)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
|
||||
|
@ -116,5 +189,14 @@ namespace PlexRequests.UI.Modules
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private bool SendMovie(CouchPotatoSettings settings, RequestedModel r, ICouchPotatoApi cp)
|
||||
{
|
||||
Log.Info("Adding movie to CP : {0}", r.Title);
|
||||
var result = cp.AddMovie(r.ImdbId, settings.ApiKey, r.Title, settings.FullUri);
|
||||
Log.Trace("Adding movie to CP result {0}", result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ using Nancy.Responses.Negotiation;
|
|||
|
||||
using PlexRequests.Api.Interfaces;
|
||||
using PlexRequests.Api.Models;
|
||||
using PlexRequests.Api.Models.Plex;
|
||||
using PlexRequests.Core;
|
||||
using PlexRequests.Core.SettingModels;
|
||||
using PlexRequests.UI.Models;
|
||||
|
@ -61,10 +62,17 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
private Response LoginUser()
|
||||
{
|
||||
var username = Request.Form.username.Value;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Incorrect User or Password" });
|
||||
}
|
||||
|
||||
var authenticated = false;
|
||||
|
||||
var settings = AuthService.GetSettings();
|
||||
var username = Request.Form.username.Value;
|
||||
|
||||
|
||||
if (IsUserInDeniedList(username, settings))
|
||||
{
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
<h4>Below you can see yours and all other requests, as well as their download and approval status.</h4>
|
||||
@if (Context.CurrentUser.IsAuthenticated())
|
||||
{
|
||||
<button id="approveAll" class="btn btn-primary" type="submit"><i class="fa fa-plus"></i> Approve All</button>
|
||||
<button id="approveAll" class="btn btn-success" type="submit"><i class="fa fa-plus"></i> Approve All</button>
|
||||
<br/>
|
||||
<br/>
|
||||
}
|
||||
<!-- Nav tabs -->
|
||||
<ul id="nav-tabs" class="nav nav-tabs" role="tablist">
|
||||
|
@ -50,7 +52,7 @@
|
|||
|
||||
|
||||
<script id="search-template" type="text/x-handlebars-template">
|
||||
<div id="{{id}}Template">
|
||||
<div id="{{requestId}}Template">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
{{#if_eq type "movie"}}
|
||||
|
@ -76,7 +78,7 @@
|
|||
<p>
|
||||
Approved:
|
||||
{{#if_eq approved false}}
|
||||
<i class="fa fa-times"></i>
|
||||
<i id="{{requestId}}notapproved" class="fa fa-times"></i>
|
||||
{{/if_eq}}
|
||||
{{#if_eq approved true}}
|
||||
<i class="fa fa-check"></i>
|
||||
|
@ -105,6 +107,12 @@
|
|||
<br />
|
||||
<br />
|
||||
{{#if_eq admin true}}
|
||||
{{#if_eq approved false}}
|
||||
<form method="POST" action="/approval/approve" id="approve{{requestId}}">
|
||||
<input name="requestId" type="text" value="{{requestId}}" hidden="hidden" />
|
||||
<button id="{{requestId}}" custom-button="{{requestId}}" style="text-align: right" class="btn btn-success approve" type="submit"><i class="fa fa-plus"></i> Approve</button>
|
||||
</form>
|
||||
{{/if_eq}}
|
||||
<form method="POST" action="/requests/delete" id="delete{{requestId}}">
|
||||
<input name="Id" type="text" value="{{requestId}}" hidden="hidden" />
|
||||
<button id="{{requestId}}" style="text-align: right" class="btn btn-danger delete" type="submit"><i class="fa fa-plus"></i> Remove</button>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue