I want to render in a template an image that originates from a ClientBundle.
But what I get instead is this:
<img="#">
I've checked the contents of the datasource and if I copy the image (data:image/png;base64...) in {0} it works.
Why it doesn't work if I pass it as a parameter?
DataResource inputCheckImage = MGWTStyle.getTheme().getMGWTClientBundle().inputCheckImage();
private static Template TEMLPATE = GWT.create(Template.class);
public interface Template extends SafeHtmlTemplates
{
#SafeHtmlTemplates.Template("<div><img src=\"{0}\"></div>")
SafeHtml content(String image);
}
//#Override
public void render(SafeHtmlBuilder safeHtmlBuilder, Action model) {
SafeUri url = inputCheckImage.getSafeUri();
SafeHtml safeHtml = TEMLPATE.content(url.asString());
safeHtmlBuilder.append(safeHtml);
}
Use SafeUri as the argument type on your template, otherwise the value will be sanitized and a data: URI is considered unsafe.
Related
In AEM, content such as pages and images contains the '/content/' prefix in them. We are able to rewrite these url via Link Checker Transformer configuration and resourceResolver.map() method. URLs are being rewritten for HTML elements <a> and <form>.
But I want it to work for <img> elements as well.
I tried including the <img> elements to the Link Checker Transformer configuration by adding it to the 'Rewrite Elements' list as img:src:
I also checked the answers from What am I missing for this CQ5/AEM URL rewriting scenario? but both attempts didn't work for this issue.
Is there any way to do this?
Even if the rewriter and Link Checker Transformer didn't work. I used a custom LinkRewriter by using the Transformer and TransformerFactory interfaces. I based on the sample from Adobe for my code. I worked out something like this:
#Component(
metatype = true,
label = "Image Link Rewriter",
description = "Maps the <img> elements src attributes"
)
#Service(value = TransformerFactory.class)
#Property(value = "global", propertyPrivate = true)
public class ImageLinkRewriter implements Transformer, TransformerFactory {
// some variables
public CustomLinkTransformer() { }
#Override
public void init(ProcessingContext context,
ProcessingComponentConfiguration config) throws IOException {
// initializations here
}
#Override
public final Transformer createTransformer() {
return new CustomLinkTransformer();
}
#Override
public void startElement(String uri, String localName,
String qName, Attributes atts) throws SAXException {
if ("img".equalsIgnoreCase(localName)) {
contentHandler.startElement(uri, localName, qName, rewriteImageLink(atts));
}
}
private Attributes rewriteImageLink(Attributes attrs) {
String attrName = "src";
AttributesImpl result = new AttributesImpl(attrs);
String link = attrs.getValue(attrName);
String mappedLink = resource.getResourceResolver().map(request, link);
result.setValue(result.getIndex(attrName), mappedLink);
return result;
}
}
Hope this helps others. Here are a few references:
TransformerFactory interface
Transformer interface
Adobe
I just wrote my first Wicket component :) It contains a ListView with some Radio input fields. Now I want to unit test if a selected value makes its way to the model.
As WicketTester.newFormTester("myForm") expects a form, I try to create a form on the fly:
public void testDataBinding()
{
Model model = ...
MyRadioComponent myRadioComponent = new MyRadioComponent (...);
Form form = new Form("myForm", ...);
form.add(myRadioComponent);
WicketTester wicketTester = new WicketTester();
wicketTester.startComponentInPage(form);
// FormTester formTester = wicketTester.newFormTester("myForm");
// ...
}
Now wicketTester.startComponentInPage(form) results in:
Failed: Component [myForm] (path = [0:x]) must be applied to a tag of type [form],
not: '<span wicket:id="myForm" id="myForm3">'
Any idea how to fix this and/or how to test such an input component the right way?
OK, in detail the solution now looks like this:
public FormTester createFormTester(Component c) {
final WicketTester wicketTester = new WicketTester();
final FormPage page = new FormPage(c);
wicketTester.startPage(page);
return wicketTester.newFormTester(page.getPathToForm());
}
private static class FormPage extends WebPage implements IMarkupResourceStreamProvider {
private final Form<Void> form;
private final Component c;
private FormPage(final Component c) {
this.c = c;
add(form = new Form<>("form"));
form.add(c);
}
public String getPathToForm() {
return form.getPageRelativePath();
}
#Override
public IResourceStream getMarkupResourceStream(MarkupContainer container, Class<?> containerClass) {
return new StringResourceStream(
"<html><body>"
+ "<form wicket:id='" + form.getId() + "'><span wicket:id='" + c.getId() + "'/></form>"
+ "</body></html>");
}
}
I believe startComponentInPage uses a <span> for its component. Wicket checks that Forms are attached to <form> tags which is why you get this error.
You need to create your own test page with a <form> inside it. See org.apache.wicket.markup.html.form.NumberTextFieldTest for an example of inline markup. Otherwise, create a Form test page class with associated html markup file.
I use junit to assert the existing of wicket components:
wicketTester.assertComponent("dev1WicketId:dev2WicketId:formWicketId", Form.class);
This works for some forms. For complex structure, it is defficult to find out the path of the form by searching all html files. Is there any method how to find out the path easy?
If you have the component you can call #getPageRelativePath(). E.g.
// Supposing c is a component that has been added to the page.
// Returns the full path to the component relative to the page, e.g., "path:to:label"
String pathToComponent = c.getPageRelativePath();
You can get the children of a markup container by using the visitChildren() method. The following example shows how to get all the Forms from a page.
List<Form> list = new ArrayList<Form<?>>();
Page page = wicketTester.getLastRenderedPage();
for (Form form : page.visitChildren(Form.class)) {
list.add(form);
}
An easy way to get those is to call getDebugSettings().setOutputComponentPath(true); when initializing your application. This will make Wicket to output these paths to the generated HTML as an attribute on every component-bound tag.
It's recommended to only enable this on debug mode, though:
public class WicketApplication extends WebApplication {
#Override
public void init() {
super.init();
if (getConfigurationType() == RuntimeConfigurationType.DEVELOPMENT) {
getDebugSettings().setOutputComponentPath(true);
}
}
}
Extending the RJo's answer.
It seems that the method page.visitChildren(<Class>) is deprecated (Wicket 6), so with an IVisitor it can be :
protected String findPathComponentOnLastRenderedPage(final String idComponent) {
final Page page = wicketTester.getLastRenderedPage();
return page.visitChildren(Component.class, new IVisitor<Component, String>() {
#Override
public void component(final Component component, final IVisit<String> visit) {
if (component.getId().equals(idComponent)) {
visit.stop(component.getPageRelativePath());
}
}
});
}
We have a web application that needs a different theme for each major client. The original developer did this by looking at the URL in javascript and adding a stylesheet to override the default theme.
One problem with this is the site has the default look for a few seconds then suddenly swaps to the correct theme. Another is that it seems to waste a lot of bandwidth/time.
My current idea is to create a "default" ClientBundle with our default look and feel extend that interface and override each entry (as needed) with the client's images using the various annotations like #ImageResouce and pointing to a different location.
Has anybody had experience doing this? One problem I forsee is not being able to use the uibinder style tags as they statically point to a specific resource bundle.
Any ideas?
Overriden bundles
Yes you can.
I've did the override thing with ClientBundles and works fine. One thing you MUST do is inherit the types of the properties too. By example:
BigBundle {
Nestedundle otherBundle();
ImageResource otherImage();
Styles css();
}
And then you must inherit this way:
OtherBigBundle extends BigBundle {
OtherNestedBundle otherBundle(); // if you want to change it
ImageResource otherImage(); // of you want to change it
OtherStyles css(); // of you want to change it
}
and OtherNestedBundle extends NestedBundle
and OtherStyles extends Styles
At least with css's: if the properties are declared NOT USING the child interface they will produce styles for the same CSS classname and all will be mixed. So declare overriden styles with the child interfaces :)
Flexible UIBinders
You can set from outside the bundle to use if you use UiField(provided=true) annotation. In this way you first set the bundle and then call the uibindler. It will use the resource field assuming it's already created.
Deferred binding
You could use GWT.runAsync for loading just the correct bundle.
Some example
The ui.xml
<ui:with field='res' type='your.package.TheBundle'/>
the corresponding class
#UiField(provided=true) TheBundle bundle;
private void createTheThing() {
this.bundle = factory.createBundle();
MyUiBindler binder = GWT.create(MyUiBindler.class);
this.panel = binder.createAndBindUi(this);
...
}
Some bundle interfaces
interface TheBundle extends ClientBundle {
#ImageResource("default.png")
ImageResource image1();
#Source("default.css")
TheCss css();
}
interface Theme1Bundle extends TheBundle {
#ImageResource("one.png")
ImageResource image1(); // type: imageresource is ok
#Source("one.css")
OneCss css(); // type: OneCss => use other compiled css class-names
interface OneCss extends TheCss { // inner-interface, just for fun
// don't need to declare each String method
}
}
If you don't override something it's ok
Options for the bundle factory
1) just altogether
if (...) {
return GWT.create(TheBundle.class);
} else if (...) {
return GWT.create(Theme1Bundle.class);
}
2) runAsync (just load the needed part... but after the initial part is executed)
if (...) {
GWT.runAsync(new RunAsyncCallback() {
public void onSuccess() {
return GWT.create(TheBundle.class);
}
// please program the onFailure method
});
} else if (...) {
GWT.runAsync(new RunAsyncCallback() {
public void onSuccess() {
return GWT.create(Theme1Bundle.class);
}
// please program the onFailure method
});
}
3) use deferred-binding and generators for autogenerating factory in compile-time based on annotated bundles like #ThemeBundle("one")
This example is from the real world. I use a DynamicEntryPointWidgetFactory (DEPWidgetFactory for short) for creating widget based on an identifier string. Each widget is an application screen and each main menu ítem has the widgetName it has to create.
In your case the id will be the theme to create.
Important: if you use runAsync you cannot create the resourcebundle just before creating the UI like in the sample code before. You must ask for the theme and when it's ready (in the callback) pass it to your widget constructor and your widget can assign it to its field.
The factory interface:
public interface DynamicEntryPointWidgetFactory
{
public void buildWidget(String widgetName, AsyncCallback<Widget> callback);
}
The annotation for widgets to generate:
#Target(ElementType.TYPE)
public #interface EntryPointWidget
{
/**
* The name wich will be used to identify this widget.
*/
String value();
}
The module configuration:
It says: the implementation for the Factory will be generated with this class (the other option is to use replace-with, but in our case we don't have predefined options for each locale or browser, but something more dynamic).
<generate-with class="com.dia.nexdia.services.gwt.rebind.entrypoint.DynamicEntryPointFactoryGenerator">
<when-type-assignable class="com.dia.nexdia.services.gwt.client.entrypoint.DynamicEntryPointWidgetFactory" />
</generate-with>
The generator:
public class DynamicEntryPointFactoryGenerator extends Generator {
#Override
public String generate(TreeLogger logger, GeneratorContext context,
String typeName) throws UnableToCompleteException {
PrintWriter pw = context.tryCreate(logger,
"x.services.gwt.client.entrypoint",
"DynamicEntryPointWidgetFactoryImpl");
if (pw != null) {
// write package, imports, whatever
pw.append("package x.services.gwt.client.entrypoint;");
pw.append("import x.services.gwt.client.entrypoint.DynamicEntryPointWidgetFactory;");
pw.append("import com.google.gwt.core.client.GWT;");
pw.append("import com.google.gwt.core.client.RunAsyncCallback;");
pw.append("import com.google.gwt.user.client.rpc.AsyncCallback;");
pw.append("import com.google.gwt.user.client.ui.Widget;");
// the class
pw.append("public class DynamicEntryPointWidgetFactoryImpl implements DynamicEntryPointWidgetFactory {");
// buildWidget method
pw.append(" public void buildWidget(String widgetName, final AsyncCallback<Widget> callback) {");
// iterates over all the classes to find those with EntryPointWidget annotation
TypeOracle oracle = context.getTypeOracle();
JPackage[] packages = oracle.getPackages();
for (JPackage pack : packages)
{
JClassType[] classes = pack.getTypes();
for (JClassType classtype : classes)
{
EntryPointWidget annotation = classtype.getAnnotation(EntryPointWidget.class);
if (annotation != null)
{
String fullName = classtype.getQualifiedSourceName();
logger.log(TreeLogger.INFO, "Entry-point widget found: " + fullName);
pw.append("if (\"" + annotation.value() + "\".equals(widgetName)) {");
pw.append(" GWT.runAsync(" + fullName + ".class, new RunAsyncCallback() {");
pw.append(" public void onFailure(Throwable t) {");
pw.append(" callback.onFailure(t);");
pw.append(" }");
pw.append(" public void onSuccess() {");
pw.append(" callback.onSuccess(new " + fullName + "());");
pw.append(" }");
pw.append(" });");
pw.append(" return;");
pw.append("}");
}
}
}
pw.append("callback.onFailure(new IllegalArgumentException(\"Widget '\" + widgetName + \"' not recognized.\"));");
pw.append(" }");
pw.append("}");
context.commit(logger, pw);
}
// return the name of the generated class
return "x.services.gwt.client.entrypoint.DynamicEntryPointWidgetFactoryImpl";
}
I want to create a pagination helper. The only parameters that it needs are currentpage, pagecount and routename.
However, I do not know if it is possible to use the return value of another html helper inside the definition of my html helper. I am referring specifically to Html.RouteLink. How can I go about doing something like this in the class definition
using System;
using System.Web.Mvc;
namespace MvcApplication1.Helpers
{
public static class LabelExtensions
{
public static string Label(this HtmlHelper helper, string routeName, int currentpage, int totalPages)
{
string html = "";
//Stuff I add to html
//I'd like to generate a similar result as the helper bellow.
//This code does not work, it's just an example of what I'm trying to accomplish
html .= Html.RouteLink( pageNo, routeName, new { page = pageNo - 1 } );
//Other stuff I do the html
return html;
}
}
}
Thank you.
Generally, yes you can use the results of other Html Helper functions within your custom functions. The exception would be any that write directly to the response stream rather than returning a string value.
I've done this sort of thing several times myself, and it works just fine...here's a sample I just totally made up right now based on something I did that I don't have the code for handy right now:
public static string RssFeed(this HtmlHelper helper, string url)
{
StringBuilder sb = new StringBuilder();
sb.Append(GetRSSMarkup(url)); // This generates the markup for the feed data
sb.Append(helper.ActionLink("Go Home","Index","Home"));
return sb.ToString();
}