Added Metadata Plugin and documentation.

This commit is contained in:
Kenan Bölükbaşı 2015-09-28 05:24:56 +03:00
parent 96512011a0
commit 4303be09ac
3 changed files with 125 additions and 4 deletions

View file

@ -39,6 +39,34 @@
**Example**: `(gh-pages :cname t)`
## SEO and Social Metadata
**Description**: Adds description and keywords metadata for SEO purposes.
Adds both Open Graph and Twitter Cards metadata for sharing posts as well.
`:twitter` keyword argument sets "site" property of Twitter.
Twitter Card specific metadata will be omitted if this property is not set.
`:card` keyword argument sets the type of Twitter card created.
Possible types are `:summary` and `:image`. Defaults to `:summary`.
Five *optional* tags for post file header are added.
`keywords:` comma, seperated, seo, keywords.
They will be generated from tags if empty.
`description:` Description to be used in SEO and
Open Graph description tags. If empty, Open Graph description will be generated
from content while SEO description metadata will be omitted.
`image:` either an absolute (`http://www.example.com/image.png`)
or a root-relative (`/static/image.png`) image URL.
`card:` Overrides Twitter Card type defined in plugin activation.
Possible values are either `image` or `summary`.
`creator:` Twitter username of the content creator.
**Example**: `(metadata :twitter "twitter_account" :card :summary)`
## Incremental Builds
**Description**: Primarily a performance enhancement. Caches the

81
plugins/metadata.lisp Normal file
View file

@ -0,0 +1,81 @@
(eval-when (:compile-toplevel :load-toplevel)
(ql:quickload 's-xml))
(defpackage :coleslaw-metadata
(:use :cl :coleslaw)
(:import-from :coleslaw
#:title
#:domain
#:tag-name
#:page-url
#:content-tags
#:content-text
#:keywords-of
#:description-of
#:image-of
#:card-format
#:creator-of)
(:import-from :s-xml
#:xml-parser-state
#:start-parse-xml)
(:export #:enable))
(in-package :coleslaw-metadata)
(defparameter *description-length* 200)
(defvar *metadata-header*
"
<meta name=\"keywords\" content=\"~A\" />~@[
<meta name=\"description\" content=\"~A\" />~]~:[~*~*~;~:*
<meta name=\"twitter:site\" content=\"@~A\" />
<meta name=\"twitter:card\" content=\"~:[summary~;summary_large_image~]\" />~@[
<meta name=\"twitter:creator\" content=\"@~A\" />~]~]
<meta property=\"og:title\" content=\"~A\" />
<meta property=\"og:site_name\" content=\"~A\" />
<meta property=\"og:url\" content=\"~A\" />
<meta property=\"og:description\" content=\"~A\" />~@[
<meta property=\"og:image\" content=\"~A\" />~]
")
(defun remove-markup (text)
(with-input-from-string (in text)
(let* ((state (make-instance 'xml-parser-state
:text-hook #'(lambda (string seed) (cons string seed))))
(result (start-parse-xml in state)))
(apply #'concatenate 'string (nreverse result)))))
(defun shorten-text (text)
(if (< *description-length* (length text))
(subseq text 0 (- *description-length* 1)) text))
(defun compile-description (text)
(shorten-text (remove #\" (remove-markup text))))
(defun root-relative-url-p (url)
(eq (elt url 0) #\/))
(defun compile-url (url)
(if (root-relative-url-p url)
(concatenate 'string (domain *config*) url)
url))
(defun compile-metadata (post twitter card)
(format nil *metadata-header*
(or (keywords-of post)
(format nil "~{~A~^, ~}" (mapcar #'tag-name (content-tags post))))
(description-of post)
twitter
(eq (or (card-format post) card) :image)
(creator-of post)
(title-of post)
(title *config*)
(concatenate 'string (domain *config*) "/" (namestring (page-url post)))
(or (description-of post)
(compile-description (content-text post)))
(when (image-of post) (compile-url (image-of post)))))
(defun enable (&key twitter card)
(flet ((inject-p (x)
(when (typep x 'post) (compile-metadata x twitter card))))
(add-injection #'inject-p :head)))

View file

@ -3,15 +3,27 @@
(defclass post (content)
((title :initarg :title :reader title-of)
(author :initarg :author :reader author-of)
(format :initarg :format :reader post-format))
(:default-initargs :author nil))
(format :initarg :format :reader post-format)
(keywords :initarg :keywords :reader keywords-of)
(description :initarg :description :reader description-of)
(image :initarg :image :reader image-of)
(card :initarg :card :reader card-format)
(creator :initarg :creator :reader creator-of))
(:default-initargs
:author nil
:keywords nil
:description nil
:image nil
:card nil
:creator nil))
(defmethod initialize-instance :after ((object post) &key)
(with-slots (url title author format text) object
(with-slots (url title author format card text) object
(setf url (compute-url object (slugify title))
format (make-keyword (string-upcase format))
text (render-text text format)
author (or author (author *config*)))))
author (or author (author *config*))
card (if card (make-keyword (string-upcase card))))))
(defmethod render ((object post) &key prev next)
(funcall (theme-fn 'post) (list :config *config*