Skip to content

Commit

Permalink
Added tags-index node.
Browse files Browse the repository at this point in the history
  • Loading branch information
svetlyak40wt committed Apr 29, 2024
1 parent 9484a46 commit 196fee8
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 14 deletions.
4 changes: 4 additions & 0 deletions docs/difference-from-coleslaw.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ For index pages a list of items was also moved and now instead of `index.content
For objects in `content.items` attribute `obj.text` was renamed to `obj.excerpt`. It is a HTML, so `noAutoescape` filter should be applied (as you did in Coleslaw themes too).
### Index by tag
Coleslaw always rendered pages where posts are grouped by tags. But with Staticl you have to include TAGS-INDEX function call into the site's pipeline. Without this step, tag objects will not have a \"url\" slot and template might be ready to render tags without the URL.
## Other field renames
- `pubdate -> site.pubdate`
Expand Down
3 changes: 2 additions & 1 deletion src/content.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@
#:content-title
#:content-excerpt-separator
#:set-metadata
#:load-content))
#:load-content
#:content-tags))
(in-package #:staticl/content)


Expand Down
4 changes: 1 addition & 3 deletions src/index/paginated.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
#:fmt)
(:import-from #:staticl/content/post
#:postp)
(:export #:paginated-index
#:make-page-title
#:make-page-filename))
(:export #:paginated-index))
(in-package #:staticl/index/paginated)


Expand Down
157 changes: 157 additions & 0 deletions src/index/tags.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
(uiop:define-package #:staticl/index/tags
(:use #:cl)
(:import-from #:staticl/index/base
#:page-size
#:index-target-path
#:index-page
#:base-index)
(:import-from #:staticl/index/base
#:base-index)
(:import-from #:serapeum
#:->
#:dict
#:fmt)
(:import-from #:alexandria
#:curry
#:rcurry)
(:import-from #:staticl/content
#:content-with-title-mixin
#:content-title
#:content-tags
#:content-with-tags-mixin)
(:import-from #:staticl/tag
#:tag
#:tag-name)
(:import-from #:staticl/site
#:site)
(:import-from #:staticl/theme
#:template-vars)
(:import-from #:staticl/url
#:object-url))
(in-package #:staticl/index/tags)


(deftype function-from-string-to-string ()
'(function (string) (values string &optional)))


(deftype function-from-string-to-pathname ()
'(function (string) (values pathname &optional)))


(declaim (ftype function-from-string-to-string
default-page-title-fn))

(defun default-page-title-fn (tag-name)
(fmt "Posts with tag \"~A\"" tag-name))


(declaim (ftype function-from-string-to-pathname
default-page-filename-fn))

(defun default-page-filename-fn (tag-name)
(make-pathname :name tag-name
:type "html"))



(defclass tags-index (base-index)
((page-filename-fn :initarg :page-filename-fn
:type (or null function-from-string-to-pathname)
:documentation "A callback to change page titles.
Accepts single argument - a tag name and should return a pathname
By default, for tag \"foo-bar\" it returns foo-bar.html.
If site has \"clean urls\" setting enabled, then additional
transformation to the pathname will be
applied automatically."
:reader page-filename-fn)
(page-title-fn :initarg :page-title-fn
:type (or null function-from-string-to-string)
:documentation "A callback to change page titles.
Accepts single argument - a tag name and should return a string.
For example, here is how you can translate page title into a russian:
```lisp
(tags-index :target-path #P\"ru/\"
:page-title-fn (lambda (tag-name)
(fmt \"Записи с тегом ~A\" tag-name)))
```
"
:reader page-title-fn))
(:default-initargs
:page-title-fn #'default-page-title-fn
:page-filename-fn #'default-page-filename-fn))


(defun tags-index (&rest initargs &key target-path page-size template page-title-fn page-filename-fn)
(declare (ignore target-path page-size template page-title-fn page-filename-fn))
(apply #'make-instance 'tags-index
initargs))


(defclass bound-tag (tag)
((index-page :initarg :index-page
:reader tag-index-page))
(:documentation "A tag bound to the index page. A URL for such tags will lead to the index page."))


(-> upgrade-tag (string index-page content-with-tags-mixin)
(values &optional))

(defun upgrade-tag (tag-name index-page content)
(loop for tag in (content-tags content)
when (string= (tag-name tag)
tag-name)
do (change-class tag 'bound-tag
:index-page index-page))
(values))


(defmethod template-vars ((site site) (tag bound-tag) &key (hash (dict)))
(let ((hash (call-next-method site tag :hash hash)))
(setf (gethash "url" hash)
(object-url site (tag-index-page tag)))
(values hash)))


(defmethod staticl/pipeline:process-items ((site site) (index tags-index) content-items)
"Here we are grouping all posts by their tags, generate an index page for each tag and upgrade tag instances to the tags bound to their corresponding index pages.
Posts on index page are sorted by their titles."

(loop with only-posts = (remove-if-not (lambda (item)
(and (typep item 'content-with-tags-mixin)
(typep item 'content-with-title-mixin)))
content-items)
with by-tag = (dict)
for post in only-posts
do (loop for tag in (content-tags post)
do (push post
(gethash (tag-name tag) by-tag)))
finally (loop for tag-name being the hash-key of by-tag
using (hash-value tagged-posts)
for sorted-posts = (sort tagged-posts #'string<
:key #'content-title)
for index-page = (make-instance 'index-page
:title (funcall (page-title-fn index)
tag-name)
:target-path (merge-pathnames
;; TODO: implement clean urls
(funcall (page-filename-fn index)
tag-name)
(uiop:ensure-directory-pathname
(index-target-path index)))
:items sorted-posts)
do (staticl/pipeline:produce-item index-page)
;; Here we change tag classes, to make them
;; render as links to the corresponding index page:
(mapc (curry #'upgrade-tag
tag-name
index-page)
sorted-posts)))
(values))
13 changes: 3 additions & 10 deletions src/user-package.lisp
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
(uiop:define-package #:staticl-user
;; This package does not use all symbols from CL package intentionally:
(:use #:cl)

(:nicknames #:staticl/user-package)
;; (:import-from #:cl
;; #:list
;; #:t
;; #:nil
;; #:lambda
;; #:let
;; #:in-package
;; #:defpackage)

(:import-from #:serapeum
#:fmt)
Expand All @@ -35,4 +26,6 @@
(:import-from #:staticl/rsync
#:rsync)
(:import-from #:staticl/index/paginated
#:paginated-index))
#:paginated-index)
(:import-from #:staticl/index/tags
#:tags-index))

0 comments on commit 196fee8

Please sign in to comment.