Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Categories Support for Posts #291

Closed
wants to merge 3 commits into from
Closed
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
25 changes: 25 additions & 0 deletions src/main/java/org/jbake/app/ConfigUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ public static interface Keys {
*/
static final String RENDER_TAGS = "render.tags";

/**
* Flag indicating if category files should be generated
*/
static final String RENDER_CATEGORIES = "render.categories";

/**
* Port used when running Jetty server
*/
Expand Down Expand Up @@ -200,6 +205,26 @@ public static interface Keys {
* How many posts per page on index
*/
static final String POSTS_PER_PAGE = "index.posts_per_page";

/**
* Should Category generation and usage be enabled? Default is true. Posts without categories will be marked as 'Uncategorized'. Default category can be changed with {#link #CATEGORY_DEFAULT}.
*/
static final String CATEGORIES_ENABLE = "categories.enable";

/**
* Default Category for posts when {@link CATEGORIES_ENABLE} is {@code true} and no category is specified for post.
*/
static final String CATEGORY_DEFAULT = "category.default";

/**
* Tags output path, used only when {@link #RENDER_CATEGORIES} is true
*/
static final String CATEGORY_PATH = "categories.path";

/**
* Should Categories be sanitized for space?
*/
static final String CATEGORY_SANITIZE = "categories.sanitize";
}

private final static Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class);
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/org/jbake/app/ContentStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ public List<ODocument> getPublishedPostsByTag(String tag) {
return query("select * from post where status='published' and ? in tags order by date desc", tag);
}

public List<ODocument> getPublishedPostsByCategories(String category) {
return query("select * from post where status='published' where ? in categories order by date desc", category);
}

public List<ODocument> getPublishedPages() {
return getPublishedContent("page");
}
Expand All @@ -153,6 +157,10 @@ public List<ODocument> getAllContent(String docType) {
return query(query + " order by date desc");
}

public List<ODocument> getAllCategoriesFromPublishedPosts() {
return query("select categories from post where status='published'");
}

public List<ODocument> getAllTagsFromPublishedPosts() {
return query("select tags from post where status='published'");
}
Expand Down Expand Up @@ -207,6 +215,16 @@ public Set<String> getTags() {
return result;
}

public Set<String> getCategories() {
List<ODocument> docs = this.getAllCategoriesFromPublishedPosts();
Set<String> result = new HashSet<String>();
for (ODocument document : docs) {
String[] categories = DBUtil.toStringArray(document.field("categories"));
Collections.addAll(result, categories);
}
return result;
}

private static void createDocType(final OSchema schema, final String doctype) {
OClass page = schema.createClass(doctype);
page.createProperty("sha1", OType.STRING).setNotNull(true);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/jbake/app/Crawler.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public static interface Status {
static final String ROOTPATH = "rootpath";
static final String ID = "id";
static final String NO_EXTENSION_URI = "noExtensionUri";
static final String CATEGORIES = "categories";
static final String CATEGORY = "category";

}
private static final Logger LOGGER = LoggerFactory.getLogger(Crawler.class);
Expand Down
73 changes: 66 additions & 7 deletions src/main/java/org/jbake/app/Renderer.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
package org.jbake.app;

import org.apache.commons.configuration.CompositeConfiguration;
import org.jbake.app.ConfigUtil.Keys;
import org.jbake.app.Crawler.Attributes;
import org.jbake.template.DelegatingTemplateEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.configuration.CompositeConfiguration;
import org.jbake.app.ConfigUtil.Keys;
import org.jbake.app.Crawler.Attributes;
import org.jbake.template.DelegatingTemplateEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Render output to a file.
*
Expand Down Expand Up @@ -348,6 +349,64 @@ public int renderTags(Set<String> tags, String tagPath) throws Exception {
}
}


/**
* Render tag files using the supplied content.
*
* @param categories The content to renderDocument
* @param categoriesPath The output path
* @throws Exception
*/
public int renderCategories(Set<String> categories, String categoriesPath) throws Exception {
int renderedCount = 0;
final List<String> errors = new LinkedList<String>();
Map<String,String> generatedCategories = new LinkedHashMap<String, String>();
for (String category : categories) {
try {
Map<String, Object> model = new HashMap<String, Object>();
model.put("renderer", renderingEngine);
model.put(Attributes.CATEGORY, category);
Map<String, Object> map = buildSimpleModel(Attributes.CATEGORY);
map.put(Attributes.ROOTPATH, "../");
model.put("content", map);

String pathCategory = category.trim().replace(" ", "-") + config.getString(Keys.OUTPUT_EXTENSION);
File path = new File(destination.getPath() + File.separator + categoriesPath + File.separator + pathCategory);
render(new ModelRenderingConfig(path, Attributes.CATEGORY, model, findTemplateName(Attributes.CATEGORY)));
generatedCategories.put(category, pathCategory);
renderedCount++;
} catch (Exception e) {
errors.add(e.getCause().getMessage());
}
}

// Add an index file at root folder of categories.
// This will prevent directory listing and also provide an option to display all categories page.
if(!categories.isEmpty()){
Map<String, Object> model = new HashMap<String, Object>();
model.put("renderer", renderingEngine);
Map<String, Object> map = buildSimpleModel(Attributes.CATEGORIES);
map.put(Attributes.ROOTPATH, "../");
map.put(Attributes.CATEGORIES, generatedCategories);
model.put("content", map);

File path = new File(destination.getPath() + File.separator + categoriesPath + File.separator + "index" + config.getString(Keys.OUTPUT_EXTENSION));
render(new ModelRenderingConfig(path, Attributes.CATEGORIES, model, findTemplateName(Attributes.CATEGORIES)));
renderedCount++;
}

if (!errors.isEmpty()) {
StringBuilder sb = new StringBuilder();
sb.append("Failed to render Categories. Cause(s):");
for(String error: errors) {
sb.append("\n" + error);
}
throw new Exception(sb.toString());
} else {
return renderedCount;
}
}

