#2319 add admin storage page

This commit is contained in:
Shinsuke Sugaya 2019-11-30 23:10:34 +09:00
parent f9dac9a544
commit 848171cedb
19 changed files with 765 additions and 1 deletions

22
pom.xml
View file

@ -50,6 +50,7 @@
<asm.version>7.2</asm.version>
<tika.version>1.22</tika.version>
<groovy.version>3.0.0-rc-1</groovy.version>
<minio.version>6.0.11</minio.version>
<!-- Crawler -->
<crawler.version>3.4.4-SNAPSHOT</crawler.version>
@ -1179,6 +1180,11 @@
<artifactId>google-http-client-jackson2</artifactId>
<version>1.33.0</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-xml</artifactId>
<version>1.33.0</version>
</dependency>
<dependency>
<groupId>org.codelibs</groupId>
<artifactId>corelib</artifactId>
@ -1249,6 +1255,17 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.code.findbugs</groupId>
<artifactId>annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- suggest library -->
<dependency>
@ -1326,6 +1343,11 @@
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>

View file

@ -394,6 +394,14 @@ public class Constants extends CoreLibConstants {
public static final String NOTIFICATION_ADVANCE_SEARCH = "notification.advance.search";
public static final String STORAGE_ENDPOINT = "storage.endpoint";
public static final String STORAGE_ACCESS_KEY = "storage.accesskey";
public static final String STORAGE_SECRET_KEY = "storage.secretkey";
public static final String STORAGE_BUCKET = "storage.bucket";
public static final String MAPPING_TYPE_ARRAY = "array";
public static final String MAPPING_TYPE_STRING = "string";

View file

@ -166,6 +166,14 @@ public class AdminGeneralAction extends FessAdminAction {
fessConfig.setLdapMemberofAttribute(form.ldapMemberofAttribute);
fessConfig.setNotificationLogin(form.notificationLogin);
fessConfig.setNotificationSearchTop(form.notificationSearchTop);
fessConfig.setStorageEndpoint(form.storageEndpoint);
if (form.storageAccessKey != null && StringUtil.isNotBlank(form.storageAccessKey.replace("*", " "))) {
fessConfig.setStorageAccessKey(form.storageAccessKey);
}
if (form.storageSecretKey != null && StringUtil.isNotBlank(form.storageSecretKey.replace("*", " "))) {
fessConfig.setStorageSecretKey(form.storageSecretKey);
}
fessConfig.setStorageBucket(form.storageBucket);
fessConfig.storeSystemProperties();
ComponentUtil.getLdapManager().updateConfig();
@ -218,6 +226,10 @@ public class AdminGeneralAction extends FessAdminAction {
form.ldapMemberofAttribute = fessConfig.getLdapMemberofAttribute();
form.notificationLogin = fessConfig.getNotificationLogin();
form.notificationSearchTop = fessConfig.getNotificationSearchTop();
form.storageEndpoint = fessConfig.getStorageEndpoint();
form.storageAccessKey = StringUtil.isNotBlank(fessConfig.getStorageAccessKey()) ? DUMMY_PASSWORD : StringUtil.EMPTY;
form.storageSecretKey = StringUtil.isNotBlank(fessConfig.getStorageSecretKey()) ? DUMMY_PASSWORD : StringUtil.EMPTY;
form.storageBucket = fessConfig.getStorageBucket();
form.logLevel = ComponentUtil.getSystemHelper().getLogLevel().toUpperCase();
}

View file

@ -162,4 +162,16 @@ public class EditForm {
@Size(max = 10)
public String logLevel;
@Size(max = 1000)
public String storageEndpoint;
@Size(max = 1000)
public String storageAccessKey;
@Size(max = 1000)
public String storageSecretKey;
@Size(max = 1000)
public String storageBucket;
}

View file

@ -0,0 +1,236 @@
/*
* Copyright 2012-2019 CodeLibs Project and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.codelibs.fess.app.web.admin.storage;
import static org.codelibs.core.stream.StreamUtil.split;
import java.io.InputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codelibs.core.lang.StringUtil;
import org.codelibs.fess.app.web.base.FessAdminAction;
import org.codelibs.fess.crawler.Constants;
import org.codelibs.fess.exception.StorageException;
import org.codelibs.fess.mylasta.direction.FessConfig;
import org.codelibs.fess.util.ComponentUtil;
import org.codelibs.fess.util.RenderDataUtil;
import org.lastaflute.web.Execute;
import org.lastaflute.web.response.ActionResponse;
import org.lastaflute.web.response.HtmlResponse;
import org.lastaflute.web.ruts.process.ActionRuntime;
import io.minio.MinioClient;
import io.minio.Result;
import io.minio.messages.Item;
/**
* @author shinsuke
*/
public class AdminStorageAction extends FessAdminAction {
private static final Logger logger = LogManager.getLogger(AdminStorageAction.class);
@Override
protected void setupHtmlData(final ActionRuntime runtime) {
super.setupHtmlData(runtime);
runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameStorage()));
}
@Execute
public HtmlResponse index() {
saveToken();
return asListHtml(StringUtil.EMPTY);
}
// TODO
// @Execute
// public HtmlResponse create() {
// }
@Execute
public HtmlResponse upload(final ItemForm form) {
validate(form, messages -> {}, () -> asListHtml(form.path));
if (form.uploadFile == null) {
throwValidationError(messages -> messages.addErrorsStorageNoUploadFile(GLOBAL), () -> asListHtml(form.path));
}
verifyToken(() -> asListHtml(form.path));
final String fileName = 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");
} catch (final Exception e) {
throwValidationError(messages -> messages.addErrorsStorageFileUploadFailure(GLOBAL, fileName), () -> asListHtml(form.path));
}
saveInfo(messages -> messages.addSuccessUploadFileToStorage(GLOBAL, fileName));
return redirect(getClass()); // no-op
}
@Execute
public ActionResponse list(final String id) {
saveToken();
return asListHtml(decodePath(id));
}
@Execute
public ActionResponse download(final String id) {
final String[] values = decodeId(id);
if (StringUtil.isEmpty(values[1])) {
throwValidationError(messages -> messages.addErrorsStorageFileNotFound(GLOBAL), () -> asListHtml(values[0]));
}
return asStream(values[1]).contentTypeOctetStream().stream(
out -> {
try (InputStream in = createClient(fessConfig).getObject(fessConfig.getStorageBucket(), values[0] + values[1])) {
out.write(in);
} catch (final Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to access {}", fessConfig.getStorageEndpoint(), e);
}
throwValidationError(messages -> messages.addErrorsStorageAccessError(GLOBAL, e.getLocalizedMessage()),
() -> asListHtml(values[0]));
}
});
}
public static List<Map<String, Object>> getFileItems(final String prefix) {
final FessConfig fessConfig = ComponentUtil.getFessConfig();
final ArrayList<Map<String, Object>> list = new ArrayList<>();
try {
final MinioClient minioClient = createClient(fessConfig);
for (final Result<Item> result : minioClient.listObjects(fessConfig.getStorageBucket(),
prefix != null && prefix.length() > 0 ? prefix + "/" : prefix, false)) {
final Map<String, Object> map = new HashMap<>();
final Item item = result.get();
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("directory", item.isDir());
if (!item.isDir()) {
map.put("lastModified", item.lastModified());
}
list.add(map);
if (list.size() > fessConfig.getStorageMaxItemsInPageAsInteger()) {
break;
}
}
} catch (final Exception e) {
throw new StorageException("Failed to access " + fessConfig.getStorageEndpoint(), e);
}
return list;
}
private static String getName(final String objectName) {
final String[] values = objectName.split("/");
if (values.length == 0) {
return StringUtil.EMPTY;
}
return values[values.length - 1];
}
protected static MinioClient createClient(final FessConfig fessConfig) {
try {
return new MinioClient(fessConfig.getStorageEndpoint(), fessConfig.getStorageAccessKey(), fessConfig.getStorageSecretKey());
} catch (final Exception e) {
throw new StorageException("Failed to create MinioClient: " + fessConfig.getStorageEndpoint(), e);
}
}
protected static String decodePath(final String id) {
final String[] values = decodeId(id);
if (StringUtil.isEmpty(values[0]) && StringUtil.isEmpty(values[1])) {
return StringUtil.EMPTY;
} else if (StringUtil.isEmpty(values[0])) {
return values[1];
} else {
return values[0] + "/" + values[1];
}
}
protected static String[] decodeId(final String id) {
final String value = URLDecoder.decode(id, Constants.UTF_8_CHARSET);
final String[] values = split(value, "/").get(stream -> stream.filter(StringUtil::isNotEmpty).toArray(n -> new String[n]));
if (values.length == 0) {
// invalid?
return new String[] { StringUtil.EMPTY, StringUtil.EMPTY };
} else if (values.length == 1) {
return new String[] { StringUtil.EMPTY, values[0] };
} else {
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < values.length - 1; i++) {
if (buf.length() > 0) {
buf.append('/');
}
buf.append(values[i]);
}
return new String[] { buf.toString(), values[values.length - 1] };
}
}
protected static String createParentId(final String prefix) {
if (prefix == null) {
return StringUtil.EMPTY;
}
final String[] values = prefix.split("/");
if (values.length > 1) {
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < values.length - 1; i++) {
if (buf.length() > 0) {
buf.append('/');
}
buf.append(values[i]);
}
return URLEncoder.encode(buf.toString(), Constants.UTF_8_CHARSET);
}
return StringUtil.EMPTY;
}
protected static List<Map<String, String>> createPathItems(final String prefix) {
final List<Map<String, String>> list = new ArrayList<>();
final StringBuilder buf = new StringBuilder();
split(prefix, "/").of(stream -> stream.filter(StringUtil::isNotEmpty).forEach(s -> {
if (buf.length() > 0) {
buf.append('/');
}
buf.append(s);
final Map<String, String> map = new HashMap<>();
map.put("id", URLEncoder.encode(buf.toString(), Constants.UTF_8_CHARSET));
map.put("name", s);
list.add(map);
}));
return list;
}
private HtmlResponse asListHtml(final String prefix) {
return asHtml(path_AdminStorage_AdminStorageJsp).useForm(ItemForm.class).renderWith(data -> {
RenderDataUtil.register(data, "endpoint", fessConfig.getStorageEndpoint());
RenderDataUtil.register(data, "bucket", fessConfig.getStorageBucket());
RenderDataUtil.register(data, "path", prefix);
RenderDataUtil.register(data, "pathItems", createPathItems(prefix));
RenderDataUtil.register(data, "parentId", createParentId(prefix));
RenderDataUtil.register(data, "fileItems", getFileItems(prefix));
});
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2012-2019 CodeLibs Project and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
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)
public String name;
public MultipartFormFile uploadFile;
}

