Add task button to run cancellable long task from UI

This commit is contained in:
robin 2019-06-18 19:40:21 +08:00 committed by Robin Shen
parent b7a2ce17f3
commit 8a0c9d0060
18 changed files with 414 additions and 347 deletions

View File

@ -244,6 +244,7 @@ import io.onedev.server.web.avatar.DefaultAvatarManager;
import io.onedev.server.web.component.diff.DiffRenderer;
import io.onedev.server.web.component.markdown.SourcePositionTrackExtension;
import io.onedev.server.web.component.markdown.emoji.EmojiExtension;
import io.onedev.server.web.component.taskbutton.TaskButton;
import io.onedev.server.web.editable.DefaultEditSupportRegistry;
import io.onedev.server.web.editable.EditSupport;
import io.onedev.server.web.editable.EditSupportLocator;
@ -483,6 +484,8 @@ public class CoreModule extends AbstractPluginModule {
bind(IssueEventBroadcaster.class);
bind(BuildEventBroadcaster.class);
bind(TaskButton.TaskFutureManager.class);
contribute(MainNavContribution.class, new MainNavContribution() {
@Override

View File

@ -1,51 +0,0 @@
package io.onedev.server.web.behavior.testform;
import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.unbescape.html.HtmlEscape;
import org.unbescape.javascript.JavaScriptEscape;
import io.onedev.server.web.behavior.AbstractPostAjaxBehavior;
@SuppressWarnings("serial")
public abstract class TestFormBehavior extends AbstractPostAjaxBehavior {
@Override
protected void respond(AjaxRequestTarget target) {
String feedbackHtml;
TestResult result = test();
if (result.isSuccessful()) {
feedbackHtml = String.format(
"<div class='test-feedback alert alert-success'>%s</div>",
HtmlEscape.escapeHtml5(result.getMessage()));
} else {
feedbackHtml = String.format(
"<div class='test-feedback alert alert-danger'>%s</div>",
HtmlEscape.escapeHtml5(result.getMessage()));
}
feedbackHtml = StringUtils.replace(feedbackHtml, "\n", "<br>");
target.appendJavaScript(String.format("var $button = $('#%s');"
+ "$button.removeAttr('disabled');"
+ "$button.val($button[0].prevValue);"
+ "$button.html($button[0].prevHtml);"
+ "$button.closest('form').append('%s');"
+ "$button.removeClass('ajax-indicator');",
getComponent().getMarkupId(), JavaScriptEscape.escapeJavaScript(feedbackHtml)));
}
public void requestTest(AjaxRequestTarget target) {
target.appendJavaScript(String.format("var $button = $('#%s');"
+ "$button.attr('disabled', 'disabled');"
+ "$button[0].prevValue = $button.val();"
+ "$button[0].prevHtml = $button.html();"
+ "$button.val($button.val() + ' in progress...');"
+ "$button.html($button.html() + ' in progress...');"
+ "$button.addClass('ajax-indicator');"
+ "$button.closest('form').children('.test-feedback').remove();",
getComponent().getMarkupId()));
target.appendJavaScript(getCallbackScript());
}
protected abstract TestResult test();
}

View File

@ -1,48 +0,0 @@
package io.onedev.server.web.behavior.testform;
public interface TestResult {
String getMessage();
boolean isSuccessful();
public static class Successful implements TestResult {
private final String message;
public Successful(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
@Override
public boolean isSuccessful() {
return true;
}
}
public static class Failed implements TestResult {
private final String message;
public Failed(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
@Override
public boolean isSuccessful() {
return false;
}
}
}

View File

@ -0,0 +1,236 @@
package io.onedev.server.web.component.taskbutton;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.html.form.Form;
import org.joda.time.DateTime;
import org.quartz.ScheduleBuilder;
import org.quartz.SimpleScheduleBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unbescape.html.HtmlEscape;
import org.unbescape.javascript.JavaScriptEscape;
import io.onedev.commons.launcher.loader.Listen;
import io.onedev.commons.utils.ExceptionUtils;
import io.onedev.commons.utils.WordUtils;
import io.onedev.commons.utils.schedule.SchedulableTask;
import io.onedev.commons.utils.schedule.TaskScheduler;
import io.onedev.server.OneDev;
import io.onedev.server.event.system.SystemStarted;
import io.onedev.server.event.system.SystemStopping;
import io.onedev.server.web.component.modal.ModalPanel;
@SuppressWarnings("serial")
public abstract class TaskButton extends AjaxButton {
private static final Logger logger = LoggerFactory.getLogger(TaskButton.class);
public TaskButton(String id) {
super(id);
}
@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
target.add(form);
}
@Override
protected void onInitialize() {
super.onInitialize();
setOutputMarkupPlaceholderTag(true);
}
private Map<String, TaskFuture> getTaskFutures() {
return TaskFutureManager.taskFutures;
}
protected void submitTask(AjaxRequestTarget target) {
target.appendJavaScript(String.format("$('#%s').closest('form').children('.task-feedback').remove();", getMarkupId()));
String path = getPath();
ExecutorService executorService = OneDev.getInstance(ExecutorService.class);
TaskFuture future = getTaskFutures().put(path, new TaskFuture(executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return runTask();
}
})));
if (future != null)
future.cancel(true);
new ModalPanel(target) {
@Override
protected Component newContent(String id) {
String message = WordUtils.uncamel(TaskButton.this.getId()).toLowerCase();
return new TaskWaitPanel(id, StringUtils.capitalize(message) + " in progress...") {
@Override
protected void check(AjaxRequestTarget target) {
Future<String> future = getTaskFutures().get(path);
if (future != null) {
if (future.isDone()) {
if (!future.isCancelled()) {
String feedback;
try {
feedback = String.format(
"<div class='task-feedback alert alert-success'>%s</div>",
HtmlEscape.escapeHtml5(future.get()));
} catch (Exception e) {
logger.error("Error " + message, e);
String suggestedSolution = ExceptionUtils.suggestSolution(e);
if (suggestedSolution != null)
logger.warn("!!! " + suggestedSolution);
feedback = "Error " + message;
if (e.getMessage() != null)
feedback += ": " + e.getMessage();
feedback += ", check server log for details.";
feedback = String.format(
"<div class='task-feedback alert alert-danger'>%s</div>",
HtmlEscape.escapeHtml5(feedback));
}
feedback = StringUtils.replace(feedback, "\n", "<br>");
target.appendJavaScript(String.format(""
+ "var $form = $('#%s').closest('form');"
+ "$form.append('%s');"
+ "$form.children('.task-feedback')[0].scrollIntoView({behavior: 'smooth', block: 'center'})",
TaskButton.this.getMarkupId(), JavaScriptEscape.escapeJavaScript(feedback)));
}
close();
}
} else {
close();
}
}
@Override
protected void onCancel(AjaxRequestTarget target) {
Future<?> future = getTaskFutures().remove(path);
if (future != null)
future.cancel(true);
}
};
}
};
}
@Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(CssHeaderItem.forReference(new TaskButtonCssResourceReference()));
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
super.onSubmit(target, form);
target.add(form);
target.focusComponent(null);
submitTask(target);
}
protected abstract String runTask();
@Singleton
public static class TaskFutureManager implements SchedulableTask {
private static final Map<String, TaskFuture> taskFutures = new ConcurrentHashMap<>();
private final TaskScheduler taskScheduler;
private String taskId;
@Inject
public TaskFutureManager(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
@Listen
public void on(SystemStarted event) {
taskId = taskScheduler.schedule(this);
}
@Listen
public void on(SystemStopping event) {
taskScheduler.unschedule(taskId);
}
@Override
public void execute() {
for (Iterator<Map.Entry<String, TaskFuture>> it = taskFutures.entrySet().iterator(); it.hasNext();) {
if (it.next().getValue().isExpired())
it.remove();
}
}
@Override
public ScheduleBuilder<?> getScheduleBuilder() {
return SimpleScheduleBuilder.repeatHourlyForever();
}
}
private static class TaskFuture implements Future<String> {
private final Date timestamp = new Date();
private final Future<String> wrapped;
public TaskFuture(Future<String> wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return wrapped.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return wrapped.isCancelled();
}
@Override
public boolean isDone() {
return wrapped.isDone();
}
public boolean isExpired() {
return timestamp.before(new DateTime().minusHours(1).toDate());
}
@Override
public String get() throws InterruptedException, ExecutionException {
return wrapped.get();
}
@Override
public String get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return get(timeout, unit);
}
}
}

