001/*
002 *  Copyright 2024 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.odfweb.actions;
017
018import java.util.Map;
019import java.util.Optional;
020
021import org.apache.avalon.framework.parameters.Parameters;
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.cocoon.ResourceNotFoundException;
025import org.apache.cocoon.acting.ServiceableAction;
026import org.apache.cocoon.environment.ObjectModelHelper;
027import org.apache.cocoon.environment.Redirector;
028import org.apache.cocoon.environment.Request;
029import org.apache.cocoon.environment.SourceResolver;
030import org.apache.commons.lang3.StringUtils;
031
032import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
033import org.ametys.cms.repository.ContentQueryHelper;
034import org.ametys.cms.repository.ContentTypeExpression;
035import org.ametys.cms.repository.LanguageExpression;
036import org.ametys.cms.transformation.xslt.ResolveURIComponent;
037import org.ametys.odf.ProgramItem;
038import org.ametys.odf.catalog.CatalogsManager;
039import org.ametys.odf.course.Course;
040import org.ametys.odf.course.CourseFactory;
041import org.ametys.odf.program.AbstractProgram;
042import org.ametys.plugins.odfweb.repository.CoursePage;
043import org.ametys.plugins.odfweb.repository.OdfPageResolver;
044import org.ametys.plugins.repository.AmetysObjectIterable;
045import org.ametys.plugins.repository.AmetysObjectResolver;
046import org.ametys.plugins.repository.query.expression.AndExpression;
047import org.ametys.plugins.repository.query.expression.Expression.Operator;
048import org.ametys.plugins.repository.query.expression.StringExpression;
049import org.ametys.runtime.config.Config;
050import org.ametys.web.WebHelper;
051
052/**
053 * Get a course page from its code. Needed for IPWeb.
054 */
055public class GetCourseForIPWebAction extends ServiceableAction
056{
057    private CatalogsManager _catalogsManager;
058    private OdfPageResolver _odfPageResolver;
059    private ContentTypeExtensionPoint _contentTypeEP;
060    private AmetysObjectResolver _resolver;
061
062    @Override
063    public void service(ServiceManager smanager) throws ServiceException
064    {
065        super.service(smanager);
066        _catalogsManager = (CatalogsManager) smanager.lookup(CatalogsManager.ROLE);
067        _odfPageResolver = (OdfPageResolver) smanager.lookup(OdfPageResolver.ROLE);
068        _contentTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE);
069        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
070    }
071    
072    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
073    {
074        // Check if IPWeb is activated in the configuration
075        if (!Config.getInstance().getValue("odf.ipweb.activate", true, false))
076        {
077            throw new UnsupportedOperationException("The IPWeb URL resolution is not configured.");
078        }
079        
080        String field = getField();
081        String lang = getLanguage();
082        String catalog = getCatalog();
083        
084        Request request = ObjectModelHelper.getRequest(objectModel);
085        
086        // Get course from code
087        String courseCode = request.getParameter("CodElp");
088        Course course = searchCourse(catalog, lang, courseCode, field);
089
090        // Get the page
091        String url = resolvePage(request, course, field);
092        redirector.redirect(false, url);
093        
094        return EMPTY_MAP;
095    }
096    
097    private String getField() throws ResourceNotFoundException
098    {
099        String field = Config.getInstance().getValue("odf.ipweb.field");
100        
101        // Check if the configured field is valid
102        if (!_contentTypeEP.getExtension(CourseFactory.COURSE_CONTENT_TYPE).hasModelItem(field))
103        {
104            throw new ResourceNotFoundException(String.format("The field '%s' is not defined on course type.", field));
105        }
106        
107        return field;
108    }
109    
110    private String getLanguage()
111    {
112        return Optional.of("odf.ipweb.lang")
113                .map(Config.getInstance()::<String>getValue)
114                .filter(StringUtils::isNotEmpty)
115                .orElseGet(() -> Config.getInstance().getValue("odf.programs.lang"));
116    }
117    
118    private String getCatalog() throws ResourceNotFoundException
119    {
120        String catalog = Optional.of("odf.ipweb.catalog")
121                .map(Config.getInstance()::<String>getValue)
122                .filter(StringUtils::isNotEmpty)
123                .orElseGet(_catalogsManager::getDefaultCatalogName);
124        
125        // Check if the catalog is valid
126        if (_catalogsManager.getCatalog(catalog) == null)
127        {
128            throw new ResourceNotFoundException(String.format("The catalog '%s' does not exist", catalog));
129        }
130        
131        return catalog;
132    }
133    
134    private Course searchCourse(String catalog, String lang, String code, String fieldCode) throws ResourceNotFoundException
135    {
136        AndExpression expression = new AndExpression();
137        expression.add(new ContentTypeExpression(Operator.EQ, CourseFactory.COURSE_CONTENT_TYPE));
138        expression.add(new StringExpression(ProgramItem.CATALOG, Operator.EQ, catalog));
139        expression.add(new LanguageExpression(Operator.EQ, lang));
140        expression.add(new StringExpression(fieldCode, Operator.EQ, code));
141        
142        String xpathQuery = ContentQueryHelper.getContentXPathQuery(expression);
143        AmetysObjectIterable<Course> courses = _resolver.query(xpathQuery);
144        
145        if (courses.getSize() > 1)
146        {
147            throw new ResourceNotFoundException(String.format("There are several courses with %s '%s' in catalog '%s' with language '%s'", fieldCode, code, catalog, lang));
148        }
149        
150        Course course = courses.stream().findFirst().orElse(null);
151        if (course == null)
152        {
153            throw new ResourceNotFoundException(String.format("There is no course with %s '%s' in catalog '%s' with language '%s'", fieldCode, code, catalog, lang));
154        }
155        
156        return course;
157    }
158    
159    private String resolvePage(Request request, Course course, String field) throws ResourceNotFoundException
160    {
161        String siteName = WebHelper.getSiteName(request);
162        
163        // Get the page
164        CoursePage page = _odfPageResolver.getCoursePage(course, (AbstractProgram) null, siteName);
165        if (page == null)
166        {
167            throw new ResourceNotFoundException(String.format("There is no page for course with code '%s' in catalog '%s' with language '%s' on site '%s'", course.<String>getValue(field), course.getCatalog(), course.getLanguage(), siteName));
168        }
169        
170        return ResolveURIComponent.resolve("page", page.getId(), false, true, false);
171    }
172}