View file

@ -0,0 +1,28 @@
/*
* Copyright 2012-2019 CodeLibs Project and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.codelibs.fess.exception;
public class StorageException extends FessSystemException {
private static final long serialVersionUID = 1L;
public StorageException(final String message, final Throwable cause) {
super(message, cause);
}
public StorageException(final String message) {
super(message);
}
}

View file

@ -396,8 +396,11 @@ public class SystemHelper {
public void setupAdminHtmlData(final TypicalAction action, final ActionRuntime runtime) {
runtime.registerData("developmentMode", ComponentUtil.getFessEsClient().isEmbedded());
final String url = ComponentUtil.getFessConfig().getOnlineHelpInstallation();
final FessConfig fessConfig = ComponentUtil.getFessConfig();
final String url = fessConfig.getOnlineHelpInstallation();
runtime.registerData("installationLink", getHelpUrl(url));
runtime.registerData("storageEnabled",
StringUtil.isNotBlank(fessConfig.getStorageEndpoint()) && StringUtil.isNotBlank(fessConfig.getStorageBucket()));
}
public String getSearchRoleByUser(final String name) {

View file

@ -352,6 +352,9 @@ public interface FessHtmlPath {
/** The path of the HTML: /admin/searchlog/admin_searchlog_details.jsp */
HtmlNext path_AdminSearchlog_AdminSearchlogDetailsJsp = new HtmlNext("/admin/searchlog/admin_searchlog_details.jsp");
/** The path of the HTML: /admin/storage/admin_storage.jsp */
HtmlNext path_AdminStorage_AdminStorageJsp = new HtmlNext("/admin/storage/admin_storage.jsp");
/** The path of the HTML: /admin/suggest/admin_suggest.jsp */
HtmlNext path_AdminSuggest_AdminSuggestJsp = new HtmlNext("/admin/suggest/admin_suggest.jsp");

