7 Commits

11 changed files with 93 additions and 88 deletions

1
.gitattributes vendored
View File

@@ -2,6 +2,7 @@
# Set default behavior to automatically normalize line endings. # Set default behavior to automatically normalize line endings.
############################################################################### ###############################################################################
* text=auto * text=auto
*.sqlce text eol=crlf
############################################################################### ###############################################################################
# Set default behavior for command prompt diff. # Set default behavior for command prompt diff.

View File

@@ -44,13 +44,13 @@ namespace FeedCenter.FeedParsers
if (node.Attributes == null) if (node.Attributes == null)
break; break;
XmlNode relNode = node.Attributes["rel"]; XmlNode relNode = GetAttribute(node, "rel");
if (relNode != null) if (relNode != null)
rel = relNode.InnerText; rel = relNode.InnerText;
if (string.IsNullOrEmpty(rel) || rel == "alternate") if (string.IsNullOrEmpty(rel) || rel == "alternate")
Feed.Link = node.Attributes["href"].InnerText.Trim(); Feed.Link = GetAttribute(node, "href").InnerText.Trim();
break; break;
@@ -103,14 +103,14 @@ namespace FeedCenter.FeedParsers
if (childNode.Attributes == null) if (childNode.Attributes == null)
break; break;
XmlNode relNode = childNode.Attributes["rel"]; XmlNode relNode = GetAttribute(childNode, "rel");
if (relNode != null) if (relNode != null)
rel = relNode.InnerText.Trim(); rel = relNode.InnerText.Trim();
if (string.IsNullOrEmpty(rel) || rel == "alternate") if (string.IsNullOrEmpty(rel) || rel == "alternate")
{ {
var link = childNode.Attributes["href"].InnerText; var link = GetAttribute(childNode, "href").InnerText;
if (link.StartsWith("/")) if (link.StartsWith("/"))
{ {
@@ -131,5 +131,13 @@ namespace FeedCenter.FeedParsers
return feedItem; return feedItem;
} }
private static XmlAttribute GetAttribute(XmlNode node, string attributeName)
{
if (node?.Attributes == null)
return null;
return node.Attributes[attributeName, node.NamespaceURI] ?? node.Attributes[attributeName];
}
} }
} }

View File

