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