001/* 002 * Copyright 2012 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.web.clientsideelement; 017 018import java.util.HashMap; 019import java.util.Map; 020import java.util.Set; 021 022import org.apache.avalon.framework.configuration.Configuration; 023import org.apache.avalon.framework.configuration.ConfigurationException; 024import org.apache.avalon.framework.configuration.DefaultConfiguration; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.commons.lang.StringUtils; 028 029import org.ametys.cms.contenttype.ContentType; 030import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 031import org.ametys.cms.contenttype.ContentTypesHelper; 032import org.ametys.cms.repository.Content; 033import org.ametys.core.DevMode; 034import org.ametys.core.DevMode.DEVMODE; 035import org.ametys.core.observation.Event; 036import org.ametys.core.observation.ObservationManager; 037import org.ametys.core.ui.Callable; 038import org.ametys.core.ui.SimpleMenu; 039import org.ametys.core.ui.StaticClientSideElement; 040import org.ametys.core.util.I18nUtils; 041import org.ametys.plugins.repository.AmetysObject; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.runtime.i18n.I18nizableText; 044import org.ametys.runtime.model.View; 045import org.ametys.web.ObservationConstants; 046import org.ametys.web.contenttype.SkinContentViewHelper; 047import org.ametys.web.contenttype.SkinContentViewHelper.SkinContentView; 048import org.ametys.web.repository.page.ContentTypesAssignmentHandler; 049import org.ametys.web.repository.page.ModifiableZoneItem; 050import org.ametys.web.repository.page.SitemapElement; 051import org.ametys.web.repository.page.ZoneItem.ZoneType; 052import org.ametys.web.skin.SkinsManager; 053 054/** 055 * Menu giving the possibility to change the view rendering the content in a given ZoneItem. 056 */ 057public class SetContentViewMenu extends SimpleMenu 058{ 059 /** The content type extension point. */ 060 protected ContentTypeExtensionPoint _contentTypeExtensionPoint; 061 062 /** The content types assignment handler */ 063 protected ContentTypesAssignmentHandler _cTypeHandler; 064 065 /** The content types helper */ 066 protected ContentTypesHelper _contentTypesHelper; 067 068 /** Repository content */ 069 protected AmetysObjectResolver _resolver; 070 071 /** Helper for skin views */ 072 protected SkinContentViewHelper _skinContentViewHelper; 073 074 /** The observation manager */ 075 protected ObservationManager _observationManager; 076 077 /** The skins manager */ 078 protected SkinsManager _skinsManager; 079 080 /** The i18n utils */ 081 protected I18nUtils _i18nUtils; 082 083 private boolean _galleryInitialized; 084 085 086 @Override 087 public void service(ServiceManager serviceManager) throws ServiceException 088 { 089 super.service(serviceManager); 090 _contentTypeExtensionPoint = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE); 091 _cTypeHandler = (ContentTypesAssignmentHandler) serviceManager.lookup(ContentTypesAssignmentHandler.ROLE); 092 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 093 _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE); 094 _contentTypesHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE); 095 _observationManager = (ObservationManager) serviceManager.lookup(ObservationManager.ROLE); 096 _skinsManager = (SkinsManager) serviceManager.lookup(SkinsManager.ROLE); 097 _skinContentViewHelper = (SkinContentViewHelper) serviceManager.lookup(SkinContentViewHelper.ROLE); 098 } 099 100 @Override 101 protected void _getGalleryItems(Map<String, Object> parameters, Map<String, Object> contextualParameters) 102 { 103 try 104 { 105 _lazyInitializeContentViewGallery(); 106 } 107 catch (Exception e) 108 { 109 throw new IllegalStateException("Unable to lookup client side element local components", e); 110 } 111 112 super._getGalleryItems(parameters, contextualParameters); 113 } 114 115 private synchronized void _lazyInitializeContentViewGallery() throws ConfigurationException 116 { 117 DEVMODE devMode = DevMode.getDeveloperMode(null); 118 119 if (!_galleryInitialized || devMode == DEVMODE.DEVELOPMENT || devMode == DEVMODE.SUPER_DEVELOPPMENT) 120 { 121 if (_galleryInitialized) 122 { 123 // reinitialize the gallery items 124 _galleryItems.clear(); 125 _initializeGalleryItemManager(); 126 } 127 128 Set<String> contentTypesIds = _contentTypeExtensionPoint.getExtensionsIds(); 129 130 if (contentTypesIds.size() > 0) 131 { 132 GalleryItem galleryItem = new GalleryItem(); 133 134 for (String contentTypeId : contentTypesIds) 135 { 136 ContentType contentType = _contentTypeExtensionPoint.getExtension(contentTypeId); 137 138 GalleryGroup galleryGroup = new GalleryGroup(contentType.getLabel()); 139 galleryItem.addGroup(galleryGroup); 140 141 for (String viewName : contentType.getViewNames(false)) 142 { 143 View view = contentType.getView(viewName); 144 145 String id = this.getId() + "." + contentTypeId + "." + viewName; 146 147 Configuration conf = _getViewItemConfiguration(id, view, contentTypeId); 148 _getGalleryItemManager().addComponent(_pluginName, null, id, StaticClientSideElement.class, conf); 149 galleryGroup.addItem(new UnresolvedItem(id, true)); 150 } 151 152 // Get other views from all skins 153 for (String skinName : _skinsManager.getSkins()) 154 { 155 Set<SkinContentView> skinViews = _skinContentViewHelper.getContentViewsFromSkin(skinName, contentType); 156 for (SkinContentView skinView : skinViews) 157 { 158 String id = this.getId() + "." + skinName + "." + contentTypeId + "." + skinView.name(); 159 Configuration conf = _getViewItemConfiguration(id, skinView, contentTypeId); 160 _getGalleryItemManager().addComponent(_pluginName, null, id, StaticClientSideElement.class, conf); 161 galleryGroup.addItem(new UnresolvedItem(id, true)); 162 } 163 } 164 } 165 166 _galleryItems.add(galleryItem); 167 } 168 169 if (_galleryItems.size() > 0) 170 { 171 try 172 { 173 _getGalleryItemManager().initialize(); 174 } 175 catch (Exception e) 176 { 177 throw new ConfigurationException("Unable to lookup parameter local components", e); 178 } 179 } 180 } 181 182 _galleryInitialized = true; 183 } 184 185 /** 186 * Get the configuration for a view item 187 * @param itemId The item id 188 * @param view The view 189 * @param cTypeId The content type id 190 * @return The configuration 191 */ 192 protected Configuration _getViewItemConfiguration(String itemId, View view, String cTypeId) 193 { 194 return new ViewItemConfigurationBuilder(itemId, this._script, _i18nUtils) 195 .withName(view.getName()) 196 .withLabel(view.getLabel()) 197 .withDescription(view.getDescription()) 198 .withContentType(cTypeId) 199 .withIconGlyph(view.getIconGlyph(), view.getIconDecorator()) 200 .withIcon(view.getSmallIcon(), view.getMediumIcon(), view.getLargeIcon()) 201 .build(); 202 } 203 204 /** 205 * Get the configuration for a skin view item 206 * @param itemId The item id 207 * @param view The skin view 208 * @param cTypeId The content type id 209 * @return The configuration 210 */ 211 protected Configuration _getViewItemConfiguration(String itemId, SkinContentView view, String cTypeId) 212 { 213 return new ViewItemConfigurationBuilder(itemId, this._script, _i18nUtils) 214 .withName(view.name()) 215 .withSkin(view.skinName()) 216 .withLabel(view.label()) 217 .withDescription(view.description()) 218 .withContentType(cTypeId) 219 .withIconGlyph(view.iconGlyph(), view.iconDecorator()) 220 .withIcon(view.iconSmall(), view.iconMedium(), view.iconLarge()) 221 .build(); 222 } 223 224 225 /** 226 * Builder to get a view item configuration 227 * 228 */ 229 public static class ViewItemConfigurationBuilder 230 { 231 private DefaultConfiguration _classConf; 232 private I18nUtils _i18nUtils; 233 private String _itemId; 234 private Script _script; 235 236 ViewItemConfigurationBuilder(String itemId, Script script, I18nUtils i18nUtils) 237 { 238 _itemId = itemId; 239 _script = script; 240 _i18nUtils = i18nUtils; 241 _classConf = new DefaultConfiguration("class"); 242 _classConf.setAttribute("name", "Ametys.ribbon.element.ui.ButtonController"); 243 } 244 245 /** 246 * Set the view name. This parameter is mandatory 247 * @param viewName the view name 248 * @return this view item configuration 249 */ 250 public ViewItemConfigurationBuilder withName(String viewName) 251 { 252 DefaultConfiguration nameConf = new DefaultConfiguration("name"); 253 nameConf.setValue(viewName); 254 _classConf.addChild(nameConf); 255 return this; 256 } 257 258 /** 259 * Set the skin name. May be null for a view common to all skin 260 * @param skinName the skin name 261 * @return this view item configuration 262 */ 263 public ViewItemConfigurationBuilder withSkin(String skinName) 264 { 265 DefaultConfiguration nameConf = new DefaultConfiguration("skinName"); 266 nameConf.setValue(skinName); 267 _classConf.addChild(nameConf); 268 return this; 269 } 270 /** 271 * Set the item's label. This parameter is mandatory 272 * @param label the label 273 * @return this view item configuration 274 */ 275 public ViewItemConfigurationBuilder withLabel(I18nizableText label) 276 { 277 DefaultConfiguration labelConf = new DefaultConfiguration("label"); 278 labelConf.setValue(_i18nUtils.translate(label)); 279 _classConf.addChild(labelConf); 280 return this; 281 } 282 283 /** 284 * Set the item's description. This parameter is mandatory 285 * @param description the description 286 * @return this view item configuration 287 */ 288 public ViewItemConfigurationBuilder withDescription(I18nizableText description) 289 { 290 DefaultConfiguration descConf = new DefaultConfiguration("description"); 291 descConf.setValue(_i18nUtils.translate(description)); 292 _classConf.addChild(descConf); 293 return this; 294 } 295 296 /** 297 * Set the content type. This parameter is mandatory 298 * @param cTypeId the content type id. 299 * @return this view item configuration 300 */ 301 public ViewItemConfigurationBuilder withContentType(String cTypeId) 302 { 303 DefaultConfiguration cTypeConf = new DefaultConfiguration("contentType"); 304 cTypeConf.setValue(cTypeId); 305 _classConf.addChild(cTypeConf); 306 return this; 307 } 308 309 /** 310 * Set the icon glyph and decorator. May be null. 311 * @param iconGlyph the icon glyph. May be null. 312 * @param iconDecorator the icon decorator. May be null. 313 * @return this view item configuration 314 */ 315 public ViewItemConfigurationBuilder withIconGlyph(String iconGlyph, String iconDecorator) 316 { 317 if (iconGlyph != null) 318 { 319 DefaultConfiguration iconGlyphConf = new DefaultConfiguration("icon-glyph"); 320 iconGlyphConf.setValue(iconGlyph); 321 _classConf.addChild(iconGlyphConf); 322 323 if (iconDecorator != null) 324 { 325 DefaultConfiguration iconDecoratorConf = new DefaultConfiguration("icon-decorator"); 326 iconDecoratorConf.setValue(iconDecorator); 327 _classConf.addChild(iconDecoratorConf); 328 } 329 } 330 331 return this; 332 } 333 334 /** 335 * Set the icon. May be null. 336 * @param iconSmall the small icon (16x16 pixels) 337 * @param iconMedium the medium icon (32x32 pixels) 338 * @param iconLarge the large icon (48x48 pixels) 339 * @return this view item configuration 340 */ 341 public ViewItemConfigurationBuilder withIcon(String iconSmall, String iconMedium, String iconLarge) 342 { 343 if (iconMedium != null) 344 { 345 DefaultConfiguration iconSmallConf = new DefaultConfiguration("icon-small"); 346 iconSmallConf.setValue(iconSmall); 347 _classConf.addChild(iconSmallConf); 348 DefaultConfiguration iconMediumConf = new DefaultConfiguration("icon-medium"); 349 iconMediumConf.setValue(iconMedium); 350 _classConf.addChild(iconMediumConf); 351 DefaultConfiguration iconLargeConf = new DefaultConfiguration("icon-large"); 352 iconLargeConf.setValue(iconLarge); 353 _classConf.addChild(iconLargeConf); 354 } 355 return this; 356 } 357 358 /** 359 * Build the configuration 360 * @return the configuration 361 */ 362 public Configuration build() 363 { 364 DefaultConfiguration conf = new DefaultConfiguration("extension"); 365 conf.setAttribute("id", _itemId); 366 367 // Common configuration 368 @SuppressWarnings("unchecked") 369 Map<String, Object> commonConfig = (Map<String, Object>) _script.getParameters().get("items-config"); 370 for (String tagName : commonConfig.keySet()) 371 { 372 DefaultConfiguration c = new DefaultConfiguration(tagName); 373 c.setValue(String.valueOf(commonConfig.get(tagName))); 374 _classConf.addChild(c); 375 } 376 377 conf.addChild(_classConf); 378 return conf; 379 } 380 } 381 382 /** 383 * Set the view of a content 384 * @param zoneItemId the id of the zone item 385 * @param viewName the name of the view to use 386 */ 387 @Callable 388 public void setContentView(String zoneItemId, String viewName) 389 { 390 AmetysObject object = _resolver.resolveById(zoneItemId); 391 if (!(object instanceof ModifiableZoneItem)) 392 { 393 throw new IllegalArgumentException("The provided ID '" + zoneItemId + "' does not reference a modifiable zone item."); 394 } 395 396 ModifiableZoneItem zoneItem = (ModifiableZoneItem) object; 397 398 if (!zoneItem.getType().equals(ZoneType.CONTENT)) 399 { 400 throw new IllegalArgumentException("The zone item '" + zoneItemId + "' does not contain a content."); 401 } 402 403 Content content = zoneItem.getContent(); 404 405 String modelViewName = viewName; 406 SkinContentView skinContentView = _skinContentViewHelper.getContentViewFromSkin(viewName, content); 407 if (skinContentView != null) 408 { 409 // the requested view is a skin view, get the associated model view to check model 410 modelViewName = skinContentView.modelViewName(); 411 } 412 413 View view = _contentTypesHelper.getView(modelViewName, content.getTypes(), content.getMixinTypes()); 414 if (view == null) 415 { 416 throw new IllegalArgumentException("The view '" + viewName + "' can't be assigned on the content '" + StringUtils.join(content.getTypes(), ",") + "'."); 417 } 418 419 if (getLogger().isDebugEnabled()) 420 { 421 getLogger().debug("Setting view '" + viewName + "' in the content zone item '" + zoneItemId + "'."); 422 } 423 424 // Set the view name. 425 zoneItem.setViewName(viewName); 426 427 zoneItem.saveChanges(); 428 429 SitemapElement sitemapElement = zoneItem.getZone().getSitemapElement(); 430 Map<String, Object> eventParams = new HashMap<>(); 431 eventParams.put(ObservationConstants.ARGS_SITEMAP_ELEMENT, sitemapElement); 432 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItemId); 433 eventParams.put(ObservationConstants.ARGS_ZONE_TYPE, ZoneType.CONTENT); 434 _observationManager.notify(new Event(ObservationConstants.EVENT_ZONEITEM_MOVED, _currentUserProvider.getUser(), eventParams)); 435 } 436}