View file

@ -605,6 +605,9 @@ public class FessLabels extends UserMessages {
/** The key of the message: Plugin */
public static final String LABELS_menu_plugin = "{labels.menu_plugin}";
/** The key of the message: Storage */
public static final String LABELS_menu_storage = "{labels.menu_storage}";
/** The key of the message: Search... */
public static final String LABELS_SIDEBAR_placeholder_search = "{labels.sidebar.placeholder_search}";
@ -2544,6 +2547,9 @@ public class FessLabels extends UserMessages {
/** The key of the message: Notification */
public static final String LABELS_general_menu_notification = "{labels.general_menu_notification}";
/** The key of the message: Storage */
public static final String LABELS_general_storage = "{labels.general_storage}";
/** The key of the message: LDAP URL */
public static final String LABELS_ldap_provider_url = "{labels.ldap_provider_url}";
@ -2574,6 +2580,18 @@ public class FessLabels extends UserMessages {
/** The key of the message: Search top page */
public static final String LABELS_notification_search_top = "{labels.notification_search_top}";
/** The key of the message: Endpoint */
public static final String LABELS_storage_endpoint = "{labels.storage_endpoint}";
/** The key of the message: Access Key */
public static final String LABELS_storage_access_key = "{labels.storage_access_key}";
/** The key of the message: Secret Key */
public static final String LABELS_storage_secret_key = "{labels.storage_secret_key}";
/** The key of the message: Bucket */
public static final String LABELS_storage_bucket = "{labels.storage_bucket}";
/** The key of the message: Send TestMail */
public static final String LABELS_send_testmail = "{labels.send_testmail}";
@ -3090,6 +3108,24 @@ public class FessLabels extends UserMessages {
/** The key of the message: Install */
public static final String LABELS_crud_button_install = "{labels.crud_button_install}";
/** The key of the message: Storage */
public static final String LABELS_storage_configuration = "{labels.storage_configuration}";
/** The key of the message: Name */
public static final String LABELS_storage_name = "{labels.storage_name}";
/** The key of the message: Size */
public static final String LABELS_storage_size = "{labels.storage_size}";
/** The key of the message: Last Modified */
public static final String LABELS_storage_last_modified = "{labels.storage_last_modified}";
/** The key of the message: Upload File */
public static final String LABELS_storage_upload_file = "{labels.storage_upload_file}";
/** The key of the message: Upload */
public static final String LABELS_storage_button_upload = "{labels.storage_button_upload}";
/**
* Assert the property is not null.
* @param property The value of the property. (NotNull)

View file

@ -404,6 +404,21 @@ public class FessMessages extends FessLabels {
/** The key of the message: {0} should be date. */
public static final String ERRORS_property_type_date = "{errors.property_type_date}";
/** The key of the message: Failed to upload {0}. */
public static final String ERRORS_storage_file_upload_failure = "{errors.storage_file_upload_failure}";
/** The key of the message: The target file is not found in Storage. */
public static final String ERRORS_storage_file_not_found = "{errors.storage_file_not_found}";
/** The key of the message: Failed to download {0}. */
public static final String ERRORS_storage_file_download_failure = "{errors.storage_file_download_failure}";
/** The key of the message: Storage access error: {0} */
public static final String ERRORS_storage_access_error = "{errors.storage_access_error}";
/** 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: Updated parameters. */
public static final String SUCCESS_update_crawler_params = "{success.update_crawler_params}";
@ -488,6 +503,9 @@ public class FessMessages extends FessLabels {
/** The key of the message: Deleting {0} plugin. */
public static final String SUCCESS_delete_plugin = "{success.delete_plugin}";
/** The key of the message: Uploaded {0} */
public static final String SUCCESS_upload_file_to_storage = "{success.upload_file_to_storage}";
/** The key of the message: Created data. */
public static final String SUCCESS_crud_create_crud_table = "{success.crud_create_crud_table}";
@ -2323,6 +2341,79 @@ public class FessMessages extends FessLabels {
return this;
}
/**
* Add the created action message for the key 'errors.storage_file_upload_failure' with parameters.
* <pre>
* message: Failed to upload {0}.
* </pre>
* @param property The property name for the message. (NotNull)
* @param arg0 The parameter arg0 for message. (NotNull)
* @return this. (NotNull)
*/
public FessMessages addErrorsStorageFileUploadFailure(String property, String arg0) {
assertPropertyNotNull(property);
add(property, new UserMessage(ERRORS_storage_file_upload_failure, arg0));
return this;
}
/**
* Add the created action message for the key 'errors.storage_file_not_found' with parameters.
* <pre>
* message: The target file is not found in Storage.
* </pre>
* @param property The property name for the message. (NotNull)
* @return this. (NotNull)
*/
public FessMessages addErrorsStorageFileNotFound(String property) {
assertPropertyNotNull(property);
add(property, new UserMessage(ERRORS_storage_file_not_found));
return this;
}
/**
* Add the created action message for the key 'errors.storage_file_download_failure' with parameters.
* <pre>
* message: Failed to download {0}.
* </pre>
* @param property The property name for the message. (NotNull)
* @param arg0 The parameter arg0 for message. (NotNull)
* @return this. (NotNull)
*/
public FessMessages addErrorsStorageFileDownloadFailure(String property, String arg0) {
assertPropertyNotNull(property);
add(property, new UserMessage(ERRORS_storage_file_download_failure, arg0));
return this;
}
/**
* Add the created action message for the key 'errors.storage_access_error' with parameters.
* <pre>
* message: Storage access error: {0}
* </pre>
* @param property The property name for the message. (NotNull)
* @param arg0 The parameter arg0 for message. (NotNull)
* @return this. (NotNull)
*/
public FessMessages addErrorsStorageAccessError(String property, String arg0) {
assertPropertyNotNull(property);
add(property, new UserMessage(ERRORS_storage_access_error, arg0));
return this;
}
/**
* Add the created action message for the key 'errors.storage_no_upload_file' with parameters.
* <pre>
* message: Upload file is required.
* </pre>
* @param property The property name for the message. (NotNull)
* @return this. (NotNull)
*/
public FessMessages addErrorsStorageNoUploadFile(String property) {
assertPropertyNotNull(property);
add(property, new UserMessage(ERRORS_storage_no_upload_file));
return this;
}
/**
* Add the created action message for the key 'success.update_crawler_params' with parameters.
* <pre>
@ -2723,6 +2814,21 @@ public class FessMessages extends FessLabels {
return this;
}
/**
* Add the created action message for the key 'success.upload_file_to_storage' with parameters.
* <pre>
* message: Uploaded {0}
* </pre>
* @param property The property name for the message. (NotNull)
* @param arg0 The parameter arg0 for message. (NotNull)
* @return this. (NotNull)
*/
public FessMessages addSuccessUploadFileToStorage(String property, String arg0) {
assertPropertyNotNull(property);
add(property, new UserMessage(SUCCESS_upload_file_to_storage, arg0));
return this;
}
/**
* Add the created action message for the key 'success.crud_create_crud_table' with parameters.
* <pre>

View file

@ -1322,6 +1322,9 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
/** The key of the configuration. e.g. plugin */
String ONLINE_HELP_NAME_PLUGIN = "online.help.name.plugin";
/** The key of the configuration. e.g. storage */
String ONLINE_HELP_NAME_STORAGE = "online.help.name.storage";
/** The key of the configuration. e.g. ja */
String ONLINE_HELP_SUPPORTED_LANGS = "online.help.supported.langs";
@ -1553,6 +1556,9 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
/** The key of the configuration. e.g. */
String PLUGIN_VERSION_FILTER = "plugin.version.filter";
/** The key of the configuration. e.g. 1000 */
String STORAGE_MAX_ITEMS_IN_PAGE = "storage.max.items.in.page";
/**
* Get the value of property as {@link String}.
* @param propertyKey The key of the property. (NotNull)
@ -5625,6 +5631,13 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
*/
String getOnlineHelpNamePlugin();
/**
* Get the value for the key 'online.help.name.storage'. <br>
* The value is, e.g. storage <br>
* @return The value of found property. (NotNull: if not found, exception but basically no way)
*/
String getOnlineHelpNameStorage();
/**
* Get the value for the key 'online.help.supported.langs'. <br>
* The value is, e.g. ja <br>
@ -6353,6 +6366,23 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
*/
Integer getPluginVersionFilterAsInteger();
/**
* Get the value for the key 'storage.max.items.in.page'. <br>
* The value is, e.g. 1000 <br>
* comment: ------
* @return The value of found property. (NotNull: if not found, exception but basically no way)
*/
String getStorageMaxItemsInPage();
/**
* Get the value for the key 'storage.max.items.in.page' as {@link Integer}. <br>
* The value is, e.g. 1000 <br>
* comment: ------
* @return The value of found property. (NotNull: if not found, exception but basically no way)
* @throws NumberFormatException When the property is not integer.
*/
Integer getStorageMaxItemsInPageAsInteger();
/**
* The simple implementation for configuration.
* @author FreeGen
@ -8450,6 +8480,10 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
return get(FessConfig.ONLINE_HELP_NAME_PLUGIN);
}
public String getOnlineHelpNameStorage() {
return get(FessConfig.ONLINE_HELP_NAME_STORAGE);
}
public String getOnlineHelpSupportedLangs() {
return get(FessConfig.ONLINE_HELP_SUPPORTED_LANGS);
}
@ -8854,6 +8888,14 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
return getAsInteger(FessConfig.PLUGIN_VERSION_FILTER);
}
public String getStorageMaxItemsInPage() {
return get(FessConfig.STORAGE_MAX_ITEMS_IN_PAGE);
}
public Integer getStorageMaxItemsInPageAsInteger() {
return getAsInteger(FessConfig.STORAGE_MAX_ITEMS_IN_PAGE);
}
@Override
protected java.util.Map<String, String> prepareGeneratedDefaultMap() {
java.util.Map<String, String> defaultMap = super.prepareGeneratedDefaultMap();
@ -9240,6 +9282,7 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
defaultMap.put(FessConfig.ONLINE_HELP_NAME_SEARCHLOG, "searchlog");
defaultMap.put(FessConfig.ONLINE_HELP_NAME_MAINTENANCE, "maintenance");
defaultMap.put(FessConfig.ONLINE_HELP_NAME_PLUGIN, "plugin");
defaultMap.put(FessConfig.ONLINE_HELP_NAME_STORAGE, "storage");
defaultMap.put(FessConfig.ONLINE_HELP_SUPPORTED_LANGS, "ja");
defaultMap.put(FessConfig.SUGGEST_POPULAR_WORD_SEED, "0");
defaultMap.put(FessConfig.SUGGEST_POPULAR_WORD_TAGS, "");
@ -9318,6 +9361,7 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
defaultMap.put(FessConfig.PLUGIN_REPOSITORIES,
"https://repo.maven.apache.org/maven2/org/codelibs/fess/,https://fess.codelibs.org/plugin/artifacts.yaml");
defaultMap.put(FessConfig.PLUGIN_VERSION_FILTER, "");
defaultMap.put(FessConfig.STORAGE_MAX_ITEMS_IN_PAGE, "1000");
defaultMap.put(FessConfig.lasta_di_SMART_DEPLOY_MODE, "hot");
defaultMap.put(FessConfig.DEVELOPMENT_HERE, "true");
defaultMap.put(FessConfig.ENVIRONMENT_TITLE, "Local Development");

View file

@ -574,6 +574,38 @@ public interface FessProp {
setSystemProperty(Constants.LDAP_MEMBEROF_ATTRIBUTE, value);
}
default void setStorageEndpoint(final String value) {
setSystemProperty(Constants.STORAGE_ENDPOINT, value);
}
default String getStorageEndpoint() {
return getSystemProperty(Constants.STORAGE_ENDPOINT, StringUtil.EMPTY);
}
default void setStorageAccessKey(final String value) {
setSystemProperty(Constants.STORAGE_ACCESS_KEY, value);
}
default String getStorageAccessKey() {
return getSystemProperty(Constants.STORAGE_ACCESS_KEY, StringUtil.EMPTY);
}
default void setStorageSecretKey(final String value) {
setSystemProperty(Constants.STORAGE_SECRET_KEY, value);
}
default String getStorageSecretKey() {
return getSystemProperty(Constants.STORAGE_SECRET_KEY, StringUtil.EMPTY);
}
default void setStorageBucket(final String value) {
setSystemProperty(Constants.STORAGE_BUCKET, value);
}
default String getStorageBucket() {
return getSystemProperty(Constants.STORAGE_BUCKET, StringUtil.EMPTY);
}
Integer getLdapMaxUsernameLengthAsInteger();
default String getLdapSecurityPrincipal(final String username) {

View file

@ -713,6 +713,7 @@ online.help.name.suggest=suggest
online.help.name.searchlog=searchlog
online.help.name.maintenance=maintenance
online.help.name.plugin=plugin
online.help.name.storage=storage
online.help.supported.langs=ja
@ -809,3 +810,9 @@ ldap.attr.homeDirectory=homeDirectory
#plugin.repositories=https://repo.maven.apache.org/maven2/org/codelibs/fess/,https://oss.sonatype.org/content/repositories/snapshots/org/codelibs/fess/,https://fess.codelibs.org/plugin/artifacts.yaml
plugin.repositories=https://repo.maven.apache.org/maven2/org/codelibs/fess/,https://fess.codelibs.org/plugin/artifacts.yaml
plugin.version.filter=
# ----------------------------------------------------------
# Storage
# ------
storage.max.items.in.page=1000

View file

@ -193,6 +193,7 @@ labels.menu_maintenance=Maintenance
labels.menu_related_content=Related Content
labels.menu_related_query=Related Query
labels.menu_plugin=Plugin
labels.menu_storage=Storage
labels.sidebar.placeholder_search=Search...
labels.sidebar.menu=MENU
labels.footer.copyright=&copy;2019 <a href="https://github.com/codelibs">CodeLibs Project</a>.
@ -839,6 +840,7 @@ labels.general_menu_logging=Logging
labels.general_menu_suggest=Suggest
labels.general_menu_ldap=LDAP
labels.general_menu_notification=Notification
labels.general_storage=Storage
labels.ldap_provider_url=LDAP URL
labels.ldap_security_principal=User DN
labels.ldap_admin_security_principal=Bind DN
@ -849,6 +851,10 @@ labels.ldap_group_filter=Group Filter
labels.ldap_memberof_attribute=memberOf Attribute
labels.notification_login=Login page
labels.notification_search_top=Search top page
labels.storage_endpoint=Endpoint
labels.storage_access_key=Access Key
labels.storage_secret_key=Secret Key
labels.storage_bucket=Bucket
labels.send_testmail=Send TestMail
labels.backup_configuration=Back Up
labels.backup_name=Name
@ -1021,3 +1027,9 @@ labels.plugin_jar_file=Jar File
labels.plugin_local_install=Local
labels.plugin_remote_install=Remote
labels.crud_button_install=Install
labels.storage_configuration=Storage
labels.storage_name=Name
labels.storage_size=Size
labels.storage_last_modified=Last Modified
labels.storage_upload_file=Upload File
labels.storage_button_upload=Upload

View file

@ -160,6 +160,12 @@ errors.property_type_float={0} should be numeric.
errors.property_type_double={0} should be numeric.
errors.property_type_date={0} should be date.
errors.storage_file_upload_failure=Failed to upload {0}.
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.
success.update_crawler_params=Updated parameters.
success.delete_doc_from_index=Started a process to delete the document from index.
success.crawling_info_delete_all=Deleted session data.
@ -188,6 +194,7 @@ success.bulk_process_started=Bulk process is started.
success.print_thread_dump=Printed thread dump to log file.
success.install_plugin=Installing {0} plugin.
success.delete_plugin=Deleting {0} plugin.
success.upload_file_to_storage=Uploaded {0}
success.crud_create_crud_table=Created data.
success.crud_update_crud_table=Updated data.

View file

@ -471,6 +471,48 @@
styleClass="form-control" />
</div>
</div>
<%-- Storage --%>
<h4><la:message key="labels.general_storage" /></h4>
<div class="form-group">
<label for="storageEndpoint"
class="col-sm-3 control-label"><la:message
key="labels.storage_endpoint" /></label>
<div class="col-sm-9">
<la:errors property="storageEndpoint" />
<la:text styleId="storageEndpoint" property="storageEndpoint"
styleClass="form-control" />
</div>
</div>
<div class="form-group">
<label for="storageAccessKey"
class="col-sm-3 control-label"><la:message
key="labels.storage_access_key" /></label>
<div class="col-sm-9">
<la:errors property="storageAccessKey" />
<la:password styleId="storageAccessKey" property="storageAccessKey"
styleClass="form-control" />
</div>
</div>
<div class="form-group">
<label for="storageSecretKey"
class="col-sm-3 control-label"><la:message
key="labels.storage_secret_key" /></label>
<div class="col-sm-9">
<la:errors property="storageSecretKey" />
<la:password styleId="storageSecretKey" property="storageSecretKey"
styleClass="form-control" />
</div>
</div>
<div class="form-group">
<label for="storageBucket"
class="col-sm-3 control-label"><la:message
key="labels.storage_bucket" /></label>
<div class="col-sm-9">
<la:errors property="storageBucket" />
<la:text styleId="storageBucket" property="storageBucket"
styleClass="form-control" />
</div>
</div>
</div>
<!-- /.box-body -->
<div class="box-footer">

View file

@ -0,0 +1,117 @@
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><la:message key="labels.admin_brand_title" /> | <la:message
key="labels.storage_configuration" /></title>
<jsp:include page="/WEB-INF/view/common/admin/head.jsp"></jsp:include>
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
<jsp:include page="/WEB-INF/view/common/admin/header.jsp"></jsp:include>
<jsp:include page="/WEB-INF/view/common/admin/sidebar.jsp">
<jsp:param name="menuCategoryType" value="system" />
<jsp:param name="menuType" value="storage" />
</jsp:include>
<div class="content-wrapper">
<section class="content-header">
<h1>
<la:message key="labels.storage_configuration" />
</h1>
<jsp:include page="/WEB-INF/view/common/admin/crud/breadcrumb.jsp"></jsp:include>
</section>
<section class="content">
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">
<la:message key="labels.storage_configuration" />
</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
<%-- Message --%>
<div>
<la:info id="msg" message="true">
<div class="alert alert-info">${msg}</div>
</la:info>
<la:errors />
</div>
<%-- List --%>
<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" />
</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 class="col-sm-12">
<table class="table table-bordered table-striped dataTable">
<tbody>
<tr>
<th><la:message key="labels.storage_name" /></th>
<th><la:message key="labels.storage_size" /></th>
<th><la:message key="labels.storage_last_modified" /></th>
</tr>
<c:if test="${not empty path and not empty parentId}">
<tr
data-href="${contextPath}/admin/storage/list/${f:u(data.parentId)}/">
<td>..</td>
<td></td>
<td></td>
</tr></c:if>
<c:if test="${not empty path and empty parentId}">
<tr
data-href="${contextPath}/admin/storage/">
<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>
<td>${f:h(data.size)}</td>
<td>${f:h(data.lastModifed)}</td>
</tr>
</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></td>
<td></td>
</tr>
</c:if>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
<!-- /.data-wrapper -->
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
</div>
</section>
</div>
<jsp:include page="/WEB-INF/view/common/admin/footer.jsp"></jsp:include>
</div>
<jsp:include page="/WEB-INF/view/common/admin/foot.jsp"></jsp:include>
</body>
</html>

View file

@ -82,6 +82,12 @@
<span><la:message key="labels.menu_plugin" /></span>
</la:link></li>
<c:if test="${storageEnabled.booleanValue()}"><li <c:if test="${param.menuType=='storage'}">class="active"</c:if>><la:link
href="/admin/storage/">
<em class='fa fa-genderless'></em>
<span><la:message key="labels.menu_storage" /></span>
</la:link></li></c:if>
</ul></li>
<li
class="treeview <c:if test="${param.menuCategoryType=='crawl'}">active</c:if>"><a