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

Implemented Categories Support #293

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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 @@ -157,6 +157,11 @@ public interface Keys {
*/
String RENDER_TAGS = "render.tags";

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

/**
* Port used when running Jetty server
*/
Expand Down Expand Up @@ -211,6 +216,26 @@ public interface Keys {
* The configured base URI for the hosted content
*/
String SITE_HOST = "site.host";

/**
* 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}.
*/
String CATEGORIES_ENABLE = "categories.enable";

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

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

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

private final static Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class);
Expand Down
50 changes: 38 additions & 12 deletions src/main/java/org/jbake/app/ContentStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,27 @@
*/
package org.jbake.app;

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

import org.jbake.model.DocumentAttributes;
import org.jbake.model.DocumentTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.OPartitionedDatabasePool;
import com.orientechnologies.orient.core.db.OPartitionedDatabasePoolFactory;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentPool;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OSchema;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;
import org.jbake.model.DocumentAttributes;
import org.jbake.model.DocumentTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
* @author jdlee
Expand Down Expand Up @@ -145,6 +144,10 @@ public DocumentList getPublishedPostsByTag(String tag) {
return query("select * from post where status='published' and ? in tags order by date desc", tag);
}

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

public DocumentList getPublishedDocumentsByTag(String tag) {
final DocumentList documents = new DocumentList();
for (final String docType : DocumentTypes.getDocumentTypes()) {
Expand All @@ -153,6 +156,15 @@ public DocumentList getPublishedDocumentsByTag(String tag) {
}
return documents;
}

public DocumentList getPublishedDocumentsByCategory(String category) {
final DocumentList documents = new DocumentList();
for (final String docType : DocumentTypes.getDocumentTypes()) {
DocumentList documentsByTag = query("select * from " + docType + " where status='published' and ? in categories order by date desc", category);
documents.addAll(documentsByTag);
}
return documents;
}

public DocumentList getPublishedPages() {
return getPublishedContent("page");
Expand All @@ -174,6 +186,10 @@ public DocumentList getAllContent(String docType) {
return query(query);
}

public DocumentList getAllCategoriesFromPublishedPosts() {
return query("select categories from post where status='published'");
}

public DocumentList getAllTagsFromPublishedPosts() {
return query("select tags from post where status='published'");
}
Expand Down Expand Up @@ -242,6 +258,16 @@ public Set<String> getAllTags() {
return result;
}

public Set<String> getCategories() {
DocumentList docs = this.getAllCategoriesFromPublishedPosts();
Set<String> result = new HashSet<String>();
for (Map<String, Object> document : docs) {
String[] categories = DBUtil.toStringArray(document.get("categories"));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to reference the crawler attribute instead of using a hard coded string value: Crawler.Attributes.CATEGORIES

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch @GeorgHenkel, Thanks. I will make the changes.

Collections.addAll(result, categories);
}
return result;
}

private void createDocType(final OSchema schema, final String doctype) {
logger.debug("Create document class '{}'", doctype );

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 @@ -50,6 +50,8 @@ interface Status {
String ALLTAGS = "alltags";
String PUBLISHED_DATE = "published_date";
String BODY = "body";
String CATEGORIES = "categories";
String CATEGORY = "category";
}

private static final Logger LOGGER = LoggerFactory.getLogger(Crawler.class);
Expand Down
74 changes: 74 additions & 0 deletions src/main/java/org/jbake/app/Renderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,81 @@ public int renderTags(String tagPath) throws Exception {
return renderedCount;
}
}

/**
* Render tag files using the supplied content.
*
* @param categories The content to renderDocument
* @param categoriesPath The output path
* @throws Exception
*/
public int renderCategories(String categoriesPath) throws Exception {
int renderedCount = 0;
final List<String> errors = new LinkedList<String>();
for (String category : db.getCategories()) {
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(" ", "-");
String uri = File.separator + categoriesPath + File.separator + pathCategory;
uri = getValidExtensionUri(uri,config);
File path = new File(destination.getPath() + uri);

render(new ModelRenderingConfig(path, Attributes.CATEGORY, model, findTemplateName(Attributes.CATEGORY)));

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.
Map<String, Object> model = new HashMap<String, Object>();
model.put("renderer", renderingEngine);
Map<String, Object> map = buildSimpleModel(Attributes.CATEGORIES);
map.put(Attributes.ROOTPATH, "../");
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;
}
}

/**
* If uri.noExtension is set to true then generate extension less uri. This method is to be used where uri.noExtension.prefix doesn't matter like categories, tags etc.
* @param uri
* @param config
* @return
*/
public static String getValidExtensionUri(String uri, CompositeConfiguration config){
boolean noExtensionUri = config.getBoolean(Keys.URI_NO_EXTENSION);

if (noExtensionUri) {
uri = uri + "/index" + config.getString(Keys.OUTPUT_EXTENSION);
} else {
uri = uri + config.getString(Keys.OUTPUT_EXTENSION);
}
return uri;
}


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

import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.IOUtils;
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 @@ -140,6 +141,31 @@ public Map<String, Object> parse(Configuration config, File file, String content
content.put(Crawler.Attributes.TAGS, tags);
}

// If categories are not disabled then add a default category
if(config.getBoolean(Keys.CATEGORIES_ENABLE)){
if (content.get("categories") != null) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here it would be better to use Crawler.Attributes.CATEGORIES instead of a string.

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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here it would be better to use Crawler.Attributes.CATEGORIES instead of the string value "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));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here it would be better to use Crawler.Attributes.CATEGORIES instead of the string value "categories"

}
content.put("primary_category", ((String[])content.get("categories"))[0]);
} else {
if (content.get("categories") != null) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here it would be better to use Crawler.Attributes.CATEGORIES instead of the string value "categories"

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 @@ -233,7 +259,13 @@ private void processHeader(Configuration config, List<String> contents, final Ma
}
} else if (key.equalsIgnoreCase(Crawler.Attributes.TAGS)) {
content.put(key, getTags(value));
} else if (isJson(value)) {
} else if (key.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 (isJson(value)) {
content.put(key, JSONValue.parse(value));
} else {
content.put(key, value);
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/org/jbake/render/CategoriesRenderer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
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;

/**
*
* This class renders Post Categories.
*
* @author Manik Magar <manik.magar@gmail.com>
*
*/
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(config.getString(Keys.CATEGORY_PATH));
} catch (Exception e) {
throw new RenderingException(e);
}
} else {
return 0;
}
}

}
44 changes: 44 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,44 @@
package org.jbake.template.model;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import org.jbake.app.ConfigUtil.Keys;
import org.jbake.app.ContentStore;
import org.jbake.app.DocumentList;
import org.jbake.template.ModelExtractor;


/**
*
* This extractor model will list of categories from all published content.
*
* @author Manik Magar <manik.magar@gmail.com>
*
*/
public class AllCategoriesExtractor implements ModelExtractor<DocumentList> {

@Override
public DocumentList get(ContentStore db, Map model, String key) {
DocumentList dl = new DocumentList();
Map<String, Object> config = (Map<String, Object>) model.get("config");

String categoryPath = config.get(Keys.CATEGORY_PATH.replace(".", "_")).toString();

for (String category : db.getCategories()){
Map<String, Object> newCategory = new HashMap<String, Object>();
String tagName = category;
newCategory.put("name",tagName);

String uri = categoryPath + File.separator + category + config.get(Keys.OUTPUT_EXTENSION.replace(".", "_")).toString();

newCategory.put("uri", uri);
newCategory.put("posts", db.getPublishedPostsByCategories(category));
newCategory.put("documents", db.getPublishedDocumentsByCategory(category));
dl.push(newCategory);
}
return dl;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jbake.template.model;

import java.util.Map;

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


public class CategoryDocumentsExtractor 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();
}
DocumentList query = db.getPublishedDocumentsByCategory(category);
return query;
}

}
Loading