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.badword;
17  
18  import java.io.BufferedReader;
19  import java.io.BufferedWriter;
20  import java.io.InputStream;
21  import java.io.InputStreamReader;
22  import java.io.OutputStreamWriter;
23  import java.io.Reader;
24  import java.io.Writer;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  
28  import javax.annotation.Resource;
29  
30  import org.codelibs.core.beans.util.BeanUtil;
31  import org.codelibs.core.concurrent.CommonPoolUtil;
32  import org.codelibs.fess.Constants;
33  import org.codelibs.fess.annotation.Secured;
34  import org.codelibs.fess.app.pager.BadWordPager;
35  import org.codelibs.fess.app.service.BadWordService;
36  import org.codelibs.fess.app.web.CrudMode;
37  import org.codelibs.fess.app.web.base.FessAdminAction;
38  import org.codelibs.fess.es.config.exentity.BadWord;
39  import org.codelibs.fess.exception.FessSystemException;
40  import org.codelibs.fess.helper.SuggestHelper;
41  import org.codelibs.fess.helper.SystemHelper;
42  import org.codelibs.fess.util.ComponentUtil;
43  import org.codelibs.fess.util.RenderDataUtil;
44  import org.dbflute.optional.OptionalEntity;
45  import org.dbflute.optional.OptionalThing;
46  import org.lastaflute.web.Execute;
47  import org.lastaflute.web.response.ActionResponse;
48  import org.lastaflute.web.response.HtmlResponse;
49  import org.lastaflute.web.response.render.RenderData;
50  import org.lastaflute.web.ruts.process.ActionRuntime;
51  
52  /**
53   * @author Keiichi Watanabe
54   */
55  public class AdminBadwordAction extends FessAdminAction {
56  
57      public static final String ROLE = "admin-badword";
58  
59      // ===================================================================================
60      //                                                                           Attribute
61      //                                                                           =========
62      @Resource
63      private BadWordService badWordService;
64      @Resource
65      private BadWordPager badWordPager;
66      @Resource
67      protected SuggestHelper suggestHelper;
68  
69      // ===================================================================================
70      //                                                                               Hook
71      //                                                                              ======
72      @Override
73      protected void setupHtmlData(final ActionRuntime runtime) {
74          super.setupHtmlData(runtime);
75          runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameBadword()));
76      }
77  
78      @Override
79      protected String getActionRole() {
80          return ROLE;
81      }
82  
83      // ===================================================================================
84      //                                                                      Search Execute
85      //                                                                      ==============
86      @Execute
87      @Secured({ ROLE, ROLE + VIEW })
88      public HtmlResponse index() {
89          return asListHtml();
90      }
91  
92      @Execute
93      @Secured({ ROLE, ROLE + VIEW })
94      public HtmlResponse list(final OptionalThing<Integer> pageNumber, final SearchForm form) {
95          pageNumber.ifPresent(num -> {
96              badWordPager.setCurrentPageNumber(pageNumber.get());
97          }).orElse(() -> {
98              badWordPager.setCurrentPageNumber(0);
99          });
100         return asHtml(path_AdminBadword_AdminBadwordJsp).renderWith(data -> {
101             searchPaging(data, form);
102         });
103     }
104 
105     @Execute
106     @Secured({ ROLE, ROLE + VIEW })
107     public HtmlResponse search(final SearchForm form) {
108         copyBeanToBean(form, badWordPager, op -> op.exclude(Constants.PAGER_CONVERSION_RULE));
109         return asHtml(path_AdminBadword_AdminBadwordJsp).renderWith(data -> {
110             searchPaging(data, form);
111         });
112     }
113 
114     @Execute
115     @Secured({ ROLE, ROLE + VIEW })
116     public HtmlResponse reset(final SearchForm form) {
117         badWordPager.clear();
118         return asHtml(path_AdminBadword_AdminBadwordJsp).renderWith(data -> {
119             searchPaging(data, form);
120         });
121     }
122 
123     protected void searchPaging(final RenderData data, final SearchForm form) {
124         RenderDataUtil.register(data, "badWordItems", badWordService.getBadWordList(badWordPager)); // page navi
125 
126         // restore from pager
127         copyBeanToBean(badWordPager, form, op -> op.include("id"));
128     }
129 
130     // ===================================================================================
131     //                                                                        Edit Execute
132     //                                                                        ============
133     // -----------------------------------------------------
134     //                                            Entry Page
135     //                                            ----------
136     @Execute
137     @Secured({ ROLE })
138     public HtmlResponse createnew() {
139         saveToken();
140         return asEditHtml().useForm(CreateForm.class, op -> {
141             op.setup(form -> {
142                 form.initialize();
143                 form.crudMode = CrudMode.CREATE;
144             });
145         });
146     }
147 
148     @Execute
149     @Secured({ ROLE })
150     public HtmlResponse edit(final EditForm form) {
151         validate(form, messages -> {}, this::asListHtml);
152         final String id = form.id;
153         badWordService.getBadWord(id).ifPresent(entity -> {
154             copyBeanToBean(entity, form, op -> {});
155         }).orElse(() -> {
156             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
157         });
158         saveToken();
159         if (form.crudMode.intValue() == CrudMode.EDIT) {
160             // back
161             form.crudMode = CrudMode.DETAILS;
162             return asDetailsHtml();
163         }
164         form.crudMode = CrudMode.EDIT;
165         return asEditHtml();
166     }
167 
168     // -----------------------------------------------------
169     //                                               Details
170     //                                               -------
171     @Execute
172     @Secured({ ROLE, ROLE + VIEW })
173     public HtmlResponse details(final int crudMode, final String id) {
174         verifyCrudMode(crudMode, CrudMode.DETAILS);
175         saveToken();
176         return asDetailsHtml().useForm(EditForm.class, op -> {
177             op.setup(form -> {
178                 badWordService.getBadWord(id).ifPresent(entity -> {
179                     copyBeanToBean(entity, form, copyOp -> {
180                         copyOp.excludeNull();
181                     });
182                     form.crudMode = crudMode;
183                 }).orElse(() -> {
184                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
185                 });
186             });
187         });
188     }
189 
190     // -----------------------------------------------------
191     //                                              Download
192     //                                               -------
193     @Execute
194     @Secured({ ROLE, ROLE + VIEW })
195     public HtmlResponse downloadpage() {
196         saveToken();
197         return asDownloadHtml();
198     }
199 
200     @Execute
201     @Secured({ ROLE, ROLE + VIEW })
202     public ActionResponse download(final DownloadForm form) {
203         verifyToken(this::asDownloadHtml);
204 
205         return asStream("badword.csv").contentTypeOctetStream().stream(out -> {
206             final Path tempFile = ComponentUtil.getSystemHelper().createTempFile("fess-badword-", ".csv").toPath();
207             try {
208                 try (Writer writer = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(tempFile), getCsvEncoding()))) {
209                     badWordService.exportCsv(writer);
210                 } catch (final Exception e) {
211                     throwValidationError(messages -> messages.addErrorsFailedToDownloadBadwordFile(GLOBAL), this::asDownloadHtml);
212                 }
213                 try (InputStream in = Files.newInputStream(tempFile)) {
214                     out.write(in);
215                 }
216             } finally {
217                 Files.delete(tempFile);
218             }
219         });
220     }
221 
222     // -----------------------------------------------------
223     //                                                Upload
224     //                                               -------
225     @Execute
226     @Secured({ ROLE })
227     public HtmlResponse uploadpage() {
228         saveToken();
229         return asUploadHtml();
230     }
231 
232     // -----------------------------------------------------
233     //                                         Actually Crud
234     //                                         -------------
235     @Execute
236     @Secured({ ROLE })
237     public HtmlResponse create(final CreateForm form) {
238         verifyCrudMode(form.crudMode, CrudMode.CREATE);
239         validate(form, messages -> {}, this::asEditHtml);
240         verifyToken(this::asEditHtml);
241         getBadWord(form).ifPresent(entity -> {
242             try {
243                 badWordService.store(entity);
244                 suggestHelper.addBadWord(entity.getSuggestWord(), false);
245                 saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
246             } catch (final Exception e) {
247                 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)),
248                         this::asEditHtml);
249             }
250         }).orElse(() -> {
251             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL), this::asEditHtml);
252         });
253         return redirect(getClass());
254     }
255 
256     @Execute
257     @Secured({ ROLE })
258     public HtmlResponse update(final EditForm form) {
259         verifyCrudMode(form.crudMode, CrudMode.EDIT);
260         validate(form, messages -> {}, this::asEditHtml);
261         verifyToken(this::asEditHtml);
262         getBadWord(form).ifPresent(entity -> {
263             try {
264                 badWordService.store(entity);
265                 suggestHelper.storeAllBadWords(false);
266                 saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
267             } catch (final Exception e) {
268                 throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
269                         this::asEditHtml);
270             }
271         }).orElse(() -> {
272             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), this::asEditHtml);
273         });
274         return redirect(getClass());
275     }
276 
277     @Execute
278     @Secured({ ROLE })
279     public HtmlResponse delete(final EditForm form) {
280         verifyCrudMode(form.crudMode, CrudMode.DETAILS);
281         validate(form, messages -> {}, this::asDetailsHtml);
282         verifyToken(this::asDetailsHtml);
283         final String id = form.id;
284         badWordService.getBadWord(id).ifPresent(entity -> {
285             try {
286                 badWordService.delete(entity);
287                 suggestHelper.deleteBadWord(entity.getSuggestWord());
288                 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
289             } catch (final Exception e) {
290                 throwValidationError(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)),
291                         this::asDetailsHtml);
292             }
293         }).orElse(() -> {
294             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asDetailsHtml);
295         });
296         return redirect(getClass());
297     }
298 
299     @Execute
300     @Secured({ ROLE })
301     public HtmlResponse upload(final UploadForm form) {
302         validate(form, messages -> {}, this::asUploadHtml);
303         verifyToken(this::asUploadHtml);
304         CommonPoolUtil.execute(() -> {
305             try (Reader reader = new BufferedReader(new InputStreamReader(form.badWordFile.getInputStream(), getCsvEncoding()))) {
306                 badWordService.importCsv(reader);
307                 suggestHelper.storeAllBadWords(false);
308             } catch (final Exception e) {
309                 throw new FessSystemException("Failed to import data.", e);
310             }
311         });
312         saveInfo(messages -> messages.addSuccessUploadBadWord(GLOBAL));
313         return redirect(getClass());
314     }
315 
316     // ===================================================================================
317     //                                                                        Assist Logic
318     //                                                                        ============
319     private static OptionalEntity<BadWord> getEntity(final CreateForm form, final String username, final long currentTime) {
320         switch (form.crudMode) {
321         case CrudMode.CREATE:
322             return OptionalEntity.of(new BadWord()).map(entity -> {
323                 entity.setCreatedBy(username);
324                 entity.setCreatedTime(currentTime);
325                 return entity;
326             });
327         case CrudMode.EDIT:
328             if (form instanceof EditForm) {
329                 return ComponentUtil.getComponent(BadWordService.class).getBadWord(((EditForm) form).id);
330             }
331             break;
332         default:
333             break;
334         }
335         return OptionalEntity.empty();
336     }
337 
338     public static OptionalEntity<BadWord> getBadWord(final CreateForm form) {
339         final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
340         final String username = systemHelper.getUsername();
341         final long currentTime = systemHelper.getCurrentTimeAsLong();
342         return getEntity(form, username, currentTime).map(entity -> {
343             entity.setUpdatedBy(username);
344             entity.setUpdatedTime(currentTime);
345             BeanUtil.copyBeanToBean(form, entity, op -> op.exclude(Constants.COMMON_CONVERSION_RULE));
346             return entity;
347         });
348     }
349 
350     // ===================================================================================
351     //                                                                        Small Helper
352     //                                                                        ============
353     protected void verifyCrudMode(final int crudMode, final int expectedMode) {
354         if (crudMode != expectedMode) {
355             throwValidationError(messages -> {
356                 messages.addErrorsCrudInvalidMode(GLOBAL, String.valueOf(expectedMode), String.valueOf(crudMode));
357             }, this::asListHtml);
358         }
359     }
360 
361     private String getCsvEncoding() {
362         return fessConfig.getCsvFileEncoding();
363     }
364 
365     // ===================================================================================
366     //                                                                              JSP
367     //                                                                           =========
368     private HtmlResponse asListHtml() {
369         return asHtml(path_AdminBadword_AdminBadwordJsp).renderWith(data -> {
370             RenderDataUtil.register(data, "badWordItems", badWordService.getBadWordList(badWordPager));
371         }).useForm(SearchForm.class, setup -> {
372             setup.setup(form -> {
373                 copyBeanToBean(badWordPager, form, op -> op.include("id"));
374             });
375         });
376     }
377 
378     private HtmlResponse asEditHtml() {
379         return asHtml(path_AdminBadword_AdminBadwordEditJsp);
380     }
381 
382     private HtmlResponse asDetailsHtml() {
383         return asHtml(path_AdminBadword_AdminBadwordDetailsJsp);
384     }
385 
386     private HtmlResponse asUploadHtml() {
387         return asHtml(path_AdminBadword_AdminBadwordUploadJsp).useForm(UploadForm.class);
388     }
389 
390     private HtmlResponse asDownloadHtml() {
391         return asHtml(path_AdminBadword_AdminBadwordDownloadJsp).useForm(DownloadForm.class);
392     }
393 
394 }