View Javadoc
1   /*
2    * Copyright 2012-2025 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.app.web.admin.pathmap;
17  
18  import org.apache.logging.log4j.LogManager;
19  import org.apache.logging.log4j.Logger;
20  import org.codelibs.fess.Constants;
21  import org.codelibs.fess.annotation.Secured;
22  import org.codelibs.fess.app.pager.PathMapPager;
23  import org.codelibs.fess.app.service.PathMappingService;
24  import org.codelibs.fess.app.web.CrudMode;
25  import org.codelibs.fess.app.web.base.FessAdminAction;
26  import org.codelibs.fess.helper.SystemHelper;
27  import org.codelibs.fess.opensearch.config.exentity.PathMapping;
28  import org.codelibs.fess.util.ComponentUtil;
29  import org.codelibs.fess.util.RenderDataUtil;
30  import org.dbflute.optional.OptionalEntity;
31  import org.dbflute.optional.OptionalThing;
32  import org.lastaflute.web.Execute;
33  import org.lastaflute.web.response.HtmlResponse;
34  import org.lastaflute.web.response.render.RenderData;
35  import org.lastaflute.web.ruts.process.ActionRuntime;
36  
37  import jakarta.annotation.Resource;
38  
39  /**
40   * Admin action for Path Map management.
41   *
42   */
43  public class AdminPathmapAction extends FessAdminAction {
44  
45      /**
46       * Role identifier for admin path mapping operations.
47       */
48      public static final String ROLE = "admin-pathmap";
49  
50      private static final Logger logger = LogManager.getLogger(AdminPathmapAction.class);
51  
52      /**
53       * Default constructor for AdminPathmapAction.
54       */
55      public AdminPathmapAction() {
56          super();
57      }
58  
59      // ===================================================================================
60      //                                                                           Attribute
61      //                                                                           =========
62      @Resource
63      private PathMappingService pathMappingService;
64      @Resource
65      private PathMapPager pathMapPager;
66  
67      // ===================================================================================
68      //                                                                               Hook
69      //                                                                              ======
70      @Override
71      protected void setupHtmlData(final ActionRuntime runtime) {
72          super.setupHtmlData(runtime);
73          runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNamePathmap()));
74      }
75  
76      @Override
77      protected String getActionRole() {
78          return ROLE;
79      }
80  
81      // ===================================================================================
82      //                                                                      Search Execute
83      //                                                                      ==============
84      /**
85       * Displays the main path mapping administration page.
86       *
87       * @param form the search form containing search criteria
88       * @return HTML response for the path mapping list page
89       */
90      @Execute
91      @Secured({ ROLE, ROLE + VIEW })
92      public HtmlResponse index(final SearchForm form) {
93          return asListHtml();
94      }
95  
96      /**
97       * Displays the path mapping list with pagination support.
98       *
99       * @param pageNumber the optional page number for pagination
100      * @param form the search form containing search criteria
101      * @return HTML response for the path mapping list page
102      */
103     @Execute
104     @Secured({ ROLE, ROLE + VIEW })
105     public HtmlResponse list(final OptionalThing<Integer> pageNumber, final SearchForm form) {
106         pageNumber.ifPresent(num -> {
107             pathMapPager.setCurrentPageNumber(pageNumber.get());
108         }).orElse(() -> {
109             pathMapPager.setCurrentPageNumber(0);
110         });
111         return asHtml(path_AdminPathmap_AdminPathmapJsp).renderWith(data -> {
112             searchPaging(data, form);
113         });
114     }
115 
116     /**
117      * Performs a search for path mappings based on the provided criteria.
118      *
119      * @param form the search form containing search criteria
120      * @return HTML response with search results
121      */
122     @Execute
123     @Secured({ ROLE, ROLE + VIEW })
124     public HtmlResponse search(final SearchForm form) {
125         copyBeanToBean(form, pathMapPager, op -> op.exclude(Constants.PAGER_CONVERSION_RULE));
126         return asHtml(path_AdminPathmap_AdminPathmapJsp).renderWith(data -> {
127             searchPaging(data, form);
128         });
129     }
130 
131     /**
132      * Resets the search criteria and displays all path mappings.
133      *
134      * @param form the search form to be reset
135      * @return HTML response for the path mapping list page
136      */
137     @Execute
138     @Secured({ ROLE, ROLE + VIEW })
139     public HtmlResponse reset(final SearchForm form) {
140         pathMapPager.clear();
141         return asHtml(path_AdminPathmap_AdminPathmapJsp).renderWith(data -> {
142             searchPaging(data, form);
143         });
144     }
145 
146     /**
147      * Handles search pagination and data preparation for rendering.
148      *
149      * @param data the render data to populate with path mapping items
150      * @param form the search form containing current search criteria
151      */
152     protected void searchPaging(final RenderData data, final SearchForm form) {
153         RenderDataUtil.register(data, "pathMappingItems", pathMappingService.getPathMappingList(pathMapPager)); // page navi
154 
155         // restore from pager
156         copyBeanToBean(pathMapPager, form, op -> op.include("regex", "replacement"));
157     }
158 
159     // ===================================================================================
160     //                                                                        Edit Execute
161     //                                                                        ============
162     // -----------------------------------------------------
163     //                                            Entry Page
164     //                                            ----------
165     /**
166      * Displays the form for creating a new path mapping.
167      *
168      * @return HTML response for the path mapping creation form
169      */
170     @Execute
171     @Secured({ ROLE })
172     public HtmlResponse createnew() {
173         saveToken();
174         return asHtml(path_AdminPathmap_AdminPathmapEditJsp).useForm(CreateForm.class, op -> {
175             op.setup(form -> {
176                 form.initialize();
177                 form.crudMode = CrudMode.CREATE;
178             });
179         });
180     }
181 
182     /**
183      * Displays the form for editing an existing path mapping.
184      *
185      * @param form the edit form containing the path mapping ID and data
186      * @return HTML response for the path mapping edit form
187      */
188     @Execute
189     @Secured({ ROLE })
190     public HtmlResponse edit(final EditForm form) {
191         validate(form, messages -> {}, this::asListHtml);
192         final String id = form.id;
193         pathMappingService.getPathMapping(id).ifPresent(entity -> {
194             copyBeanToBean(entity, form, op -> {});
195         }).orElse(() -> {
196             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
197         });
198         saveToken();
199         if (form.crudMode.intValue() == CrudMode.EDIT) {
200             // back
201             form.crudMode = CrudMode.DETAILS;
202             return asDetailsHtml();
203         }
204         form.crudMode = CrudMode.EDIT;
205         return asEditHtml();
206     }
207 
208     // -----------------------------------------------------
209     //                                               Details
210     //                                               -------
211     /**
212      * Displays the details of a specific path mapping.
213      *
214      * @param crudMode the CRUD operation mode
215      * @param id the ID of the path mapping to display
216      * @return HTML response for the path mapping details page
217      */
218     @Execute
219     @Secured({ ROLE, ROLE + VIEW })
220     public HtmlResponse details(final int crudMode, final String id) {
221         verifyCrudMode(crudMode, CrudMode.DETAILS, this::asListHtml);
222         saveToken();
223         return asHtml(path_AdminPathmap_AdminPathmapDetailsJsp).useForm(EditForm.class, op -> {
224             op.setup(form -> {
225                 pathMappingService.getPathMapping(id).ifPresent(entity -> {
226                     copyBeanToBean(entity, form, copyOp -> {
227                         copyOp.excludeNull();
228                     });
229                     form.crudMode = crudMode;
230                 }).orElse(() -> {
231                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
232                 });
233             });
234         });
235     }
236 
237     // -----------------------------------------------------
238     //                                         Actually Crud
239     //                                         -------------
240     /**
241      * Creates a new path mapping based on the provided form data.
242      *
243      * @param form the create form containing the new path mapping data
244      * @return HTML response redirecting to the path mapping list page
245      */
246     @Execute
247     @Secured({ ROLE })
248     public HtmlResponse create(final CreateForm form) {
249         verifyCrudMode(form.crudMode, CrudMode.CREATE, this::asListHtml);
250         validate(form, messages -> {}, this::asEditHtml);
251         verifyToken(this::asEditHtml);
252         getPathMapping(form).ifPresent(entity -> {
253             try {
254                 pathMappingService.store(entity);
255                 saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
256             } catch (final Exception e) {
257                 logger.warn("Failed to process a request.", e);
258                 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)),
259                         this::asEditHtml);
260             }
261         }).orElse(() -> {
262             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL), this::asEditHtml);
263         });
264         return redirect(getClass());
265     }
266 
267     /**
268      * Updates an existing path mapping with the provided form data.
269      *
270      * @param form the edit form containing the updated path mapping data
271      * @return HTML response redirecting to the path mapping list page
272      */
273     @Execute
274     @Secured({ ROLE })
275     public HtmlResponse update(final EditForm form) {
276         verifyCrudMode(form.crudMode, CrudMode.EDIT, this::asListHtml);
277         validate(form, messages -> {}, this::asEditHtml);
278         verifyToken(this::asEditHtml);
279         getPathMapping(form).ifPresent(entity -> {
280             try {
281                 pathMappingService.store(entity);
282                 saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
283             } catch (final Exception e) {
284                 logger.warn("Failed to process a request.", e);
285                 throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
286                         this::asEditHtml);
287             }
288         }).orElse(() -> {
289             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), this::asEditHtml);
290         });
291         return redirect(getClass());
292     }
293 
294     /**
295      * Deletes a path mapping based on the provided form data.
296      *
297      * @param form the edit form containing the path mapping ID to delete
298      * @return HTML response redirecting to the path mapping list page
299      */
300     @Execute
301     @Secured({ ROLE })
302     public HtmlResponse delete(final EditForm form) {
303         verifyCrudMode(form.crudMode, CrudMode.DETAILS, this::asListHtml);
304         validate(form, messages -> {}, this::asDetailsHtml);
305         verifyToken(this::asDetailsHtml);
306         final String id = form.id;
307         pathMappingService.getPathMapping(id).ifPresent(entity -> {
308             try {
309                 pathMappingService.delete(entity);
310                 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
311             } catch (final Exception e) {
312                 logger.warn("Failed to process a request.", e);
313                 throwValidationError(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)),
314                         this::asEditHtml);
315             }
316         }).orElse(() -> {
317             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asDetailsHtml);
318         });
319         return redirect(getClass());
320     }
321 
322     // ===================================================================================
323     //                                                                        Assist Logic
324     //                                                                        ============
325     private static OptionalEntity<PathMapping> getEntity(final CreateForm form, final String username, final long currentTime) {
326         switch (form.crudMode) {
327         case CrudMode.CREATE:
328             return OptionalEntity.of(new PathMapping()).map(entity -> {
329                 entity.setCreatedBy(username);
330                 entity.setCreatedTime(currentTime);
331                 return entity;
332             });
333         case CrudMode.EDIT:
334             if (form instanceof EditForm) {
335                 return ComponentUtil.getComponent(PathMappingService.class).getPathMapping(((EditForm) form).id);
336             }
337             break;
338         default:
339             break;
340         }
341         return OptionalEntity.empty();
342     }
343 
344     /**
345      * Creates a PathMapping entity from the provided form data.
346      *
347      * @param form the create form containing path mapping data
348      * @return optional PathMapping entity populated with form data
349      */
350     public static OptionalEntity<PathMapping> getPathMapping(final CreateForm form) {
351         final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
352         final String username = systemHelper.getUsername();
353         final long currentTime = systemHelper.getCurrentTimeAsLong();
354         return getEntity(form, username, currentTime).map(entity -> {
355             entity.setUpdatedBy(username);
356             entity.setUpdatedTime(currentTime);
357             copyBeanToBean(form, entity, op -> op.exclude(Constants.COMMON_CONVERSION_RULE));
358             return entity;
359         });
360     }
361 
362     // ===================================================================================
363     //                                                                        Small Helper
364     //                                                                        ============
365     //                                                                              JSP
366     //                                                                           =========
367 
368     private HtmlResponse asListHtml() {
369         return asHtml(path_AdminPathmap_AdminPathmapJsp).renderWith(data -> {
370             RenderDataUtil.register(data, "pathMappingItems", pathMappingService.getPathMappingList(pathMapPager)); // page navi
371         }).useForm(SearchForm.class, setup -> {
372             setup.setup(form -> {
373                 copyBeanToBean(pathMapPager, form, op -> op.include("regex", "replacement"));
374             });
375         });
376     }
377 
378     private HtmlResponse asEditHtml() {
379         return asHtml(path_AdminPathmap_AdminPathmapEditJsp);
380     }
381 
382     private HtmlResponse asDetailsHtml() {
383         return asHtml(path_AdminPathmap_AdminPathmapDetailsJsp);
384     }
385 }