/** * Copyright 2011-2012 Alexandre Dutra * * 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 fr.dutra.confluence2wordpress.core.metadata; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; import com.atlassian.confluence.content.render.xhtml.XhtmlException; import com.atlassian.confluence.content.render.xhtml.definition.PlainTextMacroBody; import com.atlassian.confluence.core.ContentEntityObject; import com.atlassian.confluence.xhtml.api.MacroDefinition; import com.atlassian.confluence.xhtml.api.MacroDefinitionHandler; import com.atlassian.confluence.xhtml.api.MacroDefinitionUpdater; import com.atlassian.confluence.xhtml.api.XhtmlContent; import fr.dutra.confluence2wordpress.util.CodecUtils; import fr.dutra.confluence2wordpress.wp.WordpressCategory; import fr.dutra.confluence2wordpress.wp.WordpressUser; /** * @author Alexandre Dutra * */ public class DefaultMetadataManager implements MetadataManager { //see com.atlassian.confluence.content.render.xhtml.XhtmlConstants //see com.atlassian.confluence.content.render.xhtml.storage.macro.StorageMacroConstants private static final String WORDPRESS_SYNC_INFO_TAG = "<ac:macro ac:name=\""+ WORDPRESS_SYNC_INFO_MACRO_NAME + "\" />"; private static final String WORDPRESS_META_TAG_START = "<ac:macro ac:name=\""+ WORDPRESS_METADATA_MACRO_NAME + "\">"; private static final String WORDPRESS_META_TAG_END = "</ac:macro>"; private static final String BODY_TAG_START = "<ac:plain-text-body><![CDATA["; private static final String BODY_TAG_END = "]]></ac:plain-text-body>"; private static final Pattern DRAFT_PREFIX_PATTERN = Pattern.compile("(DRAFT\\s*-\\s*).+"); private static final String RDP_CATEGORY_NAME = "Revue de presse"; private static final String RDP_PAGE_TITLE = "Revue de Presse Xebia"; private static final String RDP_POST_SLUG_FORMAT = "revue-de-presse-xebia-%1$tY-%2$02d"; private static final String XEBIA_FRANCE_LOGIN = "XebiaFrance"; private final XhtmlContent xhtmlUtils; private final ObjectMapper mapper; public DefaultMetadataManager(XhtmlContent xhtmlUtils) { super(); this.xhtmlUtils = xhtmlUtils; this.mapper = new ObjectMapper(); } public Metadata extractMetadata(ContentEntityObject page) throws MetadataException { //https://developer.atlassian.com/display/CONFDEV/Creating+a+new+Confluence+4.0+Macro MacroDefinition metadataMacro = extractMacroDefinition(page, WORDPRESS_METADATA_MACRO_NAME); if(metadataMacro == null) { return null; } String macroBody = metadataMacro.getBodyText(); if(StringUtils.isEmpty(macroBody)) { return null; } return unmarshalMetadata(macroBody); } public void storeMetadata(ContentEntityObject page, Metadata metadata) throws MetadataException { String macroBody = marshalMetadata(metadata); if( ! updateMacroDefinition(page, WORDPRESS_METADATA_MACRO_NAME, macroBody)) { StringBuilder newContent = new StringBuilder(); newContent.append(page.getBodyAsString()); newContent.append(buildMetadataMacroTag(macroBody)); page.setBodyAsString(newContent.toString()); } MacroDefinition syncInfoMacro = extractMacroDefinition(page, WORDPRESS_SYNC_INFO_MACRO_NAME); if(syncInfoMacro == null) { StringBuilder newContent = new StringBuilder(); newContent.append(WORDPRESS_SYNC_INFO_TAG); newContent.append(page.getBodyAsString()); page.setBodyAsString(newContent.toString()); } } public Metadata createMetadata( ContentEntityObject page, Set<WordpressUser> users, Set<WordpressCategory> categories) { Metadata metadata = new Metadata(); String pageTitle = page.getTitle(); Matcher matcher = DRAFT_PREFIX_PATTERN.matcher(pageTitle); if(matcher.matches()){ String prefix = matcher.group(1); metadata.setPageTitle(StringUtils.substringAfter(pageTitle, prefix)); } else { metadata.setPageTitle(pageTitle); } if(StringUtils.containsIgnoreCase(metadata.getPageTitle(), RDP_CATEGORY_NAME)) { metadata.setPageTitle(RDP_PAGE_TITLE); // to normalize the title if(categories != null) { for (WordpressCategory category : categories) { if(StringUtils.containsIgnoreCase(category.getCategoryName(), RDP_CATEGORY_NAME)){ metadata.setCategoryNames(Collections.singletonList(category.getCategoryName())); break; } } } Calendar now = Calendar.getInstance(); metadata.setPostSlug(String.format(RDP_POST_SLUG_FORMAT, now.getTime(), now.get(Calendar.WEEK_OF_YEAR))); if(users != null) { for (WordpressUser user : users) { if(XEBIA_FRANCE_LOGIN.equals(user.getLogin())){ metadata.setAuthorId(user.getId()); break; } } } } else { String creatorName = page.getCreatorName(); if(creatorName != null && users != null) { for (WordpressUser wordpressUser : users) { if(creatorName.equals(wordpressUser.getLogin())){ metadata.setAuthorId(wordpressUser.getId()); break; } } } } return metadata; } public Metadata unmarshalMetadata(String macroBody) throws MetadataException { try { String expanded = CodecUtils.decodeAndExpand(macroBody); return mapper.readValue(expanded, Metadata.class); } catch (JsonParseException e) { throw new MetadataException("Cannot unmarshal macro body: " + macroBody, e); } catch (JsonMappingException e) { throw new MetadataException("Cannot unmarshal macro body: " + macroBody, e); } catch (IOException e) { throw new MetadataException("Cannot unmarshal macro body: " + macroBody, e); } } public String marshalMetadata(Metadata metadata) throws MetadataException { try { String json = mapper.writeValueAsString(metadata); return CodecUtils.compressAndEncode(json); } catch (JsonParseException e) { throw new MetadataException("Cannot marshal metadata: " + metadata, e); } catch (JsonMappingException e) { throw new MetadataException("Cannot marshal metadata: " + metadata, e); } catch (IOException e) { throw new MetadataException("Cannot marshal metadata: " + metadata, e); } } private StringBuilder buildMetadataMacroTag(String macroBody) throws MetadataException { StringBuilder macro = new StringBuilder(); macro.append(WORDPRESS_META_TAG_START); macro.append(BODY_TAG_START); macro.append(macroBody); macro.append(BODY_TAG_END); macro.append(WORDPRESS_META_TAG_END); return macro; } private MacroDefinition extractMacroDefinition(ContentEntityObject page, final String macroName) throws MetadataException { final List<MacroDefinition> definitions = new ArrayList<MacroDefinition>(); try { xhtmlUtils.handleMacroDefinitions(page.getBodyAsString(), null, new MacroDefinitionHandler() { @Override public void handle(MacroDefinition macroDefinition) { if(macroDefinition.getName().equals(macroName)){ definitions.add(macroDefinition); } } }); } catch (XhtmlException e) { throw new MetadataException("Could not parse page: " + page.getTitle(), e); } if(definitions.isEmpty()) { return null; } MacroDefinition definition = definitions.get(0); return definition; } private boolean updateMacroDefinition(ContentEntityObject page, final String macroName, final String macroBody) throws MetadataException { final List<MacroDefinition> definitions = new ArrayList<MacroDefinition>(); String body; try { body = xhtmlUtils.updateMacroDefinitions(page.getBodyAsString(), null, new MacroDefinitionUpdater() { @Override public MacroDefinition update(MacroDefinition macroDefinition) { if(macroName.equals(macroDefinition.getName())){ macroDefinition.setBody(new PlainTextMacroBody(macroBody)); definitions.add(macroDefinition); } return macroDefinition; } }); } catch (XhtmlException e) { throw new MetadataException("Could not parse page: " + page.getTitle(), e); } if(definitions.isEmpty()) { return false; } page.setBodyAsString(body); return true; } }