View File

@ -0,0 +1,13 @@
package io.onedev.server.web.component.taskbutton;
import io.onedev.server.web.page.base.BaseDependentCssResourceReference;
public class TaskButtonCssResourceReference extends BaseDependentCssResourceReference {
private static final long serialVersionUID = 1L;
public TaskButtonCssResourceReference() {
super(TaskButtonCssResourceReference.class, "task-button.css");
}
}

View File

@ -0,0 +1,10 @@
<wicket:panel>
<div class="task-wait">
<div class="modal-body">
<div wicket:id="message"></div>
</div>
<div class="modal-footer">
<button wicket:id="cancel" class="btn btn-default">Cancel</button>
</div>
</div>
</wicket:panel>

View File

@ -0,0 +1,48 @@
package io.onedev.server.web.component.taskbutton;
import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.util.time.Duration;
@SuppressWarnings("serial")
abstract class TaskWaitPanel extends Panel {
private final String message;
public TaskWaitPanel(String id, String message) {
super(id);
this.message = message;
}
@Override
protected void onInitialize() {
super.onInitialize();
add(new Label("message", message));
add(new AjaxLink<Void>("cancel") {
@Override
public void onClick(AjaxRequestTarget target) {
onCancel(target);
}
});
add(new AbstractAjaxTimerBehavior(Duration.ONE_SECOND) {
@Override
protected void onTimer(AjaxRequestTarget target) {
check(target);
}
});
}
protected abstract void check(AjaxRequestTarget target);
protected abstract void onCancel(AjaxRequestTarget target);
}

