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}