@@ -1,7 +1,4 @@
using Common.Debug; using System;
using Common.Xml;
using FeedCenter.FeedParsers;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -9,7 +6,12 @@ using System.Net;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Common.Debug;
using Common.Update; using Common.Update;
using Common.Xml;
using FeedCenter.Data;
using FeedCenter.FeedParsers;
using FeedCenter.Properties;
namespace FeedCenter namespace FeedCenter
{ {
@@ -55,6 +57,25 @@ namespace FeedCenter
public partial class Feed public partial class Feed
{ {
// ReSharper disable once UnusedMember.Global
public string LastReadResultDescription
{
get
{
// Cast the last read result to the proper enum
var lastReadResult = LastReadResult;
// Build the name of the resource using the enum name and the value
var resourceName = $"{typeof(FeedReadResult).Name}_{lastReadResult}";
// Try to get the value from the resources
var resourceValue = Resources.ResourceManager.GetString(resourceName);
// Return the value or just the enum value if not found
return resourceValue ?? lastReadResult.ToString();
}
}
public static Feed Create(FeedCenterEntities database) public static Feed Create(FeedCenterEntities database)
{ {
return new Feed { ID = Guid.NewGuid(), CategoryID = database.DefaultCategory.ID }; return new Feed { ID = Guid.NewGuid(), CategoryID = database.DefaultCategory.ID };
@@ -75,6 +96,7 @@ namespace FeedCenter
case FeedReadResult.NotDue: case FeedReadResult.NotDue:
case FeedReadResult.NotEnabled: case FeedReadResult.NotEnabled:
case FeedReadResult.NotModified: case FeedReadResult.NotModified:
// Ignore // Ignore
break; break;
@@ -86,7 +108,7 @@ namespace FeedCenter
} }
// If the feed was successfully read and we have no last update timestamp - set the last update timestamp to now // If the feed was successfully read and we have no last update timestamp - set the last update timestamp to now
if (result == FeedReadResult.Success && LastUpdated == Data.Extensions.SqlDateTimeZero.Value) if (result == FeedReadResult.Success && LastUpdated == Extensions.SqlDateTimeZero.Value)
LastUpdated = DateTime.Now; LastUpdated = DateTime.Now;
Tracer.DecrementIndentLevel(); Tracer.DecrementIndentLevel();
@@ -120,13 +142,10 @@ namespace FeedCenter
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
// Create the web request // Create the web request
var oRequest = WebRequest.Create(new Uri(Source)); var request = WebRequest.Create(new Uri(Source));
// Attempt to cast to a web request
var webRequest = oRequest as HttpWebRequest;
// If this is an http request set some special properties // If this is an http request set some special properties
if (webRequest != null) if (request is HttpWebRequest webRequest)
{ {
// Make sure to use HTTP version 1.1 // Make sure to use HTTP version 1.1
webRequest.ProtocolVersion = HttpVersion.Version11; webRequest.ProtocolVersion = HttpVersion.Version11;
@@ -137,50 +156,56 @@ namespace FeedCenter
// Set a timeout // Set a timeout
webRequest.Timeout = 10000; webRequest.Timeout = 10000;
// Make sure the service point closes the connection right away
webRequest.ServicePoint.ConnectionLeaseTimeout = 0;
// If we need to authenticate then set the credentials // If we need to authenticate then set the credentials
if (Authenticate) if (Authenticate)
webRequest.Credentials = new NetworkCredential(Username, Password, Domain); webRequest.Credentials = new NetworkCredential(Username, Password, Domain);
// Set a user agent string // Set a user agent string
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.DefaultUserAgent)) if (string.IsNullOrWhiteSpace(Settings.Default.DefaultUserAgent))
webRequest.UserAgent = "FeedCenter/" + UpdateCheck.LocalVersion; webRequest.UserAgent = "FeedCenter/" + UpdateCheck.LocalVersion;
else else
webRequest.UserAgent = Properties.Settings.Default.DefaultUserAgent; webRequest.UserAgent = Settings.Default.DefaultUserAgent;
} }
// Set the default encoding // Set the default encoding
var encoding = Encoding.UTF8; var encoding = Encoding.UTF8;
// Attempt to get the response // Attempt to get the response
var response = (HttpWebResponse) oRequest.GetResponse(); using (var response = (HttpWebResponse) request.GetResponse())
{
// If the response included an encoding then change the encoding
if (response.ContentEncoding.Length > 0)
encoding = Encoding.GetEncoding(response.ContentEncoding);
// If the response included an encoding then change the encoding // Get the response stream
if (response.ContentEncoding.Length > 0) using (var responseStream = response.GetResponseStream())
encoding = Encoding.GetEncoding(response.ContentEncoding); {
if (responseStream == null)
return Tuple.Create(FeedReadResult.NoResponse, string.Empty);
// Get the response stream // Create the text reader
var responseStream = response.GetResponseStream(); using (StreamReader textReader = new XmlSanitizingStream(responseStream, encoding))
{
// Get the feed text
var feedText = textReader.ReadToEnd();
if (responseStream == null) // Get rid of any leading and trailing whitespace
return Tuple.Create(FeedReadResult.NoResponse, string.Empty); feedText = feedText.Trim();
// Create the text reader // Clean up common invalid XML characters
StreamReader textReader = new XmlSanitizingStream(responseStream, encoding); feedText = feedText.Replace(" ", " ");
// Get the feed text // Find ampersands that aren't properly escaped and replace them with escaped versions
var feedText = textReader.ReadToEnd(); var r = new Regex("&(?!(?:[a-z]+|#[0-9]+|#x[0-9a-f]+);)");
feedText = r.Replace(feedText, "&");
// Get rid of any leading and trailing whitespace return Tuple.Create(FeedReadResult.Success, feedText);
feedText = feedText.Trim(); }
}
// Clean up common invalid XML characters }
feedText = feedText.Replace(" ", " ");
// Find ampersands that aren't properly escaped and replace them with escaped versions
var r = new Regex("&(?!(?:[a-z]+|#[0-9]+|#x[0-9a-f]+);)");
feedText = r.Replace(feedText, "&");
return Tuple.Create(FeedReadResult.Success, feedText);
} }
catch (IOException ioException) catch (IOException ioException)
{ {
@@ -192,23 +217,25 @@ namespace FeedCenter
{ {
var result = FeedReadResult.UnknownError; var result = FeedReadResult.UnknownError;
var errorResponse = webException.Response as HttpWebResponse; if (webException.Response is HttpWebResponse errorResponse)
if (errorResponse != null)
{ {
switch (errorResponse.StatusCode) switch (errorResponse.StatusCode)
{ {
case HttpStatusCode.InternalServerError: case HttpStatusCode.InternalServerError:
return Tuple.Create(FeedReadResult.ServerError, string.Empty); return Tuple.Create(FeedReadResult.ServerError, string.Empty);
case HttpStatusCode.NotModified: case HttpStatusCode.NotModified:
return Tuple.Create(FeedReadResult.NotModified, string.Empty); return Tuple.Create(FeedReadResult.NotModified, string.Empty);
case HttpStatusCode.NotFound: case HttpStatusCode.NotFound:
return Tuple.Create(FeedReadResult.NotFound, string.Empty); return Tuple.Create(FeedReadResult.NotFound, string.Empty);
case HttpStatusCode.Unauthorized: case HttpStatusCode.Unauthorized:
case HttpStatusCode.Forbidden: case HttpStatusCode.Forbidden:
return Tuple.Create(FeedReadResult.Unauthorized, string.Empty); return Tuple.Create(FeedReadResult.Unauthorized, string.Empty);
} }
} }
@@ -218,10 +245,12 @@ namespace FeedCenter
case WebExceptionStatus.ConnectFailure: case WebExceptionStatus.ConnectFailure:
case WebExceptionStatus.NameResolutionFailure: case WebExceptionStatus.NameResolutionFailure:
result = FeedReadResult.ConnectionFailed; result = FeedReadResult.ConnectionFailed;
break; break;
case WebExceptionStatus.Timeout: case WebExceptionStatus.Timeout:
result = FeedReadResult.Timeout; result = FeedReadResult.Timeout;
break; break;
} }
@@ -321,7 +350,7 @@ namespace FeedCenter
private void ProcessActions() private void ProcessActions()
{ {
var sortedActions = from action in Actions orderby action.Sequence ascending select action; var sortedActions = from action in Actions orderby action.Sequence select action;
foreach (var feedAction in sortedActions) foreach (var feedAction in sortedActions)
{ {
@@ -329,30 +358,12 @@ namespace FeedCenter
{ {
case 0: case 0:
Title = Title.Replace(feedAction.Search, feedAction.Replace); Title = Title.Replace(feedAction.Search, feedAction.Replace);
break; break;
} }
} }
} }
#endregion #endregion
// ReSharper disable once UnusedMember.Global
public string LastReadResultDescription
{
get
{
// Cast the last read result to the proper enum
var lastReadResult = LastReadResult;
// Build the name of the resource using the enum name and the value
var resourceName = $"{typeof(FeedReadResult).Name}_{lastReadResult}";
// Try to get the value from the resources
var resourceValue = Properties.Resources.ResourceManager.GetString(resourceName);
// Return the value or just the enum value if not found
return resourceValue ?? lastReadResult.ToString();
}
}
} }
} }

