Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ ul ->

- *collectionName* : Can be used to narrow the scope of the plugin to a specific collection and therefore improve performance (defaults to 'documents').
- *indexPageLayout* : Override the name of the layout file used for the tag index pages (defaults to 'tags').
- *indexPagePath* : Override the relative output path of the tag index pages (defaults to 'tags').
- *indexPagePath* : Override the relative output path of the tag index pages (defaults to 'tags'). An Array of paths is also accepted (e.g. ['en/tags', 'de/tags'])
- *getTagWeight* : Override the function used to generate the tag weights (see below).

### Customising the weight function
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"bal-util": "~1.16.10"
},
"devDependencies": {
"coffee-script": "~1.4.0"
"coffee-script": "~1.6.0"
},
"main": "./out/tagging.plugin.js",
"scripts": {
Expand Down
279 changes: 149 additions & 130 deletions src/tagging.plugin.coffee
Original file line number Diff line number Diff line change
@@ -1,133 +1,152 @@
module.exports = (BasePlugin) ->

_ = require('lodash')
balUtil = require('bal-util')

class Tagging extends BasePlugin
name: 'tagging'

config:
collectionName: 'documents'
indexPageLayout: 'tags'
indexPagePath: 'tags'
getTagWeight: (count, maxCount) ->
# apply logarithmic weight algorithm
logmin = 0
logmax = Math.log(maxCount)
result = (Math.log(count) - logmin) / (logmax - logmin)
return result

tagCloud: null
tagCollection: null
maxCount: 0

# This is to prevent/detect recursive firings of ContextualizeAfter event
contextualizeAfterLock: false

extendCollections: (next) ->
@tagCollection = @docpad.getDatabase().createLiveChildCollection()
.setQuery("isTagIndex", tag: $exists: true)

extendTemplateData: ({templateData}) ->
me = @
templateData.getTagCloud = ->
return me.tagCloud
templateData.getTagUrl = (tag) ->
return me.getTagUrl(tag)
@

contextualizeAfter: ({collection, templateData}, next) ->
if not @contextualizeAfterLock
return @generateTags(collection, next)
else
next()
@

getTagUrl: (tag) ->
doc = @tagCollection.findOne(tag: tag)
return doc?.get('url')

generateTags: (renderCollection, next) ->

# Prepare
me = @
docpad = @docpad
config = @config
database = docpad.getDatabase()
targetedDocuments = docpad.getCollection(@config.collectionName)

# regenerate tag cloud

docpad.log 'debug', 'tagging::generateTags: Generating tag cloud'

@maxCount = 0
@tagCloud = {}

targetedDocuments.forEach (document) =>
# Prepare
tags = document.get('tags') or []

for tag in tags
@tagCloud[tag] ?=
tag: tag,
count: 0,
url: ""
weight: 0
count = ++@tagCloud[tag].count
@maxCount = count if count > @maxCount

# generate tag index pages

docpad.log 'debug', 'tagging::generateTags: Generating tag index pages'

docs_created = 0
newDocs = new docpad.FilesCollection()
for own tag of @tagCloud
# check whether a document for this tag already exists in the collection
if not @tagCollection.findOne(tag: tag)
slug = balUtil.generateSlugSync(tag)
doc = @docpad.createDocument(
slug: slug
relativePath: config.indexPagePath + "/" + slug + ".html"
isDocument: true
encoding: 'utf8'
,
data: " " # NOTE: can't be empty string due to
# quirk in FileModel (as of docpad v6.25)
meta:
layout: config.indexPageLayout
referencesOthers: true
tag: tag
)
database.add doc
newDocs.add doc

# if we're reloading (reset = false), our new document
# will not have made it into the collection of modified
# documents to render - so we need to add it
if not renderCollection.findOne(tag: tag)
renderCollection.add doc

docs_created++

docpad.log 'debug', "tagging::generateTags: #{docs_created} new docs added"

# docpad has already called load and contextualize on its documents
# so we need to call it manually here for our new docs
docpad.loadFiles {collection: newDocs}, (err) =>
if err then return next(err)

@contextualizeAfterLock = true
docpad.contextualizeFiles {collection: newDocs}, (err) =>
if err then return next(err)

@contextualizeAfterLock = false

for own tag, item of @tagCloud
@tagCloud[tag].url = @getTagUrl(tag)
@tagCloud[tag].weight = @config.getTagWeight(item.count, @maxCount)

next()

@
_ = require('lodash')
balUtil = require('bal-util')

class Tagging extends BasePlugin
name: 'tagging'

config:
collectionName: 'documents'
indexPageLayout: 'tags'
indexPagePath: 'tags'
context: null
getTagWeight: (count, maxCount) ->
# apply logarithmic weight algorithm
logmin = 0
logmax = Math.log(maxCount)
result = (Math.log(count) - logmin) / (logmax - logmin)
return result

tagCloud: {}
tagCollection: null

# This is to prevent/detect recursive firings of ContextualizeAfter event
contextualizeAfterLock: false

extendCollections: (next) ->
@tagCollection = @docpad.getDatabase().createLiveChildCollection()
.setQuery("isTagIndex", tag: $exists: true)

extendTemplateData: ({templateData}) ->
me = @
templateData.getTagCloud = (options) ->
return me.getTagCloud(options)
templateData.getTagUrl = (tag,options) ->
return me.getTagUrl(tag,options)
@

contextualizeAfter: ({collection, templateData}, next) ->
if not @contextualizeAfterLock
return @generateTags(collection, next)
else
next()
@

getTagCloud: (options) ->
context = options?.context ? 'all'
return @tagCloud[context].tags

getTagUrl: (tag,options) ->
query = options ? {}
query.tag = tag
doc = @tagCollection.findOne(query)
return doc?.get('url')

generateTags: (renderCollection, next) ->
# Prepare
me = @
docpad = @docpad
config = @config
database = docpad.getDatabase()
targetedDocuments = docpad.getCollection(@config.collectionName)

# regenerate tag cloud

docpad.log 'debug', 'tagging::generateTags: Generating tag cloud'

targetedDocuments.forEach (document) =>
# Prepare
tags = document.get('tags') or []
contexts = _(['all']).union([document.get('context') or null])
.flatten()
.compact()
.value()

for context in contexts
@tagCloud[context] ?= {tags: {}, maxCount: 0}
cloud = @tagCloud[context]

for tag in tags
cloud.tags[tag] ?=
tag: tag,
count: 0,
url: ""
weight: 0
count = ++cloud.tags[tag].count
cloud.maxCount = count if count > cloud.maxCount

# generate tag index pages

docpad.log 'debug', 'tagging::generateTags: Generating tag index pages'
docs_created = 0
newDocs = new docpad.FilesCollection()
for own context, tagCloud of @tagCloud
for own tag of tagCloud.tags

# check whether a document for this tag already exists in the collection
if not @tagCollection.findOne({tag: tag, context: context})
slug = balUtil.generateSlugSync(tag)
contextPath = if context=='all' then '' else context
relativePath = _([
contextPath,
config.indexPagePath,
slug + ".html"
]).compact().join('/')

doc = @docpad.createDocument(
slug: slug
relativePath: relativePath
context: context
isDocument: true
encoding: 'utf8'
,
data: " " # NOTE: can't be empty string due to, quirk in FileModel (as of docpad v6.25)
meta:
layout: config.indexPageLayout
referencesOthers: true
tag: tag
context: context
)
database.add doc
newDocs.add doc
docs_created++

# if we're reloading (reset = false), our new document
# will not have made it into the collection of modified
# documents to render - so we need to add it
if not renderCollection.findOne({tag: tag, context: context})
renderCollection.add doc

docpad.log 'debug', "tagging::generateTags: #{docs_created} new docs added"

# docpad has already called load and contextualize on its documents
# so we need to call it manually here for our new docs
docpad.loadFiles {collection: newDocs}, (err) =>
if err then return next(err)

@contextualizeAfterLock = true
docpad.contextualizeFiles {collection: newDocs}, (err) =>
if err then return next(err)

@contextualizeAfterLock = false

for own context, tagCloud of @tagCloud
for own tag, item of tagCloud.tags
item.url = @getTagUrl(tag, {context: context})
item.weight = @config.getTagWeight(item.count, tagCloud.maxCount)

next()

@