forked from Archives/Athou_commafeed
recover password (wip)
This commit is contained in:
@@ -44,4 +44,19 @@ public class UserDAO extends GenericDAO<User> {
|
||||
return user;
|
||||
}
|
||||
|
||||
public User findByEmail(String email) {
|
||||
CriteriaQuery<User> query = builder.createQuery(getType());
|
||||
Root<User> root = query.from(getType());
|
||||
query.where(builder.equal(root.get(User_.email), email));
|
||||
TypedQuery<User> q = em.createQuery(query);
|
||||
|
||||
User user = null;
|
||||
try {
|
||||
user = q.getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
user = null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ public class User extends AbstractModel {
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastLogin;
|
||||
|
||||
@Column(length = 40)
|
||||
private String recoverPasswordToken;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date recoverPasswordTokenDate;
|
||||
|
||||
@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST)
|
||||
private Set<UserRole> roles = Sets.newHashSet();
|
||||
|
||||
@@ -110,4 +116,20 @@ public class User extends AbstractModel {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getRecoverPasswordToken() {
|
||||
return recoverPasswordToken;
|
||||
}
|
||||
|
||||
public void setRecoverPasswordToken(String recoverPasswordToken) {
|
||||
this.recoverPasswordToken = recoverPasswordToken;
|
||||
}
|
||||
|
||||
public Date getRecoverPasswordTokenDate() {
|
||||
return recoverPasswordTokenDate;
|
||||
}
|
||||
|
||||
public void setRecoverPasswordTokenDate(Date recoverPasswordTokenDate) {
|
||||
this.recoverPasswordTokenDate = recoverPasswordTokenDate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ import com.google.api.client.util.Lists;
|
||||
|
||||
@Stateless
|
||||
public class FeedSubscriptionService {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@ApplicationException
|
||||
public class FeedSubscriptionException extends RuntimeException {
|
||||
public static class FeedSubscriptionException extends RuntimeException {
|
||||
public FeedSubscriptionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
@@ -51,10 +53,10 @@ public class FeedSubscriptionService {
|
||||
final String pubUrl = applicationSettingsService.get().getPublicUrl();
|
||||
if (pubUrl == null) {
|
||||
throw new FeedSubscriptionException(
|
||||
"Public URL of this CommaFeed is unset");
|
||||
"Public URL of this CommaFeed is unset");
|
||||
}
|
||||
if (url.startsWith(pubUrl)) {
|
||||
throw new RuntimeException(
|
||||
throw new FeedSubscriptionException(
|
||||
"Could not subscribe to a feed from this CommaFeed instance");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.commafeed.backend.services;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.Authenticator;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.PasswordAuthentication;
|
||||
import javax.mail.Session;
|
||||
@@ -10,35 +12,47 @@ import javax.mail.Transport;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.model.ApplicationSettings;
|
||||
import com.commafeed.backend.model.User;
|
||||
|
||||
public class MailService {
|
||||
@SuppressWarnings("serial")
|
||||
public class MailService implements Serializable {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(MailService.class);
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
public void sendMail(User user, String subject, String content) throws Exception {
|
||||
public void sendMail(User user, String subject, String content)
|
||||
throws Exception {
|
||||
|
||||
ApplicationSettings settings = applicationSettingsService.get();
|
||||
|
||||
|
||||
final String username = settings.getSmtpUserName();
|
||||
final String password = settings.getSmtpPassword();
|
||||
|
||||
|
||||
log.info(username);
|
||||
log.info(password);
|
||||
log.info("" + settings.isSmtpTls());
|
||||
log.info(settings.getSmtpHost());
|
||||
log.info("" + settings.getSmtpPort());
|
||||
|
||||
String dest = user.getEmail();
|
||||
|
||||
Properties props = new Properties();
|
||||
props.put("mail.smtp.auth", "true");
|
||||
props.put("mail.smtp.starttls.enable", settings.isSmtpTls());
|
||||
props.put("mail.smtp.starttls.enable", "" + settings.isSmtpTls());
|
||||
props.put("mail.smtp.host", settings.getSmtpHost());
|
||||
props.put("mail.smtp.port", settings.getSmtpPort());
|
||||
props.put("mail.smtp.port", "" + settings.getSmtpPort());
|
||||
|
||||
Session session = Session.getInstance(props,
|
||||
new javax.mail.Authenticator() {
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
Session session = Session.getInstance(props, new Authenticator() {
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
|
||||
Message message = new MimeMessage(session);
|
||||
message.setFrom(new InternetAddress(username, "CommaFeed"));
|
||||
|
||||
@@ -2,10 +2,13 @@ package com.commafeed.backend.services;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
@@ -94,4 +97,10 @@ public class UserService {
|
||||
userRoleDAO.delete(userRoleDAO.findAll(user));
|
||||
userDAO.delete(user);
|
||||
}
|
||||
|
||||
public String generateApiKey(User user) {
|
||||
byte[] key = encryptionService.getEncryptedPassword(UUID.randomUUID()
|
||||
.toString(), user.getSalt());
|
||||
return DigestUtils.sha1Hex(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ import com.commafeed.frontend.pages.GoogleImportRedirectPage;
|
||||
import com.commafeed.frontend.pages.HomePage;
|
||||
import com.commafeed.frontend.pages.LogoutPage;
|
||||
import com.commafeed.frontend.pages.NextUnreadRedirectPage;
|
||||
import com.commafeed.frontend.pages.PasswordRecoveryCallbackPage;
|
||||
import com.commafeed.frontend.pages.PasswordRecoveryPage;
|
||||
import com.commafeed.frontend.pages.WelcomePage;
|
||||
import com.commafeed.frontend.utils.exception.DisplayExceptionPage;
|
||||
|
||||
@@ -66,8 +68,13 @@ public class CommaFeedApplication extends AuthenticatedWebApplication {
|
||||
|
||||
mountPage("welcome", WelcomePage.class);
|
||||
mountPage("demo", DemoLoginPage.class);
|
||||
|
||||
mountPage("recover", PasswordRecoveryPage.class);
|
||||
mountPage("recover2", PasswordRecoveryCallbackPage.class);
|
||||
|
||||
mountPage("logout", LogoutPage.class);
|
||||
mountPage("error", DisplayExceptionPage.class);
|
||||
|
||||
mountPage("google/import/redirect", GoogleImportRedirectPage.class);
|
||||
mountPage(GoogleImportCallbackPage.PAGE_PATH,
|
||||
GoogleImportCallbackPage.class);
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.commafeed.backend.model.ApplicationSettings;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserSettings;
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.backend.services.MailService;
|
||||
import com.commafeed.frontend.CommaFeedSession;
|
||||
import com.commafeed.frontend.utils.WicketUtils;
|
||||
import com.google.api.client.util.Maps;
|
||||
@@ -61,6 +62,9 @@ public abstract class BasePage extends WebPage {
|
||||
|
||||
@Inject
|
||||
protected UserRoleDAO userRoleDAO;
|
||||
|
||||
@Inject
|
||||
MailService mailService;
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<html xmlns:wicket="http://wicket.apache.org">
|
||||
<body>
|
||||
<wicket:extend>
|
||||
<div class="container">
|
||||
<div class="text-center">
|
||||
<img src="images/logo_2.png" />
|
||||
<div wicket:id="feedback"></div>
|
||||
<form wicket:id="form">
|
||||
Password:
|
||||
<input type="password" wicket:id="password" />
|
||||
<br />
|
||||
Password:
|
||||
<input type="password" wicket:id="confirm" />
|
||||
<br />
|
||||
<input type="submit" class="btn btn-primary" value="Submit" />
|
||||
<input type="button" class="btn" wicket:id="cancel" value="Home page" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</wicket:extend>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.commafeed.frontend.pages;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.wicket.markup.html.form.Form;
|
||||
import org.apache.wicket.markup.html.form.PasswordTextField;
|
||||
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
import org.apache.wicket.validation.validator.StringValidator;
|
||||
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.services.PasswordEncryptionService;
|
||||
import com.commafeed.backend.services.UserService;
|
||||
import com.commafeed.frontend.pages.components.BootstrapFeedbackPanel;
|
||||
import com.commafeed.frontend.utils.exception.DisplayException;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class PasswordRecoveryCallbackPage extends BasePage {
|
||||
|
||||
public static final String PARAM_EMAIL = "email";
|
||||
public static final String PARAM_TOKEN = "token";
|
||||
|
||||
@Inject
|
||||
PasswordEncryptionService encryptionService;
|
||||
|
||||
@Inject
|
||||
UserService userService;
|
||||
|
||||
public PasswordRecoveryCallbackPage(PageParameters params) {
|
||||
String email = params.get(PARAM_EMAIL).toString();
|
||||
String token = params.get(PARAM_TOKEN).toString();
|
||||
|
||||
final User user = userDAO.findByEmail(email);
|
||||
if (user == null) {
|
||||
throw new DisplayException("email not found");
|
||||
}
|
||||
if (user.getRecoverPasswordToken() == null
|
||||
|| !user.getRecoverPasswordToken().equals(token)) {
|
||||
throw new DisplayException("invalid token");
|
||||
}
|
||||
if (user.getRecoverPasswordTokenDate().before(
|
||||
DateUtils.addDays(Calendar.getInstance().getTime(), -2))) {
|
||||
throw new DisplayException("token expired");
|
||||
}
|
||||
|
||||
final IModel<String> password = new Model<String>();
|
||||
final IModel<String> confirm = new Model<String>();
|
||||
add(new BootstrapFeedbackPanel("feedback"));
|
||||
Form<Void> form = new Form<Void>("form") {
|
||||
@Override
|
||||
protected void onSubmit() {
|
||||
String passwd = password.getObject();
|
||||
if (StringUtils.equals(passwd, confirm.getObject())) {
|
||||
byte[] password = encryptionService.getEncryptedPassword(
|
||||
passwd, user.getSalt());
|
||||
user.setPassword(password);
|
||||
user.setApiKey(userService.generateApiKey(user));
|
||||
userDAO.update(user);
|
||||
info("Password saved.");
|
||||
} else {
|
||||
error("Password do not match");
|
||||
}
|
||||
}
|
||||
};
|
||||
add(form);
|
||||
form.add(new PasswordTextField("password", password).setResetPassword(
|
||||
true).add(StringValidator.minimumLength(6)));
|
||||
form.add(new PasswordTextField("confirm", confirm).setResetPassword(
|
||||
true).add(StringValidator.minimumLength(6)));
|
||||
|
||||
form.add(new BookmarkablePageLink<Void>("cancel", HomePage.class));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<html xmlns:wicket="http://wicket.apache.org">
|
||||
<body>
|
||||
<wicket:extend>
|
||||
<div class="container">
|
||||
<div class="text-center">
|
||||
<img src="images/logo_2.png" />
|
||||
<div wicket:id="feedback"></div>
|
||||
<form wicket:id="form">
|
||||
Email:
|
||||
<input type="email" wicket:id="email" />
|
||||
<br />
|
||||
<input type="submit" class="btn btn-primary" value="Submit" />
|
||||
<input type="button" class="btn" wicket:id="cancel" value="Cancel" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</wicket:extend>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.commafeed.frontend.pages;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.wicket.extensions.validation.validator.RfcCompliantEmailAddressValidator;
|
||||
import org.apache.wicket.markup.html.form.Form;
|
||||
import org.apache.wicket.markup.html.form.RequiredTextField;
|
||||
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.frontend.pages.components.BootstrapFeedbackPanel;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class PasswordRecoveryPage extends BasePage {
|
||||
|
||||
private static Logger log = LoggerFactory
|
||||
.getLogger(PasswordRecoveryPage.class);
|
||||
|
||||
public PasswordRecoveryPage() {
|
||||
|
||||
IModel<String> email = new Model<String>();
|
||||
add(new BootstrapFeedbackPanel("feedback"));
|
||||
Form<String> form = new Form<String>("form", email) {
|
||||
@Override
|
||||
protected void onSubmit() {
|
||||
super.onSubmit();
|
||||
User user = userDAO.findByEmail(getModelObject());
|
||||
if (user == null) {
|
||||
error("Email not found.");
|
||||
} else {
|
||||
try {
|
||||
user.setRecoverPasswordToken(DigestUtils.sha1Hex(UUID
|
||||
.randomUUID().toString()));
|
||||
user.setRecoverPasswordTokenDate(Calendar.getInstance()
|
||||
.getTime());
|
||||
userDAO.update(user);
|
||||
mailService.sendMail(user,
|
||||
"CommaFeed - Password recovery",
|
||||
buildEmailContent(user));
|
||||
info("Email sent.");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
error("Cannot send email, please contact the staff.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
add(form);
|
||||
|
||||
form.add(new RequiredTextField<String>("email", email) {
|
||||
@Override
|
||||
protected String getInputType() {
|
||||
return "email";
|
||||
}
|
||||
}.add(RfcCompliantEmailAddressValidator.getInstance()));
|
||||
|
||||
form.add(new BookmarkablePageLink<Void>("cancel", HomePage.class));
|
||||
}
|
||||
|
||||
private String buildEmailContent(User user) {
|
||||
return "cc";
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
</p>
|
||||
<div>
|
||||
<input type="submit" class="btn btn-primary" value="Log in" />
|
||||
<a wicket:id="recover" class="pull-right">Forgot password?</a>
|
||||
</div>
|
||||
</form>
|
||||
</wicket:panel>
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
package com.commafeed.frontend.pages.components;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.wicket.authroles.authentication.panel.SignInPanel;
|
||||
import org.apache.wicket.feedback.ContainerFeedbackMessageFilter;
|
||||
import org.apache.wicket.markup.html.form.Form;
|
||||
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
|
||||
|
||||
import com.commafeed.backend.services.ApplicationSettingsService;
|
||||
import com.commafeed.frontend.pages.PasswordRecoveryPage;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class LoginPanel extends SignInPanel {
|
||||
|
||||
@Inject
|
||||
ApplicationSettingsService applicationSettingsService;
|
||||
|
||||
public LoginPanel(String id) {
|
||||
super(id);
|
||||
replace(new BootstrapFeedbackPanel("feedback",
|
||||
new ContainerFeedbackMessageFilter(this)));
|
||||
Form<?> form = (Form<?>) get("signInForm");
|
||||
form.add(new BookmarkablePageLink<Void>("recover",
|
||||
PasswordRecoveryPage.class){
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
super.onConfigure();
|
||||
String smtpHost = applicationSettingsService.get().getSmtpHost();
|
||||
setVisibilityAllowed(StringUtils.isNotBlank(smtpHost));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -226,12 +226,13 @@ public class FeedREST extends AbstractResourceREST {
|
||||
FeedInfo info = (FeedInfo) fetchFeed(url).getEntity();
|
||||
try {
|
||||
feedSubscriptionService.subscribe(getUser(), info.getUrl(),
|
||||
req.getTitle(), category);
|
||||
req.getTitle(), category);
|
||||
} catch (Exception e) {
|
||||
log.info("Failed to subscribe to URL {}: {}", url, e.getMessage());
|
||||
return Response.status(Status.SERVICE_UNAVAILABLE).entity(
|
||||
"Failed to subscribe to URL " + url + ": " + e.getMessage()
|
||||
).build();
|
||||
return Response
|
||||
.status(Status.SERVICE_UNAVAILABLE)
|
||||
.entity("Failed to subscribe to URL " + url + ": "
|
||||
+ e.getMessage()).build();
|
||||
}
|
||||
return Response.ok(Status.OK).build();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package com.commafeed.frontend.rest.resources;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.commafeed.backend.StartupBean;
|
||||
@@ -120,10 +117,10 @@ public class UserREST extends AbstractResourceREST {
|
||||
byte[] password = encryptionService.getEncryptedPassword(
|
||||
request.getPassword(), user.getSalt());
|
||||
user.setPassword(password);
|
||||
user.setApiKey(generateKey(user));
|
||||
user.setApiKey(userService.generateApiKey(user));
|
||||
}
|
||||
if (request.isNewApiKey()) {
|
||||
user.setApiKey(generateKey(user));
|
||||
user.setApiKey(userService.generateApiKey(user));
|
||||
}
|
||||
userDAO.update(user);
|
||||
return Response.ok().build();
|
||||
@@ -140,10 +137,4 @@ public class UserREST extends AbstractResourceREST {
|
||||
userService.unregister(getUser());
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
private String generateKey(User user) {
|
||||
byte[] key = encryptionService.getEncryptedPassword(UUID.randomUUID()
|
||||
.toString(), user.getSalt());
|
||||
return DigestUtils.sha1Hex(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,66 +10,107 @@
|
||||
|
||||
<div>
|
||||
<form name="settingsForm" class="form-horizontal" ng-submit="save()">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="publicUrl">Public URL</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="publicUrl" name="publicUrl"
|
||||
ng-model="settings.publicUrl" />
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="publicUrl">Public URL</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="publicUrl" name="publicUrl"
|
||||
ng-model="settings.publicUrl" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="allowRegistrations">Allow
|
||||
registrations</label>
|
||||
<div class="controls">
|
||||
<input type="checkbox" id="allowRegistrations"
|
||||
name="allowRegistrations" ng-model="settings.allowRegistrations" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="feedbackButton">Feedback
|
||||
button</label>
|
||||
<div class="controls">
|
||||
<input type="checkbox" id="feedbackButton" name="feedbackButton"
|
||||
ng-model="settings.feedbackButton" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="googleClientId">Google
|
||||
client ID</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="googleClientId"
|
||||
ng-model="settings.googleClientId" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="googleClientSecret">Google
|
||||
client secret</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="googleClientSecret"
|
||||
ng-model="settings.googleClientSecret" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="googleAnalyticsTrackingCode">Google
|
||||
Analytics tracking code</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="googleAnalyticsTrackingCode"
|
||||
ng-model="settings.googleAnalyticsTrackingCode" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="googleClientSecret">Background
|
||||
threads</label>
|
||||
<div class="controls">
|
||||
<input type="number" name="backgroundThreads"
|
||||
ng-model="settings.backgroundThreads" />
|
||||
<span class="help-inline">Requires restart</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="announcement">Announcement</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="announcement"
|
||||
ng-model="settings.announcement" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="allowRegistrations">Allow
|
||||
registrations</label>
|
||||
<div class="controls">
|
||||
<input type="checkbox" id="allowRegistrations"
|
||||
name="allowRegistrations" ng-model="settings.allowRegistrations" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="feedbackButton">Feedback button</label>
|
||||
<div class="controls">
|
||||
<input type="checkbox" id="feedbackButton"
|
||||
name="feedbackButton" ng-model="settings.feedbackButton" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="googleClientId">Google
|
||||
client ID</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="googleClientId"
|
||||
ng-model="settings.googleClientId" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="googleClientSecret">Google
|
||||
client secret</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="googleClientSecret"
|
||||
ng-model="settings.googleClientSecret" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="googleAnalyticsTrackingCode">Google
|
||||
Analytics tracking code</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="googleAnalyticsTrackingCode"
|
||||
ng-model="settings.googleAnalyticsTrackingCode" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="googleClientSecret">Background
|
||||
threads</label>
|
||||
<div class="controls">
|
||||
<input type="number" name="backgroundThreads"
|
||||
ng-model="settings.backgroundThreads" />
|
||||
<span class="help-inline">Requires restart</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="announcement">Announcement</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="announcement"
|
||||
ng-model="settings.announcement" />
|
||||
<div class="span6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="announcement">SMTP Host</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="smtpHost" ng-model="settings.smtpHost" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="announcement">SMTP Port</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="smtpPort" ng-model="settings.smtpPort" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="smtpTls">SMTP TLS </label>
|
||||
<div class="controls">
|
||||
<input type="checkbox" name="smtpTls" ng-model="settings.smtpTls" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="smtpUserName">SMTP
|
||||
Username</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="smtpUserName"
|
||||
ng-model="settings.smtpUserName" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="smtpPassword">SMTP
|
||||
Password</label>
|
||||
<div class="controls">
|
||||
<input type="password" name="smtpPassword"
|
||||
ng-model="settings.smtpPassword" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
|
||||
Reference in New Issue
Block a user