1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.app.web.admin.design;
17
18 import java.io.File;
19 import java.io.FileInputStream;
20 import java.io.UnsupportedEncodingException;
21 import java.net.URLDecoder;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Locale;
25
26 import org.apache.commons.io.FileUtils;
27 import org.codelibs.core.io.FileUtil;
28 import org.codelibs.core.io.ResourceUtil;
29 import org.codelibs.core.lang.StringUtil;
30 import org.codelibs.core.misc.Pair;
31 import org.codelibs.fess.Constants;
32 import org.codelibs.fess.app.web.base.FessAdminAction;
33 import org.codelibs.fess.exception.FessSystemException;
34 import org.dbflute.optional.OptionalEntity;
35 import org.lastaflute.web.Execute;
36 import org.lastaflute.web.response.ActionResponse;
37 import org.lastaflute.web.response.HtmlResponse;
38 import org.lastaflute.web.response.StreamResponse;
39 import org.lastaflute.web.ruts.process.ActionRuntime;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43
44
45
46
47 public class AdminDesignAction extends FessAdminAction {
48
49 private static final Logger logger = LoggerFactory.getLogger(AdminDesignAction.class);
50
51
52
53
54
55
56
57
58 @Override
59 public ActionResponse hookBefore(final ActionRuntime runtime) {
60 checkEditorStatus(runtime);
61 return super.hookBefore(runtime);
62 }
63
64 private void checkEditorStatus(final ActionRuntime runtime) {
65 if (!editable()) {
66 throwValidationError(messages -> messages.addErrorsDesignEditorDisabled(GLOBAL), () -> asListHtml());
67 }
68 }
69
70 @Override
71 protected void setupHtmlData(final ActionRuntime runtime) {
72 super.setupHtmlData(runtime);
73 runtime.registerData("editable", editable());
74 runtime.registerData("fileNameItems", loadFileNameItems());
75 runtime.registerData("jspFileNameItems", loadJspFileNameItems());
76 runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameDesign()));
77 }
78
79 private boolean editable() {
80 return fessConfig.isWebDesignEditorEnabled();
81 }
82
83 private List<Pair<String, String>> loadJspFileNameItems() {
84 final List<Pair<String, String>> jspItems = new ArrayList<>();
85 for (final Pair<String, String> p : systemHelper.getDesignJspFileNames()) {
86 jspItems.add(new Pair<>(":" + p.getFirst(), "/" + p.getSecond()));
87 }
88 for (String key : fessConfig.getVirtualHostPaths()) {
89 if (StringUtil.isBlank(key)) {
90 key = "/";
91 }
92 for (final Pair<String, String> p : systemHelper.getDesignJspFileNames()) {
93 jspItems.add(new Pair<>(key + ":" + p.getFirst(), key + "/" + p.getSecond()));
94 }
95 }
96 return jspItems;
97 }
98
99 private List<String> loadFileNameItems() {
100 final File baseDir = new File(getServletContext().getRealPath("/"));
101 final List<String> fileNameItems = new ArrayList<>();
102 final List<File> fileList = getAccessibleFileList(baseDir);
103 final int length = baseDir.getAbsolutePath().length();
104 for (final File file : fileList) {
105 fileNameItems.add(file.getAbsolutePath().substring(length));
106 }
107 return fileNameItems;
108 }
109
110
111
112
113 @Execute
114 public HtmlResponse index() {
115 saveToken();
116 return asHtml(path_AdminDesign_AdminDesignJsp).useForm(DesignForm.class);
117 }
118
119 @Execute
120 public HtmlResponse back() {
121 saveToken();
122 return asHtml(path_AdminDesign_AdminDesignJsp).useForm(DesignForm.class);
123 }
124
125 @Execute
126 public HtmlResponse upload(final UploadForm form) {
127 validate(form, messages -> {}, () -> asListHtml(form));
128 verifyToken(() -> asListHtml());
129 final String uploadedFileName = form.designFile.getFileName();
130 String fileName = form.designFileName;
131 if (StringUtil.isBlank(fileName)) {
132 fileName = uploadedFileName;
133 try {
134 int pos = fileName.indexOf('/');
135 if (pos >= 0) {
136 fileName = fileName.substring(pos + 1);
137 }
138 pos = fileName.indexOf('\\');
139 if (pos >= 0) {
140 fileName = fileName.substring(pos + 1);
141 }
142 } catch (final Exception e) {
143 throwValidationError(messages -> messages.addErrorsDesignFileNameIsInvalid("designFile"), () -> asListHtml());
144 }
145 }
146 if (StringUtil.isBlank(fileName)) {
147 throwValidationError(messages -> messages.addErrorsDesignFileNameIsNotFound("designFile"), () -> asListHtml());
148 }
149
150 File uploadFile;
151
152 if (checkFileType(fileName, fessConfig.getSupportedUploadedMediaExtentionsAsArray())
153 && checkFileType(uploadedFileName, fessConfig.getSupportedUploadedMediaExtentionsAsArray())) {
154 uploadFile = new File(getServletContext().getRealPath("/images/" + fileName));
155 } else if (checkFileType(fileName, fessConfig.getSupportedUploadedCssExtentionsAsArray())
156 && checkFileType(uploadedFileName, fessConfig.getSupportedUploadedCssExtentionsAsArray())) {
157 uploadFile = new File(getServletContext().getRealPath("/css/" + fileName));
158 } else if (checkFileType(fileName, fessConfig.getSupportedUploadedJsExtentionsAsArray())
159 && checkFileType(uploadedFileName, fessConfig.getSupportedUploadedJsExtentionsAsArray())) {
160 uploadFile = new File(getServletContext().getRealPath("/js/" + fileName));
161 } else if (fessConfig.isSupportedUploadedFile(fileName) || fessConfig.isSupportedUploadedFile(uploadedFileName)) {
162 uploadFile = ResourceUtil.getResourceAsFileNoException(fileName);
163 if (uploadFile == null) {
164 throwValidationError(messages -> messages.addErrorsDesignFileNameIsNotFound("designFileName"), () -> asListHtml());
165 return null;
166 }
167 } else {
168 throwValidationError(messages -> messages.addErrorsDesignFileIsUnsupportedType("designFileName"), () -> asListHtml());
169 return null;
170 }
171
172 final File parentFile = uploadFile.getParentFile();
173 if (!parentFile.exists() && !parentFile.mkdirs()) {
174 logger.warn("Could not create " + parentFile.getAbsolutePath());
175 }
176
177 try {
178 write(uploadFile.getAbsolutePath(), form.designFile.getFileData());
179 final String currentFileName = fileName;
180 saveInfo(messages -> messages.addSuccessUploadDesignFile(GLOBAL, currentFileName));
181 } catch (final Exception e) {
182 logger.error("Failed to write an image file: {}", fileName, e);
183 throwValidationError(messages -> messages.addErrorsFailedToWriteDesignImageFile(GLOBAL), () -> asListHtml());
184 }
185 return redirect(getClass());
186 }
187
188 private boolean checkFileType(final String fileName, final String[] exts) {
189 if (fileName == null) {
190 return false;
191 }
192 final String lFileName = fileName.toLowerCase(Locale.ENGLISH);
193 for (final String ext : exts) {
194 if (lFileName.endsWith("." + ext)) {
195 return true;
196 }
197 }
198 return false;
199 }
200
201 @Execute
202 public StreamResponse download(final FileAccessForm form) {
203 final File file = getTargetFile(form.fileName).get();
204 if (file == null) {
205 throwValidationError(messages -> messages.addErrorsTargetFileDoesNotExist(GLOBAL, form.fileName), () -> asListHtml());
206 return null;
207 }
208 validate(form, messages -> {}, () -> asListHtml());
209 verifyToken(() -> asListHtml());
210 return asStream(file.getName()).contentTypeOctetStream().stream(out -> {
211 try (FileInputStream fis = new FileInputStream(file)) {
212 out.write(fis);
213 }
214 });
215 }
216
217 @Execute
218 public HtmlResponse delete(final FileAccessForm form) {
219 getTargetFile(form.fileName).ifPresent(file -> {
220 if (!file.delete()) {
221 logger.error("Failed to delete {}", file.getAbsolutePath());
222 throwValidationError(messages -> messages.addErrorsFailedToDeleteFile(GLOBAL, form.fileName), () -> asListHtml());
223 }
224 }).orElse(() -> {
225 throwValidationError(messages -> messages.addErrorsTargetFileDoesNotExist(GLOBAL, form.fileName), () -> asListHtml());
226 });
227 saveInfo(messages -> messages.addSuccessDeleteFile(GLOBAL, form.fileName));
228 validate(form, messages -> {}, () -> asListHtml());
229 verifyToken(() -> asListHtml());
230 return redirect(getClass());
231 }
232
233
234
235
236 @Execute
237 public HtmlResponse edit(final EditForm form) {
238 final String jspType = "view";
239 final File jspFile = getJspFile(form.fileName, jspType);
240 try {
241 form.content = new String(FileUtil.readBytes(jspFile), Constants.UTF_8);
242 } catch (final UnsupportedEncodingException e) {
243 throw new FessSystemException("Invalid encoding", e);
244 }
245 saveToken();
246 return asEditHtml(form);
247 }
248
249 @Execute
250 public HtmlResponse editAsUseDefault(final EditForm form) {
251 final String jspType = "orig/view";
252 final File jspFile = getJspFile(form.fileName, jspType);
253 try {
254 form.content = new String(FileUtil.readBytes(jspFile), Constants.UTF_8);
255 } catch (final UnsupportedEncodingException e) {
256 throw new FessSystemException("Invalid encoding", e);
257 }
258 saveToken();
259 return asEditHtml(form);
260 }
261
262 @Execute
263 public HtmlResponse update(final EditForm form) {
264 final String jspType = "view";
265 final File jspFile = getJspFile(form.fileName, jspType);
266
267 if (form.content == null) {
268 form.content = StringUtil.EMPTY;
269 }
270
271 validate(form, messages -> {}, () -> asEditHtml(form));
272 verifyToken(() -> asEditHtml(form));
273 try {
274 write(jspFile.getAbsolutePath(), form.content.getBytes(Constants.UTF_8));
275 saveInfo(messages -> messages.addSuccessUpdateDesignJspFile(GLOBAL, jspFile.getAbsolutePath()));
276 } catch (final Exception e) {
277 logger.error("Failed to update {}", form.fileName, e);
278 throwValidationError(messages -> messages.addErrorsFailedToUpdateJspFile(GLOBAL), () -> asListHtml());
279 }
280 return redirect(getClass());
281 }
282
283
284
285
286 private OptionalEntity<File> getTargetFile(final String fileName) {
287 final File baseDir = new File(getServletContext().getRealPath("/"));
288 final File targetFile = new File(getServletContext().getRealPath(fileName));
289 final List<File> fileList = getAccessibleFileList(baseDir);
290 for (final File file : fileList) {
291 if (targetFile.equals(file)) {
292 return OptionalEntity.of(targetFile);
293 }
294 }
295 return OptionalEntity.empty();
296 }
297
298 private List<File> getAccessibleFileList(final File baseDir) {
299 final List<File> fileList = new ArrayList<>();
300 fileList.addAll(FileUtils.listFiles(new File(baseDir, "images"), fessConfig.getSupportedUploadedMediaExtentionsAsArray(), true));
301 fileList.addAll(FileUtils.listFiles(new File(baseDir, "css"), fessConfig.getSupportedUploadedCssExtentionsAsArray(), true));
302 fileList.addAll(FileUtils.listFiles(new File(baseDir, "js"), fessConfig.getSupportedUploadedJsExtentionsAsArray(), true));
303 return fileList;
304 }
305
306 private File getJspFile(final String fileName, final String jspType) {
307 try {
308 final String[] values = URLDecoder.decode(fileName, Constants.UTF_8).split(":");
309 if (values.length != 2) {
310 throwValidationError(messages -> messages.addErrorsInvalidDesignJspFileName(GLOBAL), () -> asListHtml());
311 }
312 final String jspFileName = systemHelper.getDesignJspFileName(values[1]);
313 if (jspFileName == null) {
314 throwValidationError(messages -> messages.addErrorsInvalidDesignJspFileName(GLOBAL), () -> asListHtml());
315 }
316 String path;
317 if ("view".equals(jspType)) {
318 path = "/WEB-INF/" + jspType + values[0] + "/" + jspFileName;
319 } else {
320 path = "/WEB-INF/" + jspType + "/" + jspFileName;
321 }
322 final File jspFile = new File(getServletContext().getRealPath(path));
323 if (!jspFile.exists()) {
324 throwValidationError(messages -> messages.addErrorsDesignJspFileDoesNotExist(GLOBAL), () -> asListHtml());
325 }
326 return jspFile;
327 } catch (final UnsupportedEncodingException e) {
328 throw new FessSystemException("Failed to decode " + fileName, e);
329 }
330 }
331
332
333
334
335
336 private HtmlResponse asListHtml() {
337 return asHtml(path_AdminDesign_AdminDesignJsp).useForm(DesignForm.class);
338 }
339
340 private HtmlResponse asListHtml(final UploadForm uploadForm) {
341 return asHtml(path_AdminDesign_AdminDesignJsp).useForm(DesignForm.class, setup -> {
342 setup.setup(form -> {
343 copyBeanToBean(uploadForm, form, op -> op.include("designFile", "designFileName"));
344 });
345 });
346 }
347
348 private HtmlResponse asEditHtml(final EditForm form) {
349 return asHtml(path_AdminDesign_AdminDesignEditJsp).renderWith(data -> {
350 data.register("displayFileName", getJspFile(form.fileName, "view").getAbsolutePath());
351 });
352 }
353 }