Is it possible to reuse code from atlassian *.soy files like createRepo.soy for own plugin?

I wanted to create window similar to 'Create Project' which is described in createRepo.soy template file. I created servlet which displays .soy file and at the begginin I wanted to just display copy of createRepo.soy. Servlet code looks like that:

@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        final SoyFileSet sfs = new SoyFileSet.Builder().add(new File("src/main/resources/cloneRepo/createRepo.soy")).build();
        final SoyTofu.Renderer renderer = sfs.compileToJavaObj().newRenderer("stash.page.createRepo");

        resp.setContentType("text/html");
        resp.getWriter().write(renderer.render());
    }

Probably some parameters like project are mising, but that's not important for now. With this code I get errors like that:

[WARNING] [talledLocalContainer] SEVERE: Servlet.service() for servlet plugins threw exception

[WARNING] [talledLocalContainer] com.google.template.soy.base.SoySyntaxException: In file src/main/resources/cloneRepo/createRepo.soy:11: Unrecognized function 'webResourceManager_requireResourcesForContext' (function call "webResourceManager_requireResourcesForContext('internal.page.createRepo')").

Is it possible to somehow reuse this file without modifying it completley?
Or maybe my whole approach is wrong?

1 answer

1 accepted

2 votes
Jason Hinch Atlassian Team Jul 08, 2012

This is definitely possible but just as a note, you shouldn't consider our soy templates as a public API. We provide no guarantee that these templates won't change or that they work correctly from within a plugin.

That said, what you need to do is add the atlassian-soy-template-renderer-api as a provided dependency to your plugin

<dependency>
  <groupId>com.atlassian.soy</groupId>
  <artifactId>soy-template-renderer-api</artifactId>
  <version>1.1.1</version>
  <scope>provided</scope>
</dependency>

You can then use the SoyTemplateRenderer to render the template. All templates are based off web resources for dependency resolution of files and custom functions. All of Stash's server side templates are currently available in the com.atlassian.stash.stash-web-plugin:server-soy-templates web resource module.

So for instance to render the createRepo page you can do the following:

soyTemplateRenderer.render(response.getWriter(), "com.atlassian.stash.stash-web-plugin:server-soy-templates", "stash.page.createRepo", new HashMap(), new HashMap());

Thanks for your help! I'm glat that it's possible at all :)

Unlucky when I tried to follow your suggestion I encountered some more problems.
First of all, from where in my servlet I should get soyTemplateRenderer? I looked at source code and found out that it's injected by spring and should be accesible as bean. So I did something like that:

WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
SoyTemplateRenderer soyTemplateRenderer = (SoyTemplateRenderer) context.getBean(SoyTemplateRenderer.class);

Unlucky, this doesn't work as I expected:

[WARNING] [talledLocalContainer] SEVERE: Servlet.service() for servlet plugins threw exception

[WARNING] [talledLocalContainer] java.lang.IllegalStateException: Context attribute is not of type WebApplicationContext: Root WebApplicationContext: startup date [Mon Jul 09 18:15:02 CEST 2012]; root of context hierarchy

Can you tell me what am I doing wrong or at least point me to some resources where I could find answer myself?

Colin Goudie Community Champion Jul 09, 2012

Just use constructor injection and also include the SoyTemplateRenderer in a component-import in your atlassian-plugin.xml

Thanks Colin! Somehow I completely forgot about this possibility. Unlucky I have one more problem. I tried to add:

<component-import key="soyTemplateRenderer" interface="com.atlassian.soy.renderer.SoyTemplateRenderer" />

to my atlassian-plugin.xml but it seems to be not working properly. If I put this line right after plugin-info section then whole plugin seems to be no installed correctly (and it says that there is 0 modules at all).

I'm sure I'm missing something really simple, but I have no idea what it could be.

Jason Hinch Atlassian Team Jul 11, 2012

Was there an exception in the logs?

Somehow it works now. I guess I had one more subltle bug in atlassian-plugin.xml.

I guess I'm almost there. I have - hopefully - last guestion. My code currently looks like that:

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
final Project project = projectService.findByKey("PROJECT_1");
if (project != null) {
try {
soyTemplateRenderer.render(
resp.getWriter(),
"com.atlassian.stash.stash-web-plugin:server-soy-templates",
"stash.page.createRepo",
ImmutableMap.<String, Object>of(
"project", project
)
);
} catch (SoyException e) {
e.printStackTrace()
}
}
}


I need to pass project parameter to the template, but I don't know how it should look like. I only know that it must be able to work with this:

{{param contentTitle: cav_i18n('stash.web.repository.create.repository', 'Create a Repository in {0}', $project.name) /}}

and

{foreach $webPanel in getWebPanels('stash.web.repository.create.beforefields', [ 'project' : $project ])}

I tried to put as parameter something like that:

ImmutableMap.<String, Object>of(
"project", project,
"project.name", project.getName()
)

but then I get:

[WARNING] [talledLocalContainer] com.google.template.soy.tofu.SoyTofuException: In template stash.page.createRepo: When evaluating "getWebPanels('stash.web.repository.create.beforefields', $ij.request, ['project': $project])": Error while computing function "getWebPanels('stash.web.repository.create.beforefields', $ij.request, ['project': $project])": Non-string found when expecting string value.

And for parameter like:

ImmutableMap.<String, Object>of(
"project", project.getKey(),
"project.name", project.getName()
)

I'm getting:

WARNING] [talledLocalContainer] com.google.template.soy.data.SoyDataException: Failed to evaluate key string "project.name" for put().


I tried few different variants but without success so far...

I wanted to try something simplier, so I replaced "stash.page.createRepo" with "stash.page.createProject" which does not require any parameters. Unlucky it still generates errors:

[WARNING] [talledLocalContainer] com.google.template.soy.tofu.SoyTofuException: In template stash.page.createProject: When evaluating "getWebPanels('stash.web.project.create.beforefields', $ij.request)": Error while computing function "getWebPanels('stash.web.project.create.beforefields', $ij.request)": Non-string found when expecting string value.

But this mean it's not related to missing parameter, but rather to some not loaded component or whatever else. Do you have any idea what could be the reason guys?

Ok, I got it now. Couple of parameters were missing, but that's it.

To check what is required it's worth to look at SoyView, InjectedDataFactory and SoyResponseBuilder (which unlucky are internal ones, so I had to rewrite part of their functionality).

Suggest an answer

Log in or Sign up to answer
How to earn badges on the Atlassian Community

How to earn badges on the Atlassian Community

Badges are a great way to show off community activity, whether you’re a newbie or a Champion.

Learn more
Community showcase
Posted Thursday in Off-topic

Friday Fun: Magic Eyes

...staring into the background. Once the image pops out in 3D, you can look around the picture and enjoy. If you will see if you are a true illusion master! :) You did it? :) Wow! Awesome! As a bonus...

425 views 79 11
Join discussion

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you