001/* 002 * Copyright 2023 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.search.ui.model; 017 018import java.util.Collection; 019import java.util.List; 020import java.util.Optional; 021 022import org.apache.avalon.framework.configuration.ConfigurationException; 023import org.apache.avalon.framework.configuration.DefaultConfiguration; 024 025import org.ametys.cms.contenttype.AbstractContentTypeViewParser; 026import org.ametys.cms.contenttype.ContentType; 027import org.ametys.cms.repository.Content; 028import org.ametys.cms.search.model.SystemProperty; 029import org.ametys.cms.search.ui.model.impl.RepeaterSearchUIColumn; 030import org.ametys.plugins.repository.model.CompositeDefinition; 031import org.ametys.plugins.repository.model.RepeaterDefinition; 032import org.ametys.runtime.model.ElementDefinition; 033import org.ametys.runtime.model.ItemParserHelper.ConfigurationAndPluginName; 034import org.ametys.runtime.model.Model; 035import org.ametys.runtime.model.ModelItem; 036import org.ametys.runtime.model.ModelItemAccessor; 037import org.ametys.runtime.model.ModelViewItem; 038import org.ametys.runtime.model.ModelViewItemGroup; 039import org.ametys.runtime.model.View; 040import org.ametys.runtime.model.ViewItem; 041import org.ametys.runtime.model.ViewParserContext; 042 043/** 044 * Component that parses the columns of a {@link StaticSearchUIModel} 045 */ 046public class StaticSearchUIModelColumnsParser extends AbstractContentTypeViewParser 047{ 048 private static final String __VIEW_NAME = "StaticSearchUIModel-Columns"; 049 050 /** Content types containing the item available in the search model's columns */ 051 protected Collection<ContentType> _contentTypes; 052 053 /** 054 * Creates a {@link StaticSearchUIModel}'s columns parser 055 * @param contentTypes The content types containing the item available in the search model's columns 056 */ 057 public StaticSearchUIModelColumnsParser(Collection<ContentType> contentTypes) 058 { 059 _contentTypes = contentTypes; 060 } 061 062 @Override 063 protected String _parseViewName(ConfigurationAndPluginName viewConfiguration) throws ConfigurationException 064 { 065 return __VIEW_NAME; 066 } 067 068 @Override 069 public View overrideView(ConfigurationAndPluginName viewConfiguration, View existingView, ViewParserContext context) throws ConfigurationException 070 { 071 throw new UnsupportedOperationException("Unable to override columns of a search model"); 072 } 073 074 @Override 075 protected void _fillViewGeneralInformation(ConfigurationAndPluginName viewConfiguration, View view, View existingView, Collection< ? extends Model> model) throws ConfigurationException 076 { 077 // Do nothing - there is no general information in search model's columns 078 } 079 080 @Override 081 protected Collection<? extends Model> _getModel() 082 { 083 return _contentTypes; 084 } 085 086 @Override 087 protected ModelItem _getModelItem(ConfigurationAndPluginName itemConfiguration, String modelItemName, Collection< ? extends ModelItemAccessor> parents) throws ConfigurationException 088 { 089 ModelItem modelItem = parents.isEmpty() && Content.ATTRIBUTE_TITLE.equals(modelItemName) 090 ? _contentTypesHelper.getTitleAttributeDefinition() 091 : super._getModelItem(itemConfiguration, modelItemName, parents); 092 093 if (modelItem instanceof SystemProperty systemProperty && !systemProperty.isDisplayable()) 094 { 095 throw new ConfigurationException("The property '" + systemProperty.getName() + "' is not displayable."); 096 } 097 098 return modelItem; 099 } 100 101 @Override 102 protected ModelViewItem createModelViewItemForAllItemsReference(ConfigurationAndPluginName itemConfiguration, ModelItem modelItem, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 103 { 104 if (modelItem instanceof CompositeDefinition compositeDefinition) 105 { 106 ModelViewItemGroup<CompositeDefinition> itemGroup = new ModelViewItemGroup<>(); 107 itemGroup.setDefinition(compositeDefinition); 108 return itemGroup; 109 } 110 else 111 { 112 return super.createModelViewItemForAllItemsReference(itemConfiguration, modelItem, referenceView, override, context); 113 } 114 } 115 116 @SuppressWarnings("unchecked") 117 @Override 118 protected ModelViewItem createModelViewItem(ConfigurationAndPluginName itemConfiguration, ModelItem modelItem, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 119 { 120 ModelViewItem<? extends ModelItem> modelViewItem = super.createModelViewItem(itemConfiguration, modelItem, referenceView, override, context); 121 122 123 if (modelViewItem instanceof SearchUIColumn searchColumn) 124 { 125 searchColumn.setName(itemConfiguration.configuration().getAttribute("name", null)); 126 searchColumn.setWidth(itemConfiguration.configuration().getChild("width").getValueAsInteger(-1)); 127 searchColumn.setHidden(itemConfiguration.configuration().getChild("hidden").getValueAsBoolean(false)); 128 129 searchColumn.setEditable(itemConfiguration.configuration().getChild("editable").getValueAsBoolean(true)); 130 searchColumn.setSortable(itemConfiguration.configuration().getChild("sortable").getValueAsBoolean(true)); 131 searchColumn.setAllowSortOnMultipleJoin(itemConfiguration.configuration().getChild("allow-sort-on-multiple-join").getValueAsBoolean(false)); 132 searchColumn.setDefaultSorter(Optional.ofNullable(itemConfiguration.configuration().getChild("default-sorter").getValue(null))); 133 134 searchColumn.setRenderer(Optional.ofNullable(itemConfiguration.configuration().getChild("renderer").getValue(null))); 135 searchColumn.setConverter(Optional.ofNullable(itemConfiguration.configuration().getChild("converter").getValue(null))); 136 } 137 138 if (modelViewItem instanceof RepeaterSearchUIColumn repeaterColumn && modelViewItem.getDefinition() instanceof RepeaterDefinition repeaterDefinition 139 && !_getModelItemReference(itemConfiguration.configuration()).endsWith(ALL_ITEMS_REFERENCE) 140 && itemConfiguration.configuration().getChildren(ADD_ITEM_TAG_NAME).length == 0 && itemConfiguration.configuration().getChildren(ADD_GROUP_TAG_NAME).length == 0) 141 { 142 // Reference to a repeater with no child => add all model items in the view item 143 DefaultConfiguration fakeConfiguration = new DefaultConfiguration(ADD_ITEM_TAG_NAME); 144 fakeConfiguration.setAttribute(ITEM_REFERENCE_ATTRIBUTE_NAME, ALL_ITEMS_REFERENCE); 145 _parseModelViewItem(new ConfigurationAndPluginName(fakeConfiguration, itemConfiguration.pluginName(), itemConfiguration.defaultI18nCatalog()), repeaterColumn, List.of(repeaterDefinition), referenceView, override, context); 146 } 147 148 return modelViewItem; 149 } 150 151 @Override 152 protected ModelViewItem _createLeafModelViewItemInstance(ModelItem modelItem) 153 { 154 return SearchUIColumnHelper.createModelItemColumn(modelItem); 155 } 156 157 @Override 158 protected ViewItem _createViewItemCopyInstanceForReferencedView(ViewItem viewItem) 159 { 160 return Optional.of(viewItem) 161 .filter(ModelViewItem.class::isInstance) 162 .map(ModelViewItem.class::cast) 163 .map(ModelViewItem::getDefinition) 164 // Keep only items that can be represented in columns 165 .filter(modelViewItem -> modelViewItem instanceof RepeaterDefinition || modelViewItem instanceof ElementDefinition) 166 .map(this::_createLeafModelViewItemInstance) 167 .map(ViewItem.class::cast) 168 .orElseGet(() -> super._createViewItemCopyInstanceForReferencedView(viewItem)); 169 } 170}