Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
dotnet-version: '7.0.x'
- name: NuGet Restore
run: dotnet restore
- name: Build
Expand Down
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<Project>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup>
<PackageOutputPath>$(MSBuildThisFileDirectory)_artifacts</PackageOutputPath>
<Authors>Aaron Bockover</Authors>
<Copyright>Copyright 2020-2021 Aaron Bockover</Copyright>
<Copyright>Copyright 2020-2023 Aaron Bockover</Copyright>
<RepositoryUrl>https://github.com/abock/goodbye-wordpress</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageTags>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<PackageDescription>
$(PackageDescription)

Note, there is a .NET 6.0 library ("Goodbye.WordPress") that
Note, there is a .NET 7.0 library ("Goodbye.WordPress") that
can be integrated easily into a new .NET console application that
allows heavy customization through overriding pieces of the export
pipeline in WordPressExporterDelegate.
Expand All @@ -23,5 +23,7 @@

<ItemGroup>
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" PrivateAssets="all" />
<PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[5.0.0]" />
</ItemGroup>
</Project>
74 changes: 74 additions & 0 deletions Goodbye.WordPress.Example/JsonConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
namespace WPExportApp {
using System;
using System.Threading.Tasks;

/// <summary>
/// Credentials Related
/// Provides
/// - Credentials
/// - Additional options i.e., for SSL
/// - Patterns for substitution/pre-processing content
/// </summary>
class JsonConfig {
public WPExpJsonConfig? Options { get; set; }

/// <summary>
/// Config file path
/// </summary>
private string JsonConfigFilePath { get; set; }

/// <remarks>
/// <see href="https://docs.microsoft.com/en-us/dotnet/api/system.environment.specialfolder">
/// Accessing Local App Data via Environment
/// </see>
/// </remarks>
public JsonConfig() {
JsonConfigFilePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
+ @"\WPExportConfig.json";

if (!System.IO.File.Exists(JsonConfigFilePath)) {
throw new InvalidOperationException($"Required config: {JsonConfigFilePath} not found!" +
"Please create the config file and run this application again.");
}
}

public class ReplacePattern {
public string Needle { get; set; }
public string Substitute { get; set; }
}

/// <summary>
/// Structure to read records from config file (json format)
/// Self explanatory props
/// </summary>
public class WPExpJsonConfig {
public string Host { get; set; }
public string Database { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string TlsVersion { get; set; }
public string ContentOutputDirectory { get; set; }
public string ArchiveOutputFilePath { get; set; }
/// <summary>
/// Some of the patterns might be internal only and should not be published online
/// Therefore, it might be good idea to keep them in the json config file.
/// </summary>
public ReplacePattern[] Patterns { get; set; }
}


/// <summary>
/// Read and Parse Config file
/// </summary>
public async Task Load() {
using System.IO.FileStream openStream = System.IO.File.OpenRead(JsonConfigFilePath);
Options = await System.Text.Json.JsonSerializer.DeserializeAsync<WPExpJsonConfig>(openStream);

if (Options == null || string.IsNullOrEmpty(Options.Host) || string.IsNullOrEmpty(
Options.Database) || string.IsNullOrEmpty(Options.Username) || string.IsNullOrEmpty(
Options.Password)) {
throw new NullReferenceException("Database Credentials are empty in Json config file!");
}
}
}
}
107 changes: 74 additions & 33 deletions Goodbye.WordPress.Example/Program.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,82 @@
using Goodbye.WordPress;
namespace WPExportApp {
using System.Threading.Tasks;
using Goodbye.WordPress;

var exporter = WordPressExporter.Create(
postReader: new MysqlPostReader(new ConnectionStringBuilder
class WPExportMain {
/// <summary>
/// Entry Point
/// </summary>
/// <param name="args">CLA</param>
static async Task Main(string[] args)
{
var config = new JsonConfig();
await config.Load();
if (config.Options == null)
throw new System.NullReferenceException();

var exporter = WordPressExporter.Create(
postReader: new MysqlPostReader(
new ConnectionStringBuilder {
Host = config.Options.Host,
Database = config.Options.Database,
Username = config.Options.Username,
Password = config.Options.Password
// , TlsVersion = config.Options.TlsVersion
}),
contentOutputDirectory: config.Options.ContentOutputDirectory,
archiveOutputFilePath: config.Options.ArchiveOutputFilePath,
// And now the delegate...
@delegate: new CustomExporterDelegate(config.Options.Patterns)
);

await exporter.ExportAsync();
}
}


sealed class CustomExporterDelegate : WordPressExporterDelegate
{
Host = "localhost",
Username = "user",
Password = "***",
Database = "wordpressdb"
}),
contentOutputDirectory: "exported-posts",
archiveOutputFilePath: "exported-posts/archive.json",

// And now the delegate...
@delegate: new CustomExporterDelegate());

await exporter.ExportAsync();

sealed class CustomExporterDelegate : WordPressExporterDelegate
{
/// <summary>Process post contents</summary>
public override Post ProcessPost(
WordPressExporter exporter,
Post post)
JsonConfig.ReplacePattern[] StrPatterns;

public CustomExporterDelegate(JsonConfig.ReplacePattern[] patterns)
{
StrPatterns = patterns;
}

// Replace weird unicode chars
private string SubstituteCommon(string str) =>
str.Replace("‘", "'").Replace("’", "'")
.Replace("“", "\"").Replace("”", "\"");

private string ProcessContent(string content) {
content = SubstituteCommon(content)
.Replace("http://", "https://");

if (StrPatterns != null && StrPatterns.Length > 0)
foreach( var pattern in StrPatterns)
content = content.Replace(pattern.Needle, pattern.Substitute);

return content;
}

/// <summary>Process post contents</summary>
public override Post ProcessPost(
WordPressExporter exporter,
Post post)
// Perform the default post processing first by calling base
=> base.ProcessPost(exporter, post) with
{
// Then replace '--' with Unicode em dash '—'
Content = post.Content.Replace("--", "—")
Content = ProcessContent(post.Content)
};

/// <summary>Add 'CustomMetadata' to each post's YAML front matter</summary>
public override void PopulatePostYamlFrontMatter(
WordPressExporter exporter,
Post post,
SharpYaml.Serialization.YamlMappingNode rootNode)
{
base.PopulatePostYamlFrontMatter(exporter, post, rootNode);
rootNode.Add("CustomMetadata", "Some Value");
/// <summary>Add 'CustomMetadata' to each post's YAML front matter</summary>
public override void PopulatePostYamlFrontMatter(
WordPressExporter exporter,
Post post,
SharpYaml.Serialization.YamlMappingNode rootNode)
{
base.PopulatePostYamlFrontMatter(exporter, post, rootNode);
// rootNode.Add("CustomMetadata", "Some Value");
}
}
}
}
24 changes: 24 additions & 0 deletions Goodbye.WordPress.Example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Example Json Config
Default config path: "$Env:LocalAppData\WPExportConfig.json"

For example, `C:\Users\UserName\AppData\Local\WPExportConfig.json`

An example config looks like following,

{
"Host": "localhost",
"Username": "user",
"Password": "your_password",
"Database": "wordpress_db_name",
"ContentOutputDirectory": "posts",
"ArchiveOutputFilePath": "posts/archive.json",
"Patterns": [
{
"Needle": "old-domain.com",
"Substitute": "new-domain.com"
}
]
}


For example without json config, please have a look at [primary ReadMe](https://github.com/abock/goodbye-wordpress#example-programcs).
10 changes: 5 additions & 5 deletions Goodbye.WordPress/Goodbye.WordPress.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MySqlConnector" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="ReverseMarkdown" Version="3.20.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1-dev-00879" />
<PackageReference Include="SharpYaml" Version="1.8.0" />
<PackageReference Include="MySqlConnector" Version="2.3.0-beta.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="ReverseMarkdown" Version="3.24.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.1-dev-00907" />
<PackageReference Include="SharpYaml" Version="2.1.0" />
</ItemGroup>
</Project>
105 changes: 73 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,45 +87,86 @@ Note that with the exception of `ConnectionStringBuilder` and `WordPressExporter
Creating the exporter via API here is equivalent to the command line invocation above, with the exception of providing a custom exporter delegate that will perform additional processing steps.

```csharp
using Goodbye.WordPress;
namespace WPExportApp {
using System.Threading.Tasks;
using Goodbye.WordPress;

class WPExportMain {
/// <summary>
/// Entry Point
/// </summary>
/// <param name="args">CLA</param>
static async Task Main(string[] args)
{
var config = new JsonConfig();
await config.Load();
if (config.Options == null)
throw new System.NullReferenceException();

var exporter = WordPressExporter.Create(
postReader: new MysqlPostReader(
new ConnectionStringBuilder {
Host = config.Options.Host,
Database = config.Options.Database,
Username = config.Options.Username,
Password = config.Options.Password
// , TlsVersion = config.Options.TlsVersion
}),
contentOutputDirectory: config.Options.ContentOutputDirectory,
archiveOutputFilePath: config.Options.ArchiveOutputFilePath,
// And now the delegate...
@delegate: new CustomExporterDelegate(config.Options.Patterns)
);

await exporter.ExportAsync();
}
}

var exporter = WordPressExporter.Create(
postReader: new MysqlPostReader(new ConnectionStringBuilder

sealed class CustomExporterDelegate : WordPressExporterDelegate
{
Host = "localhost",
Username = "user",
Password = "***",
Database = "wordpressdb"
}),
contentOutputDirectory: "exported-posts",
archiveOutputFilePath: "exported-posts/archive.json",

// And now the delegate...
@delegate: new CustomExporterDelegate());

await exporter.ExportAsync();

sealed class CustomExporterDelegate : WordPressExporterDelegate
{
/// <summary>Process post contents</summary>
public override Post ProcessPost(
WordPressExporter exporter,
Post post)
JsonConfig.ReplacePattern[] StrPatterns;

public CustomExporterDelegate(JsonConfig.ReplacePattern[] patterns)
{
StrPatterns = patterns;
}

// Replace weird unicode chars
private string SubstituteCommon(string str) =>
str.Replace("‘", "'").Replace("’", "'")
.Replace("“", "\"").Replace("”", "\"");

private string ProcessContent(string content) {
content = SubstituteCommon(content)
.Replace("http://", "https://");

if (StrPatterns != null && StrPatterns.Length > 0)
foreach( var pattern in StrPatterns)
content = content.Replace(pattern.Needle, pattern.Substitute);

return content;
}

/// <summary>Process post contents</summary>
public override Post ProcessPost(
WordPressExporter exporter,
Post post)
// Perform the default post processing first by calling base
=> base.ProcessPost(exporter, post) with
{
// Then replace '--' with Unicode em dash '—'
Content = post.Content.Replace("--", "—")
Content = ProcessContent(post.Content)
};

/// <summary>Add 'CustomMetadata' to each post's YAML front matter</summary>
public override void PopulatePostYamlFrontMatter(
WordPressExporter exporter,
Post post,
SharpYaml.Serialization.YamlMappingNode rootNode)
{
base.PopulatePostYamlFrontMatter(exporter, post, rootNode);
rootNode.Add("CustomMetadata", "Some Value");
/// <summary>Add 'CustomMetadata' to each post's YAML front matter</summary>
public override void PopulatePostYamlFrontMatter(
WordPressExporter exporter,
Post post,
SharpYaml.Serialization.YamlMappingNode rootNode)
{
base.PopulatePostYamlFrontMatter(exporter, post, rootNode);
// rootNode.Add("CustomMetadata", "Some Value");
}
}
}
```