/**
* Builds simple map of values, which are exposed when rendering index/archive/sitemap/feed/tags.
*
Expand Down
33 changes: 32 additions & 1 deletion src/main/java/org/jbake/parser/MarkupEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.IOUtils;
import org.jbake.app.ConfigUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.jbake.app.ConfigUtil.Keys;
import org.jbake.app.Crawler;
import org.json.simple.JSONValue;
Expand Down Expand Up @@ -127,6 +127,31 @@ public Map<String, Object> parse(Configuration config, File file, String content
content.put("tags", tags);
}

// If categories are not disabled then add a default category
if(config.getBoolean(Keys.CATEGORIES_ENABLE)){
if (content.get("categories") != null) {
String[] categories = (String[]) content.get("categories");
for( int i=0; i<categories.length; i++ ) {
categories[i]=categories[i].trim();
if (config.getBoolean(Keys.CATEGORY_SANITIZE)) {
categories[i]=categories[i].replace(" ", "-");
}
}
content.put("categories", categories);
} else {
String defaultCategory = config.getString(Keys.CATEGORY_DEFAULT).trim();
if (config.getBoolean(Keys.CATEGORY_SANITIZE,false)) {
defaultCategory = defaultCategory.replace(" ", "-");
}
content.put("categories", ArrayUtils.toArray(defaultCategory));
}
content.put("primary_category", ((String[])content.get("categories"))[0]);
} else {
if (content.get("categories") != null) {
LOGGER.warn("categories.enable is set as false but post {} has categories specifid. Categories will be ignored.", file);
}
}

// TODO: post parsing plugins to hook in here?

return content;
Expand Down Expand Up @@ -207,6 +232,12 @@ private void processHeader(Configuration config, List<String> contents, final Ma
for( int i=0; i<tags.length; i++ )
tags[i]=tags[i].trim();
content.put(parts[0], tags);
} else if (parts[0].equalsIgnoreCase("categories")){
List<String> categories = new ArrayList<String>();
for (String category : parts[1].split(",")){
categories.add(category.trim());
}
content.put(parts[0], categories.toArray(new String[0]));
} else if (parts[1].startsWith("{") && parts[1].endsWith("}")) {
// Json type
content.put(parts[0], JSONValue.parse(parts[1]));
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/org/jbake/render/CategoriesRenderer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.jbake.render;

import java.io.File;

import org.apache.commons.configuration.CompositeConfiguration;
import org.jbake.app.ConfigUtil.Keys;
import org.jbake.app.ContentStore;
import org.jbake.app.Renderer;
import org.jbake.template.RenderingException;

public class CategoriesRenderer implements RenderingTool {

@Override
public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException {
if (config.getBoolean(Keys.RENDER_CATEGORIES)) {
try {
return renderer.renderCategories(db.getCategories(), config.getString(Keys.CATEGORY_PATH));
} catch (Exception e) {
throw new RenderingException(e);
}
} else {
return 0;
}
}

}
29 changes: 29 additions & 0 deletions src/main/java/org/jbake/template/model/AllCategoriesExtractor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.jbake.template.model;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jbake.app.ContentStore;
import org.jbake.app.Crawler;
import org.jbake.app.DBUtil;
import org.jbake.template.ModelExtractor;

import com.orientechnologies.orient.core.record.impl.ODocument;

public class AllCategoriesExtractor implements ModelExtractor<Set<String>> {

@Override
public Set<String> get(ContentStore db, Map model, String key) {
List<ODocument> query = db.getAllCategoriesFromPublishedPosts();
Set<String> result = new HashSet<String>();
for (ODocument document : query) {
String[] categories = DBUtil.toStringArray(document.field(Crawler.Attributes.CATEGORIES));
Collections.addAll(result, categories);
}
return result;
}

}
25 changes: 25 additions & 0 deletions src/main/java/org/jbake/template/model/CategoryPostsExtractor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.jbake.template.model;

import java.util.List;
import java.util.Map;

import org.jbake.app.ContentStore;
import org.jbake.app.Crawler;
import org.jbake.app.DocumentList;
import org.jbake.template.ModelExtractor;

import com.orientechnologies.orient.core.record.impl.ODocument;

public class CategoryPostsExtractor implements ModelExtractor<DocumentList> {

@Override
public DocumentList get(ContentStore db, Map model, String key) {
String category = null;
if (model.get(Crawler.Attributes.CATEGORY) != null) {
category = model.get(Crawler.Attributes.CATEGORY).toString();
}
List<ODocument> query = db.getPublishedPostsByCategories(category);
return DocumentList.wrap(query.iterator());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ org.jbake.template.model.AllTagsExtractor=alltags
org.jbake.template.model.TypedDocumentsExtractor=pages,posts,indexs,archives,feeds
org.jbake.template.model.PublishedDateExtractor=published_date
org.jbake.template.model.DBExtractor=db
org.jbake.template.model.TagPostsExtractor=tag_posts
org.jbake.template.model.TagPostsExtractor=tag_posts
org.jbake.template.model.CategoryPostsExtractor=category_posts
org.jbake.template.model.AllCategoriesExtractor=all_categories
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ org.jbake.render.FeedRenderer
org.jbake.render.IndexRenderer
org.jbake.render.SitemapRenderer
org.jbake.render.TagsRenderer
#org.jbake.render.DatesRenderer
#org.jbake.render.DatesRenderer
org.jbake.render.CategoriesRenderer
17 changes: 16 additions & 1 deletion src/main/resources/default.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ template.sitemap.file=sitemap.ftl
template.post.file=post.ftl
# filename of page template file
template.page.file=page.ftl
# filename of page template file
template.category.file=category.ftl
# file name of page template for {site.url}/categories
template.categories.file=categories.ftl
# folder that contains all content files
content.folder=content
# folder that contains all asset files
Expand Down Expand Up @@ -88,4 +92,15 @@ db.path=cache
# enable extension-less URI option?
uri.noExtension=false
# Set to a prefix path (starting with a slash) for which to generate extension-less URI's (i.e. a folder with index.html in)
uri.noExtension.prefix=
uri.noExtension.prefix=
#Enable usage of categories. If disabled then nothing related to categories is processed or generated.
categories.enable=true
# Should categories pages be genetated
render.categories=true
#What should be the default category when no categories are specified for post and categories are enabled.
category.default=Uncategorized
# where to generate category pages
categories.path=categories
# Should spaces be replaced with hyphens
categories.sanitize=false

Loading