View File

@@ -9,8 +9,8 @@ namespace FeedCenter
private static void InitializeUpdate() private static void InitializeUpdate()
{ {
UpdateCheck.ApplicationName = Properties.Resources.ApplicationDisplayName; UpdateCheck.ApplicationName = Properties.Resources.ApplicationDisplayName;
UpdateCheck.UpdateServerType = ServerType.GitHub;
UpdateCheck.UpdateServer = Settings.Default.VersionLocation; UpdateCheck.UpdateServer = Settings.Default.VersionLocation;
UpdateCheck.UpdateFile = Settings.Default.VersionFile;
UpdateCheck.ApplicationShutdown = ApplicationShutdown; UpdateCheck.ApplicationShutdown = ApplicationShutdown;
UpdateCheck.ApplicationCurrentMessage = ApplicationCurrentMessage; UpdateCheck.ApplicationCurrentMessage = ApplicationCurrentMessage;
UpdateCheck.ApplicationUpdateMessage = ApplicationUpdateMessage; UpdateCheck.ApplicationUpdateMessage = ApplicationUpdateMessage;

View File

@@ -12,7 +12,7 @@ namespace FeedCenter.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.6.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -262,16 +262,7 @@ namespace FeedCenter.Properties {
[global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("FeedCenterSetup.xml")] [global::System.Configuration.DefaultSettingValueAttribute("https://api.github.com/repos/ckaczor/FeedCenter/releases/latest")]
public string VersionFile {
get {
return ((string)(this["VersionFile"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://server/FeedCenter/")]
public string VersionLocation { public string VersionLocation {
get { get {
return ((string)(this["VersionLocation"])); return ((string)(this["VersionLocation"]));

View File

@@ -62,11 +62,8 @@
<Setting Name="MultipleLineDisplay" Provider="Common.Settings.GenericSettingsProvider" Type="FeedCenter.Options.MultipleLineDisplay" Scope="User"> <Setting Name="MultipleLineDisplay" Provider="Common.Settings.GenericSettingsProvider" Type="FeedCenter.Options.MultipleLineDisplay" Scope="User">
<Value Profile="(Default)">Normal</Value> <Value Profile="(Default)">Normal</Value>
</Setting> </Setting>
<Setting Name="VersionFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">FeedCenterSetup.xml</Value>
</Setting>
<Setting Name="VersionLocation" Type="System.String" Scope="Application"> <Setting Name="VersionLocation" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://server/FeedCenter/</Value> <Value Profile="(Default)">https://api.github.com/repos/ckaczor/FeedCenter/releases/latest</Value>
</Setting> </Setting>
<Setting Name="LastCategoryID" Type="System.String" Scope="User"> <Setting Name="LastCategoryID" Type="System.String" Scope="User">
<Value Profile="(Default)" /> <Value Profile="(Default)" />

View File

@@ -82,11 +82,8 @@
<setting name="VersionCheckInterval" serializeAs="String"> <setting name="VersionCheckInterval" serializeAs="String">
<value>01:00:00</value> <value>01:00:00</value>
</setting> </setting>
<setting name="VersionFile" serializeAs="String">
<value>FeedCenterSetup.xml</value>
</setting>
<setting name="VersionLocation" serializeAs="String"> <setting name="VersionLocation" serializeAs="String">
<value>http://server/FeedCenter/</value> <value>https://api.github.com/repos/ckaczor/FeedCenter/releases/latest</value>
</setting> </setting>
</FeedCenter.Properties.Settings> </FeedCenter.Properties.Settings>
</applicationSettings> </applicationSettings>

View File

@@ -61,9 +61,7 @@
</ItemGroup> </ItemGroup>
<Import Project="$(WixTargetsPath)" /> <Import Project="$(WixTargetsPath)" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent>rem D:\Code\Personal\CreateInstallDescriptor\bin\Debug\CreateInstallDescriptor.exe !(TargetPath) $(TargetDir)\$(TargetName).xml <PostBuildEvent />
rem copy $(TargetDir)\$(TargetName).xml \\server\d\FeedCenter
rem copy $(TargetDir)\$(TargetName).exe \\server\d\FeedCenter</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- <!--
To modify your build process, add your task inside one of the targets below and uncomment it. To modify your build process, add your task inside one of the targets below and uncomment it.

2
Common

Submodule Common updated: 81ef8f451c...686f33982d

View File

@@ -177,6 +177,8 @@
<File Source="$(var.FeedCenter.TargetDir)\HtmlAgilityPack.dll" /> <File Source="$(var.FeedCenter.TargetDir)\HtmlAgilityPack.dll" />
<File Source="$(var.FeedCenter.TargetDir)\Newtonsoft.Json.dll" />
<File Source="$(var.FeedCenter.TargetDir)\EntityFramework.dll" /> <File Source="$(var.FeedCenter.TargetDir)\EntityFramework.dll" />
<File Source="$(var.FeedCenter.TargetDir)\EntityFramework.SqlServer.dll" /> <File Source="$(var.FeedCenter.TargetDir)\EntityFramework.SqlServer.dll" />
<File Source="$(var.FeedCenter.TargetDir)\EntityFramework.SqlServerCompact.dll" /> <File Source="$(var.FeedCenter.TargetDir)\EntityFramework.SqlServerCompact.dll" />

View File

@@ -1,4 +1,4 @@
version: 1.0.{build} version: 1.0.0.{build}
pull_requests: pull_requests:
do_not_increment_build_number: true do_not_increment_build_number: true
skip_tags: true skip_tags: true