1   package com.atlassian.plugins.codegen.modules.confluence.blueprint;
2   
3   import com.atlassian.plugins.codegen.ClassId;
4   import com.atlassian.plugins.codegen.ComponentDeclaration;
5   import com.atlassian.plugins.codegen.modules.common.ContextProviderProperties;
6   import com.atlassian.plugins.codegen.modules.common.Resource;
7   import com.atlassian.plugins.codegen.modules.common.web.WebItemProperties;
8   import com.atlassian.plugins.codegen.modules.common.web.WebResourceProperties;
9   import com.atlassian.plugins.codegen.modules.common.web.WebResourceTransformation;
10  import com.atlassian.plugins.codegen.modules.common.web.WebResourceTransformer;
11  import com.google.common.collect.Lists;
12  
13  import java.util.List;
14  
15  import static com.atlassian.fugue.Option.some;
16  import static com.atlassian.plugins.codegen.modules.confluence.blueprint.BlueprintI18nProperty.*;
17  import static com.atlassian.plugins.codegen.modules.confluence.blueprint.BlueprintPromptEntry.*;
18  import static com.atlassian.plugins.codegen.modules.confluence.blueprint.BlueprintProperties.*;
19  import static com.atlassian.plugins.codegen.modules.confluence.blueprint.ContentTemplateProperties.CONTENT_I18N_DEFAULT_VALUE;
20  
21  /**
22   * Different to the {@link BlueprintModuleCreator}, the generator takes simple objects provided by the prompter
23   * and creates complex {@link BlueprintProperties}.
24   *
25   * This class is needed for Confluence Blueprints because unlike other SDK module-creators, the Blueprint creator
26   * adds multiple modules and converts a small amount of user input into a large data structure.
27   *
28   * @since 4.1.8
29   */
30  public class BlueprintBuilder
31  {
32      private final BlueprintPromptEntries promptProps;
33      private final BlueprintProperties props;
34      private BlueprintStringer stringer;
35  
36      public BlueprintBuilder(BlueprintPromptEntries promptProps)
37      {
38          this.promptProps = promptProps;
39          props = new BlueprintProperties();
40      }
41  
42      @SuppressWarnings("unchecked")
43      public BlueprintProperties build()
44      {
45          String pluginKey = promptProps.getPluginKey();
46          props.setPluginKey(pluginKey);
47  
48          String indexKey = (String) promptProps.get(BlueprintPromptEntry.INDEX_KEY_PROMPT);
49          props.setProperty(INDEX_KEY, indexKey);
50  
51          stringer = new BlueprintStringer(indexKey, props.getPluginKey());
52          String blueprintModuleKey = stringer.makeBlueprintModuleKey();
53          props.setModuleKey(blueprintModuleKey);
54  
55          WebItemProperties webItem = makeWebItem(pluginKey, blueprintModuleKey, indexKey, promptProps);
56          props.setWebItem(webItem);
57          String blueprintName = (String) promptProps.get(WEB_ITEM_NAME_PROMPT);
58          props.setModuleName(stringer.makeBlueprintModuleName(blueprintName));
59  
60          String indexTitleI18nKey = stringer.makeI18nKey("index.page.title");
61          props.setIndexTitleI18nKey(indexTitleI18nKey);
62          props.addI18nProperty(indexTitleI18nKey, blueprintName + "s");  // THIS IS NOT OPTIMISED
63  
64          List<String> contentTemplateKeys = (List<String>) promptProps.get(CONTENT_TEMPLATE_KEYS_PROMPT);
65          ContextProviderProperties contextProvider = null;
66          String packageName = cleanupPackage(pluginKey);
67          if ((Boolean) promptProps.get(CONTEXT_PROVIDER_PROMPT))
68          {
69              contextProvider = new ContextProviderProperties(packageName + ".ContentTemplateContextProvider");
70          }
71  
72          boolean hasDialogWizard = (Boolean)promptProps.get(DIALOG_WIZARD_PROMPT);
73          boolean skipEditor = (Boolean) promptProps.get(SKIP_PAGE_EDITOR_PROMPT);
74          String placeholderI18nKey = null;
75          String mentionPlaceholderI18nKey = null;
76          if (!skipEditor)
77          {
78              placeholderI18nKey = CONTENT_TEMPLATE_PLACEHOLDER.getI18nKey(stringer);
79              mentionPlaceholderI18nKey = CONTENT_TEMPLATE_MENTION_PLACEHOLDER.getI18nKey(stringer);
80              props.addI18nProperty(placeholderI18nKey, CONTENT_TEMPLATE_PLACEHOLDER.getI18nValue());
81              props.addI18nProperty(mentionPlaceholderI18nKey, CONTENT_TEMPLATE_MENTION_PLACEHOLDER.getI18nValue());
82          }
83  
84          for (int i = 0; i < contentTemplateKeys.size(); i++)
85          {
86              String contentTemplateKey = contentTemplateKeys.get(i);
87              String moduleName = stringer.makeContentTemplateName(blueprintName, i);
88              String description = "Contains Storage-format XML used by the " + blueprintName + " Blueprint";
89              ContentTemplateProperties contentTemplate = makeContentTemplate(contentTemplateKey, moduleName, contextProvider,
90                  pluginKey, CONTENT_I18N_DEFAULT_VALUE, description);
91  
92              contentTemplate.setProperty(DIALOG_WIZARD, String.valueOf(hasDialogWizard));
93              if (!skipEditor)
94              {
95                  contentTemplate.setProperty(CONTENT_TEMPLATE_PLACEHOLDER.getPropertyKey(), placeholderI18nKey);
96                  contentTemplate.setProperty(CONTENT_TEMPLATE_MENTION_PLACEHOLDER.getPropertyKey(), mentionPlaceholderI18nKey);
97              }
98  
99              props.addContentTemplate(contentTemplate);
100         }
101 
102         // Reused for icon CSS, how-to-use and dialog-wizard.
103         WebResourceProperties webResource = new WebResourceProperties();
104         webResource.addContext("atl.general");
105         webResource.addContext("atl.admin");
106         webResource.addDependency("com.atlassian.confluence.plugins.confluence-create-content-plugin:resources");
107 
108         Resource cssResource = new Resource();
109         cssResource.setType("download");
110         cssResource.setName("blueprints.css");
111         cssResource.setLocation("css/blueprints.css");
112         webResource.addResource(cssResource);
113         webResource.setProperty("INDEX_KEY", indexKey);
114 
115         props.setWebResource(webResource);
116 
117         boolean hasHowToUse = (Boolean)promptProps.get(HOW_TO_USE_PROMPT);
118 
119         String soyPackage = stringer.makeSoyTemplatePackage(blueprintName);
120 
121         if (hasHowToUse)
122         {
123             props.setHowToUseTemplate(soyPackage + ".howToUse");
124         }
125         if (hasDialogWizard)
126         {
127             DialogWizardProperties wizard = new DialogWizardProperties();
128             wizard.setModuleKey(indexKey + "-wizard");
129             DialogPageProperties wizardPage = new DialogPageProperties(0, soyPackage, stringer);
130             wizard.setDialogPages(Lists.newArrayList(wizardPage));
131             props.setDialogWizard(wizard);
132             addJsToWebResource(webResource);
133             addJsI18n(webResource);
134             webResource.setProperty(BlueprintProperties.PLUGIN_KEY, pluginKey);
135             webResource.setProperty(BlueprintProperties.WEB_ITEM_KEY, webItem.getModuleKey());
136             webResource.setProperty(BlueprintProperties.WIZARD_FORM_TITLE_FIELD_ID, indexKey + "-blueprint-page-title");
137         }
138         if (hasHowToUse || hasDialogWizard)
139         {
140             addSoyTemplateToWebResource(webResource, props.getPluginKey(), indexKey, soyPackage, hasHowToUse, hasDialogWizard);
141         }
142 
143         if ((Boolean)promptProps.get(EVENT_LISTENER_PROMPT))
144         {
145             ClassId classId = ClassId.packageAndClass(packageName, "BlueprintCreatedListener");
146             ComponentDeclaration component = ComponentDeclaration.builder(classId, "blueprint-created-listener")
147                 .name(some("Blueprint Created Event Listener"))
148                 .build();
149             props.setEventListener(component);
150         }
151 
152         if (skipEditor)
153         {
154             props.setCreateResult(BlueprintProperties.CREATE_RESULT_VIEW);
155         }
156 
157         if ((Boolean)promptProps.get(INDEX_PAGE_TEMPLATE_PROMPT))
158         {
159             String contentTemplateKey = INDEX_TEMPLATE_DEFAULT_KEY;
160             String moduleName = "Custom Index Page Content Template";
161             String description = "Contains Storage-format XML used by the " + blueprintName + " Blueprint's Index page";
162             ContentTemplateProperties contentTemplate = makeContentTemplate(contentTemplateKey, moduleName, contextProvider,
163                 pluginKey, ContentTemplateProperties.INDEX_TEMPLATE_CONTENT_VALUE, description);
164             props.setIndexPageContentTemplate(contentTemplate);
165         }
166 
167         return props;
168     }
169 
170     private String cleanupPackage(String pluginKey)
171     {
172         return pluginKey.replaceAll("[^a-z\\.]", "_");
173     }
174 
175     private void addJsI18n(WebResourceProperties properties)
176     {
177         addI18n(properties, WIZARD_FORM_TITLE_FIELD_LABEL);
178         addI18n(properties, WIZARD_FORM_TITLE_FIELD_PLACEHOLDER);
179         addI18n(properties, WIZARD_FORM_TITLE_FIELD_ERROR);
180         addI18n(properties, WIZARD_FORM_JSVAR_FIELD_LABEL);
181         addI18n(properties, WIZARD_FORM_JSVAR_FIELD_PLACEHOLDER);
182         addI18n(properties, WIZARD_FORM_PRE_RENDER_TEXT);
183         addI18n(properties, WIZARD_FORM_POST_RENDER_TEXT);
184         addI18n(properties, WIZARD_FORM_FIELD_REQUIRED);
185     }
186 
187     private void addI18n(WebResourceProperties properties, BlueprintI18nProperty i18n)
188     {
189         String i18nKey = i18n.getI18nKey(stringer);
190         properties.setProperty(i18n.getPropertyKey(), i18nKey);
191         properties.addI18nProperty(i18nKey, i18n.getI18nValue());
192     }
193 
194     private void addJsToWebResource(WebResourceProperties properties)
195     {
196         // JS transformer
197         WebResourceTransformation transformation = new WebResourceTransformation("js");
198         WebResourceTransformer transformer = new WebResourceTransformer();
199         transformer.setModuleKey("jsI18n");
200         transformation.addTransformer(transformer);
201         properties.addTransformation(transformation);
202 
203         Resource soyResource = new Resource();
204         soyResource.setType("download");
205         soyResource.setName("dialog-wizard.js");
206         soyResource.setLocation("js/dialog-wizard.js");
207         properties.addResource(soyResource);
208     }
209 
210     private void addSoyTemplateToWebResource(WebResourceProperties properties, String pluginKey, String indexKey,
211         String soyPackage,
212         boolean hasHowToUse, boolean hasDialogWizard)
213     {
214         // Soy transformer
215         WebResourceTransformation transformation = new WebResourceTransformation("soy");
216         WebResourceTransformer transformer = new WebResourceTransformer();
217         transformer.setModuleKey("soyTransformer");
218         transformer.addFunctions("com.atlassian.confluence.plugins.soy:soy-core-functions");
219         transformation.addTransformer(transformer);
220         properties.addTransformation(transformation);
221 
222         Resource soyResource = new Resource();
223         soyResource.setType("download");
224         soyResource.setName("templates-soy.js");
225         soyResource.setLocation("soy/my-templates.soy");
226         properties.addResource(soyResource);
227 
228         properties.setProperty(BlueprintProperties.SOY_PACKAGE, soyPackage);
229 
230         if (hasHowToUse)
231         {
232             properties.put("HOW_TO_USE", true);
233             addI18n(properties, HOW_TO_USE_HEADING);
234             addI18n(properties, HOW_TO_USE_CONTENT);
235         }
236 
237         if (hasDialogWizard)
238         {
239             properties.put(DIALOG_WIZARD, true);
240             properties.setProperty(INDEX_KEY, indexKey);
241         }
242     }
243 
244     private ContentTemplateProperties makeContentTemplate(String contentTemplateKey,
245         String moduleName, final ContextProviderProperties contextProvider, String pluginKey,
246         final String contentTextValue, final String description)
247     {
248         ContentTemplateProperties template = new ContentTemplateProperties(contentTemplateKey);
249 
250         template.setModuleName(moduleName);
251         template.setDescription(description);
252         template.setNameI18nKey(pluginKey + "." + contentTemplateKey + ".name");
253         template.setDescriptionI18nKey(pluginKey + "." + contentTemplateKey + ".desc");
254         template.setContentText(pluginKey + "." + contentTemplateKey + ".content.text", contentTextValue);
255 
256         if (contextProvider != null)
257         {
258             template.setContextProvider(contextProvider);
259         }
260 
261         Resource templateResource = new Resource();
262         templateResource.setName("template");
263         templateResource.setType("download");
264 
265         templateResource.setLocation("xml/" + contentTemplateKey + ".xml");
266         template.addResource(templateResource);
267 
268         return template;
269     }
270 
271     private WebItemProperties makeWebItem(String pluginKey, String blueprintModuleKey, String indexKey, BlueprintPromptEntries promptProps)
272     {
273         WebItemProperties webItem = new WebItemProperties();
274 
275         webItem.setModuleKey(blueprintModuleKey + "-web-item");
276         webItem.setModuleName((String) promptProps.get(WEB_ITEM_NAME_PROMPT));
277         webItem.setDescription((String) promptProps.get(WEB_ITEM_DESC_PROMPT));
278         webItem.setNameI18nKey(pluginKey + ".blueprint.display.name");
279         webItem.setDescriptionI18nKey(pluginKey + ".blueprint.display.desc");
280         webItem.setSection("system.create.dialog/content");
281         webItem.addParam(WEB_ITEM_BLUEPRINT_KEY, blueprintModuleKey);
282         webItem.setStyleClass("icon-" + indexKey + "-blueprint large");
283 
284         return webItem;
285     }
286 }