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