Added Metadata Plugin and documentation.
This commit is contained in:
parent
96512011a0
commit
4303be09ac
3 changed files with 125 additions and 4 deletions
|
@ -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
81
plugins/metadata.lisp
Normal 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)))
|
|
@ -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*
|
||||
|
|
Loading…
Add table
Reference in a new issue