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
155            || contextParameters != null && "admin".equals(contextParameters.get(WorkspaceMatcher.WORKSPACE_NAME))
156            || hasRight(getRights(contextParameters)))
157        {
158            List<Script> scripts = super.getScripts(ignoreRights, contextParameters);
159            for (Script script : scripts)
160            {
161                cssFiles.addAll(script.getCSSFiles());
162                scriptFiles.addAll(script.getScriptFiles());
163                parameters.putAll(script.getParameters());
164            }
165            
166            if (_primaryMenuItem != null)
167            {
168                for (Script script : _primaryMenuItem.getScripts(ignoreRights, contextParameters))
169                {
170                    Map<String, Object> primaryParameters = script.getParameters();
171                    parameters.put("primary-menu-item-id", script.getId());
172                    for (String paramId : primaryParameters.keySet())
173                    {
174                        if (!parameters.containsKey(paramId))
175                        {
176                            parameters.put(paramId, primaryParameters.get(paramId));
177                        }
178                    }
179                }
180            }
181            
182            // Menu items
183            if (_menuItems.size() > 0)
184            {
185                List<String> menuItems = new ArrayList<>();
186                for (ClientSideElement element : _menuItems)
187                {
188                    menuItems.add(element.getId());
189                }
190                parameters.put("menu-items", menuItems);
191            }
192
193            result.add(new Script(this.getId(), _script.getScriptClassname(), scriptFiles, cssFiles, parameters));
194        }
195        
196        return result;
197    }
198    
199    @Override
200    public List<ClientSideElement> getReferencedClientSideElements(Map<String, Object> contextParameters)
201    {
202        if (hasRight(getRights(contextParameters)))
203        {
204            return Collections.unmodifiableList(_referencedClientSideElement);
205        }
206        else
207        {
208            return List.of();
209        }
210    }
211    
212    
213    private void _resolveMenuItems() throws Exception
214    {
215        if (_unresolvedMenuItems != null)
216        {
217            _menuItemManager.initialize();
218            
219            for (UnresolvedItem unresolvedItem : _unresolvedMenuItems)
220            {
221                String id = unresolvedItem.getId();
222                ClientSideElement element;
223                if (unresolvedItem.isLocalItem())
224                {
225                    try
226                    {
227                        element = _menuItemManager.lookup(id);
228                    }
229                    catch (ComponentException e)
230                    {
231                        throw new Exception("Unable to lookup client side element role: '" + id + "'", e);
232                    }
233                }
234                else
235                {
236                    element = _ribbonControlManager.getExtension(id);
237                }
238                
239                if (unresolvedItem.isPrimary())
240                {
241                    _primaryMenuItem = element;
242                }
243                
244                _menuItems.add(element);
245                _referencedClientSideElement.add(element);
246            }
247        }
248        
249        _unresolvedMenuItems = null;
250    }
251    
252    class UnresolvedItem
253    {
254        private String _itemId;
255        private boolean _local;
256        private boolean _primary;
257        
258        public UnresolvedItem(String id, boolean local)
259        {
260            _itemId = id;
261            _local = local;
262            _primary = false;
263        }
264        
265        public UnresolvedItem(String id, boolean local, boolean primary)
266        {
267            _itemId = id;
268            _local = local;
269            _primary = primary;
270        }
271        
272        public String getId ()
273        {
274            return _itemId;
275        }
276        
277        public boolean isLocalItem ()
278        {
279            return _local;
280        }
281        
282        public boolean isPrimary ()
283        {
284            return _primary;
285        }
286    }
287}