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.content.consistency;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.Iterator;
021import java.util.LinkedHashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Optional;
025
026import org.apache.avalon.framework.component.Component;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.cocoon.ProcessingException;
031
032import org.ametys.cms.search.ui.model.SearchUIColumn;
033import org.ametys.cms.search.ui.model.SearchUIColumnHelper;
034import org.ametys.plugins.workflow.support.WorkflowHelper;
035import org.ametys.runtime.i18n.I18nizableText;
036import org.ametys.runtime.model.DefinitionContext;
037import org.ametys.runtime.model.ModelItem;
038
039import com.opensymphony.workflow.loader.StepDescriptor;
040
041/**
042 * Provide the search model for Content consistency search
043 */
044public class ContentConsistencySearchModel implements Component, Serviceable
045{
046    /** The avalon role */
047    public static final String ROLE = ContentConsistencySearchModel.class.getName();
048    
049    private WorkflowHelper _workflowHelper;
050    private ContentConsistencyModel _contentConsistencyModel;
051    
052    public void service(ServiceManager manager) throws ServiceException
053    {
054        _workflowHelper = (WorkflowHelper) manager.lookup(WorkflowHelper.ROLE);
055        _contentConsistencyModel = (ContentConsistencyModel) manager.lookup(ContentConsistencyModel.ROLE);
056    }
057    
058    /**
059     * Get the model for a content consistency search
060     * @return the model
061     * @throws ProcessingException if an error occurs processing the model
062     */
063    public Map<String, Object> getModel() throws ProcessingException
064    {
065        Map<String, Object> model = new HashMap<>();
066        
067        // Proxy configuration
068        model.put("searchMethodName", "searchResults");
069        model.put("searchRole", "org.ametys.cms.content.consistency.ContentConsistencySearcher");
070        
071        model.put("columns", getColumns());
072        
073        model.put("criteria", getCriteria());
074        
075        model.put("facets", getFacets());
076        
077        return model;
078    }
079    
080    /**
081     * Provide the list of column.
082     * Any column additional column must not be sortable as there info won't be present in the search result.
083     * @return a json description of the column
084     * @throws ProcessingException if an error occurs processing the columns
085     */
086    protected List<Map<String, Object>> getColumns() throws ProcessingException
087    {
088        List<SearchUIColumn> columns = new ArrayList<>();
089
090        columns.add(getSearchUIColumn(ContentConsistencyModel.TITLE,
091                                        new I18nizableText("plugin.cms", "PLUGINS_CMS_METADATA_TITLE_LABEL"),
092                                        Optional.empty(),
093                                        Optional.empty(),
094                                        false));
095        
096        columns.add(getSearchUIColumn(ContentConsistencyModel.CONTENT_TYPES,
097                                        new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_ALL_CONTENT_TYPE"),
098                                        Optional.of("Ametys.plugins.cms.content.tool.GlobalContentConsistencyTool.renderContentType"),
099                                        Optional.empty(),
100                                        false));
101        
102        columns.add(getSearchUIColumn(ContentConsistencyModel.CREATOR,
103                                        new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_CREATOR"),
104                                        Optional.of("Ametys.plugins.cms.search.SearchGridHelper.renderUser"),
105                                        Optional.empty(),
106                                        true));
107        
108        columns.add(getSearchUIColumn(ContentConsistencyModel.CREATION_DATE,
109                                        new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_CREATIONDATE"),
110                                        Optional.empty(),
111                                        Optional.empty(),
112                                        true));
113        
114        columns.add(getSearchUIColumn(ContentConsistencyModel.LAST_CONTRIBUTOR,
115                                        new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_AUTHOR"),
116                                        Optional.of("Ametys.plugins.cms.search.SearchGridHelper.renderUser"),
117                                        Optional.empty(),
118                                        false));
119        
120        columns.add(getSearchUIColumn(ContentConsistencyModel.LAST_MODIFICATION_DATE,
121                                        new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_LASTMODIFIED"),
122                                        Optional.empty(),
123                                        Optional.empty(),
124                                        true));
125        
126        columns.add(getSearchUIColumn(ContentConsistencyModel.LAST_VALIDATOR,
127                                        new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_LAST_VALIDATOR"),
128                                        Optional.of("Ametys.plugins.cms.search.SearchGridHelper.renderUser"),
129                                        Optional.empty(),
130                                        true));
131        
132        columns.add(getSearchUIColumn(ContentConsistencyModel.LAST_VALIDATION_DATE,
133                                        new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_LASTVALIDATION"),
134                                        Optional.empty(),
135                                        Optional.empty(),
136                                        true));
137        
138        columns.add(getSearchUIColumn(ContentConsistencyModel.WORKFLOW_STEP,
139                                        new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_WORKFLOW_STEP"),
140                                        Optional.of("Ametys.plugins.cms.search.SearchGridHelper.renderWorkflowStep"),
141                                        Optional.of("Ametys.plugins.cms.search.SearchGridHelper.convertWorkflowStep"),
142                                        false));
143        
144        columns.add(getSearchUIColumn(ContentConsistencyModel.DATE,
145                                        new I18nizableText("plugin.cms", "PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_CHECK_DATE"),
146                                        Optional.empty(),
147                                        Optional.empty(),
148                                        false));
149        
150        columns.add(getSearchUIColumn(ContentConsistencyModel.SUCCESS,
151                                        new I18nizableText("plugin.cms", "PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_SUCCESS_COUNT"),
152                                        Optional.empty(),
153                                        Optional.empty(),
154                                        true));
155        
156        columns.add(getSearchUIColumn(ContentConsistencyModel.UNAUTHORIZED,
157                                        new I18nizableText("plugin.cms", "PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_UNAUTHORIZED_COUNT"),
158                                        Optional.empty(),
159                                        Optional.empty(),
160                                        false));
161        
162        columns.add(getSearchUIColumn(ContentConsistencyModel.UNKNOWN,
163                                        new I18nizableText("plugin.cms", "PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_UNKNOWN_COUNT"),
164                                        Optional.empty(),
165                                        Optional.empty(),
166                                        false));
167        
168        columns.add(getSearchUIColumn(ContentConsistencyModel.NOT_FOUND,
169                                        new I18nizableText("plugin.cms", "PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_NOT_FOUND_COUNT"),
170                                        Optional.empty(),
171                                        Optional.empty(),
172                                        false));
173        
174        columns.add(getSearchUIColumn(ContentConsistencyModel.SERVER_ERROR,
175                                        new I18nizableText("plugin.cms", "PLUGINS_CMS_GLOBAL_CONTENT_CONSISTENCY_SERVER_ERROR_COUNT"),
176                                        Optional.empty(),
177                                        Optional.empty(),
178                                        false));
179
180        List<Map<String, Object>>  columnsInfo = new ArrayList<>();
181        DefinitionContext definitionContext = DefinitionContext.newInstance();
182        for (SearchUIColumn column : columns)
183        {
184            columnsInfo.add(column.toJSON(definitionContext));
185        }
186        return columnsInfo;
187    }
188    
189    /**
190     * Retrieves the column corresponding to the given item name
191     * @param name the name of the column's item
192     * @param label the column's label
193     * @param renderer the column's renderer
194     * @param converter  the column's converter
195     * @param hidden <code>true</code> if the column should be hidden, <code>false</code> otherwise
196     * @return the search column's info
197     */
198    protected SearchUIColumn getSearchUIColumn(String name, I18nizableText label, Optional<String> renderer, Optional<String> converter, boolean hidden)
199    {
200        ModelItem modelItem = _contentConsistencyModel.getModelItem(name);
201        SearchUIColumn<? extends ModelItem> column = SearchUIColumnHelper.createModelItemColumn(modelItem);
202
203        column.setLabel(label);
204        column.setRenderer(renderer);
205        column.setConverter(converter);
206        column.setHidden(hidden);
207        
208        return column;
209    }
210    
211    /**
212     * Get the criteria for the search
213     * @return the JSON description of the criteria
214     */
215    protected Map<String, Object> getCriteria()
216    {
217        Map<String, Object> criteria = new LinkedHashMap<>(); // order is important here
218        criteria.put("title", Map.of(
219                "label", new I18nizableText("plugin.cms", "PLUGINS_CMS_METADATA_TITLE_LABEL"),
220                "description", new I18nizableText("plugin.cms", "PLUGINS_CMS_METADATA_TITLE_DESCRIPTION"),
221                "type", "STRING"
222                ));
223        
224        List<Map<String, Object>> workflowStep = new ArrayList<>();
225        workflowStep.add(Map.of("label", new I18nizableText("plugin.cms", "WIDGET_COMBOBOX_ALL_OPTIONS"), "value", 0));
226        for (Iterator iterator = _workflowHelper.getWorkflowDescriptor("content").getSteps().iterator(); iterator.hasNext();)
227        {
228            StepDescriptor step = (StepDescriptor) iterator.next();
229            workflowStep.add(Map.of("label", new I18nizableText(null, step.getName()), "value", step.getId()));
230        }
231
232        criteria.put("workflowStep", Map.of(
233                "label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_WORKFLOW_STEP"),
234                "description", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_WORKFLOW_STEP"),
235                "type", "LONG",
236                "enumeration", workflowStep,
237                "default-value", 0 // - Tous -
238                ));
239        
240        Map<String, Object> ctype = new HashMap<>();
241        ctype.put("label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_ALL_CONTENT_TYPE"));
242        ctype.put("description", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_ALL_CONTENT_TYPE"));
243        ctype.put("type", "STRING");
244        ctype.put("multiple", false);
245        ctype.put("enumeration", null);
246        ctype.put("widget", "edition.select-content-types");
247        ctype.put("widget-params", Map.of("emptyText", new I18nizableText("plugin.cms", "WIDGET_COMBOBOX_ALL_OPTIONS")));
248        criteria.put("contentTypes", ctype);
249        
250        criteria.put(ContentConsistencyModel.CREATOR, Map.of(
251                "label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_CREATOR"),
252                "description", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_CREATOR"),
253                "type", "USER"
254                ));
255        criteria.put("contributor", Map.of(
256                "label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_AUTHOR"),
257                "description", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_AUTHOR"),
258                "type", "USER"
259                ));
260        criteria.put(ContentConsistencyModel.LAST_VALIDATOR, Map.of(
261                "label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_LAST_VALIDATOR"),
262                "description", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_LAST_VALIDATOR"),
263                "type", "USER"
264                ));
265        return criteria;
266    }
267    
268    /**
269     * Get the facets for the search
270     * @return the JSON description of the facets
271     */
272    protected List<Map<String, Object>> getFacets()
273    {
274        List<Map<String, Object>> facets = new ArrayList<>();
275        facets.add(Map.of(
276                "name", ContentConsistencyModel.CREATOR,
277                "label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_CREATOR"),
278                "type", "criterion",
279                "children", new ArrayList()));
280        facets.add(Map.of(
281                "name", ContentConsistencyModel.LAST_CONTRIBUTOR,
282                "label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_AUTHOR"),
283                "type", "criterion",
284                "children", new ArrayList())); // needs to be mutable to add the value later
285        facets.add(Map.of(
286                "name", ContentConsistencyModel.LAST_VALIDATOR,
287                "label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_LAST_VALIDATOR"),
288                "type", "criterion",
289                "children", new ArrayList())); // needs to be mutable to add the value later
290        facets.add(Map.of(
291                "name", ContentConsistencyModel.WORKFLOW_STEP,
292                "label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_WORKFLOW_STEP"),
293                "type", "criterion",
294                "children", new ArrayList())); // needs to be mutable to add the value later
295        facets.add(Map.of(
296                "name", ContentConsistencyModel.CONTENT_TYPES,
297                "label", new I18nizableText("plugin.cms", "UITOOL_SEARCH_CONTENT_ALL_CONTENT_TYPE"),
298                "type", "criterion",
299                "children", new ArrayList())); // needs to be mutable to add the value later
300        return facets;
301    }
302}