View Javadoc
1   /*
2    * Copyright 2012-2021 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.dict.synonym;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import javax.annotation.Resource;
25  
26  import org.codelibs.core.beans.util.BeanUtil;
27  import org.codelibs.core.lang.StringUtil;
28  import org.codelibs.fess.Constants;
29  import org.codelibs.fess.annotation.Secured;
30  import org.codelibs.fess.app.pager.SynonymPager;
31  import org.codelibs.fess.app.service.SynonymService;
32  import org.codelibs.fess.app.web.CrudMode;
33  import org.codelibs.fess.app.web.admin.dict.AdminDictAction;
34  import org.codelibs.fess.app.web.base.FessAdminAction;
35  import org.codelibs.fess.app.web.base.FessBaseAction;
36  import org.codelibs.fess.dict.synonym.SynonymItem;
37  import org.codelibs.fess.util.ComponentUtil;
38  import org.codelibs.fess.util.RenderDataUtil;
39  import org.dbflute.optional.OptionalEntity;
40  import org.dbflute.optional.OptionalThing;
41  import org.lastaflute.web.Execute;
42  import org.lastaflute.web.response.ActionResponse;
43  import org.lastaflute.web.response.HtmlResponse;
44  import org.lastaflute.web.response.render.RenderData;
45  import org.lastaflute.web.ruts.process.ActionRuntime;
46  import org.lastaflute.web.validation.VaErrorHook;
47  import org.lastaflute.web.validation.exception.ValidationErrorException;
48  
49  /**
50   * @author shinsuke
51   * @author Keiichi Watanabe
52   */
53  public class AdminDictSynonymAction extends FessAdminAction {
54  
55      public static final String ROLE = "admin-dict";
56  
57      // ===================================================================================
58      //                                                                           Attribute
59      //                                                                           =========
60      @Resource
61      private SynonymService synonymService;
62      @Resource
63      private SynonymPager synonymPager;
64  
65      // ===================================================================================
66      //                                                                               Hook
67      //                                                                              ======
68      @Override
69      protected void setupHtmlData(final ActionRuntime runtime) {
70          super.setupHtmlData(runtime);
71          runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameDictSynonym()));
72      }
73  
74      @Override
75      protected String getActionRole() {
76          return ROLE;
77      }
78  
79      // ===================================================================================
80      //                                                                      Search Execute
81      //                                                                      ==============
82      @Execute
83      @Secured({ ROLE, ROLE + VIEW })
84      public HtmlResponse index(final SearchForm form) {
85          validate(form, messages -> {}, this::asDictIndexHtml);
86          synonymPager.clear();
87          return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
88              searchPaging(data, form);
89          });
90      }
91  
92      @Execute
93      @Secured({ ROLE, ROLE + VIEW })
94      public HtmlResponse list(final OptionalThing<Integer> pageNumber, final SearchForm form) {
95          validate(form, messages -> {}, this::asDictIndexHtml);
96          pageNumber.ifPresent(num -> {
97              synonymPager.setCurrentPageNumber(pageNumber.get());
98          }).orElse(() -> {
99              synonymPager.setCurrentPageNumber(0);
100         });
101         return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
102             searchPaging(data, form);
103         });
104     }
105 
106     @Execute
107     @Secured({ ROLE, ROLE + VIEW })
108     public HtmlResponse search(final SearchForm form) {
109         validate(form, messages -> {}, this::asDictIndexHtml);
110         copyBeanToBean(form, synonymPager, op -> op.exclude(Constants.PAGER_CONVERSION_RULE));
111         return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
112             searchPaging(data, form);
113         });
114     }
115 
116     @Execute
117     @Secured({ ROLE, ROLE + VIEW })
118     public HtmlResponse reset(final SearchForm form) {
119         validate(form, messages -> {}, this::asDictIndexHtml);
120         synonymPager.clear();
121         return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
122             searchPaging(data, form);
123         });
124     }
125 
126     protected void searchPaging(final RenderData data, final SearchForm form) {
127         // page navi
128         RenderDataUtil.register(data, "synonymItemItems", synonymService.getSynonymList(form.dictId, synonymPager));
129 
130         // restore from pager
131         BeanUtil.copyBeanToBean(synonymPager, form, op -> {
132             op.exclude(Constants.PAGER_CONVERSION_RULE);
133         });
134     }
135 
136     // ===================================================================================
137     //                                                                        Edit Execute
138     //                                                                        ============
139     // -----------------------------------------------------
140     //                                            Entry Page
141     //                                            ----------
142     @Execute
143     @Secured({ ROLE })
144     public HtmlResponse createnew(final String dictId) {
145         saveToken();
146         return asHtml(path_AdminDictSynonym_AdminDictSynonymEditJsp).useForm(CreateForm.class, op -> {
147             op.setup(form -> {
148                 form.initialize();
149                 form.crudMode = CrudMode.CREATE;
150                 form.dictId = dictId;
151             });
152         });
153     }
154 
155     @Execute
156     @Secured({ ROLE })
157     public HtmlResponse edit(final EditForm form) {
158         validate(form, messages -> {}, () -> asListHtml(form.dictId));
159         synonymService.getSynonymItem(form.dictId, form.id).ifPresent(entity -> {
160             form.inputs = entity.getInputsValue();
161             form.outputs = entity.getOutputsValue();
162         }).orElse(() -> {
163             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.getDisplayId()),
164                     () -> asListHtml(form.dictId));
165         });
166         saveToken();
167         if (form.crudMode.intValue() == CrudMode.EDIT) {
168             // back
169             form.crudMode = CrudMode.DETAILS;
170             return asDetailsHtml();
171         }
172         form.crudMode = CrudMode.EDIT;
173         return asEditHtml();
174     }
175 
176     // -----------------------------------------------------
177     //                                               Details
178     //                                               -------
179     @Execute
180     @Secured({ ROLE, ROLE + VIEW })
181     public HtmlResponse details(final String dictId, final int crudMode, final long id) {
182         verifyCrudMode(crudMode, CrudMode.DETAILS, dictId);
183         saveToken();
184         return asDetailsHtml().useForm(EditForm.class, op -> {
185             op.setup(form -> {
186                 synonymService.getSynonymItem(dictId, id).ifPresent(entity -> {
187                     form.inputs = entity.getInputsValue();
188                     form.outputs = entity.getOutputsValue();
189                 }).orElse(() -> {
190                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, dictId + ":" + id),
191                             () -> asListHtml(dictId));
192                 });
193                 form.id = id;
194                 form.crudMode = crudMode;
195                 form.dictId = dictId;
196             });
197         });
198     }
199 
200     // -----------------------------------------------------
201     //                                              Download
202     //                                               -------
203     @Execute
204     @Secured({ ROLE, ROLE + VIEW })
205     public HtmlResponse downloadpage(final String dictId) {
206         saveToken();
207         return asHtml(path_AdminDictSynonym_AdminDictSynonymDownloadJsp).useForm(DownloadForm.class, op -> {
208             op.setup(form -> {
209                 form.dictId = dictId;
210             });
211         }).renderWith(data -> {
212             synonymService.getSynonymFile(dictId).ifPresent(file -> {
213                 RenderDataUtil.register(data, "path", file.getPath());
214             }).orElse(() -> {
215                 throwValidationError(messages -> messages.addErrorsFailedToDownloadSynonymFile(GLOBAL), this::asDictIndexHtml);
216             });
217         });
218     }
219 
220     @Execute
221     @Secured({ ROLE, ROLE + VIEW })
222     public ActionResponse download(final DownloadForm form) {
223         validate(form, messages -> {}, () -> downloadpage(form.dictId));
224         verifyTokenKeep(() -> downloadpage(form.dictId));
225         return synonymService.getSynonymFile(form.dictId)
226                 .map(file -> asStream(new File(file.getPath()).getName()).contentTypeOctetStream().stream(out -> {
227                     file.writeOut(out);
228                 })).orElseGet(() -> {
229                     throwValidationError(messages -> messages.addErrorsFailedToDownloadSynonymFile(GLOBAL),
230                             () -> downloadpage(form.dictId));
231                     return null;
232                 });
233     }
234 
235     // -----------------------------------------------------
236     //                                                Upload
237     //                                               -------
238     @Execute
239     @Secured({ ROLE })
240     public HtmlResponse uploadpage(final String dictId) {
241         saveToken();
242         return asHtml(path_AdminDictSynonym_AdminDictSynonymUploadJsp).useForm(UploadForm.class, op -> {
243             op.setup(form -> {
244                 form.dictId = dictId;
245             });
246         }).renderWith(data -> {
247             synonymService.getSynonymFile(dictId).ifPresent(file -> {
248                 RenderDataUtil.register(data, "path", file.getPath());
249             }).orElse(() -> {
250                 throwValidationError(messages -> messages.addErrorsFailedToDownloadSynonymFile(GLOBAL), this::asDictIndexHtml);
251             });
252         });
253     }
254 
255     @Execute
256     @Secured({ ROLE })
257     public HtmlResponse upload(final UploadForm form) {
258         validate(form, messages -> {}, () -> uploadpage(form.dictId));
259         verifyToken(() -> uploadpage(form.dictId));
260         return synonymService.getSynonymFile(form.dictId).map(file -> {
261             try (InputStream inputStream = form.synonymFile.getInputStream()) {
262                 file.update(inputStream);
263             } catch (final IOException e) {
264                 throwValidationError(messages -> messages.addErrorsFailedToUploadSynonymFile(GLOBAL),
265                         () -> redirectWith(getClass(), moreUrl("uploadpage/" + form.dictId)));
266             }
267             saveInfo(messages -> messages.addSuccessUploadSynonymFile(GLOBAL));
268             return redirectWith(getClass(), moreUrl("list/1").params("dictId", form.dictId));
269         }).orElseGet(() -> {
270             throwValidationError(messages -> messages.addErrorsFailedToUploadSynonymFile(GLOBAL), () -> uploadpage(form.dictId));
271             return null;
272         });
273 
274     }
275 
276     // -----------------------------------------------------
277     //                                         Actually Crud
278     //                                         -------------
279     @Execute
280     @Secured({ ROLE })
281     public HtmlResponse create(final CreateForm form) {
282         verifyCrudMode(form.crudMode, CrudMode.CREATE, form.dictId);
283         validate(form, messages -> {}, this::asEditHtml);
284         verifyToken(this::asEditHtml);
285         createSynonymItem(form, this::asEditHtml).ifPresent(entity -> {
286             try {
287                 synonymService.store(form.dictId, entity);
288                 saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
289             } catch (final Exception e) {
290                 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)),
291                         this::asEditHtml);
292             }
293         }).orElse(() -> {
294             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL), this::asEditHtml);
295         });
296         return redirectWith(getClass(), moreUrl("list/1").params("dictId", form.dictId));
297     }
298 
299     @Execute
300     @Secured({ ROLE })
301     public HtmlResponse update(final EditForm form) {
302         verifyCrudMode(form.crudMode, CrudMode.EDIT, form.dictId);
303         validate(form, messages -> {}, this::asEditHtml);
304         verifyToken(this::asEditHtml);
305         createSynonymItem(form, this::asEditHtml).ifPresent(entity -> {
306             try {
307                 synonymService.store(form.dictId, entity);
308                 saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
309             } catch (final Exception e) {
310                 throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
311                         this::asEditHtml);
312             }
313         }).orElse(() -> {
314             saveToken();
315             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.getDisplayId()), this::asEditHtml);
316         });
317         return redirectWith(getClass(), moreUrl("list/1").params("dictId", form.dictId));
318     }
319 
320     @Execute
321     @Secured({ ROLE })
322     public HtmlResponse delete(final EditForm form) {
323         verifyCrudMode(form.crudMode, CrudMode.DETAILS, form.dictId);
324         validate(form, messages -> {}, this::asDetailsHtml);
325         verifyToken(this::asDetailsHtml);
326         synonymService.getSynonymItem(form.dictId, form.id).ifPresent(entity -> {
327             try {
328                 synonymService.delete(form.dictId, entity);
329                 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
330             } catch (final Exception e) {
331                 throwValidationError(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)),
332                         this::asEditHtml);
333             }
334         }).orElse(() -> {
335             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.getDisplayId()), this::asDetailsHtml);
336         });
337         return redirectWith(getClass(), moreUrl("list/1").params("dictId", form.dictId));
338     }
339 
340     //===================================================================================
341     //                                                                        Assist Logic
342     //                                                                        ============
343 
344     private static OptionalEntity<SynonymItem> getEntity(final CreateForm form) {
345         switch (form.crudMode) {
346         case CrudMode.CREATE:
347             final SynonymItem entity = new SynonymItem(0, StringUtil.EMPTY_STRINGS, StringUtil.EMPTY_STRINGS);
348             return OptionalEntity.of(entity);
349         case CrudMode.EDIT:
350             if (form instanceof EditForm) {
351                 return ComponentUtil.getComponent(SynonymService.class).getSynonymItem(form.dictId, ((EditForm) form).id);
352             }
353             break;
354         default:
355             break;
356         }
357         return OptionalEntity.empty();
358     }
359 
360     protected OptionalEntity<SynonymItem> createSynonymItem(final CreateForm form, final VaErrorHook hook) {
361         try {
362             return createSynonymItem(this, form, hook);
363         } catch (final ValidationErrorException e) {
364             saveToken();
365             throw e;
366         }
367     }
368 
369     public static OptionalEntity<SynonymItem> createSynonymItem(final FessBaseAction action, final CreateForm form,
370             final VaErrorHook hook) {
371         return getEntity(form).map(entity -> {
372             final String[] newInputs = splitLine(form.inputs);
373             validateSynonymString(action, newInputs, "inputs", hook);
374             entity.setNewInputs(newInputs);
375             final String[] newOutputs = splitLine(form.outputs);
376             validateSynonymString(action, newOutputs, "outputs", hook);
377             entity.setNewOutputs(newOutputs);
378             return entity;
379         });
380     }
381 
382     // ===================================================================================
383     //                                                                        Small Helper
384     //                                                                        ============
385     protected void verifyCrudMode(final int crudMode, final int expectedMode, final String dictId) {
386         if (crudMode != expectedMode) {
387             throwValidationError(messages -> {
388                 messages.addErrorsCrudInvalidMode(GLOBAL, String.valueOf(expectedMode), String.valueOf(crudMode));
389             }, () -> asListHtml(dictId));
390         }
391     }
392 
393     private static void validateSynonymString(final FessBaseAction action, final String[] values, final String propertyName,
394             final VaErrorHook hook) {
395         if (values.length == 0) {
396             return;
397         }
398         for (final String value : values) {
399             if (value.indexOf(',') >= 0) {
400                 action.throwValidationError(messages -> {
401                     messages.addErrorsInvalidStrIsIncluded(propertyName, value, ",");
402                 }, hook);
403             }
404             if (value.indexOf("=>") >= 0) {
405                 action.throwValidationError(messages -> {
406                     messages.addErrorsInvalidStrIsIncluded(propertyName, value, "=>");
407                 }, hook);
408             }
409         }
410     }
411 
412     private static String[] splitLine(final String value) {
413         if (StringUtil.isBlank(value)) {
414             return StringUtil.EMPTY_STRINGS;
415         }
416         final String[] values = value.split("[\r\n]");
417         final List<String> list = new ArrayList<>(values.length);
418         for (final String line : values) {
419             if (StringUtil.isNotBlank(line)) {
420                 list.add(line.trim());
421             }
422         }
423         return list.toArray(new String[list.size()]);
424     }
425 
426     // ===================================================================================
427     //                                                                              JSP
428     //                                                                           =========
429 
430     protected HtmlResponse asDictIndexHtml() {
431         return redirect(AdminDictAction.class);
432     }
433 
434     private HtmlResponse asListHtml(final String dictId) {
435         return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
436             RenderDataUtil.register(data, "synonymItemItems", synonymService.getSynonymList(dictId, synonymPager));
437         }).useForm(SearchForm.class, setup -> {
438             setup.setup(form -> {
439                 copyBeanToBean(synonymPager, form, op -> op.include("id"));
440             });
441         });
442     }
443 
444     private HtmlResponse asEditHtml() {
445         return asHtml(path_AdminDictSynonym_AdminDictSynonymEditJsp);
446     }
447 
448     private HtmlResponse asDetailsHtml() {
449         return asHtml(path_AdminDictSynonym_AdminDictSynonymDetailsJsp);
450     }
451 
452 }