1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 org.apache.logging.log4j.LogManager;
25 import org.apache.logging.log4j.Logger;
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 import jakarta.annotation.Resource;
50
51
52
53
54
55 public class AdminDictSynonymAction extends FessAdminAction {
56
57
58
59
60 public static final String ROLE = "admin-dict";
61
62 private static final Logger logger = LogManager.getLogger(AdminDictSynonymAction.class);
63
64
65
66
67 @Resource
68 private SynonymService synonymService;
69 @Resource
70 private SynonymPager synonymPager;
71
72
73
74
75 public AdminDictSynonymAction() {
76 super();
77 }
78
79
80
81
82 @Override
83 protected void setupHtmlData(final ActionRuntime runtime) {
84 super.setupHtmlData(runtime);
85 runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameDictSynonym()));
86 }
87
88 @Override
89 protected String getActionRole() {
90 return ROLE;
91 }
92
93
94
95
96
97
98
99
100
101
102 @Execute
103 @Secured({ ROLE, ROLE + VIEW })
104 public HtmlResponse index(final SearchForm form) {
105 validate(form, messages -> {}, this::asDictIndexHtml);
106 synonymPager.clear();
107 return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
108 searchPaging(data, form);
109 });
110 }
111
112
113
114
115
116
117
118
119 @Execute
120 @Secured({ ROLE, ROLE + VIEW })
121 public HtmlResponse list(final OptionalThing<Integer> pageNumber, final SearchForm form) {
122 validate(form, messages -> {}, this::asDictIndexHtml);
123 pageNumber.ifPresent(num -> {
124 synonymPager.setCurrentPageNumber(pageNumber.get());
125 }).orElse(() -> {
126 synonymPager.setCurrentPageNumber(0);
127 });
128 return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
129 searchPaging(data, form);
130 });
131 }
132
133
134
135
136
137
138
139 @Execute
140 @Secured({ ROLE, ROLE + VIEW })
141 public HtmlResponse search(final SearchForm form) {
142 validate(form, messages -> {}, this::asDictIndexHtml);
143 copyBeanToBean(form, synonymPager, op -> op.exclude(Constants.PAGER_CONVERSION_RULE));
144 return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
145 searchPaging(data, form);
146 });
147 }
148
149
150
151
152
153
154
155 @Execute
156 @Secured({ ROLE, ROLE + VIEW })
157 public HtmlResponse reset(final SearchForm form) {
158 validate(form, messages -> {}, this::asDictIndexHtml);
159 synonymPager.clear();
160 return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
161 searchPaging(data, form);
162 });
163 }
164
165
166
167
168
169
170
171 protected void searchPaging(final RenderData data, final SearchForm form) {
172
173 RenderDataUtil.register(data, "synonymItemItems", synonymService.getSynonymList(form.dictId, synonymPager));
174
175
176 BeanUtil.copyBeanToBean(synonymPager, form, op -> {
177 op.exclude(Constants.PAGER_CONVERSION_RULE);
178 });
179 }
180
181
182
183
184
185
186
187
188
189
190
191
192
193 @Execute
194 @Secured({ ROLE })
195 public HtmlResponse createnew(final String dictId) {
196 saveToken();
197 return asHtml(path_AdminDictSynonym_AdminDictSynonymEditJsp).useForm(CreateForm.class, op -> {
198 op.setup(form -> {
199 form.initialize();
200 form.crudMode = CrudMode.CREATE;
201 form.dictId = dictId;
202 });
203 });
204 }
205
206
207
208
209
210
211
212 @Execute
213 @Secured({ ROLE })
214 public HtmlResponse edit(final EditForm form) {
215 validate(form, messages -> {}, () -> asListHtml(form.dictId));
216 synonymService.getSynonymItem(form.dictId, form.id).ifPresent(entity -> {
217 form.inputs = entity.getInputsValue();
218 form.outputs = entity.getOutputsValue();
219 }).orElse(() -> {
220 throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.getDisplayId()),
221 () -> asListHtml(form.dictId));
222 });
223 saveToken();
224 if (form.crudMode.intValue() == CrudMode.EDIT) {
225
226 form.crudMode = CrudMode.DETAILS;
227 return asDetailsHtml();
228 }
229 form.crudMode = CrudMode.EDIT;
230 return asEditHtml();
231 }
232
233
234
235
236
237
238
239
240
241
242
243
244 @Execute
245 @Secured({ ROLE, ROLE + VIEW })
246 public HtmlResponse details(final String dictId, final int crudMode, final long id) {
247 verifyCrudMode(crudMode, CrudMode.DETAILS, dictId);
248 saveToken();
249 return asDetailsHtml().useForm(EditForm.class, op -> {
250 op.setup(form -> {
251 synonymService.getSynonymItem(dictId, id).ifPresent(entity -> {
252 form.inputs = entity.getInputsValue();
253 form.outputs = entity.getOutputsValue();
254 }).orElse(() -> {
255 throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, dictId + ":" + id),
256 () -> asListHtml(dictId));
257 });
258 form.id = id;
259 form.crudMode = crudMode;
260 form.dictId = dictId;
261 });
262 });
263 }
264
265
266
267
268
269
270
271
272
273
274 @Execute
275 @Secured({ ROLE, ROLE + VIEW })
276 public HtmlResponse downloadpage(final String dictId) {
277 saveToken();
278 return asHtml(path_AdminDictSynonym_AdminDictSynonymDownloadJsp).useForm(DownloadForm.class, op -> {
279 op.setup(form -> {
280 form.dictId = dictId;
281 });
282 }).renderWith(data -> {
283 synonymService.getSynonymFile(dictId).ifPresent(file -> {
284 RenderDataUtil.register(data, "path", file.getPath());
285 }).orElse(() -> {
286 throwValidationError(messages -> messages.addErrorsFailedToDownloadSynonymFile(GLOBAL), this::asDictIndexHtml);
287 });
288 });
289 }
290
291
292
293
294
295
296
297 @Execute
298 @Secured({ ROLE, ROLE + VIEW })
299 public ActionResponse download(final DownloadForm form) {
300 validate(form, messages -> {}, () -> downloadpage(form.dictId));
301 verifyTokenKeep(() -> downloadpage(form.dictId));
302 return synonymService.getSynonymFile(form.dictId)
303 .map(file -> asStream(new File(file.getPath()).getName()).contentTypeOctetStream().stream(out -> {
304 file.writeOut(out);
305 }))
306 .orElseGet(() -> {
307 throwValidationError(messages -> messages.addErrorsFailedToDownloadSynonymFile(GLOBAL),
308 () -> downloadpage(form.dictId));
309 return null;
310 });
311 }
312
313
314
315
316
317
318
319
320
321
322 @Execute
323 @Secured({ ROLE })
324 public HtmlResponse uploadpage(final String dictId) {
325 saveToken();
326 return asHtml(path_AdminDictSynonym_AdminDictSynonymUploadJsp).useForm(UploadForm.class, op -> {
327 op.setup(form -> {
328 form.dictId = dictId;
329 });
330 }).renderWith(data -> {
331 synonymService.getSynonymFile(dictId).ifPresent(file -> {
332 RenderDataUtil.register(data, "path", file.getPath());
333 }).orElse(() -> {
334 throwValidationError(messages -> messages.addErrorsFailedToDownloadSynonymFile(GLOBAL), this::asDictIndexHtml);
335 });
336 });
337 }
338
339
340
341
342
343
344
345 @Execute
346 @Secured({ ROLE })
347 public HtmlResponse upload(final UploadForm form) {
348 validate(form, messages -> {}, () -> uploadpage(form.dictId));
349 verifyToken(() -> uploadpage(form.dictId));
350 return synonymService.getSynonymFile(form.dictId).map(file -> {
351 try (InputStream inputStream = form.synonymFile.getInputStream()) {
352 file.update(inputStream);
353 } catch (final IOException e) {
354 logger.warn("Failed to process a request.", e);
355 throwValidationError(messages -> messages.addErrorsFailedToUploadSynonymFile(GLOBAL),
356 () -> redirectWith(getClass(), moreUrl("uploadpage/" + form.dictId)));
357 }
358 saveInfo(messages -> messages.addSuccessUploadSynonymFile(GLOBAL));
359 return redirectWith(getClass(), moreUrl("list/1").params("dictId", form.dictId));
360 }).orElseGet(() -> {
361 throwValidationError(messages -> messages.addErrorsFailedToUploadSynonymFile(GLOBAL), () -> uploadpage(form.dictId));
362 return null;
363 });
364
365 }
366
367
368
369
370
371
372
373
374
375
376 @Execute
377 @Secured({ ROLE })
378 public HtmlResponse create(final CreateForm form) {
379 verifyCrudMode(form.crudMode, CrudMode.CREATE, form.dictId);
380 validate(form, messages -> {}, this::asEditHtml);
381 verifyToken(this::asEditHtml);
382 createSynonymItem(form, this::asEditHtml).ifPresent(entity -> {
383 try {
384 synonymService.store(form.dictId, entity);
385 saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
386 } catch (final Exception e) {
387 logger.warn("Failed to process a request.", e);
388 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)),
389 this::asEditHtml);
390 }
391 }).orElse(() -> {
392 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL), this::asEditHtml);
393 });
394 return redirectWith(getClass(), moreUrl("list/1").params("dictId", form.dictId));
395 }
396
397
398
399
400
401
402
403 @Execute
404 @Secured({ ROLE })
405 public HtmlResponse update(final EditForm form) {
406 verifyCrudMode(form.crudMode, CrudMode.EDIT, form.dictId);
407 validate(form, messages -> {}, this::asEditHtml);
408 verifyToken(this::asEditHtml);
409 createSynonymItem(form, this::asEditHtml).ifPresent(entity -> {
410 try {
411 synonymService.store(form.dictId, entity);
412 saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
413 } catch (final Exception e) {
414 logger.warn("Failed to process a request.", e);
415 throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
416 this::asEditHtml);
417 }
418 }).orElse(() -> {
419 saveToken();
420 throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.getDisplayId()), this::asEditHtml);
421 });
422 return redirectWith(getClass(), moreUrl("list/1").params("dictId", form.dictId));
423 }
424
425
426
427
428
429
430
431 @Execute
432 @Secured({ ROLE })
433 public HtmlResponse delete(final EditForm form) {
434 verifyCrudMode(form.crudMode, CrudMode.DETAILS, form.dictId);
435 validate(form, messages -> {}, this::asDetailsHtml);
436 verifyToken(this::asDetailsHtml);
437 synonymService.getSynonymItem(form.dictId, form.id).ifPresent(entity -> {
438 try {
439 synonymService.delete(form.dictId, entity);
440 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
441 } catch (final Exception e) {
442 logger.warn("Failed to process a request.", e);
443 throwValidationError(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)),
444 this::asEditHtml);
445 }
446 }).orElse(() -> {
447 throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.getDisplayId()), this::asDetailsHtml);
448 });
449 return redirectWith(getClass(), moreUrl("list/1").params("dictId", form.dictId));
450 }
451
452
453
454
455
456 private static OptionalEntity<SynonymItem> getEntity(final CreateForm form) {
457 switch (form.crudMode) {
458 case CrudMode.CREATE:
459 final SynonymItem entity = new SynonymItem(0, StringUtil.EMPTY_STRINGS, StringUtil.EMPTY_STRINGS);
460 return OptionalEntity.of(entity);
461 case CrudMode.EDIT:
462 if (form instanceof EditForm) {
463 return ComponentUtil.getComponent(SynonymService.class).getSynonymItem(form.dictId, ((EditForm) form).id);
464 }
465 break;
466 default:
467 break;
468 }
469 return OptionalEntity.empty();
470 }
471
472
473
474
475
476
477
478
479 protected OptionalEntity<SynonymItem> createSynonymItem(final CreateForm form, final VaErrorHook hook) {
480 try {
481 return createSynonymItem(this, form, hook);
482 } catch (final ValidationErrorException e) {
483 saveToken();
484 throw e;
485 }
486 }
487
488
489
490
491
492
493
494
495
496 public static OptionalEntity<SynonymItem> createSynonymItem(final FessBaseAction action, final CreateForm form,
497 final VaErrorHook hook) {
498 return getEntity(form).map(entity -> {
499 final String[] newInputs = splitLine(form.inputs);
500 validateSynonymString(action, newInputs, "inputs", hook);
501 entity.setNewInputs(newInputs);
502 final String[] newOutputs = splitLine(form.outputs);
503 validateSynonymString(action, newOutputs, "outputs", hook);
504 entity.setNewOutputs(newOutputs);
505 return entity;
506 });
507 }
508
509
510
511
512
513
514
515
516
517
518
519 protected void verifyCrudMode(final int crudMode, final int expectedMode, final String dictId) {
520 if (crudMode != expectedMode) {
521 throwValidationError(messages -> {
522 messages.addErrorsCrudInvalidMode(GLOBAL, String.valueOf(expectedMode), String.valueOf(crudMode));
523 }, () -> asListHtml(dictId));
524 }
525 }
526
527 private static void validateSynonymString(final FessBaseAction action, final String[] values, final String propertyName,
528 final VaErrorHook hook) {
529 if (values.length == 0) {
530 return;
531 }
532 for (final String value : values) {
533 if (value.indexOf(',') >= 0) {
534 action.throwValidationError(messages -> {
535 messages.addErrorsInvalidStrIsIncluded(propertyName, value, ",");
536 }, hook);
537 }
538 if (value.indexOf("=>") >= 0) {
539 action.throwValidationError(messages -> {
540 messages.addErrorsInvalidStrIsIncluded(propertyName, value, "=>");
541 }, hook);
542 }
543 }
544 }
545
546 private static String[] splitLine(final String value) {
547 if (StringUtil.isBlank(value)) {
548 return StringUtil.EMPTY_STRINGS;
549 }
550 final String[] values = value.split("[\r\n]");
551 final List<String> list = new ArrayList<>(values.length);
552 for (final String line : values) {
553 if (StringUtil.isNotBlank(line)) {
554 list.add(line.trim());
555 }
556 }
557 return list.toArray(new String[list.size()]);
558 }
559
560
561
562
563
564
565
566
567
568
569 protected HtmlResponse asDictIndexHtml() {
570 return redirect(AdminDictAction.class);
571 }
572
573 private HtmlResponse asListHtml(final String dictId) {
574 return asHtml(path_AdminDictSynonym_AdminDictSynonymJsp).renderWith(data -> {
575 RenderDataUtil.register(data, "synonymItemItems", synonymService.getSynonymList(dictId, synonymPager));
576 }).useForm(SearchForm.class, setup -> {
577 setup.setup(form -> {
578 copyBeanToBean(synonymPager, form, op -> op.include("id"));
579 });
580 });
581 }
582
583 private HtmlResponse asEditHtml() {
584 return asHtml(path_AdminDictSynonym_AdminDictSynonymEditJsp);
585 }
586
587 private HtmlResponse asDetailsHtml() {
588 return asHtml(path_AdminDictSynonym_AdminDictSynonymDetailsJsp);
589 }
590
591 }