diff options
author | James Moger <james.moger@gitblit.com> | 2013-06-19 16:26:32 -0400 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2013-06-19 16:26:58 -0400 |
commit | 430496317177893eeb94579b2946dbafea6d0727 (patch) | |
tree | 85a37b386d1d144d51e2644d302ec00b7f691583 /src/main/java/com/gitblit/wicket/freemarker | |
parent | bdfb4cbb8175c09beaf77c7270d36403b127a0de (diff) | |
download | gitblit-430496317177893eeb94579b2946dbafea6d0727.tar.gz gitblit-430496317177893eeb94579b2946dbafea6d0727.zip |
Generate filterable project/repository list with FreeMarker
Diffstat (limited to 'src/main/java/com/gitblit/wicket/freemarker')
4 files changed, 388 insertions, 0 deletions
diff --git a/src/main/java/com/gitblit/wicket/freemarker/Freemarker.java b/src/main/java/com/gitblit/wicket/freemarker/Freemarker.java new file mode 100644 index 00000000..ad7aa964 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/freemarker/Freemarker.java @@ -0,0 +1,46 @@ +/*
+ * Copyright 2013 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.freemarker;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+import freemarker.template.Configuration;
+import freemarker.template.DefaultObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+public class Freemarker {
+
+ private static final Configuration fm;
+
+ static {
+ fm = new Configuration();
+ fm.setObjectWrapper(new DefaultObjectWrapper());
+ fm.setOutputEncoding("UTF-8");
+ fm.setClassForTemplateLoading(Freemarker.class, "templates");
+ }
+
+ public static Template getTemplate(String name) throws IOException {
+ return fm.getTemplate(name);
+ }
+
+ public static void evaluate(Template template, Map<String, Object> values, Writer out) throws TemplateException, IOException {
+ template.process(values, out);
+ }
+
+}
diff --git a/src/main/java/com/gitblit/wicket/freemarker/FreemarkerPanel.java b/src/main/java/com/gitblit/wicket/freemarker/FreemarkerPanel.java new file mode 100644 index 00000000..d57c3a09 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/freemarker/FreemarkerPanel.java @@ -0,0 +1,308 @@ +/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.freemarker;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Map;
+
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.IMarkupCacheKeyProvider;
+import org.apache.wicket.markup.IMarkupResourceStreamProvider;
+import org.apache.wicket.markup.MarkupStream;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.StringResourceStream;
+import org.apache.wicket.util.string.Strings;
+
+import com.gitblit.utils.StringUtils;
+
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+/**
+ * This class allows FreeMarker to be used as a Wicket preprocessor or as a
+ * snippet injector for something like a CMS. There are some cases where Wicket
+ * is not flexible enough to generate content, especially when you need to generate
+ * hybrid HTML/JS content outside the scope of Wicket.
+ *
+ * @author James Moger
+ *
+ */
+@SuppressWarnings("unchecked")
+public class FreemarkerPanel extends Panel
+ implements
+ IMarkupResourceStreamProvider,
+ IMarkupCacheKeyProvider
+{
+ private static final long serialVersionUID = 1L;
+
+ private final String template;
+ private boolean parseGeneratedMarkup;
+ private boolean escapeHtml;
+ private boolean throwFreemarkerExceptions;
+ private transient String stackTraceAsString;
+ private transient String evaluatedTemplate;
+
+
+ /**
+ * Construct.
+ *
+ * @param id
+ * Component id
+ * @param template
+ * The Freemarker template
+ * @param values
+ * values map that can be substituted by Freemarker.
+ */
+ public FreemarkerPanel(final String id, String template, final Map<String, Object> values)
+ {
+ this(id, template, Model.ofMap(values));
+ }
+
+ /**
+ * Construct.
+ *
+ * @param id
+ * Component id
+ * @param templateResource
+ * The Freemarker template as a string resource
+ * @param model
+ * Model with variables that can be substituted by Freemarker.
+ */
+ public FreemarkerPanel(final String id, final String template, final IModel< ? extends Map<String, Object>> model)
+ {
+ super(id, model);
+ this.template = template;
+ }
+
+ /**
+ * Gets the Freemarker template.
+ *
+ * @return the Freemarker template
+ */
+ private Template getTemplate()
+ {
+ if (StringUtils.isEmpty(template))
+ {
+ throw new IllegalArgumentException("Template not specified!");
+ }
+
+ try {
+ return Freemarker.getTemplate(template);
+ } catch (IOException e) {
+ onException(e);
+ }
+
+ return null;
+ }
+
+ /**
+ * @see org.apache.wicket.markup.html.panel.Panel#onComponentTagBody(org.apache.wicket.markup.
+ * MarkupStream, org.apache.wicket.markup.ComponentTag)
+ */
+ @Override
+ protected void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag)
+ {
+ if (!Strings.isEmpty(stackTraceAsString))
+ {
+ // TODO: only display the Freemarker error/stacktrace in development
+ // mode?
+ replaceComponentTagBody(markupStream, openTag, Strings
+ .toMultilineMarkup(stackTraceAsString));
+ }
+ else if (!parseGeneratedMarkup)
+ {
+ // check that no components have been added in case the generated
+ // markup should not be
+ // parsed
+ if (size() > 0)
+ {
+ throw new WicketRuntimeException(
+ "Components cannot be added if the generated markup should not be parsed.");
+ }
+
+ if (evaluatedTemplate == null)
+ {
+ // initialize evaluatedTemplate
+ getMarkupResourceStream(null, null);
+ }
+ replaceComponentTagBody(markupStream, openTag, evaluatedTemplate);
+ }
+ else
+ {
+ super.onComponentTagBody(markupStream, openTag);
+ }
+ }
+
+ /**
+ * Either print or rethrow the throwable.
+ *
+ * @param exception
+ * the cause
+ * @param markupStream
+ * the markup stream
+ * @param openTag
+ * the open tag
+ */
+ private void onException(final Exception exception)
+ {
+ if (!throwFreemarkerExceptions)
+ {
+ // print the exception on the panel
+ stackTraceAsString = Strings.toString(exception);
+ }
+ else
+ {
+ // rethrow the exception
+ throw new WicketRuntimeException(exception);
+ }
+ }
+
+ /**
+ * Gets whether to escape HTML characters.
+ *
+ * @return whether to escape HTML characters. The default value is false.
+ */
+ public void setEscapeHtml(boolean value)
+ {
+ this.escapeHtml = value;
+ }
+
+ /**
+ * Evaluates the template and returns the result.
+ *
+ * @param templateReader
+ * used to read the template
+ * @return the result of evaluating the velocity template
+ */
+ private String evaluateFreemarkerTemplate(Template template)
+ {
+ if (evaluatedTemplate == null)
+ {
+ // Get model as a map
+ final Map<String, Object> map = (Map<String, Object>)getDefaultModelObject();
+
+ // create a writer for capturing the Velocity output
+ StringWriter writer = new StringWriter();
+
+ // string to be used as the template name for log messages in case
+ // of error
+ try
+ {
+ // execute the Freemarker script and capture the output in writer
+ Freemarker.evaluate(template, map, writer);
+
+ // replace the tag's body the Freemarker output
+ evaluatedTemplate = writer.toString();
+
+ if (escapeHtml)
+ {
+ // encode the result in order to get valid html output that
+ // does not break the rest of the page
+ evaluatedTemplate = Strings.escapeMarkup(evaluatedTemplate).toString();
+ }
+ return evaluatedTemplate;
+ }
+ catch (IOException e)
+ {
+ onException(e);
+ }
+ catch (TemplateException e)
+ {
+ onException(e);
+ }
+ return null;
+ }
+ return evaluatedTemplate;
+ }
+
+ /**
+ * Gets whether to parse the resulting Wicket markup.
+ *
+ * @return whether to parse the resulting Wicket markup. The default is false.
+ */
+ public void setParseGeneratedMarkup(boolean value)
+ {
+ this.parseGeneratedMarkup = value;
+ }
+
+ /**
+ * Whether any Freemarker exception should be trapped and displayed on the panel (false) or thrown
+ * up to be handled by the exception mechanism of Wicket (true). The default is false, which
+ * traps and displays any exception without having consequences for the other components on the
+ * page.
+ * <p>
+ * Trapping these exceptions without disturbing the other components is especially useful in CMS
+ * like applications, where 'normal' users are allowed to do basic scripting. On errors, you
+ * want them to be able to have them correct them while the rest of the application keeps on
+ * working.
+ * </p>
+ *
+ * @return Whether any Freemarker exceptions should be thrown or trapped. The default is false.
+ */
+ public void setThrowFreemarkerExceptions(boolean value)
+ {
+ this.throwFreemarkerExceptions = value;
+ }
+
+ /**
+ * @see org.apache.wicket.markup.IMarkupResourceStreamProvider#getMarkupResourceStream(org.apache
+ * .wicket.MarkupContainer, java.lang.Class)
+ */
+ public final IResourceStream getMarkupResourceStream(MarkupContainer container,
+ Class< ? > containerClass)
+ {
+ Template template = getTemplate();
+ if (template == null)
+ {
+ throw new WicketRuntimeException("could not find Freemarker template for panel: " + this);
+ }
+
+ // evaluate the template and return a new StringResourceStream
+ StringBuffer sb = new StringBuffer();
+ sb.append("<wicket:panel>");
+ sb.append(evaluateFreemarkerTemplate(template));
+ sb.append("</wicket:panel>");
+ return new StringResourceStream(sb.toString());
+ }
+
+ /**
+ * @see org.apache.wicket.markup.IMarkupCacheKeyProvider#getCacheKey(org.apache.wicket.
+ * MarkupContainer, java.lang.Class)
+ */
+ public final String getCacheKey(MarkupContainer container, Class< ? > containerClass)
+ {
+ // don't cache the evaluated template
+ return null;
+ }
+
+ /**
+ * @see org.apache.wicket.Component#onDetach()
+ */
+ @Override
+ protected void onDetach()
+ {
+ super.onDetach();
+ stackTraceAsString = null;
+ evaluatedTemplate = null;
+ }
+}
diff --git a/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableProjectList.fm b/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableProjectList.fm new file mode 100644 index 00000000..691f089b --- /dev/null +++ b/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableProjectList.fm @@ -0,0 +1,15 @@ +<div ng-controller="${ngCtrl}" style="border: 1px solid #ddd;border-radius: 4px;margin-bottom: 20px;">
+ <div class="header" style="padding: 5px;border: none;"><i class="icon-folder-close"></i> <span wicket:id="${ngList}Title"></span>
+ <div style="padding: 5px 0px 0px;">
+ <input type="text" ng-model="query.n" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
+ </div>
+ </div>
+
+ <div ng-repeat="item in ${ngList} | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
+ <a href="project/{{item.p}}" title="{{item.i}}"><b>{{item.n}}</b></a>
+ <span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>
+ <span class="pull-right">
+ <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;" wicket:message="title:gb.repositories">{{item.c | number}}</span>
+ </span>
+ </div>
+</div>
\ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableRepositoryList.fm b/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableRepositoryList.fm new file mode 100644 index 00000000..cf1b7a8b --- /dev/null +++ b/src/main/java/com/gitblit/wicket/freemarker/templates/FilterableRepositoryList.fm @@ -0,0 +1,19 @@ +<div ng-controller="${ngCtrl}" style="border: 1px solid #ddd;border-radius: 4px;">
+ <div class="header" style="padding: 5px;border: none;"><i wicket:id="${ngList}Icon"></i> <span wicket:id="${ngList}Title"></span>
+ <div class="hidden-phone pull-right">
+ <span wicket:id="${ngList}Button"></span>
+ </div>
+ <div style="padding: 5px 0px 0px;">
+ <input type="text" ng-model="query.r" class="input-large" wicket:message="placeholder:gb.filter" style="border-radius: 14px; padding: 3px 14px;margin: 0px;"></input>
+ </div>
+ </div>
+
+ <div ng-repeat="item in ${ngList} | filter:query" style="padding: 3px;border-top: 1px solid #ddd;">
+ <b><span class="repositorySwatch" style="background-color:{{item.c}};"><span ng-show="item.wc">!</span><span ng-show="!item.wc"> </span></span></b>
+ <a href="summary/?r={{item.r}}" title="{{item.i}}">{{item.p}}<b>{{item.n}}</b></a>
+ <span class="link hidden-tablet hidden-phone" style="color: #bbb;" title="{{item.d}}">{{item.t}}</span>
+ <span ng-show="item.s" class="pull-right">
+ <span style="padding: 0px 5px;color: #888;font-weight:bold;vertical-align:middle;">{{item.s | number}} <i style="vertical-align:baseline;" class="iconic-star"></i></span>
+ </span>
+ </div>
+</div>
\ No newline at end of file |