New: Added CAPTCHA support to Rarbg.
This commit is contained in:
parent
e9eab0ae48
commit
73fb216e6f
|
@ -81,7 +81,7 @@ namespace NzbDrone.Api.Extensions.Pipelines
|
||||||
{
|
{
|
||||||
if (request.Url.Query.IsNotNullOrWhiteSpace())
|
if (request.Url.Query.IsNotNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
return string.Concat(request.Url.Path, request.Url.Query);
|
return string.Concat(request.Url.Path, "?", request.Url.Query);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace NzbDrone.Api
|
||||||
|
|
||||||
Get["schema"] = x => GetTemplates();
|
Get["schema"] = x => GetTemplates();
|
||||||
Post["test"] = x => Test(ReadResourceFromRequest(true));
|
Post["test"] = x => Test(ReadResourceFromRequest(true));
|
||||||
Post["connectData/{stage}"] = x => ConnectData(x.stage, ReadResourceFromRequest(true));
|
Post["action/{action}"] = x => RequestAction(x.action, ReadResourceFromRequest(true));
|
||||||
|
|
||||||
GetResourceAll = GetAll;
|
GetResourceAll = GetAll;
|
||||||
GetResourceById = GetProviderById;
|
GetResourceById = GetProviderById;
|
||||||
|
@ -183,13 +183,13 @@ namespace NzbDrone.Api
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Response ConnectData(string stage, TProviderResource providerResource)
|
private Response RequestAction(string action, TProviderResource providerResource)
|
||||||
{
|
{
|
||||||
var providerDefinition = GetDefinition(providerResource, true, false);
|
var providerDefinition = GetDefinition(providerResource, true, false);
|
||||||
|
|
||||||
if (!providerDefinition.Enable) return "{}";
|
var query = ((IDictionary<string, object>)Request.Query.ToDictionary()).ToDictionary(k => k.Key, k => k.Value.ToString());
|
||||||
|
|
||||||
object data = _providerFactory.ConnectData(providerDefinition, stage, (IDictionary<string, object>) Request.Query.ToDictionary());
|
var data = _providerFactory.RequestAction(providerDefinition, action, query);
|
||||||
Response resp = JsonConvert.SerializeObject(data);
|
Response resp = JsonConvert.SerializeObject(data);
|
||||||
resp.ContentType = "application/json";
|
resp.ContentType = "application/json";
|
||||||
return resp;
|
return resp;
|
||||||
|
|
|
@ -104,7 +104,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException(string.Format("HttpCurl method {0} not supported", request.Method));
|
throw new NotSupportedException(string.Format("HttpCurl method {0} not supported", request.Method));
|
||||||
}
|
}
|
||||||
curlEasy.UserAgent = UserAgentBuilder.UserAgent;
|
curlEasy.UserAgent = request.UseSimplifiedUserAgent ? UserAgentBuilder.UserAgentSimplified : UserAgentBuilder.UserAgent; ;
|
||||||
curlEasy.FollowLocation = request.AllowAutoRedirect;
|
curlEasy.FollowLocation = request.AllowAutoRedirect;
|
||||||
|
|
||||||
if (request.RequestTimeout != TimeSpan.Zero)
|
if (request.RequestTimeout != TimeSpan.Zero)
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||||
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
||||||
|
|
||||||
webRequest.Method = request.Method.ToString();
|
webRequest.Method = request.Method.ToString();
|
||||||
webRequest.UserAgent = UserAgentBuilder.UserAgent;
|
webRequest.UserAgent = request.UseSimplifiedUserAgent ? UserAgentBuilder.UserAgentSimplified : UserAgentBuilder.UserAgent;
|
||||||
webRequest.KeepAlive = request.ConnectionKeepAlive;
|
webRequest.KeepAlive = request.ConnectionKeepAlive;
|
||||||
webRequest.AllowAutoRedirect = request.AllowAutoRedirect;
|
webRequest.AllowAutoRedirect = request.AllowAutoRedirect;
|
||||||
webRequest.CookieContainer = cookies;
|
webRequest.CookieContainer = cookies;
|
||||||
|
|
|
@ -34,6 +34,7 @@ namespace NzbDrone.Common.Http
|
||||||
public byte[] ContentData { get; set; }
|
public byte[] ContentData { get; set; }
|
||||||
public string ContentSummary { get; set; }
|
public string ContentSummary { get; set; }
|
||||||
public bool SuppressHttpError { get; set; }
|
public bool SuppressHttpError { get; set; }
|
||||||
|
public bool UseSimplifiedUserAgent { get; set; }
|
||||||
public bool AllowAutoRedirect { get; set; }
|
public bool AllowAutoRedirect { get; set; }
|
||||||
public bool ConnectionKeepAlive { get; set; }
|
public bool ConnectionKeepAlive { get; set; }
|
||||||
public bool LogResponseContent { get; set; }
|
public bool LogResponseContent { get; set; }
|
||||||
|
|
|
@ -19,6 +19,7 @@ namespace NzbDrone.Common.Http
|
||||||
public Dictionary<string, string> Segments { get; private set; }
|
public Dictionary<string, string> Segments { get; private set; }
|
||||||
public HttpHeader Headers { get; private set; }
|
public HttpHeader Headers { get; private set; }
|
||||||
public bool SuppressHttpError { get; set; }
|
public bool SuppressHttpError { get; set; }
|
||||||
|
public bool UseSimplifiedUserAgent { get; set; }
|
||||||
public bool AllowAutoRedirect { get; set; }
|
public bool AllowAutoRedirect { get; set; }
|
||||||
public bool ConnectionKeepAlive { get; set; }
|
public bool ConnectionKeepAlive { get; set; }
|
||||||
public bool LogResponseContent { get; set; }
|
public bool LogResponseContent { get; set; }
|
||||||
|
@ -99,6 +100,7 @@ namespace NzbDrone.Common.Http
|
||||||
{
|
{
|
||||||
request.Method = Method;
|
request.Method = Method;
|
||||||
request.SuppressHttpError = SuppressHttpError;
|
request.SuppressHttpError = SuppressHttpError;
|
||||||
|
request.UseSimplifiedUserAgent = UseSimplifiedUserAgent;
|
||||||
request.AllowAutoRedirect = AllowAutoRedirect;
|
request.AllowAutoRedirect = AllowAutoRedirect;
|
||||||
request.ConnectionKeepAlive = ConnectionKeepAlive;
|
request.ConnectionKeepAlive = ConnectionKeepAlive;
|
||||||
request.LogResponseContent = LogResponseContent;
|
request.LogResponseContent = LogResponseContent;
|
||||||
|
|
|
@ -6,12 +6,16 @@ namespace NzbDrone.Common.Http
|
||||||
public static class UserAgentBuilder
|
public static class UserAgentBuilder
|
||||||
{
|
{
|
||||||
public static string UserAgent { get; private set; }
|
public static string UserAgent { get; private set; }
|
||||||
|
public static string UserAgentSimplified { get; private set; }
|
||||||
|
|
||||||
static UserAgentBuilder()
|
static UserAgentBuilder()
|
||||||
{
|
{
|
||||||
UserAgent = string.Format("Sonarr/{0} ({1} {2}) ",
|
UserAgent = string.Format("Sonarr/{0} ({1} {2})",
|
||||||
BuildInfo.Version,
|
BuildInfo.Version,
|
||||||
OsInfo.Os, OsInfo.Version.ToString(2));
|
OsInfo.Os, OsInfo.Version.ToString(2));
|
||||||
|
|
||||||
|
UserAgentSimplified = string.Format("Sonarr/{0}",
|
||||||
|
BuildInfo.Version.ToString(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,6 +30,7 @@ namespace NzbDrone.Core.Annotations
|
||||||
Hidden,
|
Hidden,
|
||||||
Tag,
|
Tag,
|
||||||
Action,
|
Action,
|
||||||
Url
|
Url,
|
||||||
|
Captcha
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -50,7 +50,7 @@ namespace NzbDrone.Core.Download
|
||||||
|
|
||||||
public ProviderDefinition Definition { get; set; }
|
public ProviderDefinition Definition { get; set; }
|
||||||
|
|
||||||
public object ConnectData(string stage, IDictionary<string, object> query) { return null; }
|
public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; }
|
||||||
|
|
||||||
protected TSettings Settings
|
protected TSettings Settings
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
using NzbDrone.Common.Exceptions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Http.CloudFlare
|
||||||
|
{
|
||||||
|
public class CloudFlareCaptchaException : NzbDroneException
|
||||||
|
{
|
||||||
|
public HttpResponse Response { get; set; }
|
||||||
|
|
||||||
|
public CloudFlareCaptchaRequest CaptchaRequest { get; set; }
|
||||||
|
|
||||||
|
public CloudFlareCaptchaException(HttpResponse response, CloudFlareCaptchaRequest captchaRequest)
|
||||||
|
: base("Unable to access {0}, blocked by CloudFlare CAPTCHA. Likely due to shared-IP VPN.", response.Request.Url.Host)
|
||||||
|
{
|
||||||
|
Response = response;
|
||||||
|
CaptchaRequest = captchaRequest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Http.CloudFlare
|
||||||
|
{
|
||||||
|
public class CloudFlareCaptchaRequest
|
||||||
|
{
|
||||||
|
public string Host { get; set; }
|
||||||
|
public string SiteKey { get; set; }
|
||||||
|
|
||||||
|
public string Ray { get; set; }
|
||||||
|
public string SecretToken { get; set; }
|
||||||
|
|
||||||
|
public HttpUri ResponseUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Exceptions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Http.CloudFlare
|
||||||
|
{
|
||||||
|
public class CloudFlareHttpInterceptor : IHttpRequestInterceptor
|
||||||
|
{
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
private const string _cloudFlareChallengeScript = "cdn-cgi/scripts/cf.challenge.js";
|
||||||
|
private static readonly Regex _cloudFlareRegex = new Regex(@"data-ray=""(?<Ray>[\w-_]+)"".*?data-sitekey=""(?<SiteKey>[\w-_]+)"".*?data-stoken=""(?<SecretToken>[\w-_]+)""", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public CloudFlareHttpInterceptor(Logger logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpRequest PreRequest(HttpRequest request)
|
||||||
|
{
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpResponse PostResponse(HttpResponse response)
|
||||||
|
{
|
||||||
|
if (response.StatusCode == HttpStatusCode.Forbidden && response.Content.Contains(_cloudFlareChallengeScript))
|
||||||
|
{
|
||||||
|
_logger.Error("CloudFlare CAPTCHA block on {0}", response.Request.Url);
|
||||||
|
throw new CloudFlareCaptchaException(response, CreateCaptchaRequest(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CloudFlareCaptchaRequest CreateCaptchaRequest(HttpResponse response)
|
||||||
|
{
|
||||||
|
var match = _cloudFlareRegex.Match(response.Content);
|
||||||
|
|
||||||
|
if (!match.Success)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CloudFlareCaptchaRequest
|
||||||
|
{
|
||||||
|
Host = response.Request.Url.Host,
|
||||||
|
SiteKey = match.Groups["SiteKey"].Value,
|
||||||
|
Ray = match.Groups["Ray"].Value,
|
||||||
|
SecretToken = match.Groups["SecretToken"].Value,
|
||||||
|
ResponseUrl = response.Request.Url + new HttpUri("/cdn-cgi/l/chk_captcha")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Common.TPL;
|
using NzbDrone.Common.TPL;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Http.CloudFlare;
|
||||||
using NzbDrone.Core.Indexers.Exceptions;
|
using NzbDrone.Core.Indexers.Exceptions;
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
using NzbDrone.Core.Parser;
|
using NzbDrone.Core.Parser;
|
||||||
|
@ -21,7 +22,7 @@ namespace NzbDrone.Core.Indexers
|
||||||
{
|
{
|
||||||
protected const int MaxNumResultsPerQuery = 1000;
|
protected const int MaxNumResultsPerQuery = 1000;
|
||||||
|
|
||||||
private readonly IHttpClient _httpClient;
|
protected readonly IHttpClient _httpClient;
|
||||||
|
|
||||||
public override bool SupportsRss { get { return true; } }
|
public override bool SupportsRss { get { return true; } }
|
||||||
public override bool SupportsSearch { get { return true; } }
|
public override bool SupportsSearch { get { return true; } }
|
||||||
|
@ -313,6 +314,10 @@ namespace NzbDrone.Core.Indexers
|
||||||
{
|
{
|
||||||
_logger.Warn("Request limit reached");
|
_logger.Warn("Request limit reached");
|
||||||
}
|
}
|
||||||
|
catch (CloudFlareCaptchaException)
|
||||||
|
{
|
||||||
|
return new ValidationFailure("CaptchaToken", "Site protected by CloudFlare CAPTCHA. Valid CAPTCHA token required.");
|
||||||
|
}
|
||||||
catch (UnsupportedFeedException ex)
|
catch (UnsupportedFeedException ex)
|
||||||
{
|
{
|
||||||
_logger.Warn(ex, "Indexer feed is not supported");
|
_logger.Warn(ex, "Indexer feed is not supported");
|
||||||
|
|
|
@ -65,7 +65,8 @@ namespace NzbDrone.Core.Indexers
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual ProviderDefinition Definition { get; set; }
|
public virtual ProviderDefinition Definition { get; set; }
|
||||||
public object ConnectData(string stage, IDictionary<string, object> query) { return null; }
|
|
||||||
|
public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; }
|
||||||
|
|
||||||
protected TSettings Settings
|
protected TSettings Settings
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Exceptions;
|
||||||
|
using NzbDrone.Core.Http.CloudFlare;
|
||||||
using NzbDrone.Core.Parser;
|
using NzbDrone.Core.Parser;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Indexers.Rarbg
|
namespace NzbDrone.Core.Indexers.Rarbg
|
||||||
{
|
{
|
||||||
|
@ -30,5 +35,78 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||||
{
|
{
|
||||||
return new RarbgParser();
|
return new RarbgParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||||
|
{
|
||||||
|
if (action == "checkCaptcha")
|
||||||
|
{
|
||||||
|
Settings.Validate().Filter("BaseUrl").ThrowOnError();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new HttpRequestBuilder(Settings.BaseUrl.Trim('/'))
|
||||||
|
.Resource("/pubapi_v2.php?get_token=get_token")
|
||||||
|
.Accept(HttpAccept.Json)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_httpClient.Get(request);
|
||||||
|
}
|
||||||
|
catch (CloudFlareCaptchaException ex)
|
||||||
|
{
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
captchaRequest = new
|
||||||
|
{
|
||||||
|
host = ex.CaptchaRequest.Host,
|
||||||
|
ray = ex.CaptchaRequest.Ray,
|
||||||
|
siteKey = ex.CaptchaRequest.SiteKey,
|
||||||
|
secretToken = ex.CaptchaRequest.SecretToken,
|
||||||
|
responseUrl = ex.CaptchaRequest.ResponseUrl.FullUri,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
captchaToken = ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (action == "getCaptchaCookie")
|
||||||
|
{
|
||||||
|
if (query["responseUrl"].IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("QueryParam responseUrl invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query["ray"].IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("QueryParam ray invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query["captchaResponse"].IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("QueryParam captchaResponse invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new HttpRequestBuilder(query["responseUrl"])
|
||||||
|
.AddQueryParam("id", query["ray"])
|
||||||
|
.AddQueryParam("g-recaptcha-response", query["captchaResponse"])
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
request.UseSimplifiedUserAgent = true;
|
||||||
|
request.AllowAutoRedirect = false;
|
||||||
|
|
||||||
|
var response = _httpClient.Get(request);
|
||||||
|
|
||||||
|
var cfClearanceCookie = response.GetCookies()["cf_clearance"];
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
captchaToken = cfClearanceCookie
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new { };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -79,6 +79,12 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||||
.Resource("/pubapi_v2.php")
|
.Resource("/pubapi_v2.php")
|
||||||
.Accept(HttpAccept.Json);
|
.Accept(HttpAccept.Json);
|
||||||
|
|
||||||
|
if (Settings.CaptchaToken.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
requestBuilder.UseSimplifiedUserAgent = true;
|
||||||
|
requestBuilder.SetCookie("cf_clearance", Settings.CaptchaToken);
|
||||||
|
}
|
||||||
|
|
||||||
requestBuilder.AddQueryParam("mode", mode);
|
requestBuilder.AddQueryParam("mode", mode);
|
||||||
|
|
||||||
if (tvdbId.HasValue)
|
if (tvdbId.HasValue)
|
||||||
|
|
|
@ -30,6 +30,9 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||||
[FieldDefinition(1, Type = FieldType.Checkbox, Label = "Ranked Only", HelpText = "Only include ranked results.")]
|
[FieldDefinition(1, Type = FieldType.Checkbox, Label = "Ranked Only", HelpText = "Only include ranked results.")]
|
||||||
public bool RankedOnly { get; set; }
|
public bool RankedOnly { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Type = FieldType.Captcha, Label = "CAPTCHA Token", HelpText = "CAPTCHA Clearance token used to handle CloudFlare Anti-DDOS measures on shared-ip VPNs.")]
|
||||||
|
public string CaptchaToken { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Cache;
|
using NzbDrone.Common.Cache;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Indexers.Rarbg
|
namespace NzbDrone.Core.Indexers.Rarbg
|
||||||
|
@ -28,9 +29,17 @@ namespace NzbDrone.Core.Indexers.Rarbg
|
||||||
{
|
{
|
||||||
return _tokenCache.Get(settings.BaseUrl, () =>
|
return _tokenCache.Get(settings.BaseUrl, () =>
|
||||||
{
|
{
|
||||||
var url = settings.BaseUrl.Trim('/') + "/pubapi_v2.php?get_token=get_token";
|
var requestBuilder = new HttpRequestBuilder(settings.BaseUrl.Trim('/'))
|
||||||
|
.Resource("/pubapi_v2.php?get_token=get_token")
|
||||||
|
.Accept(HttpAccept.Json);
|
||||||
|
|
||||||
var response = _httpClient.Get<JObject>(new HttpRequest(url, HttpAccept.Json));
|
if (settings.CaptchaToken.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
requestBuilder.UseSimplifiedUserAgent = true;
|
||||||
|
requestBuilder.SetCookie("cf_clearance", settings.CaptchaToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = _httpClient.Get<JObject>(requestBuilder.Build());
|
||||||
|
|
||||||
return response.Resource["token"].ToString();
|
return response.Resource["token"].ToString();
|
||||||
}, TimeSpan.FromMinutes(14.0));
|
}, TimeSpan.FromMinutes(14.0));
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace NzbDrone.Core.Metadata
|
||||||
public abstract List<ImageFileResult> SeasonImages(Series series, Season season);
|
public abstract List<ImageFileResult> SeasonImages(Series series, Season season);
|
||||||
public abstract List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile);
|
public abstract List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile);
|
||||||
|
|
||||||
public object ConnectData(string stage, IDictionary<string, object> query) { return null; }
|
public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; }
|
||||||
|
|
||||||
protected TSettings Settings
|
protected TSettings Settings
|
||||||
{
|
{
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace NzbDrone.Core.Notifications
|
||||||
return GetType().Name;
|
return GetType().Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual object ConnectData(string stage, IDictionary<string, object> query) { return null; }
|
public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Twitter
|
||||||
|
{
|
||||||
|
public class OAuthToken
|
||||||
|
{
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
public string AccessTokenSecret { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Exceptions;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Twitter
|
namespace NzbDrone.Core.Notifications.Twitter
|
||||||
{
|
{
|
||||||
|
@ -34,26 +36,45 @@ namespace NzbDrone.Core.Notifications.Twitter
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override object ConnectData(string stage, IDictionary<string, object> query)
|
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||||
{
|
{
|
||||||
if (stage == "step1")
|
if (action == "startOAuth")
|
||||||
{
|
{
|
||||||
|
Settings.Validate().Filter("ConsumerKey", "ConsumerSecret").ThrowOnError();
|
||||||
|
|
||||||
|
if (query["callbackUrl"].IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("QueryParam callbackUrl invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var oauthRedirectUrl = _twitterService.GetOAuthRedirect(Settings.ConsumerKey, Settings.ConsumerSecret, query["callbackUrl"]);
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
nextStep = "step2",
|
oauthUrl = oauthRedirectUrl
|
||||||
action = "openWindow",
|
|
||||||
url = _twitterService.GetOAuthRedirect(query["consumerKey"].ToString(), query["consumerSecret"].ToString(), query["callbackUrl"].ToString())
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (stage == "step2")
|
else if (action == "getOAuthToken")
|
||||||
{
|
{
|
||||||
|
Settings.Validate().Filter("ConsumerKey", "ConsumerSecret").ThrowOnError();
|
||||||
|
|
||||||
|
if (query["oauth_token"].IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("QueryParam oauth_token invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query["oauth_verifier"].IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("QueryParam oauth_verifier invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var oauthToken = _twitterService.GetOAuthToken(Settings.ConsumerKey, Settings.ConsumerSecret, query["oauth_token"], query["oauth_verifier"]);
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
action = "updateFields",
|
accessToken = oauthToken.AccessToken,
|
||||||
fields = _twitterService.GetOAuthToken(query["consumerKey"].ToString(), query["consumerSecret"].ToString(), query["oauth_token"].ToString(), query["oauth_verifier"].ToString())
|
accessTokenSecret = oauthToken.AccessTokenSecret
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return new {};
|
return new { };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Name
|
public override string Name
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace NzbDrone.Core.Notifications.Twitter
|
||||||
void SendNotification(string message, TwitterSettings settings);
|
void SendNotification(string message, TwitterSettings settings);
|
||||||
ValidationFailure Test(TwitterSettings settings);
|
ValidationFailure Test(TwitterSettings settings);
|
||||||
string GetOAuthRedirect(string consumerKey, string consumerSecret, string callbackUrl);
|
string GetOAuthRedirect(string consumerKey, string consumerSecret, string callbackUrl);
|
||||||
object GetOAuthToken(string consumerKey, string consumerSecret, string oauthToken, string oauthVerifier);
|
OAuthToken GetOAuthToken(string consumerKey, string consumerSecret, string oauthToken, string oauthVerifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TwitterService : ITwitterService
|
public class TwitterService : ITwitterService
|
||||||
|
@ -43,14 +43,14 @@ namespace NzbDrone.Core.Notifications.Twitter
|
||||||
return HttpUtility.ParseQueryString(response.Content);
|
return HttpUtility.ParseQueryString(response.Content);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object GetOAuthToken(string consumerKey, string consumerSecret, string oauthToken, string oauthVerifier)
|
public OAuthToken GetOAuthToken(string consumerKey, string consumerSecret, string oauthToken, string oauthVerifier)
|
||||||
{
|
{
|
||||||
// Creating a new instance with a helper method
|
// Creating a new instance with a helper method
|
||||||
var oAuthRequest = OAuthRequest.ForAccessToken(consumerKey, consumerSecret, oauthToken, "", oauthVerifier);
|
var oAuthRequest = OAuthRequest.ForAccessToken(consumerKey, consumerSecret, oauthToken, "", oauthVerifier);
|
||||||
oAuthRequest.RequestUrl = "https://api.twitter.com/oauth/access_token";
|
oAuthRequest.RequestUrl = "https://api.twitter.com/oauth/access_token";
|
||||||
var qscoll = OAuthQuery(oAuthRequest);
|
var qscoll = OAuthQuery(oAuthRequest);
|
||||||
|
|
||||||
return new
|
return new OAuthToken
|
||||||
{
|
{
|
||||||
AccessToken = qscoll["oauth_token"],
|
AccessToken = qscoll["oauth_token"],
|
||||||
AccessTokenSecret = qscoll["oauth_token_secret"]
|
AccessTokenSecret = qscoll["oauth_token_secret"]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
@ -19,6 +20,10 @@ namespace NzbDrone.Core.Notifications.Twitter
|
||||||
RuleFor(c => c.DirectMessage).Equal(true)
|
RuleFor(c => c.DirectMessage).Equal(true)
|
||||||
.WithMessage("Using Direct Messaging is recommended, or use a private account.")
|
.WithMessage("Using Direct Messaging is recommended, or use a private account.")
|
||||||
.AsWarning();
|
.AsWarning();
|
||||||
|
|
||||||
|
RuleFor(c => c.AuthorizeNotification).Empty()
|
||||||
|
.When(c => c.AccessToken.IsNullOrWhiteSpace() || c.AccessTokenSecret.IsNullOrWhiteSpace())
|
||||||
|
.WithMessage("Authenticate app.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -527,6 +527,9 @@
|
||||||
<Compile Include="Housekeeping\HousekeepingCommand.cs" />
|
<Compile Include="Housekeeping\HousekeepingCommand.cs" />
|
||||||
<Compile Include="Housekeeping\HousekeepingService.cs" />
|
<Compile Include="Housekeeping\HousekeepingService.cs" />
|
||||||
<Compile Include="Housekeeping\IHousekeepingTask.cs" />
|
<Compile Include="Housekeeping\IHousekeepingTask.cs" />
|
||||||
|
<Compile Include="Http\CloudFlare\CloudFlareCaptchaException.cs" />
|
||||||
|
<Compile Include="Http\CloudFlare\CloudFlareCaptchaRequest.cs" />
|
||||||
|
<Compile Include="Http\CloudFlare\CloudFlareHttpInterceptor.cs" />
|
||||||
<Compile Include="Http\HttpProxySettingsProvider.cs" />
|
<Compile Include="Http\HttpProxySettingsProvider.cs" />
|
||||||
<Compile Include="Http\TorcacheHttpInterceptor.cs" />
|
<Compile Include="Http\TorcacheHttpInterceptor.cs" />
|
||||||
<Compile Include="Indexers\BitMeTv\BitMeTv.cs" />
|
<Compile Include="Indexers\BitMeTv\BitMeTv.cs" />
|
||||||
|
@ -810,6 +813,7 @@
|
||||||
<Compile Include="Notifications\Synology\SynologyIndexer.cs" />
|
<Compile Include="Notifications\Synology\SynologyIndexer.cs" />
|
||||||
<Compile Include="Notifications\Synology\SynologyIndexerProxy.cs" />
|
<Compile Include="Notifications\Synology\SynologyIndexerProxy.cs" />
|
||||||
<Compile Include="Notifications\Synology\SynologyIndexerSettings.cs" />
|
<Compile Include="Notifications\Synology\SynologyIndexerSettings.cs" />
|
||||||
|
<Compile Include="Notifications\Twitter\OAuthToken.cs" />
|
||||||
<Compile Include="Notifications\Twitter\TwitterException.cs" />
|
<Compile Include="Notifications\Twitter\TwitterException.cs" />
|
||||||
<Compile Include="Notifications\Webhook\WebhookEpisode.cs" />
|
<Compile Include="Notifications\Webhook\WebhookEpisode.cs" />
|
||||||
<Compile Include="Notifications\Webhook\WebhookException.cs" />
|
<Compile Include="Notifications\Webhook\WebhookException.cs" />
|
||||||
|
@ -1054,6 +1058,7 @@
|
||||||
<Compile Include="Validation\FolderValidator.cs" />
|
<Compile Include="Validation\FolderValidator.cs" />
|
||||||
<Compile Include="Validation\IpValidation.cs" />
|
<Compile Include="Validation\IpValidation.cs" />
|
||||||
<Compile Include="Validation\LanguageValidator.cs" />
|
<Compile Include="Validation\LanguageValidator.cs" />
|
||||||
|
<Compile Include="Validation\NzbDroneValidationExtensions.cs" />
|
||||||
<Compile Include="Validation\NzbDroneValidationFailure.cs" />
|
<Compile Include="Validation\NzbDroneValidationFailure.cs" />
|
||||||
<Compile Include="Validation\NzbDroneValidationResult.cs" />
|
<Compile Include="Validation\NzbDroneValidationResult.cs" />
|
||||||
<Compile Include="Validation\NzbDroneValidationState.cs" />
|
<Compile Include="Validation\NzbDroneValidationState.cs" />
|
||||||
|
|
|
@ -12,6 +12,6 @@ namespace NzbDrone.Core.ThingiProvider
|
||||||
IEnumerable<ProviderDefinition> DefaultDefinitions { get; }
|
IEnumerable<ProviderDefinition> DefaultDefinitions { get; }
|
||||||
ProviderDefinition Definition { get; set; }
|
ProviderDefinition Definition { get; set; }
|
||||||
ValidationResult Test();
|
ValidationResult Test();
|
||||||
object ConnectData(string stage, IDictionary<string, object> query);
|
object RequestAction(string stage, IDictionary<string, string> query);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,6 +20,6 @@ namespace NzbDrone.Core.ThingiProvider
|
||||||
void SetProviderCharacteristics(TProvider provider, TProviderDefinition definition);
|
void SetProviderCharacteristics(TProvider provider, TProviderDefinition definition);
|
||||||
TProvider GetInstance(TProviderDefinition definition);
|
TProvider GetInstance(TProviderDefinition definition);
|
||||||
ValidationResult Test(TProviderDefinition definition);
|
ValidationResult Test(TProviderDefinition definition);
|
||||||
object ConnectData(TProviderDefinition definition, string stage, IDictionary<string, object> query );
|
object RequestAction(TProviderDefinition definition, string action, IDictionary<string, string> query);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -81,9 +81,9 @@ namespace NzbDrone.Core.ThingiProvider
|
||||||
return GetInstance(definition).Test();
|
return GetInstance(definition).Test();
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConnectData(TProviderDefinition definition, string stage, IDictionary<string, object> query)
|
public object RequestAction(TProviderDefinition definition, string action, IDictionary<string, string> query)
|
||||||
{
|
{
|
||||||
return GetInstance(definition).ConnectData(stage, query);
|
return GetInstance(definition).RequestAction(action, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TProvider> GetAvailableProviders()
|
public List<TProvider> GetAvailableProviders()
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Validation
|
||||||
|
{
|
||||||
|
public static class NzbDroneValidationExtensions
|
||||||
|
{
|
||||||
|
public static NzbDroneValidationResult Filter(this NzbDroneValidationResult result, params string[] fields)
|
||||||
|
{
|
||||||
|
var failures = result.Failures.Where(v => fields.Contains(v.PropertyName)).ToArray();
|
||||||
|
|
||||||
|
return new NzbDroneValidationResult(failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ThrowOnError(this NzbDroneValidationResult result)
|
||||||
|
{
|
||||||
|
if (!result.IsValid)
|
||||||
|
{
|
||||||
|
throw new ValidationException(result.Errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,6 @@
|
||||||
<label class="col-sm-3 control-label"></label>
|
<label class="col-sm-3 control-label"></label>
|
||||||
|
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
<button class="form-control {{name}}" data-value="{{value}}">{{label}}</button>
|
<button class="form-control {{name}}" validation-name="{{name}}" data-value="{{value}}">{{label}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<div class="form-group {{#if advanced}}advanced-setting{{/if}}">
|
||||||
|
<label class="col-sm-3 control-label">{{label}}</label>
|
||||||
|
|
||||||
|
<div class="col-sm-5">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" name="fields.{{order}}.value" validation-name="{{name}}" spellcheck="false" class="form-control x-captcha" readonly />
|
||||||
|
<span class="input-group-btn"><button class="btn btn-primary x-captcha-refresh" title="Refresh CAPTCHA Token"><i class="icon-sonarr-refresh" /></button></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span class="col-sm-1 help-inline">
|
||||||
|
<i class="icon-sonarr-form-warning" title="Expires periodically and will need to be refreshed. Refreshing the CAPTCHA Token will embed a temporary Google reCaptcha widget on this page."/>
|
||||||
|
</span>
|
||||||
|
</div>
|
|
@ -49,6 +49,10 @@ var _fieldBuilder = function(field) {
|
||||||
return _templateRenderer.call(field, 'Form/ActionTemplate');
|
return _templateRenderer.call(field, 'Form/ActionTemplate');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (field.type === 'captcha') {
|
||||||
|
return _templateRenderer.call(field, 'Form/CaptchaTemplate');
|
||||||
|
}
|
||||||
|
|
||||||
return _templateRenderer.call(field, 'Form/TextboxTemplate');
|
return _templateRenderer.call(field, 'Form/TextboxTemplate');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
var _ = require('underscore');
|
||||||
|
var $ = require('jquery');
|
||||||
var vent = require('vent');
|
var vent = require('vent');
|
||||||
var Marionette = require('marionette');
|
var Marionette = require('marionette');
|
||||||
var DeleteView = require('../Delete/IndexerDeleteView');
|
var DeleteView = require('../Delete/IndexerDeleteView');
|
||||||
|
@ -12,7 +14,8 @@ var view = Marionette.ItemView.extend({
|
||||||
template : 'Settings/Indexers/Edit/IndexerEditViewTemplate',
|
template : 'Settings/Indexers/Edit/IndexerEditViewTemplate',
|
||||||
|
|
||||||
events : {
|
events : {
|
||||||
'click .x-back' : '_back'
|
'click .x-back' : '_back',
|
||||||
|
'click .x-captcha-refresh' : '_onRefreshCaptcha'
|
||||||
},
|
},
|
||||||
|
|
||||||
_deleteView : DeleteView,
|
_deleteView : DeleteView,
|
||||||
|
@ -38,6 +41,77 @@ var view = Marionette.ItemView.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
require('../Add/IndexerSchemaModal').open(this.targetCollection);
|
require('../Add/IndexerSchemaModal').open(this.targetCollection);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onRefreshCaptcha : function(event) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var target = $(event.target).parents('.input-group');
|
||||||
|
|
||||||
|
this.ui.indicator.show();
|
||||||
|
|
||||||
|
this.model.requestAction("checkCaptcha")
|
||||||
|
.then(function(result) {
|
||||||
|
if (!result.captchaRequest) {
|
||||||
|
self.model.setFieldValue('CaptchaToken', '');
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._showCaptcha(target, result.captchaRequest);
|
||||||
|
})
|
||||||
|
.always(function() {
|
||||||
|
self.ui.indicator.hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showCaptcha : function(target, captchaRequest) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var widget = $('<div class="g-recaptcha"></div>').insertAfter(target);
|
||||||
|
|
||||||
|
return this._loadRecaptchaWidget(widget[0], captchaRequest.siteKey, captchaRequest.secretToken)
|
||||||
|
.then(function(captchaResponse) {
|
||||||
|
target.parents('.form-group').removeAllErrors();
|
||||||
|
widget.remove();
|
||||||
|
|
||||||
|
var queryParams = {
|
||||||
|
responseUrl : captchaRequest.responseUrl,
|
||||||
|
ray : captchaRequest.ray,
|
||||||
|
captchaResponse: captchaResponse
|
||||||
|
};
|
||||||
|
|
||||||
|
return self.model.requestAction("getCaptchaCookie", queryParams);
|
||||||
|
})
|
||||||
|
.then(function(response) {
|
||||||
|
self.model.setFieldValue('CaptchaToken', response.captchaToken);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadRecaptchaWidget : function(widget, sitekey, stoken) {
|
||||||
|
var promise = $.Deferred();
|
||||||
|
|
||||||
|
var renderWidget = function() {
|
||||||
|
window.grecaptcha.render(widget, {
|
||||||
|
'sitekey' : sitekey,
|
||||||
|
'stoken' : stoken,
|
||||||
|
'callback' : promise.resolve
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (window.grecaptcha) {
|
||||||
|
renderWidget();
|
||||||
|
} else {
|
||||||
|
window.grecaptchaLoadCallback = function() {
|
||||||
|
delete window.grecaptchaLoadCallback;
|
||||||
|
renderWidget();
|
||||||
|
};
|
||||||
|
|
||||||
|
$.getScript('https://www.google.com/recaptcha/api.js?onload=grecaptchaLoadCallback&render=explicit')
|
||||||
|
.fail(function() { promise.reject(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
var $ = require('jquery');
|
||||||
var vent = require('vent');
|
var vent = require('vent');
|
||||||
var Marionette = require('marionette');
|
var Marionette = require('marionette');
|
||||||
var DeleteView = require('../Delete/NotificationDeleteView');
|
var DeleteView = require('../Delete/NotificationDeleteView');
|
||||||
|
@ -90,21 +91,45 @@ var view = Marionette.ItemView.extend({
|
||||||
this.ui.indicator.show();
|
this.ui.indicator.show();
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var callbackUrl = window.location.origin + '/oauth.html';
|
|
||||||
var fields = this.model.get('fields');
|
|
||||||
var consumerKeyObj = _.findWhere(fields, { name: 'ConsumerKey' });
|
|
||||||
var consumerSecretObj = _.findWhere(fields, { name: 'ConsumerSecret' });
|
|
||||||
var queryParams = {
|
|
||||||
callbackUrl: callbackUrl,
|
|
||||||
consumerKey: (consumerKeyObj ? consumerKeyObj.value : ''),
|
|
||||||
consumerSecret: (consumerSecretObj ? consumerSecretObj.value : '')
|
|
||||||
};
|
|
||||||
|
|
||||||
var promise = this.model.connectData(this.ui.authorizedNotificationButton.data('value'), queryParams);
|
var promise = this.model.requestAction('startOAuth', { callbackUrl: window.location.origin + '/oauth.html' })
|
||||||
|
.then(function(response) {
|
||||||
|
return self._showOAuthWindow(response.oauthUrl);
|
||||||
|
})
|
||||||
|
.then(function(responseQueryParams) {
|
||||||
|
return self.model.requestAction('getOAuthToken', responseQueryParams);
|
||||||
|
})
|
||||||
|
.then(function(response) {
|
||||||
|
self.model.setFieldValue('AccessToken', response.accessToken);
|
||||||
|
self.model.setFieldValue('AccessTokenSecret', response.accessTokenSecret);
|
||||||
|
});
|
||||||
|
|
||||||
promise.always(function() {
|
promise.always(function() {
|
||||||
self.ui.indicator.hide();
|
self.ui.indicator.hide();
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showOAuthWindow : function(oauthUrl) {
|
||||||
|
var promise = $.Deferred();
|
||||||
|
|
||||||
|
window.open(oauthUrl);
|
||||||
|
var selfWindow = window;
|
||||||
|
selfWindow.onCompleteOauth = function(query, callback) {
|
||||||
|
delete selfWindow.onCompleteOauth;
|
||||||
|
|
||||||
|
var queryParams = {};
|
||||||
|
var splitQuery = query.substring(1).split('&');
|
||||||
|
_.each(splitQuery, function (param) {
|
||||||
|
var paramSplit = param.split('=');
|
||||||
|
queryParams[paramSplit[0]] = paramSplit[1];
|
||||||
|
});
|
||||||
|
|
||||||
|
callback();
|
||||||
|
|
||||||
|
promise.resolve(queryParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,93 +4,38 @@ var DeepModel = require('backbone.deepmodel');
|
||||||
var Messenger = require('../Shared/Messenger');
|
var Messenger = require('../Shared/Messenger');
|
||||||
|
|
||||||
module.exports = DeepModel.extend({
|
module.exports = DeepModel.extend({
|
||||||
connectData : function(action, initialQueryParams) {
|
|
||||||
|
getFieldValue : function(name) {
|
||||||
|
var index = _.indexOf(_.pluck(this.get('fields'), 'name'), name);
|
||||||
|
return this.get('fields.' + index + '.value');
|
||||||
|
},
|
||||||
|
|
||||||
|
setFieldValue : function(name, value) {
|
||||||
|
var index = _.indexOf(_.pluck(this.get('fields'), 'name'), name);
|
||||||
|
return this.set('fields.' + index + '.value', value);
|
||||||
|
},
|
||||||
|
|
||||||
|
requestAction : function(action, queryParams) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.trigger('connect:sync');
|
this.trigger('validation:sync');
|
||||||
|
|
||||||
var promise = $.Deferred();
|
|
||||||
|
|
||||||
var callAction = function(action, queryParams) {
|
|
||||||
|
|
||||||
if (queryParams) {
|
|
||||||
action = action + '?' + $.param(queryParams, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var params = {
|
var params = {
|
||||||
url : self.collection.url + '/connectData/' + action,
|
url : this.collection.url + '/action/' + action,
|
||||||
contentType : 'application/json',
|
contentType : 'application/json',
|
||||||
data : JSON.stringify(self.toJSON()),
|
data : JSON.stringify(this.toJSON()),
|
||||||
type : 'POST',
|
type : 'POST',
|
||||||
isValidatedCall : true
|
isValidatedCall : true
|
||||||
};
|
};
|
||||||
|
|
||||||
var ajaxPromise = $.ajax(params);
|
if (queryParams) {
|
||||||
ajaxPromise.fail(promise.reject);
|
params.url += '?' + $.param(queryParams, true);
|
||||||
|
|
||||||
ajaxPromise.success(function(response) {
|
|
||||||
if (response.action)
|
|
||||||
{
|
|
||||||
if (response.action === 'openWindow')
|
|
||||||
{
|
|
||||||
window.open(response.url);
|
|
||||||
var selfWindow = window;
|
|
||||||
|
|
||||||
selfWindow.onCompleteOauth = function(query, callback) {
|
|
||||||
delete selfWindow.onCompleteOauth;
|
|
||||||
|
|
||||||
if (response.nextStep) {
|
|
||||||
var queryParams = {};
|
|
||||||
var splitQuery = query.substring(1).split('&');
|
|
||||||
|
|
||||||
_.each(splitQuery, function (param) {
|
|
||||||
var paramSplit = param.split('=');
|
|
||||||
queryParams[paramSplit[0]] = paramSplit[1];
|
|
||||||
});
|
|
||||||
|
|
||||||
callAction(response.nextStep, _.extend(initialQueryParams, queryParams));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
promise.resolve(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
var promise = $.ajax(params);
|
||||||
};
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (response.action === 'updateFields')
|
|
||||||
{
|
|
||||||
_.each(self.get('fields'), function (value, index) {
|
|
||||||
var fieldValue = _.find(response.fields, function (field, key) {
|
|
||||||
return key === value.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fieldValue) {
|
|
||||||
self.set('fields.' + index + '.value', fieldValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (response.nextStep) {
|
|
||||||
callAction(response.nextStep, initialQueryParams);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
promise.resolve(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
callAction(action, initialQueryParams);
|
|
||||||
|
|
||||||
Messenger.monitor({
|
|
||||||
promise : promise,
|
|
||||||
successMessage : 'Connecting for \'{0}\' succeeded'.format(this.get('name')),
|
|
||||||
errorMessage : 'Connecting for \'{0}\' failed'.format(this.get('name'))
|
|
||||||
});
|
|
||||||
|
|
||||||
promise.fail(function(response) {
|
promise.fail(function(response) {
|
||||||
self.trigger('connect:failed', response);
|
self.trigger('validation:failed', response);
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
|
|
|
@ -75,6 +75,8 @@ module.exports = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
$.fn.removeAllErrors = function() {
|
$.fn.removeAllErrors = function() {
|
||||||
|
this.removeClass('has-error');
|
||||||
|
this.removeClass('has-warning');
|
||||||
this.find('.has-error').removeClass('has-error');
|
this.find('.has-error').removeClass('has-error');
|
||||||
this.find('.has-warning').removeClass('has-warning');
|
this.find('.has-warning').removeClass('has-warning');
|
||||||
this.find('.error').removeClass('error');
|
this.find('.error').removeClass('error');
|
||||||
|
|
Loading…
Reference in New Issue