Skip to content

Commit

Permalink
Merge pull request #42 from empress/import-scripts
Browse files Browse the repository at this point in the history
Add Import scripts for Ghost, WordPress, and Tumblr
  • Loading branch information
mansona committed Apr 27, 2021
2 parents 6cbaed3 + 1292797 commit c9271b9
Show file tree
Hide file tree
Showing 9 changed files with 499 additions and 21 deletions.
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ npm init ember-app super-blog
cd super-blog

# you can replace the template with the one you want to use
npx ember-cli install empress-blog empress-blog-casper-template
npx ember install empress-blog empress-blog-casper-template
```

It will ask you if you want to update the `index.html` file and you should say yes 👍
Expand All @@ -62,7 +62,7 @@ This addon comes with helpful blueprints to generate posts and authors for you i
expects. The first thing you should do is generate an author as follows:

```sh
ember g author your-name
npx ember g author your-name
```

Then you should be able to edit the file `author/your-name.md` to update the details.
Expand All @@ -71,7 +71,7 @@ Next you will want to generate some posts. If you only have one author generated
blog) you can generate a post as simply as running:

```sh
ember g post "this is a post I want to write"
npx ember g post "this is a post I want to write"
```

### Configuring
Expand Down Expand Up @@ -150,6 +150,29 @@ and I'd be happy to help you. You can check out the documentation for
If you do end up writing an empress-blog template please let us know so I can
include your template in a list of existing templates.

Importing content
------------------------------------------------------------------------------

If you would like to import your blog content from another platform into
empress-blog we have a beta import function that we would love for people to try
out and give feedback. It requires that you create an export file from your
respective blog platform and then run the following command from the command
line:

```sh
npx ember empress-blog:import --type=[import_type] [dump_file]
```

Currently available `import_type`s are:
- [Ghost](https://ghost.org/faq/the-importer/)
- [WordPress](https://en.support.wordpress.com/export/)
- [Tumblr](https://tumblr.zendesk.com/hc/en-us/articles/360005118894-Export-your-blog)

**Note:** When exporting a Tumblr blog you will be downloading a zip file which
you need to unpack. Once you unpack your export zip file you will find a
`posts.zip` file which also needs to be unpacked. Your `dump_file` will be the
resulting `posts.xml` file.

Upgrade notes
------------------------------------------------------------------------------

Expand Down
6 changes: 6 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ const AutomaticNewTag = require('./lib/automatic-new-tag');
module.exports = {
name: require('./package').name,

includedCommands: function() {
return {
'empress-blog:import': require('./lib/import.js'),
}
},

config() {
return {
blog: {},
Expand Down
88 changes: 88 additions & 0 deletions lib/import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const { existsSync, readFileSync, writeFileSync, mkdirSync } = require('fs');
const { safeDump } = require('js-yaml');

const importers = require('./importers');

const supportedTypes = ['wordpress', 'ghost', 'tumblr'];

function ensureFolder(dir) {
if (!existsSync(dir)){
mkdirSync(dir);
}
}

function dump(data) {
return safeDump(data, { skipInvalid: true });
}

module.exports = {
name: 'empress-blog:import',
description: 'Imports data from other blog systems',
works: 'insideProject',

async run(commandOptions, rawArgs) {
if (!commandOptions.type || !supportedTypes.includes(commandOptions.type)) {
throw new Error(`You must run this command with the '--type=' parameter. Supported types are: ${supportedTypes}`)
}

const fileName = rawArgs[0];

if (!fileName) {
throw new Error('You must pass in the export file from your blog as the first argument');
}

if (!existsSync(fileName)) {
throw new Error(`File "${fileName}" does not exits`);
}

let data = await importers[commandOptions.type](readFileSync(fileName));

if(data.tags) {
ensureFolder('tag');

data.tags.forEach((tag) => {
writeFileSync(`tag/${tag.id}.md`, `---
${dump({
name: tag.name,
image: '',
})}---
${tag.description}`)
})
}

if(data.authors) {
ensureFolder('author');

data.authors.forEach((author) => {
writeFileSync(`author/${author.id}.md`, `---
${dump({
name: author.name,
id: author.id,
image: author.image,
coverImage: author.coverImage,
website: author.website,
twitter: author.twitter,
facebook: author.facebook,
location: author.location,
})}---
${author.bio}`)
})
}

if(data.content) {
ensureFolder('content');

data.content.forEach((content) => {
writeFileSync(`content/${content.id}.md`, `---
${dump({
title: content.title,
image: content.image,
authors: content.authors,
date: content.date,
tags: content.tags,
})}---
${content.content}`)
})
}
},
};
73 changes: 73 additions & 0 deletions lib/importers/ghost.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const _ = require('lodash');

module.exports = async function ghostImport(fileContents) {

const data = await JSON.parse(fileContents).db[0].data;

let authorMap = {};

let authors = data.users.map((author) => {
authorMap[author.id] = author.slug;

return {
id: author.slug,
name: author.name || '',
image: author.profile_image || '',
coverImage: author.cover_image || '',
website: author.website || '',
twitter: author.twitter || '',
facebook: author.facebook || '',
location: author.location || '',
bio: author.bio || '',
}
})

let tagMap = {};

let tags = data.tags.map((tag) => {
tagMap[tag.id] = tag.slug;

return {
id: tag.slug,
name: tag.name,
image: tag.feature_image || '',
description: tag.description || ''
}
});

let content = _.chain(data.posts)
.map((post) => {
// console.log(post);

// don't import draft posts
if(post.status !== 'published') {
return;
}

let result = {
id: post.slug,
title: post.title || '',
image: post.feature_image || '',
date: post.created_at,
content: post.plaintext,
tags: _.chain(data.posts_tags)
.filter(item => item.post_id === post.id)
.map(item => tagMap[item.tag_id])
.value(),
authors: _.chain(data.posts_authors)
.filter(item => item.post_id === post.id)
.map(item => authorMap[item.author_id])
.value(),
}

return result;
})
.compact()
.value();

return {
tags,
authors,
content,
}
}
2 changes: 2 additions & 0 deletions lib/importers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var requireDirectory = require('require-directory');
module.exports = requireDirectory(module);
72 changes: 72 additions & 0 deletions lib/importers/tumblr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const { promisify } = require("util");
const { parseString } = require("xml2js");
const _ = require("lodash");

const parse = promisify(parseString);

module.exports = async function tumblrImport(fileContents) {
const data = await parse(fileContents);

let channel = data.tumblr.posts[0];

let allAuthors = [];
let allTags = [];

let allContent = _.chain(channel.post)
.map(post => {
let postType = post.$.type;
let id = post.$.slug;
let date = new Date(post.$.date);
let author = post.$.tumblelog;
let tag = post.$.type;
let title, content;

switch (postType) {
case "regular":
title = post["regular-title"][0];
content = post["regular-body"][0];
break;
case "quote":
title = post["quote-text"][0];
content = post["quote-text"][0];
break;
case "conversation":
title = post["conversation-title"][0];
content = post["conversation-text"][0];
}

if (!allAuthors.find(existingAuthor => existingAuthor.id === author)) {
allAuthors.push({
id: author,
name: author
});
}

if (!allTags.find(existingTag => existingTag.id === tag)) {
allTags.push({
id: tag,
name: tag
});
}


let result = {
id,
title,
authors: [author],
date,
tags: [tag],
content
};

return result;
})
.compact()
.value();

return {
tags: allTags,
authors: allAuthors,
content: allContent
};
};
64 changes: 64 additions & 0 deletions lib/importers/wordpress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const { promisify } = require('util');
const { parseString } = require('xml2js');
const _ = require('lodash');

const parse = promisify(parseString);

module.exports = async function worpressImport(fileContents) {

const data = await parse(fileContents);

let channel = data.rss.channel[0];

let authors = channel['wp:author'].map((author) => {
return {
name: author['wp:author_display_name'][0].trim(),
id: author['wp:author_login'][0].trim(),
}
})

let postTags = {};

let content = _.chain(channel.item)
.map((post) => {
if(post['wp:post_type'][0].trim() !== 'post') {
return;
}

// don't import draft posts
if(post['wp:status'][0].trim() !== 'publish') {
return;
}

let result = {
id: post['wp:post_name'][0].trim(),
title: post.title[0],
authors: [post['dc:creator'][0].trim()],
date: (new Date(post['wp:post_date_gmt'][0])),
tags: (post.category || []).map((category) => {
// collect tags from the posts because wordpress exports don't quite
// include tags correctly
postTags[category.$.nicename] = category._.trim();
return category.$.nicename
}),
content: post['content:encoded'][0].trim(),
}

return result;
})
.compact()
.value();

let tags = _.map(postTags, (name, id) => {
return {
id,
name,
}
})

return {
tags,
authors,
content,
}
}
Loading

0 comments on commit c9271b9

Please sign in to comment.