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.reqheader;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.apache.logging.log4j.LogManager;
24  import org.apache.logging.log4j.Logger;
25  import org.codelibs.fess.Constants;
26  import org.codelibs.fess.annotation.Secured;
27  import org.codelibs.fess.app.pager.ReqHeaderPager;
28  import org.codelibs.fess.app.service.RequestHeaderService;
29  import org.codelibs.fess.app.service.WebConfigService;
30  import org.codelibs.fess.app.web.CrudMode;
31  import org.codelibs.fess.app.web.base.FessAdminAction;
32  import org.codelibs.fess.helper.SystemHelper;
33  import org.codelibs.fess.opensearch.config.exentity.RequestHeader;
34  import org.codelibs.fess.opensearch.config.exentity.WebConfig;
35  import org.codelibs.fess.util.ComponentUtil;
36  import org.codelibs.fess.util.RenderDataUtil;
37  import org.dbflute.optional.OptionalEntity;
38  import org.dbflute.optional.OptionalThing;
39  import org.lastaflute.web.Execute;
40  import org.lastaflute.web.response.HtmlResponse;
41  import org.lastaflute.web.response.render.RenderData;
42  import org.lastaflute.web.ruts.process.ActionRuntime;
43  
44  import jakarta.annotation.Resource;
45  
46  /**
47   * Admin action for Request Header management.
48   *
49   */
50  public class AdminReqheaderAction extends FessAdminAction {
51  
52      /**
53       * Default constructor.
54       */
55      public AdminReqheaderAction() {
56          super();
57      }
58  
59      /** Role name for admin request header operations */
60      public static final String ROLE = "admin-reqheader";
61  
62      private static final Logger logger = LogManager.getLogger(AdminReqheaderAction.class);
63  
64      // ===================================================================================
65      //                                                                           Attribute
66      //                                                                           =========
67      @Resource
68      private RequestHeaderService requestHeaderService;
69      @Resource
70      private ReqHeaderPager reqHeaderPager;
71      /** Web configuration service for managing web crawl configurations. */
72      @Resource
73      protected WebConfigService webConfigService;
74  
75      // ===================================================================================
76      //                                                                               Hook
77      //                                                                              ======
78      @Override
79      protected void setupHtmlData(final ActionRuntime runtime) {
80          super.setupHtmlData(runtime);
81          runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameReqheader()));
82      }
83  
84      @Override
85      protected String getActionRole() {
86          return ROLE;
87      }
88  
89      // ===================================================================================
90      //                                                                      Search Execute
91      //                                                                      ==============
92      /**
93       * Displays the request header management index page.
94       *
95       * @param form the search form for filtering
96       * @return HTML response for the request header list page
97       */
98      @Execute
99      @Secured({ ROLE, ROLE + VIEW })
100     public HtmlResponse index(final SearchForm form) {
101         return asListHtml();
102     }
103 
104     /**
105      * Displays a paginated list of request header items.
106      *
107      * @param pageNumber the page number to display (optional)
108      * @param form the search form containing filter criteria
109      * @return HTML response with the request header list
110      */
111     @Execute
112     @Secured({ ROLE, ROLE + VIEW })
113     public HtmlResponse list(final OptionalThing<Integer> pageNumber, final SearchForm form) {
114         pageNumber.ifPresent(num -> {
115             reqHeaderPager.setCurrentPageNumber(pageNumber.get());
116         }).orElse(() -> {
117             reqHeaderPager.setCurrentPageNumber(0);
118         });
119         return asHtml(path_AdminReqheader_AdminReqheaderJsp).renderWith(data -> {
120             searchPaging(data, form);
121         });
122     }
123 
124     /**
125      * Searches for request header items based on the provided search criteria.
126      *
127      * @param form the search form containing search criteria
128      * @return HTML response with filtered request header results
129      */
130     @Execute
131     @Secured({ ROLE, ROLE + VIEW })
132     public HtmlResponse search(final SearchForm form) {
133         copyBeanToBean(form, reqHeaderPager, op -> op.exclude(Constants.PAGER_CONVERSION_RULE));
134         return asHtml(path_AdminReqheader_AdminReqheaderJsp).renderWith(data -> {
135             searchPaging(data, form);
136         });
137     }
138 
139     /**
140      * Resets the search criteria and displays all request header items.
141      *
142      * @param form the search form to reset
143      * @return HTML response with the reset request header list
144      */
145     @Execute
146     @Secured({ ROLE, ROLE + VIEW })
147     public HtmlResponse reset(final SearchForm form) {
148         reqHeaderPager.clear();
149         return asHtml(path_AdminReqheader_AdminReqheaderJsp).renderWith(data -> {
150             searchPaging(data, form);
151         });
152     }
153 
154     /**
155      * Sets up search paging data for rendering the request header list.
156      *
157      * @param data the render data to populate
158      * @param form the search form containing current search criteria
159      */
160     protected void searchPaging(final RenderData data, final SearchForm form) {
161         RenderDataUtil.register(data, "requestHeaderItems", requestHeaderService.getRequestHeaderList(reqHeaderPager)); // page navi
162         RenderDataUtil.register(data, "displayCreateLink", !crawlingConfigHelper.getAllWebConfigList(false, false, false, null).isEmpty());
163 
164         // restore from pager
165         copyBeanToBean(reqHeaderPager, form, op -> op.include("id"));
166     }
167 
168     // ===================================================================================
169     //                                                                        Edit Execute
170     //                                                                        ============
171     // -----------------------------------------------------
172     //                                            Entry Page
173     //                                            ----------
174     /**
175      * Displays the form for creating a new request header item.
176      *
177      * @return HTML response for the create form
178      */
179     @Execute
180     @Secured({ ROLE })
181     public HtmlResponse createnew() {
182         saveToken();
183         return asHtml(path_AdminReqheader_AdminReqheaderEditJsp).useForm(CreateForm.class, op -> {
184             op.setup(form -> {
185                 form.initialize();
186                 form.crudMode = CrudMode.CREATE;
187             });
188         }).renderWith(data -> {
189             registerWebConfigItems(data);
190         });
191     }
192 
193     /**
194      * Displays the form for editing an existing request header item.
195      *
196      * @param form the edit form containing the ID of the item to edit
197      * @return HTML response for the edit form
198      */
199     @Execute
200     @Secured({ ROLE })
201     public HtmlResponse edit(final EditForm form) {
202         validate(form, messages -> {}, this::asListHtml);
203         final String id = form.id;
204         requestHeaderService.getRequestHeader(id).ifPresent(entity -> {
205             copyBeanToBean(entity, form, op -> {});
206         }).orElse(() -> {
207             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
208         });
209         saveToken();
210         if (form.crudMode.intValue() == CrudMode.EDIT) {
211             // back
212             form.crudMode = CrudMode.DETAILS;
213             return asDetailsHtml();
214         }
215         form.crudMode = CrudMode.EDIT;
216         return asEditHtml();
217     }
218 
219     // -----------------------------------------------------
220     //                                               Details
221     //                                               -------
222     /**
223      * Displays the details of a request header item.
224      *
225      * @param crudMode the CRUD mode for the operation
226      * @param id the ID of the request header item to display
227      * @return HTML response for the details page
228      */
229     @Execute
230     @Secured({ ROLE, ROLE + VIEW })
231     public HtmlResponse details(final int crudMode, final String id) {
232         verifyCrudMode(crudMode, CrudMode.DETAILS, this::asListHtml);
233         saveToken();
234         return asDetailsHtml().useForm(EditForm.class, op -> {
235             op.setup(form -> {
236                 requestHeaderService.getRequestHeader(id).ifPresent(entity -> {
237                     copyBeanToBean(entity, form, copyOp -> {
238                         copyOp.excludeNull();
239                     });
240                     form.crudMode = crudMode;
241                 }).orElse(() -> {
242                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
243                 });
244             });
245         });
246     }
247 
248     // -----------------------------------------------------
249     //                                         Actually Crud
250     //                                         -------------
251     /**
252      * Creates a new request header item.
253      *
254      * @param form the create form containing the new item data
255      * @return HTML response redirecting to the list page after creation
256      */
257     @Execute
258     @Secured({ ROLE })
259     public HtmlResponse create(final CreateForm form) {
260         verifyCrudMode(form.crudMode, CrudMode.CREATE, this::asListHtml);
261         validate(form, messages -> {}, this::asEditHtml);
262         verifyToken(this::asEditHtml);
263         getRequestHeader(form).ifPresent(entity -> {
264             try {
265                 requestHeaderService.store(entity);
266                 saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
267             } catch (final Exception e) {
268                 logger.warn("Failed to process a request.", e);
269                 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)),
270                         this::asEditHtml);
271             }
272         }).orElse(() -> {
273             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL), this::asEditHtml);
274         });
275         return redirect(getClass());
276     }
277 
278     /**
279      * Updates an existing request header item.
280      *
281      * @param form the edit form containing the updated item data
282      * @return HTML response redirecting to the list page after update
283      */
284     @Execute
285     @Secured({ ROLE })
286     public HtmlResponse update(final EditForm form) {
287         verifyCrudMode(form.crudMode, CrudMode.EDIT, this::asListHtml);
288         validate(form, messages -> {}, this::asEditHtml);
289         verifyToken(this::asEditHtml);
290         getRequestHeader(form).ifPresent(entity -> {
291             try {
292                 requestHeaderService.store(entity);
293                 saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
294             } catch (final Exception e) {
295                 logger.warn("Failed to process a request.", e);
296                 throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
297                         this::asEditHtml);
298             }
299         }).orElse(() -> {
300             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), this::asEditHtml);
301         });
302         return redirect(getClass());
303     }
304 
305     /**
306      * Deletes a request header item.
307      *
308      * @param form the edit form containing the ID of the item to delete
309      * @return HTML response redirecting to the list page after deletion
310      */
311     @Execute
312     @Secured({ ROLE })
313     public HtmlResponse delete(final EditForm form) {
314         verifyCrudMode(form.crudMode, CrudMode.DETAILS, this::asListHtml);
315         validate(form, messages -> {}, this::asDetailsHtml);
316         verifyToken(this::asDetailsHtml);
317         final String id = form.id;
318         requestHeaderService.getRequestHeader(id).ifPresent(entity -> {
319             try {
320                 requestHeaderService.delete(entity);
321                 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
322             } catch (final Exception e) {
323                 logger.warn("Failed to process a request.", e);
324                 throwValidationError(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)),
325                         this::asEditHtml);
326             }
327         }).orElse(() -> {
328             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asDetailsHtml);
329         });
330         return redirect(getClass());
331     }
332 
333     // ===================================================================================
334     //                                                                        Assist Logic
335     //                                                                        ============
336     /**
337      * Creates a RequestHeader entity from form data with user and timestamp information.
338      *
339      * @param form the form containing the request header data
340      * @param username the username of the user performing the operation
341      * @param currentTime the current timestamp
342      * @return optional entity containing the request header data, or empty if creation fails
343      */
344     public static OptionalEntity<RequestHeader> getEntity(final CreateForm form, final String username, final long currentTime) {
345         switch (form.crudMode) {
346         case CrudMode.CREATE:
347             return OptionalEntity.of(new RequestHeader()).map(entity -> {
348                 entity.setCreatedBy(username);
349                 entity.setCreatedTime(currentTime);
350                 return entity;
351             });
352         case CrudMode.EDIT:
353             if (form instanceof EditForm) {
354                 return ComponentUtil.getComponent(RequestHeaderService.class).getRequestHeader(((EditForm) form).id);
355             }
356             break;
357         default:
358             break;
359         }
360         return OptionalEntity.empty();
361     }
362 
363     /**
364      * Creates a RequestHeader entity from the provided form data.
365      *
366      * @param form the form containing the request header data
367      * @return optional entity containing the request header data, or empty if creation fails
368      */
369     public static OptionalEntity<RequestHeader> getRequestHeader(final CreateForm form) {
370         final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
371         final String username = systemHelper.getUsername();
372         final long currentTime = systemHelper.getCurrentTimeAsLong();
373         return getEntity(form, username, currentTime).map(entity -> {
374             entity.setUpdatedBy(username);
375             entity.setUpdatedTime(currentTime);
376             copyBeanToBean(form, entity, op -> op.exclude(Constants.COMMON_CONVERSION_RULE));
377             return entity;
378         });
379     }
380 
381     /**
382      * Registers web configuration items for rendering in the UI.
383      *
384      * @param data the render data to populate with web config items
385      */
386     protected void registerWebConfigItems(final RenderData data) {
387         final List<Map<String, String>> itemList = new ArrayList<>();
388         final List<WebConfig> webConfigList = crawlingConfigHelper.getAllWebConfigList(false, false, false, null);
389         for (final WebConfig webConfig : webConfigList) {
390             itemList.add(createItem(webConfig.getName(), webConfig.getId().toString()));
391         }
392         RenderDataUtil.register(data, "webConfigItems", itemList);
393     }
394 
395     /**
396      * Creates a map item for UI rendering with label and value.
397      *
398      * @param label the display label for the item
399      * @param value the value for the item
400      * @return map containing the label and value
401      */
402     protected Map<String, String> createItem(final String label, final String value) {
403         final Map<String, String> map = new HashMap<>(2);
404         map.put(Constants.ITEM_LABEL, label);
405         map.put(Constants.ITEM_VALUE, value);
406         return map;
407     }
408 
409     // ===================================================================================
410     //                                                                        Small Helper
411     //                                                                        ============
412     //                                                                              JSP
413     //                                                                           =========
414 
415     private HtmlResponse asListHtml() {
416         return asHtml(path_AdminReqheader_AdminReqheaderJsp).renderWith(data -> {
417             RenderDataUtil.register(data, "requestHeaderItems", requestHeaderService.getRequestHeaderList(reqHeaderPager)); // page navi
418             RenderDataUtil.register(data, "displayCreateLink",
419                     !crawlingConfigHelper.getAllWebConfigList(false, false, false, null).isEmpty());
420         }).useForm(SearchForm.class, setup -> {
421             setup.setup(form -> {
422                 copyBeanToBean(reqHeaderPager, form, op -> op.include("id"));
423             });
424         });
425     }
426 
427     private HtmlResponse asEditHtml() {
428         return asHtml(path_AdminReqheader_AdminReqheaderEditJsp).renderWith(data -> {
429             registerWebConfigItems(data);
430         });
431     }
432 
433     private HtmlResponse asDetailsHtml() {
434         return asHtml(path_AdminReqheader_AdminReqheaderDetailsJsp).renderWith(data -> {
435             registerWebConfigItems(data);
436         });
437     }
438 }