parent
40d2e9c284
commit
f3b811320f
5 changed files with 248 additions and 34 deletions
|
@ -73,17 +73,25 @@ public class AdminStorageAction extends FessAdminAction {
|
|||
if (form.uploadFile == null) {
|
||||
throwValidationError(messages -> messages.addErrorsStorageNoUploadFile(GLOBAL), () -> asListHtml(form.path));
|
||||
}
|
||||
logger.debug("form.path = {}", form.path);
|
||||
verifyToken(() -> asListHtml(form.path));
|
||||
final String fileName = form.uploadFile.getFileName();
|
||||
final String objectName = getObjectName(form.path, form.uploadFile.getFileName());
|
||||
try (final InputStream in = form.uploadFile.getInputStream()) {
|
||||
final MinioClient minioClient = createClient(fessConfig);
|
||||
minioClient.putObject(fessConfig.getStorageBucket(), form.uploadFile.getFileName(), in, (long) form.uploadFile.getFileSize(),
|
||||
null, null, "application/octet-stream");
|
||||
minioClient.putObject(fessConfig.getStorageBucket(), objectName, in, (long) form.uploadFile.getFileSize(), null, null,
|
||||
"application/octet-stream");
|
||||
} catch (final Exception e) {
|
||||
throwValidationError(messages -> messages.addErrorsStorageFileUploadFailure(GLOBAL, fileName), () -> asListHtml(form.path));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to upload {}", objectName, e);
|
||||
}
|
||||
throwValidationError(messages -> messages.addErrorsStorageFileUploadFailure(GLOBAL, e.getLocalizedMessage()),
|
||||
() -> asListHtml(form.path));
|
||||
}
|
||||
saveInfo(messages -> messages.addSuccessUploadFileToStorage(GLOBAL, fileName));
|
||||
return redirect(getClass()); // no-op
|
||||
saveInfo(messages -> messages.addSuccessUploadFileToStorage(GLOBAL, form.uploadFile.getFileName()));
|
||||
if (StringUtil.isEmpty(form.path)) {
|
||||
return redirect(getClass());
|
||||
}
|
||||
return redirectWith(getClass(), moreUrl("list/" + encodeId(form.path)));
|
||||
}
|
||||
|
||||
@Execute
|
||||
|
@ -96,7 +104,7 @@ public class AdminStorageAction extends FessAdminAction {
|
|||
public ActionResponse download(final String id) {
|
||||
final String[] values = decodeId(id);
|
||||
if (StringUtil.isEmpty(values[1])) {
|
||||
throwValidationError(messages -> messages.addErrorsStorageFileNotFound(GLOBAL), () -> asListHtml(values[0]));
|
||||
throwValidationError(messages -> messages.addErrorsStorageFileNotFound(GLOBAL), () -> asListHtml(encodeId(values[0])));
|
||||
}
|
||||
return asStream(values[1]).contentTypeOctetStream().stream(
|
||||
out -> {
|
||||
|
@ -107,11 +115,43 @@ public class AdminStorageAction extends FessAdminAction {
|
|||
logger.debug("Failed to access {}", fessConfig.getStorageEndpoint(), e);
|
||||
}
|
||||
throwValidationError(messages -> messages.addErrorsStorageAccessError(GLOBAL, e.getLocalizedMessage()),
|
||||
() -> asListHtml(values[0]));
|
||||
() -> asListHtml(encodeId(values[0])));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Execute
|
||||
public HtmlResponse delete(final String id) {
|
||||
final String[] values = decodeId(id);
|
||||
if (StringUtil.isEmpty(values[1])) {
|
||||
throwValidationError(messages -> messages.addErrorsStorageFileNotFound(GLOBAL), () -> asListHtml(encodeId(values[0])));
|
||||
}
|
||||
logger.debug("values[0] = {}, values[1] = {}", values[0], values[1]);
|
||||
final String objectName = getObjectName(values[0], values[1]);
|
||||
try {
|
||||
final MinioClient minioClient = createClient(fessConfig);
|
||||
minioClient.removeObject(fessConfig.getStorageBucket(), objectName);
|
||||
} catch (final Exception e) {
|
||||
logger.debug("Failed to delete {}", values[1], e);
|
||||
throwValidationError(messages -> messages.addErrorsFailedToDeleteFile(GLOBAL, e.getLocalizedMessage()),
|
||||
() -> asListHtml(encodeId(values[0])));
|
||||
}
|
||||
saveInfo(messages -> messages.addSuccessDeleteFile(GLOBAL, values[1]));
|
||||
if (StringUtil.isEmpty(values[0])) {
|
||||
return redirect(getClass());
|
||||
}
|
||||
return redirectWith(getClass(), moreUrl("list/" + encodeId(values[0])));
|
||||
}
|
||||
|
||||
@Execute
|
||||
public HtmlResponse createDir(final ItemForm form) {
|
||||
validate(form, messages -> {}, () -> asListHtml(form.path));
|
||||
if (StringUtil.isBlank(form.name)) {
|
||||
throwValidationError(messages -> messages.addErrorsStorageDirectoryNameIsInvalid(GLOBAL), () -> asListHtml(form.path));
|
||||
}
|
||||
return redirectWith(getClass(), moreUrl("list/" + encodeId(getObjectName(form.path, form.name))));
|
||||
}
|
||||
|
||||
public static List<Map<String, Object>> getFileItems(final String prefix) {
|
||||
final FessConfig fessConfig = ComponentUtil.getFessConfig();
|
||||
final ArrayList<Map<String, Object>> list = new ArrayList<>();
|
||||
|
@ -124,7 +164,8 @@ public class AdminStorageAction extends FessAdminAction {
|
|||
final String objectName = item.objectName();
|
||||
map.put("id", URLEncoder.encode(objectName, Constants.UTF_8_CHARSET));
|
||||
map.put("name", getName(objectName));
|
||||
map.put("size", item.size());
|
||||
map.put("hashCode", item.hashCode());
|
||||
map.put("size", item.objectSize());
|
||||
map.put("directory", item.isDir());
|
||||
if (!item.isDir()) {
|
||||
map.put("lastModified", item.lastModified());
|
||||
|
@ -201,7 +242,7 @@ public class AdminStorageAction extends FessAdminAction {
|
|||
}
|
||||
buf.append(values[i]);
|
||||
}
|
||||
return URLEncoder.encode(buf.toString(), Constants.UTF_8_CHARSET);
|
||||
return urlEncode(buf.toString());
|
||||
}
|
||||
return StringUtil.EMPTY;
|
||||
}
|
||||
|
@ -215,13 +256,32 @@ public class AdminStorageAction extends FessAdminAction {
|
|||
}
|
||||
buf.append(s);
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
map.put("id", URLEncoder.encode(buf.toString(), Constants.UTF_8_CHARSET));
|
||||
map.put("id", urlEncode(buf.toString()));
|
||||
map.put("name", s);
|
||||
list.add(map);
|
||||
}));
|
||||
return list;
|
||||
}
|
||||
|
||||
protected static String getPathPrefix(final String path) {
|
||||
return StringUtil.isEmpty(path) ? StringUtil.EMPTY : path + "/";
|
||||
}
|
||||
|
||||
protected static String getObjectName(final String path, final String name) {
|
||||
return getPathPrefix(path) + name;
|
||||
}
|
||||
|
||||
protected static String urlEncode(final String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
return URLEncoder.encode(str, Constants.UTF_8_CHARSET);
|
||||
}
|
||||
|
||||
protected static String encodeId(final String str) {
|
||||
return urlEncode(urlEncode(str));
|
||||
}
|
||||
|
||||
private HtmlResponse asListHtml(final String prefix) {
|
||||
return asHtml(path_AdminStorage_AdminStorageJsp).useForm(ItemForm.class).renderWith(data -> {
|
||||
RenderDataUtil.register(data, "endpoint", fessConfig.getStorageEndpoint());
|
||||
|
|
|
@ -18,10 +18,9 @@ package org.codelibs.fess.app.web.admin.storage;
|
|||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.lastaflute.web.ruts.multipart.MultipartFormFile;
|
||||
import org.lastaflute.web.validation.Required;
|
||||
|
||||
public class ItemForm {
|
||||
@Required
|
||||
|
||||
public String path;
|
||||
|
||||
@Size(max = 100)
|
||||
|
|
|
@ -419,6 +419,9 @@ public class FessMessages extends FessLabels {
|
|||
/** The key of the message: Upload file is required. */
|
||||
public static final String ERRORS_storage_no_upload_file = "{errors.storage_no_upload_file}";
|
||||
|
||||
/** The key of the message: Directory name is invalid. */
|
||||
public static final String ERRORS_storage_directory_name_is_invalid = "{errors.storage_directory_name_is_invalid}";
|
||||
|
||||
/** The key of the message: Updated parameters. */
|
||||
public static final String SUCCESS_update_crawler_params = "{success.update_crawler_params}";
|
||||
|
||||
|
@ -2414,6 +2417,20 @@ public class FessMessages extends FessLabels {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the created action message for the key 'errors.storage_directory_name_is_invalid' with parameters.
|
||||
* <pre>
|
||||
* message: Directory name is invalid.
|
||||
* </pre>
|
||||
* @param property The property name for the message. (NotNull)
|
||||
* @return this. (NotNull)
|
||||
*/
|
||||
public FessMessages addErrorsStorageDirectoryNameIsInvalid(String property) {
|
||||
assertPropertyNotNull(property);
|
||||
add(property, new UserMessage(ERRORS_storage_directory_name_is_invalid));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the created action message for the key 'success.update_crawler_params' with parameters.
|
||||
* <pre>
|
||||
|
|
|
@ -165,6 +165,7 @@ errors.storage_file_not_found=The target file is not found in Storage.
|
|||
errors.storage_file_download_failure=Failed to download {0}.
|
||||
errors.storage_access_error=Storage access error: {0}
|
||||
errors.storage_no_upload_file=Upload file is required.
|
||||
errors.storage_directory_name_is_invalid=Directory name is invalid.
|
||||
|
||||
success.update_crawler_params=Updated parameters.
|
||||
success.delete_doc_from_index=Started a process to delete the document from index.
|
||||
|
|
|
@ -42,20 +42,100 @@
|
|||
<div class="data-wrapper">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
Path: ${f:h(endpoint)}/${f:h(bucket)}<c:forEach var="item" varStatus="s" items="${pathItems}">/<a href="${contextPath}/admin/storage/list/${f:u(item.id)}/">${f:h(item.name)}</a></c:forEach>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<la:form action="/admin/storage/upload/" enctype="multipart/form-data" styleClass="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="uploadFile"> <la:message key="labels.storage_upload_file" />
|
||||
</label> <input type="file" name="uploadFile" class="form-control" />
|
||||
<a class="fa fa-home" aria-hidden="true" href="${contextPath}/admin/storage/">(Bucket: ${f:h(endpoint)}/${f:h(bucket)})</a>
|
||||
<c:forEach var="item" varStatus="s" items="${pathItems}">
|
||||
<i class="fa fa-chevron-right" aria-hidden="true"></i>
|
||||
<span><a href="${contextPath}/admin/storage/list/${f:u(item.id)}/">${f:h(item.name)}</a></span>
|
||||
</c:forEach>
|
||||
<i class="fa fa-chevron-right" aria-hidden="true"></i>
|
||||
|
||||
<div type="button" class="btn btn-success btn-xs" name="createDir" data-toggle="modal"
|
||||
data-target="#createDir">
|
||||
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||
</div>
|
||||
|
||||
<div class="modal modal-primary" id="createDir"
|
||||
tabindex="-1" role="dialog"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">
|
||||
<la:message key="labels.crud_title_create" />
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body col-sm-12">
|
||||
<la:form action="/admin/storage/createDir/" enctype="multipart/form-data" styleClass="form-inline">
|
||||
<div class="form-group">
|
||||
<input type="text" name="name" class="form-control" />
|
||||
</div>
|
||||
<input type="hidden" name="path" value="${path}" />
|
||||
<button type="submit" class="btn btn-success" name="createDir">
|
||||
<em class="fa fa-make"></em>
|
||||
<la:message key="labels.crud_button_create" />
|
||||
</button>
|
||||
</la:form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline pull-left" data-dismiss="modal">
|
||||
<la:message key="labels.crud_button_cancel" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success" name="upload">
|
||||
<em class="fa fa-upload"></em>
|
||||
<la:message key="labels.storage_button_upload" />
|
||||
</button>
|
||||
</la:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div type="button" class="btn btn-success pull-right" name="upload" data-toggle="modal"
|
||||
data-target="#uploadeFile"
|
||||
value="<la:message key="labels.storage_button_upload" />"
|
||||
>
|
||||
<em class="fa fa-upload"></em>
|
||||
<la:message key="labels.storage_button_upload" />
|
||||
</div>
|
||||
<div class="modal modal-primary" id="uploadeFile"
|
||||
tabindex="-1" role="dialog"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">
|
||||
<la:message key="labels.storage_upload_file" />
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body col-sm-12">
|
||||
<la:form action="/admin/storage/upload/" enctype="multipart/form-data" styleClass="form-inline">
|
||||
<div class="form-group">
|
||||
<input type="file" name="uploadFile" class="form-control" />
|
||||
</div>
|
||||
<input type="hidden" name="path" value="${path}" />
|
||||
<button type="submit" class="btn btn-success" name="upload">
|
||||
<em class="fa fa-upload"></em>
|
||||
<la:message key="labels.storage_button_upload" />
|
||||
</button>
|
||||
</la:form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline pull-left" data-dismiss="modal">
|
||||
<la:message key="labels.crud_button_cancel" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<table class="table table-bordered table-striped dataTable">
|
||||
<tbody>
|
||||
|
@ -77,23 +157,81 @@
|
|||
<td>..</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr></c:if>
|
||||
<c:forEach var="data" varStatus="s" items="${fileItems}">
|
||||
<c:if test="${not data.directory}">
|
||||
<tr
|
||||
data-href="${contextPath}/admin/storage/download/${f:u(data.id)}/">
|
||||
<td>${f:h(data.name)}</td>
|
||||
<tr>
|
||||
<td>
|
||||
<em class="fa fa-file"></em>
|
||||
${f:h(data.name)}
|
||||
</td>
|
||||
<td>${f:h(data.size)}</td>
|
||||
<td>${f:h(data.lastModifed)}</td>
|
||||
</tr>
|
||||
</c:if><c:if test="${data.directory.booleanValue()}">
|
||||
<td>${f:h(data.lastModified)}</td>
|
||||
</c:if>
|
||||
<c:if test="${data.directory.booleanValue()}">
|
||||
<tr
|
||||
data-href="${contextPath}/admin/storage/list/${f:u(data.id)}/">
|
||||
<td>${f:h(data.name)}</td>
|
||||
<td>
|
||||
<em class="fa fa-folder-open"></em>
|
||||
${f:h(data.name)}
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</c:if>
|
||||
<td>
|
||||
<c:if test="${not data.directory}">
|
||||
<a class="btn btn-primary btn-xs" role="button" name="download" data-toggle="modal"
|
||||
href="${contextPath}/admin/storage/download/${f:u(data.id)}/" download="${f:u(data.name)}"
|
||||
value="<la:message key="labels.design_download_button" />"
|
||||
>
|
||||
<em class="fa fa-download"></em>
|
||||
<la:message key="labels.design_download_button" />
|
||||
</a>
|
||||
<button type="button" class="btn btn-danger btn-xs" name="delete" data-toggle="modal"
|
||||
data-target="#confirmToDelete-${f:h(data.hashCode)}"
|
||||
value="<la:message key="labels.design_delete_button" />"
|
||||
>
|
||||
<em class="fa fa-times"></em>
|
||||
<la:message key="labels.design_delete_button" />
|
||||
</button>
|
||||
<div class="modal modal-danger fade" id="confirmToDelete-${f:h(data.hashCode)}"
|
||||
tabindex="-1" role="dialog"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">
|
||||
<la:message key="labels.crud_title_delete" /> : ${f:h(data.name)}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
<la:message key="labels.crud_delete_confirmation" />
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline pull-left" data-dismiss="modal">
|
||||
<la:message key="labels.crud_button_cancel" />
|
||||
</button>
|
||||
<la:form action="${contextPath}/admin/storage/delete/${f:u(data.id)}/" styleClass="form-horizontal">
|
||||
<button type="submit" class="btn btn-outline btn-danger" name="delete"
|
||||
value="<la:message key="labels.crud_button_delete" />"
|
||||
>
|
||||
<em class="fa fa-trash"></em>
|
||||
<la:message key="labels.crud_button_delete" />
|
||||
</button>
|
||||
</la:form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</c:if>
|
||||
</td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -114,4 +252,3 @@
|
|||
<jsp:include page="/WEB-INF/view/common/admin/foot.jsp"></jsp:include>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue