diff --git a/README.markdown b/README.markdown index 14d7f389..d3fdd414 100644 --- a/README.markdown +++ b/README.markdown @@ -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 \ No newline at end of file diff --git a/lib/jekyll-import/importers/drupal_common.rb b/lib/jekyll-import/importers/drupal_common.rb index 80fb4156..f2e4c6a6 100644 --- a/lib/jekyll-import/importers/drupal_common.rb +++ b/lib/jekyll-import/importers/drupal_common.rb @@ -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] diff --git a/script/drupal7-comment-migrator/0-README.md b/script/drupal7-comment-migrator/0-README.md new file mode 100644 index 00000000..e262d969 --- /dev/null +++ b/script/drupal7-comment-migrator/0-README.md @@ -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. +``` +
+ {% assign nid = page.nid | append: "" %} + {% assign comments0 = site.data.comments[nid] | where: "pid", 0 %} + + {% if comments0 and comments0 != empty %} + + {% else %} +

No archived comments

+ {% endif %} +
+``` +9. Add a file called in the `_includes` folder called `archived-comments.html`. This file can be recursive if you are using Jekyll 4: +``` +
  • +

    {{ comment.name }} commented on {{ comment.date | date: "%Y-%m-%d" }}:

    +

    {{ comment.body | markdownify }}

    + {% assign cid = comment.cid | append: "" %} + {% assign repliesToComment = comment.replies | where: "pid", cid %} + {% if repliesToComment and repliesToComment != empty %} + + {% endif %} +
  • +``` +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: +``` + +
  • +

    {{ comment0.name }} commented on {{ comment0.date | date: "%Y-%m-%d" }}:

    +

    {{ comment0.body | markdownify }}

    + {% assign cid0 = comment0.cid | append: "" %} + {% assign repliesTo0 = comment0.replies | where: "pid", cid0 %} + {% if repliesTo0 and repliesTo0 != empty %} + + {% endif %} +
  • + +``` +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) \ No newline at end of file diff --git a/script/drupal7-comment-migrator/1-get-all-drupal-comments.sql b/script/drupal7-comment-migrator/1-get-all-drupal-comments.sql new file mode 100644 index 00000000..4cc4dafb --- /dev/null +++ b/script/drupal7-comment-migrator/1-get-all-drupal-comments.sql @@ -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; \ No newline at end of file diff --git a/script/drupal7-comment-migrator/2-generate-comments-yaml.py b/script/drupal7-comment-migrator/2-generate-comments-yaml.py new file mode 100644 index 00000000..36f02560 --- /dev/null +++ b/script/drupal7-comment-migrator/2-generate-comments-yaml.py @@ -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}") diff --git a/script/drupal7-comment-migrator/3-find-max-comment-depth.py b/script/drupal7-comment-migrator/3-find-max-comment-depth.py new file mode 100644 index 00000000..c6a7e617 --- /dev/null +++ b/script/drupal7-comment-migrator/3-find-max-comment-depth.py @@ -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)}") diff --git a/script/drupal7-comment-migrator/4-finished-example.png b/script/drupal7-comment-migrator/4-finished-example.png new file mode 100644 index 00000000..f1a71a8f Binary files /dev/null and b/script/drupal7-comment-migrator/4-finished-example.png differ