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
11 changes: 11 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,14 @@ irb> JekyllImport::Importers.const_get(importer_class).run(options_hash)

jekyll-import has its own documentation site, found at https://import.jekyllrb.com.
Dedicated [documentation for each migrator](https://import.jekyllrb.com/docs/home/) is available there.

## Contributing

1. Make your changes to the appropriate importer file(s) in `lib/jekyll-import`
1. For local testing only, bump the version in lib/jekyll-import/version.rb according to [semantic versioning](https://semver.org/) rules. Let's call this new version x.y.z as a placeholder.
1. Run `gem build jekyll-import.gemspec` to build the gem
1. Run `gem install ./jekyll-import-x.y.z.gem` to install the gem globally on your machine
1. In the project that depends on jekyll-import, bump the version to x.y.z in your Gemfile.lock
1. In that same project, run `bundle install` to validate that updated dependency version
1. Again in that project, run `bundle info jekyll-import` to ensure that it is referencing the new version
1. Run jekyll-import as usual
1 change: 1 addition & 0 deletions lib/jekyll-import/importers/drupal_common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def process(options)
data["layout"] = post[:type]
data["name"] = post[:name]
data["mail"] = post[:mail]
data["nid"] = post[:nid]
title = data["title"] = post[:title].strip.force_encoding("UTF-8")
time = data["created"] = post[:created]

Expand Down
106 changes: 106 additions & 0 deletions script/drupal7-comment-migrator/0-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Migrating Old Drupal Comments to Jekyll

The contents of this folder will help you migrate and display comments from Drupal 7 (and maybe Drupal 8, YMMV) into Jekyll. Please note that this will only result in displaying old comments; If you need an interactive comments feature on your Jekyll site, consider adding a service such as [Disqus](https://disqus.com/).

## Prerequisites

1. You should have the ability to run queries against the source Drupal 7 database. (You would need this anyway in order to use jekyll-import!)
2. You should have Python3 installed, or access to a Python virtual environment.
1. You will need to install pyyaml like so: `pip install pyyaml`

## Instructions

1. Run `1-get-all-drupal-comments.sql` against the Drupal database.
2. Export all of those results into a file called `comments.csv`. Place that file in the root of your Jekyll project.
3. In your Jekyll project's `_config.yml`, add this line: `data_dir: _data`. If you already have a data_dir pointing somewhere else, you may need to modify the remaining scripts accordingly.
4. In the `_data` folder (or wherever your data_dir is), make a new folder called `comments`.
5. Run `2-generate-comments-yaml.py`, which will generate a `.yml` file for each post that has comments. The name of the file is the node id of the post.
6. Run `3-find-max-comment-depth.py` to determine the max comment depth of each post, as well as the max depth across all posts. This value will be important if, for reasons explained later, you cannot use recursion in your Liquid templates.
7. Now let's add some CSS to `assets/css/main.scss` to ensure our comments will look nice. You can change this later as you see fit:
```
.archived-comment-ul {
list-style-type: none;
}

.archived-comment-li {
border-left: 5px gray solid;
padding: 10px;
margin: 30px 0 10px 0;
background-color: #3e4459;
list-style-type: none;
}
```
8. In your `_layouts/post.html` file (or wherever your post page is), add a section that looks like this toward the bottom. You may need to change the HTML a bit to suit your needs.
```
<section class="page__content" itemprop="text">
{% assign nid = page.nid | append: "" %}
{% assign comments0 = site.data.comments[nid] | where: "pid", 0 %}

{% if comments0 and comments0 != empty %}
<ul class="archived-comment-ul">
{% for comment0 in comments0 %}
{% include archived-comments.html comment0=comment0 %}
{% endfor %}
</ul>
{% else %}
<p>No archived comments</p>
{% endif %}
</section>
```
9. Add a file called in the `_includes` folder called `archived-comments.html`. This file can be recursive if you are using Jekyll 4:
```
<li class="archived-comment-li" style="margin-left: {{ level | times: 20 }}px">
<p><strong>{{ comment.name }}</strong> commented on {{ comment.date | date: "%Y-%m-%d" }}:</p>
<div><p>{{ comment.body | markdownify }}</p></div>
{% assign cid = comment.cid | append: "" %}
{% assign repliesToComment = comment.replies | where: "pid", cid %}
{% if repliesToComment and repliesToComment != empty %}
<ul class="archived-comment-ul">
{% assign levelPlusOne = level | plus: 1 %}
{% for replyToComment in repliesToComment %}
{% render "archived-comments.html" comment=replyToComment level=levelPlusOne %}
{% endfor %}
</ul>
{% endif %}
</li>
```
However, if you're using Jekyll 3 or if you encounter a "Nesting too deep included" error, you will not be able to use recursion. This means your `archived-comments.html` file will have to handle the maximum possible comment depth. The following is an example that allows for a max depth of 3. It's not ideal, but it is what it is:
```
<!-- LEVEL 0 START ------------------------------------------>
<li class="archived-comment-li">
<p><strong>{{ comment0.name }}</strong> commented on {{ comment0.date | date: "%Y-%m-%d" }}:</p>
<div><p>{{ comment0.body | markdownify }}</p></div>
{% assign cid0 = comment0.cid | append: "" %}
{% assign repliesTo0 = comment0.replies | where: "pid", cid0 %}
{% if repliesTo0 and repliesTo0 != empty %}
<ul class="archived-comment-ul">
{% for replyTo0 in repliesTo0 %}
<!-- LEVEL 1 START ------------------------------------------>
<li class="archived-comment-li">
<p><strong>{{ replyTo0.name }}</strong> replied on {{ replyTo0.date | date: "%Y-%m-%d" }}:</p>
<div><p>{{ replyTo0.body | markdownify }}</p></div>
{% assign cid1 = replyTo0.cid | append: "" %}
{% assign repliesTo1 = replyTo0.replies | where: "pid", cid1 %}
{% if repliesTo1 and repliesTo1 != empty %}
<ul class="archived-comment-ul">
{% for replyTo1 in repliesTo1 %}
<!-- LEVEL 2 START ------------------------------------------>
<li class="archived-comment-li">
<p><strong>{{ replyTo1.name }}</strong> replied on {{ replyTo1.date | date: "%Y-%m-%d" }}:</p>
<div><p>{{ replyTo1.body | markdownify }}</p></div>
<!-- ******* IF MAX DEPTH IS 3, SO WE DON'T NEED TO NEST FURTHER ******* -->
</li>
<!-- LEVEL 2 END -------------------------------------------->
{% endfor %}
</ul>
{% endif %}
</li>
<!-- LEVEL 1 END -------------------------------------------->
{% endfor %}
</ul>
{% endif %}
</li>
<!-- LEVEL 0 END -------------------------------------------->
```
10. Rebuild / serve the Jekyll site. If all goes well, you'll end up with posts and comments that look something like this:
![Finished Example](4-finished-example.png)
4 changes: 4 additions & 0 deletions script/drupal7-comment-migrator/1-get-all-drupal-comments.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SELECT c.cid, c.nid, c.pid, c.name, c.created, cb.comment_body_value
FROM comment c
JOIN field_data_comment_body cb ON c.cid = cb.entity_id
ORDER BY c.nid, c.created;
52 changes: 52 additions & 0 deletions script/drupal7-comment-migrator/2-generate-comments-yaml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import csv
import os
import yaml
from datetime import datetime
from collections import defaultdict

INPUT_CSV = 'comments.csv' # Adjust if your CSV is named differently
OUTPUT_DIR = '_data/comments/'

# Ensure output directory exists
os.makedirs(OUTPUT_DIR, exist_ok=True)

# Read and group comments by nid (Drupal node ID)
comments_by_nid = defaultdict(list)

with open(INPUT_CSV, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
try:
comment = {
'cid': int(row['cid']),
'pid': int(row['pid']),
'name': row['name'] or 'Anonymous',
'date': datetime.utcfromtimestamp(int(row['created'])).isoformat(),
'body': row['comment_body_value'].strip(),
'replies': []
}
nid = row['nid']
comments_by_nid[nid].append(comment)
except Exception as e:
print(f"Error processing row {row}: {e}")

# Nest replies by cid/pid
for nid, comments in comments_by_nid.items():
by_cid = {c['cid']: c for c in comments}
top_level = []

for comment in comments:
if comment['pid'] == 0:
top_level.append(comment)
elif comment['pid'] in by_cid:
by_cid[comment['pid']]['replies'].append(comment)
else:
# Orphaned reply — treat as top-level
top_level.append(comment)

# Write YAML file for this post
output_path = os.path.join(OUTPUT_DIR, f'{nid}.yml')
with open(output_path, 'w', encoding='utf-8') as f:
yaml.dump(top_level, f, allow_unicode=True, sort_keys=False)

print(f"✅ Finished writing {len(comments_by_nid)} YAML files to {OUTPUT_DIR}")
31 changes: 31 additions & 0 deletions script/drupal7-comment-migrator/3-find-max-comment-depth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os
import yaml

def get_max_depth(comment, current_depth=1):
if not comment.get('replies'):
return current_depth
return max(get_max_depth(reply, current_depth + 1) for reply in comment['replies'])

def max_depth_in_file(filepath):
with open(filepath, 'r') as f:
data = yaml.safe_load(f)
if not isinstance(data, list):
return 0
return max(get_max_depth(comment) for comment in data)

def overall_max_depth(directory):
max_depth = 0
for filename in os.listdir(directory):
if filename.endswith('.yml') or filename.endswith('.yaml'):
path = os.path.join(directory, filename)
try:
file_max = max_depth_in_file(path)
print(f"{filename}: max depth = {file_max}")
max_depth = max(max_depth, file_max)
except Exception as e:
print(f"Error processing {filename}: {e}")
return max_depth

# Change this path to your actual _data/comments directory
directory_path = '_data/comments'
print(f"\nOverall max depth: {overall_max_depth(directory_path)}")
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.