read

Since I write my blog posts in Markdown format I decided I wanted a way to backup my posts in a way that is human readable. The result is a feature in Texo to export my blog entries as zipped up Markdown files.

Screenshot of Export Interface

When I click on the button in the above screenshot all my blog entries are saved as Markdown files, zipped up, and presented to me as a download. The final zip is organized into folders of YEAR/MONTH.

Export Zip File

The code to do this is pretty simple. First I have a function that constructs the Markdown given a post dictionary.

def generateMarkdownFile(post):
   result = """Title: %s
Date: %s
Author: %s
Status: %s
Tags: %s
Slug: %s

%s""" % (
         post["title"],
         post["publishedDateTime"],
         post["author"],
         post["status"],
         post["tagList"],
         post["slug"],
         post["content"],
      )

   filename = os.path.join(config.UPLOAD_PATH, post["slug"]) + ".md"

   with open(filename, "w") as blogFile:
      blogFile.write(result)

   return filename

This function writes out a file to my temporary upload path with the post data converted to a format that Texo actually knows how to import. It is simple and human readable. The next part was to write a controller action to get my posts, write Markdown files, then zip them up and serve. This method is a bit big and needs a bit of refactoring, but it does the job for now.

@route("/admin/utilities/exportmarkdownfiles", method="GET")
@route("/admin/utilities/exportmarkdownfiles", method="POST")
@view("admin-export-markdown-files.html")
@requireSession
def adminExportMarkdownFiles():
   logger = logging.getLogger(__name__)

   if "btnExport" in request.all:
      posts = postservice.getAllPosts()
      zipfilePath = os.path.join(config.UPLOAD_PATH, "blog-posts.zip")

      zf = zipfile.ZipFile(zipfilePath, mode="w")
      filenames = []

      try:
         #
         # Write each post to a file, adding each file to a ZIP
         #
         for post in posts:
            filename = postservice.generateMarkdownFile(post=post)
            filenames.append(filename)

            writtenFilename = "%s/%s/%s" % (post["publishedYear"], post["publishedMonth"], os.path.basename(filename),)
            zf.write(filename, compress_type=compression, arcname=writtenFilename)

      except Exception as e:
         logger.error("There was an error writing zipfile: %s", e.message)

      finally:
         zf.close()

      #
      # Clean out markdown files
      #
      for filename in filenames:
         try:
            os.remove(filename)
         except Exception as e:
            logger.error("Unable to remove %s" % (filename,))

      #
      # Serve up the ZIP file as a download
      #
      return static_file(os.path.basename(zipfilePath), root=config.UPLOAD_PATH)

   return {
      "title": "Export Markdown Files"
   }

Basically this method starts up a zipfile output, gets all posts, and creates Markdown files for each post, adding to the zip file after each Markdown file is created. At the end I clean out all the generated Markdown files and serve the ZIP file back as a static download.

Also, as a final note, I added a fun easter-egg to my site. Try out the old Konami code on the home page. :) Cheers!

Blog Logo

Adam Presley


Published

Image

Adam.Blog()

Adam Presley's blog

Back to Overview