Org2Blog Developer Documentation.
- About
- Loading Up The Source Code By Hand
- Software Packages Used
- Getting Started With Development
- Package Questions And Answers
- MetaWeblog
- Data Model & Functional Design
Here is where to start:
The constant org2blog-def--package
helps manage Org2Blog’s dependencies. Here are helper functions to quickly get you started:
org2blog-def-checkout-statement
creates Git commands to get the code from GitHub into the directory you want it stored. Call it and input the directory name. Finally copy and paste the commands from*Messages*
into your Terminal to run them.org2blog-def-load-statement
creates Elisp code to load the libraries. Call it and input the directory name. Copy and paste the commands from*Messages*
into your init file. Finally run them. Now they will get loaded each time you start Emacs.
For example, you should see something like this:
cd ~/tmp
git clone https://github.com/hniksic/emacs-htmlize.git ~/tmp/htmlize
cd htmlize
git checkout release/1.56
cd ..
And this
(add-to-list 'load-path "/Users/gcr/mysrc/htmlize")
Note: the correct tag of the project is loaded automatically.
Now you’ve got everything you need to run Org2Blog against its source code.
With this set up you can start playing around with things and even making changes. When you scratch an itch: create your branch and submit a pull request. It’s fun, easy, and makes Org2Blog better for all its users.
Org2Blog uses these libraries:
(Source code for this image follows)
@startmindmap
!theme plain
caption Read Left To Right
title Referenced Libraries
header
Org2Blog Docs
endheader
center footer Org2Blog DEVELOPMENT
* org2blog
left side
** Internal
*** org
*** ox-wp
*** subr-x
** External
*** htmlize
*** hydra
*** xml-rpc
*** metaweblog
@endmindmap
- You may have already set up your codebase to run Org2Blog, but if you haven’t, then find out how up in the Installation section.
- Readme
- If you made any changes in the README then rebuild the Table of Contents just to be sure it’s correct. Follow the directions here.
- Contributing
- Read the contributing guidelines.
- Before your commit make sure that
byte-compile-file
,checkdoc
, andpackage-lint-current-buffer
don’t report any errors. The first two are included with Emacs.package-lint
you can either install using MELPA or you can also install it by hand like you did the other packages, like this:cd ~/src git clone https://github.com/purcell/package-lint.git
Use this code to load it:
(add-to-list 'load-path "~/src/package-lint") (require 'package-lint)
- Note: Org2Blog uses non-standard separators in its naming. The naming will address it in a future release.
error: `org2blog/wp-version' contains a non-standard separator `/', use hyphens instead (see Elisp Coding Conventions).
- Testing
- Programmatic Interactive System Testing
- Working with posts and pages is the most critical 80% of this package. This core functionality should always work well and be easy to test. And it’s easy to test. It only takes 3 steps to get the system tests running.
- Define three system variables for the blog you will test against like this:
O2BXMLRPC="https://yourblog.com/xmlrpc.php" O2BUSER="user" O2BPASSWORD="password"
- Load and evaluate System Test Program.
- Start Emacs in an empty environment before loading Org2Blog and performing the testing by starting Emacs like this:
emacs --no-init-file
- Load (or open and evalute it) it because it’s not a package.
- Start Emacs in an empty environment before loading Org2Blog and performing the testing by starting Emacs like this:
- Now you’ve got everything you need to start automatically going through the entire blogging process. The test functions will log you in, create and display posts, modify them, publish them, and finally trash them. At each step, there is a pause so you can observe what is happening on the blog. Testing is a great way to see how the workflow works, too, if you’ve never blogged before. These four functions cover everything.
defun org2blog--test-buffer-post
defun org2blog--test-buffer-page
defun org2blog--test-subtree-post
defun org2blog--test-subtree-page
- Define three system variables for the blog you will test against like this:
- If you need a test WordPress system to use you can set up a free WordPress blog here.
- Working with posts and pages is the most critical 80% of this package. This core functionality should always work well and be easy to test. And it’s easy to test. It only takes 3 steps to get the system tests running.
- Manual System Testing
- Here is a detailed Test Plan for manually testing every feature of this system. It’s a great way to see everything that can be done with Org2Blog.
- Programmatic Interactive System Testing
- Release Process.
- Rules
In some ways Org2Blog can be surprising. Since it bridges that gap between Org mode documents and WordPress blog posts sometimes there can be a little friction. That is where most of the questions come from in the form of something like “Why does Org2Blog …fill in the blank…? Because it’s really weird!”. Be at ease though, this section should clear up some the weirdness ASAP.
Most software out there has some version of Create, read, update and delete (CRUD). In our case it has to do with WordPress Entries and Pages. In techie language you would talk about CRUD’ing them. In WordPress language you talk about Saving, Viewing, Publishing, and Trashing. Org2Blog chose to use the WordPress language: it’s less surprising and makes it easier to keep the idea that Org2Blog fits into your WordPress workflow in your mind.
Take time to learn that workflow outside of Org2Blog. It will save you from uncomfortable situations where your Entry enters a weird state. At least it can feel weird. For example when you make changes to an Entry and save it, it will enter the Status of Draft
. From here you only have two options to move it back to a Published state: Save the changes you made, or Save it without any changes. If you’ve never encountered this before it can be upsetting when the URL for your Entry always says preview=true
. Whenever you get into a confusing situation be sure to access your blog inside of the WordPress UI to find out more about what is happening. Usually it’s something really simple. Then step back and see what Org2Blog is doing within the WordPress workflow.
Those words are also used because they reflect the natural workflow of working with WordPress that looks like this:
⮎Save → View → Publish⮌ Trash⁉
Blogging with WordPress is an iterative workflow, going through the cycle as many times as desired. Org2Blog supports and facilitates this workflow very well. This workflow is so important in fact that the entire right side of the main menu is dedicated to realizing it.
WordPress doesn’t see much difference between a Post
and a Page
, so Org2Blog doesn’t either. Here are some terms to help clarify things:
- Blog is shorthand for
Web Log
- Every post you make on your blog is called an
Entry
- Org2Blog stores
Entries
in either aBuffer
or aSubtree
- Every
Entry
can be either aPost
or aPage
Here is how to visualize it remembering this is supposed to make it easier to make sense of how Org2Blog works behind the scenes:
(Source code for this image follows)
@startmindmap
!theme plain
caption Read Left To Right
title Data Flow From Org2Blog Entries To WordPress
header
Org2Blog Docs
endheader
center footer Org2Blog README
* Org2Blog
** 🠊 WordPress Post
** 🠊 WordPress Page
left side
** Buffer Entry 🠊
** Subtree Entry 🠊
@endmindmap
This simplicity can actually lead to some less comfortable situations where you accidentally publish one thing as another (it’s pretty easy to fix anyway though).
Although Org2Blog is implemented how WordPress works, it can surprising to see these words used. However you’ll get used to it pretty quickly.
When Org2Blog was created its technical name, its package name, was org2blog
. Unbeknownst to us there was another package out there named Org2BlogAtom with the same package name!
These unforeseen naming conflicts do happen more than you might thing and it had to be resolved. Since they both had the same package name they needed some way to differentiate themselves from each other and the slash/suffix approach was chosen resulting in org2blog/atom
and org2blog/wp
. So why doesn’t this package say ‘Org2Blog/WP’ all over the place today?
That is another historical accident. This package became known simply as Org2Blog without the /WP, and the name stuck. Part of the reason might be that Org2BlogAtom seems unavailable and no longer maintained. Its wiki page hasn’t had any updates on the topic either. Having made this decision it made sense to change the artifact naming scheme to org2blog
instead of org2blog/wp
. It’s easier to understand and adheres to artifact naming best practices. Over time existing /wp
names are slowly being migrated. That still doesn’t answer the original question yet!
Org2Blog is blogging software. You write everything in Org mode and publish it to a blog. It’s pretty simple. Currently it publishes to WordPress. Could it publish to any other blog? With some work definitely. Its impossible to rule out using Org2Blog to blog to other blogs in addition to WordPress.
In that historical context and considering goals today the name remains Org2Blog instead of Org2WordPress.
Preemptive TL;DR: It doesn’t—Packages are not supposed to manage autoloads.
The autoload facility delays loading Elisp files until their contents are actually used improving Emacs startup times. To state it even more simply: it’s how to lazy-load packages. Anytime you see code prefixed with the default magic autoload comment ;;;###autoload
you can use it (for example call a function) before the its package is loaded. Org2Blog has lots of autoload
‘ed functions. Emacs learns about them by reading the autoloads file. There are three entities that can manage the autoloads file along with their decision of whether or not they will:
- Org2Blog: Won’t do it
- You: Should not do it
- A Package Manager: Will do it by design—And Easily
Org2Blog does not manage an autoloads file because packages are not supposed to manage it. Usage and management of an autoloads file is a personal decision made by the user or their choice package manager. By design packages never assume the responsibility. For reference at the moment there are 5,258 packages in MELPA and only one of them includes an autoloads file. Like anything there are exceptions to the rule but Org2Blog isn’t one of them. Another entity who can manage the autoloads file is you.
With an inordinate amount of effort you can create the autoloads file and load it yourself. However it’s likely not worth the effort. Disk drives today are fast. Disk drives of 20 years ago are almost as fast (this applies to whatever the current year is). Drive speed improvements take care of the load time issue. That leaves the time required to manage the autoloads file. If you want to manage the autoloads file yourself you need to create, load it, and update it whenever autoloaded values are changed. It’s even more work better left to a program. If you insist then have at it. Otherwise make your life easy and let the package manager do it for you.
Package Managers by design are responsible for creating the autoloads file for you. It requires no effort and likely zero customization on your part. It’s that simpler. Even better though would be something simpler.
The simplest way to handle autoloads is simply never to use them at all. There is essentially never a good justification for using autoloads. There is almost always another way to achieve your goal. The worst part is that once people start relying on that features autoload behavior you can never remove it later on without creating pain for the user.
The best code is the code that doesn’t exist: that includes autoloads. Bit by bit Org2Blog will keep moving towards a future without them.
Via Wikipedia:
The
MetaWeblog
API
is an application programming interface created by software developer Dave Winerin 2002 that enables weblog entries to be written, edited, and deleted using web services.The
API
is implemented as an XML-RPC web service with three methods whose names describe their function:metaweblog.newPost()
,metaweblog.getPost()
andmetaweblog.editPost()
. These methods take arguments that specify the blog author’s username and password along with information related to an individual weblog entry.
- BlogEngine
- cloudscribe.SimpleContent
- ExpressionEngine
- Hexo
- To provide context for number of users it serves many Chinese COM sites (via apanly: 163, 51cto, chinaunix, cnblogs, csdn, oschina, sina)
- Hugo
- Jekyll
- Joomla
- Kentico CMS
- Sinatra
- WordPress
The official definition only contains three methods:
metaweblog.editPost()
metaweblog.getPost()
metaweblog.newPost()
That doesn’t seem like enough. A cursory glance at various implementations
reveals there are likely more. The problem is that neither the specification
homepage―nor Web.Archive.Org’s snapshot of it―is available to confirm that.
This leaves us in the situation of having to deduce the current MetaWeblog
API
specification by studying what is implemented by the servers and the
client software. Since WordPress definitely supports MetaWeblog
, Org2Blog
starts here.
This library provides the following for what is believed to be the current MetaWeblog API standard:
Method URL | Supported |
---|---|
metaWeblog.deletePost | Yes |
metaWeblog.editPost | Yes |
metaWeblog.getCategories | Yes |
metaWeblog.getPost | Yes |
metaWeblog.getRecentPosts | Yes |
metaWeblog.getTemplate | No |
metaWeblog.getUsersBlogs | Yes |
metaWeblog.newMediaObject | Yes |
metaWeblog.newPost | Yes |
metaWeblog.setTemplate | No |
- Note
- This reflects the subset supported by WordPress.
- This definition reflects what other implementations support.
- Looking at other implementations the support for
getTemplate
andsetTemplate
is about fifty-percent. The reason why is unknown. It will be helpful to understand why.
In addition to MetaWeblog this library provides the following XML-RPC WordPress API methods:
wp.deletePage
wp.editPage
wp.getPageList
wp.getPages
wp.getTags
wp.newCategory
wp.newPage
- Org2Blog maintains MetaWeblog as an independent library for common usage
- MetaWeblog provides critical functionality for Org2Blog
Org2Blog’s data model addresses only two ideas:
- You write an
Entry
(SOURCE
) that gets published to either a WordPressPost
orPage
(DEST
) - An
Entry
is defined in either aBuffer
or aSubtree
All of the functions revolve around these two ideas.
Object-Orientation
is not used with either the data or implementation. With only two data types the effort isn’t justified. With that in mind functions must manually manually address this implementation scenario:
Consequently key functions all either include (or deduce) the variables:
SOURCE
- Either ~’buffer~ or ~’subtree~
DEST
- Either ~’post~ ~’page~
From the user’s perspective the source data is virtually identical: you write an Entry
that gets published out to WordPress. It’s really that simple.
From WordPress’s perspective a Post
and Page
are almost identical too. Therefore many of the functions can be reused with slight differences.
Since WordPress follows the CRUD model Org2Blog will only need four implementations for each operation type documented here addressing SOURCE 🠆 DEST
:
Buffer
🠆Post
Buffer
🠆Page
Subtree
🠆Post
Subtree
🠆Page
Resulting in 4x4 combination. Here is how the each work.
In the interest of brevity the org2blog-
prefix is removed from the function names.
The node document format below is:
- Function name: newline
- Argument names: newline
- Values passed to next function in round parens: (🠆 args…)
org2blog--new
creates the content but doesn’t publish it yet. See Save.
It works like this:
- Confirm the destination type is valid or error out
- Maybe login
- Prepare a buffer to population with
Entry
content - Insert
Entry
specific content - Maybe track it’s creation
(Source code for this image follows)
@startmindmap
!theme plain
caption Read Left To Right
title Creation Function Flow
header
Org2Blog Docs
endheader
center footer Org2Blog DEVELOPMENT
* -new\nsource
left side
** buffer-new\n('buffer 🠆)
** subtree-new\n('subtree 🠆)
@endmindmap
org2blog-entry-view
works like this:
Subtree
processing is almost identical to aBuffer
. Therefore make a note right away this for a subtree- Get the
Post ID
. If there isn’t one then error out. - Prepare the preview URL
- Open in web browser
(Source code for this image follows)
@startmindmap
!theme plain
caption Read Left To Right
title View Function Flow
header
Org2Blog Docs
endheader
center footer Org2Blog DEVELOPMENT
* entry-view\nsource dest
left side
** buffer-post-view\n('buffer 'post 🠆)
** buffer-page-view\n('buffer 'page 🠆)
** subtree-view\ndest\n('subtree dest 🠆)
*** subtree-post-view\n('post 🠆)
*** subtree-page-view\n('page 🠆)
@endmindmap
From a WordPress perspective both Create and Save are the same thing. The only difference is whether or not they’re a Draft, and private, or published and pubic.
org2blog-entry-save
works like this:
- Maybe login
Subtree
processing is almost identical to aBuffer
. Therefore make a note right away this for a subtree- Maybe auto-save and auto-post it
- Either create or save the post
- Update the
Entry
with the new ID
(Source code for this image follows)
@startmindmap
!theme plain
caption Read Left To Right
title Create & Save Function Flow
header
Org2Blog Docs
endheader
center footer Org2Blog DEVELOPMENT
* entry-save\nsource type &publish
left side
** buffer-post-save\n&publish\n('buffer 'post publish)
*** buffer-post-publish\n(t)
** subtree-post-save\n&publish\n('subtree 'post publish)
*** subtree-post-publish\n(t)
** buffer-page-save\n&publish\n('buffer 'page publish)
*** buffer-page-publish\n(t)
** subtree-page-save\n&publish\n('subtree 'page publish)
*** subtree-page-publish\n(t)
@endmindmap
org2blog-entry-trash
works like this:
- Get the
Post ID
- Maybe confirm the trashing
- Trash it
@startmindmap
!theme plain
caption Read Left To Right
title Delete Flow
header
Org2Blog Docs
endheader
center footer Org2Blog DEVELOPMENT
* entry-trash\n&post-id
left side
** buffer-post-trash\n&post-id\n('post post-id)
** subtree-post-trash\n&post-id\n('post post-id)
** buffer-page-trash\n&page-id\n('page page-id)
** subtree-page-trash\n&page-id\n('page post-id)
@endmindmap