001/*
002 *  Copyright 2013 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.cms.clientsideelement;
017
018import java.util.ArrayList;
019import java.util.Collections;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023
024import org.apache.avalon.framework.component.ComponentException;
025import org.apache.avalon.framework.configuration.Configuration;
026import org.apache.avalon.framework.configuration.ConfigurationException;
027import org.apache.avalon.framework.configuration.DefaultConfiguration;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.slf4j.LoggerFactory;
031
032import org.ametys.core.ui.ClientSideElement;
033import org.ametys.core.ui.MenuClientSideElement;
034import org.ametys.core.ui.RibbonControlsManager;
035import org.ametys.core.ui.SimpleMenu;
036import org.ametys.core.ui.StaticClientSideElement;
037import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
038import org.ametys.runtime.workspace.WorkspaceMatcher;
039
040/**
041 * A smart menu to use for content
042 *
043 */
044public class SmartContentMenu extends SmartContentClientSideElement implements MenuClientSideElement
045{
046    /** The client side element component manager for menu items. */
047    protected ThreadSafeComponentManager<ClientSideElement> _menuItemManager;
048    /** The ribbon control manager */
049    protected RibbonControlsManager _ribbonControlManager;
050    /** The service manager. */
051    protected ServiceManager _serviceManager;
052    
053    /** The menu items */
054    protected List<ClientSideElement> _menuItems;
055    /** The primary menu item */
056    protected ClientSideElement _primaryMenuItem;
057
058    private List<ClientSideElement> _referencedClientSideElement;
059    private List<UnresolvedItem> _unresolvedMenuItems;
060    
061    @Override
062    public void service(ServiceManager serviceManager) throws ServiceException
063    {
064        super.service(serviceManager);
065        _serviceManager = serviceManager;
066        _ribbonControlManager = (RibbonControlsManager) serviceManager.lookup(RibbonControlsManager.ROLE);
067    }
068    
069    @Override
070    public void configure(Configuration configuration) throws ConfigurationException
071    {
072        _menuItemManager = new ThreadSafeComponentManager<>();
073        _menuItemManager.setLogger(LoggerFactory.getLogger("cms.plugin.threadsafecomponent"));
074        _menuItemManager.service(_serviceManager);
075        
076        super.configure(configuration);
077        
078        // Menu items.
079        _referencedClientSideElement = new ArrayList<>();
080        _unresolvedMenuItems = new ArrayList<>();
081        _menuItems = new ArrayList<>();
082        
083        _configureItemsMenu(configuration);
084    }
085    
086    /**
087     * Configure the items menu
088     * @param configuration the configuration
089     * @throws ConfigurationException If the configuration is not correct.
090     */
091    protected void _configureItemsMenu(Configuration configuration) throws ConfigurationException
092    {
093        for (Configuration menuItemConfiguration : configuration.getChildren("menu-items"))
094        {
095            for (Configuration itemConfig : menuItemConfiguration.getChildren("item"))
096            {
097                boolean isPrimary = itemConfig.getAttributeAsBoolean("primaryItem", false);
098                
099                if (itemConfig.getAttribute("ref", null) != null)
100                {
101                    _unresolvedMenuItems.add(new UnresolvedItem(itemConfig.getAttribute("ref"), false, isPrimary));
102                }
103                else
104                {
105                    String id = itemConfig.getAttribute("id");
106
107                    DefaultConfiguration conf = new DefaultConfiguration("extension");
108                    conf.setAttribute("id", id);
109                    conf.addChild(itemConfig.getChild("class"));
110                    
111                    if (itemConfig.getChild("menu-items", false) != null || itemConfig.getChild("gallery-item", false) != null)
112                    {
113                        if (itemConfig.getChild("menu-items", false) != null)
114                        {
115                            conf.addChild(itemConfig.getChild("menu-items"));
116                        }
117                        
118                        if (itemConfig.getChild("gallery-item", false) != null)
119                        {
120                            conf.addChild(itemConfig.getChild("gallery-item"));
121                        }
122                        
123                        _menuItemManager.addComponent(_pluginName, null, id, SimpleMenu.class, conf);
124                    }
125                    else
126                    {
127                        _menuItemManager.addComponent(_pluginName, null, id, StaticClientSideElement.class, conf);
128                    }
129                    _unresolvedMenuItems.add(new UnresolvedItem(id, true, isPrimary));
130                }
131            }
132        }        
133    }
134    
135    @Override
136    public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters)
137    {
138        List<Script> result = new ArrayList<>();
139        
140        try
141        {
142            _resolveMenuItems();
143        }
144        catch (Exception e)
145        {
146            throw new IllegalStateException("Unable to lookup client side element local components", e);
147        }
148        
149        Map<String, Object> parameters = new HashMap<>();
150        List<ScriptFile> cssFiles = new ArrayList<>();
151        List<ScriptFile> scriptFiles = new ArrayList<>();
152
153        // FIXME handle rights for workspace admin, here is a temporary workaround
154        if (ignoreRights || (contextParameters != null && "admin".equals(contextParameters.get(WorkspaceMatcher.WORKSPACE_NAME))) || hasRight(getRights(contextParameters)))
155        {
156            List<Script> scripts = super.getScripts(ignoreRights, contextParameters);
157            for (Script script : scripts)
158            {
159                cssFiles.addAll(script.getCSSFiles());
160                scriptFiles.addAll(script.getScriptFiles());
161                parameters.putAll(script.getParameters());
162            }
163            
164            if (_primaryMenuItem != null)
165            {
166                for (Script script : _primaryMenuItem.getScripts(ignoreRights, contextParameters))
167                {
168                    Map<String, Object> primaryParameters = script.getParameters();
169                    parameters.put("primary-menu-item-id", script.getId());
170                    for (String paramId : primaryParameters.keySet())
171                    {
172                        if (!parameters.containsKey(paramId))
173                        {
174                            parameters.put(paramId, primaryParameters.get(paramId));
175                        }
176                    }
177                }
178            }
179            
180            // Menu items
181            if (_menuItems.size() > 0)
182            {
183                List<String> menuItems = new ArrayList<>();
184                for (ClientSideElement element : _menuItems)
185                {
186                    menuItems.add(element.getId());
187                }
188                parameters.put("menu-items", menuItems);
189            }
190
191            result.add(new Script(this.getId(), _script.getScriptClassname(), scriptFiles, cssFiles, parameters));
192        }
193        
194        return result;
195    }
196    
197    @Override
198    public List<ClientSideElement> getReferencedClientSideElements()
199    {
200        return Collections.unmodifiableList(_referencedClientSideElement);
201    }
202    
203    
204    private void _resolveMenuItems() throws Exception
205    {
206        if (_unresolvedMenuItems != null)
207        {
208            _menuItemManager.initialize();
209            
210            for (UnresolvedItem unresolvedItem : _unresolvedMenuItems)
211            {
212                String id = unresolvedItem.getId();
213                ClientSideElement element;
214                if (unresolvedItem.isLocalItem())
215                {
216                    try
217                    {
218                        element = _menuItemManager.lookup(id);
219                    }
220                    catch (ComponentException e)
221                    {
222                        throw new Exception("Unable to lookup client side element role: '" + id + "'", e);
223                    }
224                }
225                else
226                {
227                    element = _ribbonControlManager.getExtension(id);
228                }
229                
230                if (unresolvedItem.isPrimary())
231                {
232                    _primaryMenuItem = element;
233                }
234                
235                _menuItems.add(element);
236                _referencedClientSideElement.add(element);
237            }
238        }
239        
240        _unresolvedMenuItems = null;
241    }
242    
243    class UnresolvedItem
244    {
245        private String _itemId;
246        private boolean _local;
247        private boolean _primary;
248        
249        public UnresolvedItem(String id, boolean local)
250        {
251            _itemId = id;
252            _local = local;
253            _primary = false;
254        }
255        
256        public UnresolvedItem(String id, boolean local, boolean primary)
257        {
258            _itemId = id;
259            _local = local;
260            _primary = primary;
261        }
262        
263        public String getId ()
264        {
265            return _itemId;
266        }
267        
268        public boolean isLocalItem ()
269        {
270            return _local;
271        }
272        
273        public boolean isPrimary ()
274        {
275            return _primary;
276        }
277    }
278}