mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 14:07:55 +01:00
Closes #161: password recovery by email
This commit is contained in:
parent
332fd9d1f6
commit
4cf1f29e0a
@ -75,6 +75,8 @@ public class EmailUtil {
|
|||||||
email.setCharset("UTF-8");
|
email.setCharset("UTF-8");
|
||||||
email.setHostName(ConfigUtil.getConfigStringValue(ConfigType.SMTP_HOSTNAME));
|
email.setHostName(ConfigUtil.getConfigStringValue(ConfigType.SMTP_HOSTNAME));
|
||||||
email.setSmtpPort(ConfigUtil.getConfigIntegerValue(ConfigType.SMTP_PORT));
|
email.setSmtpPort(ConfigUtil.getConfigIntegerValue(ConfigType.SMTP_PORT));
|
||||||
|
email.setAuthentication(ConfigUtil.getConfigStringValue(ConfigType.SMTP_USERNAME),
|
||||||
|
ConfigUtil.getConfigStringValue(ConfigType.SMTP_PASSWORD));
|
||||||
email.addTo(recipientUser.getEmail(), recipientUser.getUsername());
|
email.addTo(recipientUser.getEmail(), recipientUser.getUsername());
|
||||||
ConfigDao configDao = new ConfigDao();
|
ConfigDao configDao = new ConfigDao();
|
||||||
Config themeConfig = configDao.getById(ConfigType.THEME);
|
Config themeConfig = configDao.getById(ConfigType.THEME);
|
||||||
@ -87,7 +89,7 @@ public class EmailUtil {
|
|||||||
}
|
}
|
||||||
email.setFrom(ConfigUtil.getConfigStringValue(ConfigType.SMTP_FROM), appName);
|
email.setFrom(ConfigUtil.getConfigStringValue(ConfigType.SMTP_FROM), appName);
|
||||||
java.util.Locale userLocale = LocaleUtil.getLocale(System.getenv(Constants.DEFAULT_LANGUAGE_ENV));
|
java.util.Locale userLocale = LocaleUtil.getLocale(System.getenv(Constants.DEFAULT_LANGUAGE_ENV));
|
||||||
email.setSubject(subject);
|
email.setSubject(appName + " - " + subject);
|
||||||
email.setTextMsg(MessageUtil.getMessage(userLocale, "email.no_html.error"));
|
email.setTextMsg(MessageUtil.getMessage(userLocale, "email.no_html.error"));
|
||||||
|
|
||||||
// Add automatic parameters
|
// Add automatic parameters
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<table style="width: 100%; font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';">
|
<table style="width: 100%; font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';">
|
||||||
<tr style="background: #242424; color: #fff;">
|
<tr style="background: #242424; color: #fff;">
|
||||||
<td style="padding: 12px; font-size: 16px; font-weight: bold;">
|
<td style="padding: 12px; font-size: 16px; font-weight: bold;">
|
||||||
${base_url}
|
${app_name}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1,32 +1,11 @@
|
|||||||
package com.sismics.docs.rest.resource;
|
package com.sismics.docs.rest.resource;
|
||||||
|
|
||||||
import java.io.IOException;
|
import com.google.common.base.Strings;
|
||||||
import java.nio.file.DirectoryStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
import javax.json.Json;
|
|
||||||
import javax.json.JsonArrayBuilder;
|
|
||||||
import javax.json.JsonObjectBuilder;
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
import javax.persistence.Query;
|
|
||||||
import javax.ws.rs.*;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
|
|
||||||
import com.sismics.docs.core.constant.ConfigType;
|
import com.sismics.docs.core.constant.ConfigType;
|
||||||
import com.sismics.docs.core.dao.jpa.*;
|
import com.sismics.docs.core.dao.jpa.ConfigDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
import com.sismics.docs.core.event.RebuildIndexAsyncEvent;
|
import com.sismics.docs.core.event.RebuildIndexAsyncEvent;
|
||||||
import com.sismics.rest.util.ValidationUtil;
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.apache.log4j.Appender;
|
|
||||||
import org.apache.log4j.Level;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import com.sismics.docs.core.model.jpa.User;
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
import com.sismics.docs.core.util.ConfigUtil;
|
import com.sismics.docs.core.util.ConfigUtil;
|
||||||
@ -36,10 +15,28 @@ import com.sismics.docs.core.util.jpa.PaginatedLists;
|
|||||||
import com.sismics.docs.rest.constant.BaseFunction;
|
import com.sismics.docs.rest.constant.BaseFunction;
|
||||||
import com.sismics.rest.exception.ForbiddenClientException;
|
import com.sismics.rest.exception.ForbiddenClientException;
|
||||||
import com.sismics.rest.exception.ServerException;
|
import com.sismics.rest.exception.ServerException;
|
||||||
|
import com.sismics.rest.util.ValidationUtil;
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
import com.sismics.util.log4j.LogCriteria;
|
import com.sismics.util.log4j.LogCriteria;
|
||||||
import com.sismics.util.log4j.LogEntry;
|
import com.sismics.util.log4j.LogEntry;
|
||||||
import com.sismics.util.log4j.MemoryAppender;
|
import com.sismics.util.log4j.MemoryAppender;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.log4j.Appender;
|
||||||
|
import org.apache.log4j.Level;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.json.Json;
|
||||||
|
import javax.json.JsonArrayBuilder;
|
||||||
|
import javax.json.JsonObjectBuilder;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
import javax.ws.rs.*;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General app REST resource.
|
* General app REST resource.
|
||||||
@ -126,6 +123,7 @@ public class AppResource extends BaseResource {
|
|||||||
* @apiParam {String} username SMTP username
|
* @apiParam {String} username SMTP username
|
||||||
* @apiParam {String} password SMTP password
|
* @apiParam {String} password SMTP password
|
||||||
* @apiError (client) ForbiddenError Access denied
|
* @apiError (client) ForbiddenError Access denied
|
||||||
|
* @apiError (client) ValidationError Validation error
|
||||||
* @apiPermission admin
|
* @apiPermission admin
|
||||||
* @apiVersion 1.5.0
|
* @apiVersion 1.5.0
|
||||||
*
|
*
|
||||||
@ -147,18 +145,25 @@ public class AppResource extends BaseResource {
|
|||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
checkBaseFunction(BaseFunction.ADMIN);
|
checkBaseFunction(BaseFunction.ADMIN);
|
||||||
ValidationUtil.validateRequired(hostname, "hostname");
|
if (!Strings.isNullOrEmpty(portStr)) {
|
||||||
ValidationUtil.validateInteger(portStr, "port");
|
ValidationUtil.validateInteger(portStr, "port");
|
||||||
ValidationUtil.validateRequired(from, "from");
|
}
|
||||||
|
|
||||||
|
// Just update the changed configuration
|
||||||
ConfigDao configDao = new ConfigDao();
|
ConfigDao configDao = new ConfigDao();
|
||||||
configDao.update(ConfigType.SMTP_HOSTNAME, hostname);
|
if (!Strings.isNullOrEmpty(hostname)) {
|
||||||
configDao.update(ConfigType.SMTP_PORT, portStr);
|
configDao.update(ConfigType.SMTP_HOSTNAME, hostname);
|
||||||
configDao.update(ConfigType.SMTP_FROM, from);
|
}
|
||||||
if (username != null) {
|
if (!Strings.isNullOrEmpty(portStr)) {
|
||||||
|
configDao.update(ConfigType.SMTP_PORT, portStr);
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(from)) {
|
||||||
|
configDao.update(ConfigType.SMTP_FROM, from);
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(username)) {
|
||||||
configDao.update(ConfigType.SMTP_USERNAME, username);
|
configDao.update(ConfigType.SMTP_USERNAME, username);
|
||||||
}
|
}
|
||||||
if (password != null) {
|
if (!Strings.isNullOrEmpty(password)) {
|
||||||
configDao.update(ConfigType.SMTP_PASSWORD, password);
|
configDao.update(ConfigType.SMTP_PASSWORD, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,15 @@ angular.module('docs',
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.state('passwordreset', {
|
||||||
|
url: '/passwordreset/:key',
|
||||||
|
views: {
|
||||||
|
'page': {
|
||||||
|
templateUrl: 'partial/docs/passwordreset.html',
|
||||||
|
controller: 'PasswordReset'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.state('tag', {
|
.state('tag', {
|
||||||
url: '/tag',
|
url: '/tag',
|
||||||
abstract: true,
|
abstract: true,
|
||||||
|
@ -46,22 +46,22 @@ angular.module('docs').controller('Login', function(Restangular, $scope, $rootSc
|
|||||||
$uibModal.open({
|
$uibModal.open({
|
||||||
templateUrl: 'partial/docs/passwordlost.html',
|
templateUrl: 'partial/docs/passwordlost.html',
|
||||||
controller: 'LoginModalPasswordLost'
|
controller: 'LoginModalPasswordLost'
|
||||||
}).result.then(function (email) {
|
}).result.then(function (username) {
|
||||||
if (name === null) {
|
if (username === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a password lost email
|
// Send a password lost email
|
||||||
Restangular.one('user').post('passwordLost', {
|
Restangular.one('user').post('password_lost', {
|
||||||
email: email
|
username: username
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
var title = $translate.instant('login.password_lost_sent_title');
|
var title = $translate.instant('login.password_lost_sent_title');
|
||||||
var msg = $translate.instant('login.password_lost_sent_message', { email: email });
|
var msg = $translate.instant('login.password_lost_sent_message', { username: username });
|
||||||
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||||
$dialog.messageBox(title, msg, btns);
|
$dialog.messageBox(title, msg, btns);
|
||||||
}, function () {
|
}, function () {
|
||||||
var title = $translate.instant('login.password_lost_error_title');
|
var title = $translate.instant('login.password_lost_error_title');
|
||||||
var msg = $translate.instant('login.password_lost_error_message', { email: email });
|
var msg = $translate.instant('login.password_lost_error_message');
|
||||||
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||||
$dialog.messageBox(title, msg, btns);
|
$dialog.messageBox(title, msg, btns);
|
||||||
});
|
});
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
* Login modal password lost controller.
|
* Login modal password lost controller.
|
||||||
*/
|
*/
|
||||||
angular.module('docs').controller('LoginModalPasswordLost', function ($scope, $uibModalInstance) {
|
angular.module('docs').controller('LoginModalPasswordLost', function ($scope, $uibModalInstance) {
|
||||||
$scope.email = '';
|
$scope.username = '';
|
||||||
$scope.close = function(name) {
|
$scope.close = function(username) {
|
||||||
$uibModalInstance.close(name);
|
$uibModalInstance.close(username);
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password reset controller.
|
||||||
|
*/
|
||||||
|
angular.module('docs').controller('PasswordReset', function($scope, Restangular, $state, $stateParams, $translate, $dialog) {
|
||||||
|
$scope.submit = function () {
|
||||||
|
Restangular.one('user').post('password_reset', {
|
||||||
|
key: $stateParams.key,
|
||||||
|
password: $scope.password
|
||||||
|
}).then(function () {
|
||||||
|
$state.go('login');
|
||||||
|
}, function () {
|
||||||
|
var title = $translate.instant('passwordreset.error_title');
|
||||||
|
var msg = $translate.instant('passwordreset.error_message');
|
||||||
|
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||||
|
$dialog.messageBox(title, msg, btns);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
@ -5,29 +5,29 @@
|
|||||||
*/
|
*/
|
||||||
angular.module('docs').controller('SettingsConfig', function($scope, $rootScope, Restangular) {
|
angular.module('docs').controller('SettingsConfig', function($scope, $rootScope, Restangular) {
|
||||||
// Get the app configuration
|
// Get the app configuration
|
||||||
Restangular.one('app').get().then(function(data) {
|
Restangular.one('app').get().then(function (data) {
|
||||||
$scope.app = data;
|
$scope.app = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enable/disable guest login
|
// Enable/disable guest login
|
||||||
$scope.changeGuestLogin = function(enabled) {
|
$scope.changeGuestLogin = function (enabled) {
|
||||||
Restangular.one('app').post('guest_login', {
|
Restangular.one('app').post('guest_login', {
|
||||||
enabled: enabled
|
enabled: enabled
|
||||||
}).then(function() {
|
}).then(function () {
|
||||||
$scope.app.guest_login = enabled;
|
$scope.app.guest_login = enabled;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch the current theme configuration
|
// Fetch the current theme configuration
|
||||||
Restangular.one('theme').get().then(function(data) {
|
Restangular.one('theme').get().then(function (data) {
|
||||||
$scope.theme = data;
|
$scope.theme = data;
|
||||||
$rootScope.appName = $scope.theme.name;
|
$rootScope.appName = $scope.theme.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the theme
|
// Update the theme
|
||||||
$scope.update = function() {
|
$scope.update = function () {
|
||||||
$scope.theme.name = $scope.theme.name.length === 0 ? 'Sismics Docs' : $scope.theme.name;
|
$scope.theme.name = $scope.theme.name.length === 0 ? 'Sismics Docs' : $scope.theme.name;
|
||||||
Restangular.one('theme').post('', $scope.theme).then(function() {
|
Restangular.one('theme').post('', $scope.theme).then(function () {
|
||||||
var stylesheet = $('#theme-stylesheet')[0];
|
var stylesheet = $('#theme-stylesheet')[0];
|
||||||
stylesheet.href = stylesheet.href.replace(/\?.*|$/, '?' + new Date().getTime());
|
stylesheet.href = stylesheet.href.replace(/\?.*|$/, '?' + new Date().getTime());
|
||||||
$rootScope.appName = $scope.theme.name;
|
$rootScope.appName = $scope.theme.name;
|
||||||
@ -36,7 +36,7 @@ angular.module('docs').controller('SettingsConfig', function($scope, $rootScope,
|
|||||||
|
|
||||||
// Send an image
|
// Send an image
|
||||||
$scope.sendingImage = false;
|
$scope.sendingImage = false;
|
||||||
$scope.sendImage = function(type, image) {
|
$scope.sendImage = function (type, image) {
|
||||||
// Build the payload
|
// Build the payload
|
||||||
var formData = new FormData();
|
var formData = new FormData();
|
||||||
formData.append('image', image);
|
formData.append('image', image);
|
||||||
@ -64,4 +64,11 @@ angular.module('docs').controller('SettingsConfig', function($scope, $rootScope,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Edit SMTP config
|
||||||
|
$scope.editSmtpConfig = function () {
|
||||||
|
Restangular.one('app').post('config_smtp', $scope.smtp).then(function () {
|
||||||
|
$scope.smtpUpdated = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
});
|
});
|
@ -49,6 +49,7 @@
|
|||||||
<script src="app/docs/controller/Main.js" type="text/javascript"></script>
|
<script src="app/docs/controller/Main.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/Login.js" type="text/javascript"></script>
|
<script src="app/docs/controller/Login.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/LoginModalPasswordLost.js" type="text/javascript"></script>
|
<script src="app/docs/controller/LoginModalPasswordLost.js" type="text/javascript"></script>
|
||||||
|
<script src="app/docs/controller/PasswordReset.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/Navigation.js" type="text/javascript"></script>
|
<script src="app/docs/controller/Navigation.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/Footer.js" type="text/javascript"></script>
|
<script src="app/docs/controller/Footer.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/document/Document.js" type="text/javascript"></script>
|
<script src="app/docs/controller/document/Document.js" type="text/javascript"></script>
|
||||||
|
@ -12,15 +12,21 @@
|
|||||||
"login_failed_message": "Username or password invalid",
|
"login_failed_message": "Username or password invalid",
|
||||||
"password_lost_btn": "Password lost?",
|
"password_lost_btn": "Password lost?",
|
||||||
"password_lost_sent_title": "Password reset email sent",
|
"password_lost_sent_title": "Password reset email sent",
|
||||||
"password_lost_sent_message": "An email has been sent to <strong>{{ email }}</strong> to reset your password",
|
"password_lost_sent_message": "An email has been sent to <strong>{{ username }}</strong> to reset your password",
|
||||||
"password_lost_error_title": "Password reset error",
|
"password_lost_error_title": "Password reset error",
|
||||||
"password_lost_error_message": "Unable to send a password reset email, please contact your administrator for a manual reset"
|
"password_lost_error_message": "Unable to send a password reset email, please contact your administrator for a manual reset"
|
||||||
},
|
},
|
||||||
"passwordlost": {
|
"passwordlost": {
|
||||||
"title": "Password lost",
|
"title": "Password lost",
|
||||||
"message": "Please enter your email address to receive a password reset link",
|
"message": "Please enter your username to receive a password reset link. If you don't remember your username, please contact your administrator",
|
||||||
"submit": "Reset my password"
|
"submit": "Reset my password"
|
||||||
},
|
},
|
||||||
|
"passwordreset": {
|
||||||
|
"message": "Please enter a new password",
|
||||||
|
"submit": "Change my password",
|
||||||
|
"error_title": "Error changing your password",
|
||||||
|
"error_message": "Your password recovery request is expired, please ask a new one on the login page"
|
||||||
|
},
|
||||||
"index": {
|
"index": {
|
||||||
"toggle_navigation": "Toggle navigation",
|
"toggle_navigation": "Toggle navigation",
|
||||||
"nav_documents": "Documents",
|
"nav_documents": "Documents",
|
||||||
@ -295,7 +301,14 @@
|
|||||||
"custom_css_placeholder": "Custom CSS to add after the main stylesheet",
|
"custom_css_placeholder": "Custom CSS to add after the main stylesheet",
|
||||||
"logo": "Logo (squared size)",
|
"logo": "Logo (squared size)",
|
||||||
"background_image": "Background image",
|
"background_image": "Background image",
|
||||||
"uploading_image": "Uploading the image..."
|
"uploading_image": "Uploading the image...",
|
||||||
|
"title_smtp": "Email <small>configuration</small>",
|
||||||
|
"smtp_hostname": "SMTP hostname",
|
||||||
|
"smtp_port": "SMTP port",
|
||||||
|
"smtp_from": "Sender e-mail",
|
||||||
|
"smtp_username": "SMTP username",
|
||||||
|
"smtp_password": "SMTP password",
|
||||||
|
"smtp_updated": "SMTP configuration updated successfully"
|
||||||
},
|
},
|
||||||
"log": {
|
"log": {
|
||||||
"title": "Server <small>logs</small>",
|
"title": "Server <small>logs</small>",
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>
|
<p>
|
||||||
<label for="share-result">{{ 'passwordlost.message' | translate }}</label>
|
<label for="share-result">{{ 'passwordlost.message' | translate }}</label>
|
||||||
<input name="email" class="form-control" type="email" required id="share-result" ng-model="email" />
|
<input name="username" class="form-control" type="text" required id="share-result" ng-model="username" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button ng-click="close(email)" class="btn btn-primary" ng-disabled="!form.$valid">
|
<button ng-click="close(username)" class="btn btn-primary" ng-disabled="!form.$valid">
|
||||||
<span class="glyphicon glyphicon-envelope"></span> {{ 'passwordlost.submit' | translate }}
|
<span class="glyphicon glyphicon-envelope"></span> {{ 'passwordlost.submit' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button ng-click="close(null)" class="btn btn-default">{{ 'cancel' | translate }}</button>
|
<button ng-click="close(null)" class="btn btn-default">{{ 'cancel' | translate }}</button>
|
||||||
|
25
docs-web/src/main/webapp/src/partial/docs/passwordreset.html
Normal file
25
docs-web/src/main/webapp/src/partial/docs/passwordreset.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-offset-1 col-xs-10 col-sm-offset-2 col-sm-8">
|
||||||
|
<form class="form-horizontal" name="form" novalidate>
|
||||||
|
<div class="form-group" ng-class="{ 'has-error': !form.password.$valid && form.$dirty }">
|
||||||
|
<label for="inputPassword" class="col-sm-4 control-label">{{ 'passwordreset.message' | translate }}</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="password" name="password" ng-model="password" required ng-minlength="8" ng-maxlength="50" class="form-control" id="inputPassword">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<span class="help-block" ng-show="form.password.$error.required && form.$dirty">{{ 'validation.required' | translate }}</span>
|
||||||
|
<span class="help-block" ng-show="form.password.$error.minlength && form.$dirty">{{ 'validation.too_short' | translate }}</span>
|
||||||
|
<span class="help-block" ng-show="form.password.$error.maxlength && form.$dirty">{{ 'validation.too_long' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-4 col-sm-5">
|
||||||
|
<button class="btn btn-primary" ng-disabled="!form.$valid" ng-click="submit()">
|
||||||
|
<span class="glyphicon glyphicon-lock"></span>
|
||||||
|
{{ 'passwordreset.submit' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -74,3 +74,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<h1 translate="settings.config.title_smtp"></h1>
|
||||||
|
<div uib-alert ng-class="'alert-success'" ng-show="smtpUpdated">{{ 'settings.config.smtp_updated' | translate }}</div>
|
||||||
|
<form class="form-horizontal" name="smtpForm" ng-show="!smtpUpdated" novalidate>
|
||||||
|
<div class="form-group" ng-class="{ 'has-error': !smtpForm.hostname.$valid && smtpForm.$dirty }">
|
||||||
|
<label class="col-sm-2 control-label" for="smtpHostname">{{ 'settings.config.smtp_hostname' | translate }}</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<input name="hostname" type="text" class="form-control" id="smtpHostname" ng-model="smtp.hostname" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" ng-class="{ 'has-error': !smtpForm.port.$valid && smtpForm.$dirty }">
|
||||||
|
<label class="col-sm-2 control-label" for="smtpPort">{{ 'settings.config.smtp_port' | translate }}</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<input name="port" type="number" class="form-control" id="smtpPort" ng-model="smtp.port" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" ng-class="{ 'has-error': !smtpForm.from.$valid && smtpForm.$dirty }">
|
||||||
|
<label class="col-sm-2 control-label" for="smtpFrom">{{ 'settings.config.smtp_from' | translate }}</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<input name="from" type="email" class="form-control" id="smtpFrom" ng-model="smtp.from" />
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<span class="help-block" ng-show="smtpForm.from.$error.email && smtpForm.$dirty">{{ 'validation.email' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label" for="smtpUsername">{{ 'settings.config.smtp_username' | translate }}</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<input name="username" type="text" class="form-control" id="smtpUsername" ng-model="smtp.username" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label" for="smtpPassword">{{ 'settings.config.smtp_password' | translate }}</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<input name="password" type="password" class="form-control" id="smtpPassword" ng-model="smtp.password" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<button type="submit" class="btn btn-primary" ng-click="editSmtpConfig()" ng-disabled="!smtpForm.$valid">
|
||||||
|
<span class="glyphicon glyphicon-pencil"></span> {{ 'edit' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
Loading…
Reference in New Issue
Block a user