001/*
002 *  Copyright 2022 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.plugins.forms.helper;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.stream.Collectors;
023
024import org.apache.avalon.framework.component.Component;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.avalon.framework.service.Serviceable;
028
029import org.ametys.core.ui.Callable;
030import org.ametys.plugins.forms.question.FormQuestionType;
031import org.ametys.plugins.forms.question.sources.ChoiceSourceType;
032import org.ametys.plugins.forms.question.types.CheckBoxQuestionType;
033import org.ametys.plugins.forms.question.types.ChoicesListQuestionType;
034import org.ametys.plugins.forms.repository.Form;
035import org.ametys.plugins.forms.repository.FormQuestion;
036import org.ametys.plugins.forms.repository.type.Rule.QuestionRuleType;
037import org.ametys.plugins.repository.AmetysObjectResolver;
038import org.ametys.runtime.i18n.I18nizableText;
039
040/**
041 * Helper for getting rules properties
042 */
043public class QuestionRuleHelper implements Serviceable, Component
044{
045    /** The Ametys object resolver */
046    protected AmetysObjectResolver _resolver;
047    
048    public void service(ServiceManager manager) throws ServiceException
049    {
050        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
051    }
052    
053    /**
054     * Get all choices list and booleans questions as source for the rule
055     * @param formId id of the current form
056     * @param questionId id of the current question
057     * @return a map containing the list of questions
058     */
059    @Callable
060    public Map<String, Object> getSourceQuestions(String formId, String questionId)
061    {
062        Map<String, Object> results = new HashMap<>();
063        Form form = _resolver.resolveById(formId);
064        List<FormQuestion> questions = form.getQuestions()
065            .stream()
066            .filter(question -> this._isChoiceListWithNoTooManyOptions(question) || question.getType() instanceof CheckBoxQuestionType) // Question must be a choice list with not too many options
067            .filter(question -> !question.isModifiable()) // Question must be not modifiable
068            .filter(question -> !question.getId().equals(questionId)) // Remove the given question from the list
069            .collect(Collectors.toList());
070        List<Object> sources = new ArrayList<>();
071        for (FormQuestion question : questions)
072        {
073            Map<String, String> properties = new HashMap<>();
074            properties.put("label", question.getTitle());
075            properties.put("id", question.getId());
076            sources.add(properties);
077        }
078        results.put("data", sources);
079        
080        return results;
081    }
082    
083    private boolean _isChoiceListWithNoTooManyOptions(FormQuestion question)
084    {
085        FormQuestionType type = question.getType();
086        if (type instanceof ChoicesListQuestionType cLType)
087        {
088            ChoiceSourceType sourceType = cLType.getSourceType(question);
089            return !sourceType.remoteData();
090        }
091        
092        return false;
093    }
094    
095    /**
096     * Get all the available options for the selected question
097     * @param questionId id of the selected question
098     * @return a map containing the list of all the options as map (label for display and unique value)
099     */
100    @Callable
101    public Map<String, Object> getSourceOptions(String questionId)
102    {
103        Map<String, Object> results = new HashMap<>();
104        FormQuestion formQuestion = _resolver.resolveById(questionId);
105        List<Object> options = new ArrayList<>();
106        if (formQuestion.getType() instanceof ChoicesListQuestionType type && !type.getSourceType(formQuestion).remoteData())
107        {
108            Map<String, I18nizableText> choices = type.getOptions(formQuestion);
109            for (String optionValue : choices.keySet())
110            {
111                Map<String, Object> choicesToJson = new HashMap<>();
112                choicesToJson.put("label", choices.get(optionValue));
113                choicesToJson.put("value", optionValue);
114                options.add(choicesToJson);
115            }
116            if (type.hasOtherOption(formQuestion))
117            {
118                Map<String, Object> choicesToJson = new HashMap<>();
119                choicesToJson.put("label", new I18nizableText("plugin.forms", "PLUGINS_FORMS_DISPLAY_OTHER_OPTION_COMBOBOX"));
120                choicesToJson.put("value", ChoicesListQuestionType.OTHER_OPTION_VALUE);
121                options.add(choicesToJson);
122            }
123        }
124        else
125        {
126            options.add(Map.of(
127                    "label", new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTION_RULES_OPTION_CHECKED"),
128                    "value", "true"
129                    ));
130            
131            options.add(Map.of(
132                    "label", new I18nizableText("plugin.forms", "PLUGINS_FORMS_QUESTION_RULES_OPTION_UNCHECKED"),
133                    "value", "false"
134                    ));
135        }
136        results.put("data", options);
137        return results;
138    }
139    
140    /**
141     * Get the actions available to apply to this question 
142     * @return a list of actions (currently hide or show)
143     */
144    @Callable
145    public Map<String, Object> getRuleActions()
146    {
147        Map<String, Object> results = new HashMap<>();
148        
149        List<Object> actions = new ArrayList<>();
150        
151        actions.add(Map.of(
152            "label", QuestionRuleType.HIDE.getLabel(),
153            "value", QuestionRuleType.HIDE.name()
154        ));
155        
156        actions.add(Map.of(
157            "label", QuestionRuleType.SHOW.getLabel(),
158            "value", QuestionRuleType.SHOW.name()
159        ));
160        
161        results.put("data", actions);
162        return results;
163    }
164}