View File

@ -0,0 +1,13 @@
.task-wait .modal-body {
padding: 20px;
font-size: 18px;
font-weight: bold;
text-align: center;
}
.task-wait .modal-footer {
text-align: center;
}
form>.task-feedback {
margin-top: 20px;
margin-bottom: 0;
}

View File

@ -16,30 +16,24 @@ import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import io.onedev.commons.utils.ExceptionUtils;
import io.onedev.server.OneDev;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.model.support.authenticator.Authenticated;
import io.onedev.server.web.behavior.testform.TestFormBehavior;
import io.onedev.server.web.behavior.testform.TestResult;
import io.onedev.server.web.component.modal.ModalPanel;
import io.onedev.server.web.component.taskbutton.TaskButton;
import io.onedev.server.web.editable.BeanContext;
import io.onedev.server.web.editable.BeanEditor;
import io.onedev.server.web.editable.PropertyUpdating;
import io.onedev.server.web.editable.PropertyContext;
import io.onedev.server.web.editable.PropertyEditor;
import io.onedev.server.web.editable.PropertyUpdating;
import io.onedev.server.web.page.admin.AdministrationPage;
@SuppressWarnings("serial")
public class AuthenticatorPage extends AdministrationPage {
private static final Logger logger = LoggerFactory.getLogger(AuthenticatorPage.class);
private AuthenticationToken token = new AuthenticationToken();
public AuthenticatorPage(PageParameters params) {
@ -55,7 +49,6 @@ public class AuthenticatorPage extends AdministrationPage {
PropertyEditor<Serializable> editor =
PropertyContext.edit("editor", bean, "authenticator");
editor.setOutputMarkupId(true);
Button saveButton = new Button("save") {
@Override
@ -67,59 +60,7 @@ public class AuthenticatorPage extends AdministrationPage {
}
};
AjaxButton testButton = new AjaxButton("test") {
private TestFormBehavior testBehavior;
@Override
protected void onInitialize() {
super.onInitialize();
add(testBehavior = new TestFormBehavior() {
@Override
protected TestResult test() {
try {
Authenticated authenticated = bean.getAuthenticator().authenticate(
new UsernamePasswordToken(token.getUserName(), token.getPassword()));
StringBuilder retrievedInfoBuilder = new StringBuilder();
if (authenticated.getFullName() != null) {
retrievedInfoBuilder.append("Full name: ")
.append(authenticated.getFullName())
.append("\n");
}
if (authenticated.getEmail() != null) {
retrievedInfoBuilder.append("Email: ")
.append(authenticated.getEmail())
.append("\n");
}
if (authenticated.getGroupNames() != null) {
retrievedInfoBuilder.append("Groups: ")
.append(Joiner.on(", ").join(authenticated.getGroupNames()))
.append("\n");
}
StringBuilder messageBuilder =
new StringBuilder("Test successful: authentication passed");
if (retrievedInfoBuilder.length() != 0) {
messageBuilder.append(" with below information retrieved:\n")
.append(retrievedInfoBuilder);
}
return new TestResult.Successful(messageBuilder.toString());
} catch (AuthenticationException e) {
return new TestResult.Successful("Test successful: authentication not passed: " + e.getMessage());
} catch (Exception e) {
logger.error("Error testing external authentication", e);
String suggestedSolution = ExceptionUtils.suggestSolution(e);
if (suggestedSolution != null)
logger.warn("!!! " + suggestedSolution);
return new TestResult.Failed("Error testing external authentication: "
+ e.getMessage() + ", check server log for details.");
}
}
});
setOutputMarkupPlaceholderTag(true);
}
TaskButton testButton = new TaskButton("test") {
@Override
protected void onConfigure() {
@ -138,8 +79,6 @@ public class AuthenticatorPage extends AdministrationPage {
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
super.onSubmit(target, form);
new ModalPanel(target) {
@Override
@ -162,7 +101,7 @@ public class AuthenticatorPage extends AdministrationPage {
target.add(tokenEditor);
target.focusComponent(null);
close();
testBehavior.requestTest(target);
submitTask(target);
}
};
@ -189,11 +128,41 @@ public class AuthenticatorPage extends AdministrationPage {
}
};
target.add(form);
target.focusComponent(null);
}
@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
target.add(editor);
protected String runTask() {
try {
Authenticated authenticated = bean.getAuthenticator().authenticate(
new UsernamePasswordToken(token.getUserName(), token.getPassword()));
StringBuilder retrievedInfoBuilder = new StringBuilder();
if (authenticated.getFullName() != null) {
retrievedInfoBuilder.append("Full name: ")
.append(authenticated.getFullName())
.append("\n");
}
if (authenticated.getEmail() != null) {
retrievedInfoBuilder.append("Email: ")
.append(authenticated.getEmail())
.append("\n");
}
if (authenticated.getGroupNames() != null) {
retrievedInfoBuilder.append("Groups: ")
.append(Joiner.on(", ").join(authenticated.getGroupNames()))
.append("\n");
}
StringBuilder messageBuilder =
new StringBuilder("Test successful: authentication passed");
if (retrievedInfoBuilder.length() != 0) {
messageBuilder.append(" with below information retrieved:\n")
.append(retrievedInfoBuilder);
}
return messageBuilder.toString();
} catch (AuthenticationException e) {
return "Test successful: authentication not passed: " + e.getMessage();
}
}
};

View File

@ -4,7 +4,7 @@
<div wicket:id="editor"></div>
<div class="actions">
<button wicket:id="save" class="btn btn-primary dirty-aware">Save</button>
<input wicket:id="test" type="submit" class="btn btn-primary" value="Test">
<input wicket:id="testingExecutor" type="submit" class="btn btn-primary" value="Test">
<button wicket:id="cancel" class="btn btn-default">Cancel</button>
</div>
</form>

View File

@ -15,15 +15,11 @@ import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
import io.onedev.commons.utils.ExceptionUtils;
import io.onedev.server.model.support.JobExecutor;
import io.onedev.server.web.behavior.testform.TestFormBehavior;
import io.onedev.server.web.behavior.testform.TestResult;
import io.onedev.server.web.component.beaneditmodal.BeanEditModalPanel;
import io.onedev.server.web.component.taskbutton.TaskButton;
import io.onedev.server.web.editable.BeanContext;
import io.onedev.server.web.editable.BeanEditor;
import io.onedev.server.web.editable.BeanUpdating;
@ -33,8 +29,6 @@ import io.onedev.server.web.util.Testable;
@SuppressWarnings("serial")
abstract class JobExecutorEditPanel extends Panel {
private static final Logger logger = LoggerFactory.getLogger(JobExecutorEditPanel.class);
private final List<JobExecutor> executors;
private final int executorIndex;
@ -70,7 +64,6 @@ abstract class JobExecutorEditPanel extends Panel {
bean.setExecutor(executors.get(executorIndex));
BeanEditor editor = BeanContext.edit("editor", bean);
editor.setOutputMarkupId(true);
AjaxButton saveButton = new AjaxButton("save") {
@ -109,36 +102,9 @@ abstract class JobExecutorEditPanel extends Panel {
};
AjaxButton testButton = new AjaxButton("test") {
TaskButton testButton = new TaskButton("testingExecutor") {
private TestFormBehavior testBehavior;
private Serializable testData;
@Override
protected void onInitialize() {
super.onInitialize();
add(testBehavior = new TestFormBehavior() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected TestResult test() {
try {
((Testable)bean.getExecutor()).test(testData);
return new TestResult.Successful("Job executor tested successfully");
} catch (Exception e) {
logger.error("Error testing job executor", e);
String suggestedSolution = ExceptionUtils.suggestSolution(e);
if (suggestedSolution != null)
logger.warn("!!! " + suggestedSolution);
return new TestResult.Failed("Error testing job executor: " + e.getMessage() + ", check server log for details");
}
}
});
setOutputMarkupPlaceholderTag(true);
}
@SuppressWarnings("unchecked")
@Override
@ -182,8 +148,6 @@ abstract class JobExecutorEditPanel extends Panel {
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
super.onSubmit(target, form);
checkNameDuplication(editor, bean.getExecutor());
if (!editor.hasErrors(true)) {
if (testData != null) {
@ -194,23 +158,25 @@ abstract class JobExecutorEditPanel extends Panel {
close();
target.add(editor);
target.focusComponent(null);
testBehavior.requestTest(target);
submitTask(target);
}
};
} else {
target.add(editor);
target.focusComponent(null);
testBehavior.requestTest(target);
submitTask(target);
}
} else {
target.add(form);
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
target.add(editor);
protected String runTask() {
((Testable)bean.getExecutor()).test(testData);
return "Job executor tested successfully";
}
};

View File

@ -4,7 +4,7 @@
<div wicket:id="editor"></div>
<div class="actions">
<input wicket:id="save" type="submit" class="btn btn-primary dirty-aware" value="Save setting">
<input wicket:id="test" type="submit" class="btn btn-primary" value="Send test mail">
<input wicket:id="sendingTestMail" type="submit" class="btn btn-primary" value="Send test mail">
</div>
</form>
</wicket:extend>

View File

@ -1,36 +1,28 @@
package io.onedev.server.web.page.admin.mailsetting;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Sets;
import io.onedev.commons.utils.ExceptionUtils;
import io.onedev.server.OneDev;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.entitymanager.UserManager;
import io.onedev.server.model.User;
import io.onedev.server.notification.MailManager;
import io.onedev.server.web.behavior.testform.TestFormBehavior;
import io.onedev.server.web.behavior.testform.TestResult;
import io.onedev.server.web.editable.BeanUpdating;
import io.onedev.server.web.component.taskbutton.TaskButton;
import io.onedev.server.web.editable.BeanContext;
import io.onedev.server.web.editable.BeanEditor;
import io.onedev.server.web.editable.BeanUpdating;
import io.onedev.server.web.page.admin.AdministrationPage;
@SuppressWarnings("serial")
public class MailSettingPage extends AdministrationPage {
private static final Logger logger = LoggerFactory.getLogger(MailSettingPage.class);
public MailSettingPage(PageParameters params) {
super(params);
}
@ -55,37 +47,7 @@ public class MailSettingPage extends AdministrationPage {
}
};
AjaxButton testButton = new AjaxButton("test") {
private TestFormBehavior testBehavior;
@Override
protected void onInitialize() {
super.onInitialize();
add(testBehavior = new TestFormBehavior() {
@Override
protected TestResult test() {
User currentUser = OneDev.getInstance(UserManager.class).getCurrent();
try {
OneDev.getInstance(MailManager.class).sendMail(mailSettingHolder.getMailSetting(),
Sets.newHashSet(currentUser.getEmail()),
"Test email from OneDev", "Great, your mail setting is working!");
return new TestResult.Successful("Test mail has been sent to " +
currentUser.getEmail() + ", please check your mail box.");
} catch (Exception e) {
logger.error("Error sending test email", e);
String suggestedSolution = ExceptionUtils.suggestSolution(e);
if (suggestedSolution != null)
logger.warn("!!! " + suggestedSolution);
return new TestResult.Failed("Error sending test email: " + e.getMessage() + ", check server log for details.");
}
}
});
setOutputMarkupPlaceholderTag(true);
}
TaskButton testButton = new TaskButton("sendingTestMail") {
@Override
protected void onConfigure() {
@ -102,17 +64,12 @@ public class MailSettingPage extends AdministrationPage {
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
super.onSubmit(target, form);
target.add(editor);
target.focusComponent(null);
testBehavior.requestTest(target);
}
@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
target.add(editor);
protected String runTask() {
User currentUser = OneDev.getInstance(UserManager.class).getCurrent();
OneDev.getInstance(MailManager.class).sendMail(mailSettingHolder.getMailSetting(),
Sets.newHashSet(currentUser.getEmail()),
"Test email from OneDev", "Great, your mail setting is working!");
return "Test mail has been sent to " + currentUser.getEmail() + ", please check your mail box";
}
};

View File

@ -105,10 +105,6 @@ form .required {
color: #B94A48;
}
form>.test-feedback {
margin-top: 16px;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;

View File

@ -1,11 +1,11 @@
<wicket:extend>
<div id="forget" class="center-box">
<h1><img src="/img/logo.png" class="logo"></img> Reset Password</h1>
<h1><img src="/img/logo.png" class="logo"> Reset Password</h1>
<form wicket:id="form">
<div wicket:id="feedback"></div>
<div wicket:id="editor"></div>
<div class="actions">
<input wicket:id="reset" type="submit" value="Reset password" class="btn btn-primary"></input>
<input wicket:id="resettingPassword" type="submit" value="Reset password" class="btn btn-primary">
</div>
</form>
</div>

View File

@ -5,23 +5,19 @@ import java.util.Arrays;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
import io.onedev.commons.launcher.loader.AppLoader;
import io.onedev.server.OneDev;
import io.onedev.server.OneException;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.entitymanager.UserManager;
import io.onedev.server.model.User;
import io.onedev.server.notification.MailManager;
import io.onedev.server.web.behavior.testform.TestFormBehavior;
import io.onedev.server.web.behavior.testform.TestResult;
import io.onedev.server.web.component.taskbutton.TaskButton;
import io.onedev.server.web.editable.BeanContext;
import io.onedev.server.web.editable.annotation.Editable;
import io.onedev.server.web.page.base.BasePage;
@ -33,81 +29,50 @@ public class ForgetPage extends BasePage {
super(params);
}
private static final Logger logger = LoggerFactory.getLogger(ForgetPage.class);
@Override
protected void onInitialize() {
super.onInitialize();
final HelperBean bean = new HelperBean();
HelperBean bean = new HelperBean();
Form<?> form = new Form<Void>("form");
form.add(new NotificationPanel("feedback", form));
form.add(BeanContext.edit("editor", bean));
form.add(new AjaxButton("reset") {
private TestFormBehavior testBehavior;
form.add(new TaskButton("resettingPassword") {
@Override
protected void onInitialize() {
super.onInitialize();
add(testBehavior = new TestFormBehavior() {
protected String runTask() {
UserManager userManager = OneDev.getInstance(UserManager.class);
User user = userManager.findByName(bean.getUserNameOrEmailAddress());
if (user == null) {
user = userManager.findByEmail(bean.getUserNameOrEmailAddress());
}
if (user == null) {
throw new OneException("No user found with name or email: " + bean.getUserNameOrEmailAddress());
} else {
SettingManager configManager = OneDev.getInstance(SettingManager.class);
if (configManager.getMailSetting() != null) {
String password = RandomStringUtils.random(10, true, true);
user.setPassword(AppLoader.getInstance(PasswordService.class).encryptPassword(password));
userManager.save(user);
MailManager mailManager = OneDev.getInstance(MailManager.class);
String mailBody = String.format("Dear %s, "
+ "<p style='margin: 16px 0;'>"
+ "Per your request, password of your user \"%s\" has been reset to:<br>"
+ "%s<br><br>"
+ "Please login and change the password in your earliest convenience."
+ "<p style='margin: 16px 0;'>"
+ "-- Sent by OneDev",
user.getDisplayName(), user.getName(), password);
@Override
protected TestResult test() {
UserManager userManager = OneDev.getInstance(UserManager.class);
User user = userManager.findByName(bean.getUserNameOrEmailAddress());
if (user == null) {
user = userManager.findByEmail(bean.getUserNameOrEmailAddress());
}
if (user == null) {
return new TestResult.Failed("No user found with name or email: " + bean.getUserNameOrEmailAddress());
} else {
SettingManager configManager = OneDev.getInstance(SettingManager.class);
if (configManager.getMailSetting() != null) {
String password = RandomStringUtils.random(10, true, true);
user.setPassword(AppLoader.getInstance(PasswordService.class).encryptPassword(password));
userManager.save(user);
MailManager mailManager = OneDev.getInstance(MailManager.class);
try {
String mailBody = String.format("Dear %s, "
+ "<p style='margin: 16px 0;'>"
+ "Per your request, password of your user \"%s\" has been reset to:<br>"
+ "%s<br><br>"
+ "Please login and change the password in your earliest convenience."
+ "<p style='margin: 16px 0;'>"
+ "-- Sent by OneDev",
user.getDisplayName(), user.getName(), password);
mailManager.sendMail(configManager.getMailSetting(), Arrays.asList(user.getEmail()),
"Your OneDev password has been reset", mailBody);
return new TestResult.Successful("Please check your email " + user.getEmail() + " for the reset password.");
} catch (Exception e) {
logger.error("Error sending password reset email", e);
return new TestResult.Failed("Error sending password reset email: " + e.getMessage());
}
} else {
return new TestResult.Failed("Unable to send password reset email as smtp setting is not defined");
}
}
mailManager.sendMail(configManager.getMailSetting(), Arrays.asList(user.getEmail()),
"Your OneDev password has been reset", mailBody);
return "Please check your email " + user.getEmail() + " for the reset password";
} else {
throw new OneException("Unable to send password reset email as smtp setting is not defined");
}
});
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
super.onSubmit(target, form);
target.add(form);
target.focusComponent(null);
testBehavior.requestTest(target);
}
@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
target.add(form);
}
}
});

View File

@ -1,3 +1,2 @@
<wicket:extend>
<a wicket:id="test">test</a>
</wicket:extend>

View File

@ -3,7 +3,6 @@ package io.onedev.server.web.page.test;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import io.onedev.server.web.page.base.BasePage;
@ -18,14 +17,6 @@ public class TestPage extends BasePage {
@Override
protected void onInitialize() {
super.onInitialize();
add(new Link<Void>("test") {
@Override
public void onClick() {
}
});
}
@Override