001/* 002 * Copyright 2011 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.translationflagging; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.List; 022import java.util.Map; 023import java.util.Map.Entry; 024import java.util.Set; 025 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.commons.lang.StringUtils; 029 030import org.ametys.core.observation.Event; 031import org.ametys.core.observation.ObservationManager; 032import org.ametys.core.ui.Callable; 033import org.ametys.core.ui.StaticClientSideElement; 034import org.ametys.plugins.repository.AmetysObjectIterable; 035import org.ametys.plugins.repository.AmetysObjectResolver; 036import org.ametys.plugins.repository.UnknownAmetysObjectException; 037import org.ametys.plugins.repository.metadata.CompositeMetadata; 038import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 039import org.ametys.plugins.repository.metadata.UnknownMetadataException; 040import org.ametys.web.ObservationConstants; 041import org.ametys.web.repository.page.ModifiablePage; 042import org.ametys.web.repository.page.Page; 043import org.ametys.web.repository.site.Site; 044import org.ametys.web.repository.site.SiteManager; 045import org.ametys.web.repository.sitemap.Sitemap; 046 047/** 048 * This element creates a ribbon button to flag translation on a page. 049 * Provides callable methods to set and get translations. 050 */ 051public class TranslationFlaggingClientSideElement extends StaticClientSideElement 052{ 053 /** Constant for the translations metadata name */ 054 public static final String TRANSLATIONS_META = "translations"; 055 056 private AmetysObjectResolver _resolver; 057 private SiteManager _siteManager; 058 private ObservationManager _observationManager; 059 060 @Override 061 public void service(ServiceManager smanager) throws ServiceException 062 { 063 super.service(smanager); 064 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 065 _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE); 066 _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE); 067 } 068 069 /** 070 * Get all the translations associated with a page 071 * @param pageId The page Id 072 * @return The page and its translations data 073 */ 074 @Callable 075 public Map<String, Object> getTranslations(String pageId) 076 { 077 Map<String, Object> result = new HashMap<>(); 078 079 Page page = _resolver.resolveById(pageId); 080 result.put("page", _page2json(page)); 081 082 Map<String, Page> translatedPages = _getTranslations(page); 083 084 List<Map<String, String>> translations = new ArrayList<>(); 085 for (Page translatedPage : translatedPages.values()) 086 { 087 translations.add(_page2json(translatedPage)); 088 } 089 result.put("translations", translations); 090 091 return result; 092 } 093 094 private Map<String, String> _page2json (Page page) 095 { 096 Map<String, String> pageData = new HashMap<>(); 097 098 pageData.put("id", page.getId()); 099 pageData.put("site", page.getSiteName()); 100 pageData.put("lang", page.getSitemapName()); 101 pageData.put("path", page.getPathInSitemap()); 102 pageData.put("title", page.getTitle()); 103 104 return pageData; 105 } 106 107 /** 108 * Set the translations for a specific page 109 * @param pageId The current page id 110 * @param translations an association language-pageId, to set as translations 111 * @return The list of updated pages. 112 */ 113 @Callable 114 public List<String> setTranslations(String pageId, Map<String, String> translations) 115 { 116 Page currentPage = _resolver.resolveById(pageId); 117 118 // Get the collection of pages to clean. 119 Set<Page> pagesToClean = new HashSet<>(_getTranslations(currentPage).values()); 120 121 // Get the translated pages from the request. 122 Map<String, Page> pages = _getTranslatedPages(translations, pageId); 123 124 // Compute all modified pages to notify the observer. 125 Set<Page> modifiedPages = new HashSet<>(pagesToClean); 126 modifiedPages.addAll(pages.values()); 127 128 // The old pages minus the new ones will be the pages to clean. 129 pagesToClean.removeAll(pages.values()); 130 131 // Clean the old pages: remove the translations metadata. 132 for (Page page : pagesToClean) 133 { 134 if (page instanceof ModifiablePage) 135 { 136 _cleanPage((ModifiablePage) page); 137 } 138 } 139 140 // Set the translations. 141 for (Page page : pages.values()) 142 { 143 if (page instanceof ModifiablePage) 144 { 145 _setTranslations((ModifiablePage) page, pages); 146 } 147 } 148 149 // Notify observers that page data has been changed 150 151 List<String> modifiedPageIds = new ArrayList<>(); 152 for (Page modifiedPage : modifiedPages) 153 { 154 if (modifiedPage != null) 155 { 156 modifiedPageIds.add(modifiedPage.getId()); 157 158 Map<String, Object> eventParams = new HashMap<>(); 159 eventParams.put(ObservationConstants.ARGS_PAGE, modifiedPage); 160 161 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_UPDATED, _currentUserProvider.getUser(), eventParams)); 162 } 163 } 164 165 return modifiedPageIds; 166 } 167 168 /** 169 * Get the translation of a given page. 170 * @param page the page. 171 * @return the translated pages as a Map of pages, indexed by sitemap name (language). 172 */ 173 protected Map<String, Page> _getTranslations(Page page) 174 { 175 Map<String, Page> translations = new HashMap<>(); 176 177 CompositeMetadata meta = page.getMetadataHolder(); 178 179 try 180 { 181 CompositeMetadata transMeta = meta.getCompositeMetadata(TRANSLATIONS_META); 182 for (String lang : transMeta.getMetadataNames()) 183 { 184 String translatedPageId = transMeta.getString(lang); 185 186 try 187 { 188 Page translatedPage = _resolver.resolveById(translatedPageId); 189 translations.put(lang, translatedPage); 190 } 191 catch (UnknownAmetysObjectException e) 192 { 193 // Ignore : a removed page should be ignored 194 } 195 } 196 } 197 catch (UnknownMetadataException e) 198 { 199 // Ignore : the translations composite metadata doesn't exist, just return an empty map. 200 } 201 202 return translations; 203 } 204 205 /** 206 * Get translated pages from the map. 207 * @param translations the associative list of translations. 208 * @param pageId the current page ID. 209 * @return the translated pages as a Map of pages, indexed by sitemap name. 210 */ 211 protected Map<String, Page> _getTranslatedPages(Map<String, String> translations, String pageId) 212 { 213 Map<String, Page> pages = new HashMap<>(); 214 215 Page currentPage = _resolver.resolveById(pageId); 216 String siteName = currentPage.getSiteName(); 217 218 pages.put(currentPage.getSitemapName(), currentPage); 219 220 Site site = _siteManager.getSite(siteName); 221 222 AmetysObjectIterable<Sitemap> sitemaps = site.getSitemaps(); 223 224 for (Sitemap sitemap : sitemaps) 225 { 226 String name = sitemap.getSitemapName(); 227 if (!name.equals(currentPage.getSitemapName())) 228 { 229 if (translations.containsKey(name) && StringUtils.isNotBlank(translations.get(name))) 230 { 231 Page page = _resolver.resolveById(translations.get(name)); 232 if (name.equals(page.getSitemapName())) 233 { 234 pages.put(name, page); 235 } 236 } 237 else 238 { 239 pages.put(name, null); 240 } 241 } 242 } 243 244 return pages; 245 } 246 247 /** 248 * Set the translations of a page in its metadata. 249 * @param page the page. 250 * @param pages the translated pages to set. 251 */ 252 protected void _setTranslations(ModifiablePage page, Map<String, Page> pages) 253 { 254 ModifiableCompositeMetadata meta = page.getMetadataHolder(); 255 256 ModifiableCompositeMetadata transMeta = meta.getCompositeMetadata(TRANSLATIONS_META, true); 257 258 for (Entry<String, Page> entry : pages.entrySet()) 259 { 260 if (!entry.getKey().equals(page.getSitemapName())) 261 { 262 if (entry.getValue() == null) 263 { 264 // Remove metadata. 265 if (transMeta.hasMetadata(entry.getKey())) 266 { 267 transMeta.removeMetadata(entry.getKey()); 268 } 269 } 270 else 271 { 272 transMeta.setMetadata(entry.getKey(), entry.getValue().getId()); 273 } 274 } 275 } 276 277 page.saveChanges(); 278 } 279 280 /** 281 * Clean a page of its translations metadata. 282 * @param page the page to clean. 283 */ 284 protected void _cleanPage(ModifiablePage page) 285 { 286 ModifiableCompositeMetadata meta = page.getMetadataHolder(); 287 288 if (meta.hasMetadata(TRANSLATIONS_META)) 289 { 290 meta.removeMetadata(TRANSLATIONS_META); 291 page.saveChanges(); 292 } 293 } 294 295}