diff --git a/controllers/ng/base.go b/controllers/ng/base.go
index d703d5921..c0eb02c54 100644
--- a/controllers/ng/base.go
+++ b/controllers/ng/base.go
@@ -1,20 +1,111 @@
package ng
import (
+ "net/http"
+ "os"
"path/filepath"
+ "strings"
"github.com/astaxie/beego"
+ "github.com/beego/i18n"
+ "github.com/vmware/harbor/dao"
+ "github.com/vmware/harbor/utils/log"
)
type BaseController struct {
beego.Controller
+ i18n.Locale
+ SelfRegistration bool
+ IsAdmin bool
+ AuthMode string
+}
+
+type langType struct {
+ Lang string
+ Name string
}
const (
- viewPath = "sections"
- prefixNg = "ng"
+ viewPath = "sections"
+ prefixNg = "ng"
+ defaultLang = "en-US"
)
+var supportLanguages map[string]langType
+
+// Prepare extracts the language information from request and populate data for rendering templates.
+func (b *BaseController) Prepare() {
+
+ var lang string
+ al := b.Ctx.Request.Header.Get("Accept-Language")
+
+ if len(al) > 4 {
+ al = al[:5] // Only compare first 5 letters.
+ if i18n.IsExist(al) {
+ lang = al
+ }
+ }
+
+ if _, exist := supportLanguages[lang]; exist == false { //Check if support the request language.
+ lang = defaultLang //Set default language if not supported.
+ }
+
+ sessionLang := b.GetSession("lang")
+ if sessionLang != nil {
+ b.SetSession("Lang", lang)
+ lang = sessionLang.(string)
+ }
+
+ curLang := langType{
+ Lang: lang,
+ }
+
+ restLangs := make([]*langType, 0, len(langTypes)-1)
+ for _, v := range langTypes {
+ if lang != v.Lang {
+ restLangs = append(restLangs, v)
+ } else {
+ curLang.Name = v.Name
+ }
+ }
+
+ // Set language properties.
+ b.Lang = lang
+ b.Data["Lang"] = curLang.Lang
+ b.Data["CurLang"] = curLang.Name
+ b.Data["RestLangs"] = restLangs
+
+ authMode := strings.ToLower(os.Getenv("AUTH_MODE"))
+ if authMode == "" {
+ authMode = "db_auth"
+ }
+ b.AuthMode = authMode
+ b.Data["AuthMode"] = b.AuthMode
+
+ selfRegistration := strings.ToLower(os.Getenv("SELF_REGISTRATION"))
+
+ if selfRegistration == "on" {
+ b.SelfRegistration = true
+ }
+
+ sessionUserID := b.GetSession("userId")
+ if sessionUserID != nil {
+ b.Data["Username"] = b.GetSession("username")
+ b.Data["UserId"] = sessionUserID.(int)
+
+ var err error
+ b.IsAdmin, err = dao.IsAdminRole(sessionUserID.(int))
+ if err != nil {
+ log.Errorf("Error occurred in IsAdminRole:%v", err)
+ b.CustomAbort(http.StatusInternalServerError, "Internal error.")
+ }
+ }
+
+ b.Data["IsAdmin"] = b.IsAdmin
+ b.Data["SelfRegistration"] = b.SelfRegistration
+
+}
+
func (bc *BaseController) Forward(title, templateName string) {
bc.Layout = filepath.Join(prefixNg, "layout.htm")
bc.TplName = filepath.Join(prefixNg, templateName)
@@ -26,3 +117,41 @@ func (bc *BaseController) Forward(title, templateName string) {
bc.LayoutSections["FooterContent"] = filepath.Join(prefixNg, viewPath, "footer-content.htm")
}
+
+var langTypes []*langType
+
+func init() {
+
+ //conf/app.conf -> os.Getenv("config_path")
+ configPath := os.Getenv("CONFIG_PATH")
+ if len(configPath) != 0 {
+ log.Infof("Config path: %s", configPath)
+ beego.AppConfigPath = configPath
+ if err := beego.ParseConfig(); err != nil {
+ log.Warningf("Failed to parse config file: %s, error: %v", configPath, err)
+ }
+ }
+
+ beego.AddFuncMap("i18n", i18n.Tr)
+
+ langs := strings.Split(beego.AppConfig.String("lang::types"), "|")
+ names := strings.Split(beego.AppConfig.String("lang::names"), "|")
+
+ supportLanguages = make(map[string]langType)
+
+ langTypes = make([]*langType, 0, len(langs))
+ for i, v := range langs {
+ t := langType{
+ Lang: v,
+ Name: names[i],
+ }
+ langTypes = append(langTypes, &t)
+ supportLanguages[v] = t
+ }
+
+ for _, lang := range langs {
+ if err := i18n.SetMessage(lang, "static/i18n/"+"locale_"+lang+".ini"); err != nil {
+ log.Errorf("Fail to set message file: %s", err.Error())
+ }
+ }
+}
diff --git a/controllers/ng/password.go b/controllers/ng/password.go
new file mode 100644
index 000000000..a360254bd
--- /dev/null
+++ b/controllers/ng/password.go
@@ -0,0 +1,175 @@
+package ng
+
+import (
+ "bytes"
+ "net/http"
+ "os"
+ "regexp"
+ "text/template"
+
+ "github.com/astaxie/beego"
+ "github.com/vmware/harbor/dao"
+ "github.com/vmware/harbor/models"
+ "github.com/vmware/harbor/utils"
+ "github.com/vmware/harbor/utils/log"
+)
+
+type CommonController struct {
+ BaseController
+}
+
+func (cc *CommonController) Render() error {
+ return nil
+}
+
+type messageDetail struct {
+ Hint string
+ URL string
+ UUID string
+}
+
+// SendEmail verifies the Email address and contact SMTP server to send reset password Email.
+func (cc *CommonController) SendEmail() {
+
+ email := cc.GetString("email")
+
+ pass, _ := regexp.MatchString(`^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`, email)
+
+ if !pass {
+ cc.CustomAbort(http.StatusBadRequest, "email_content_illegal")
+ } else {
+
+ queryUser := models.User{Email: email}
+ exist, err := dao.UserExists(queryUser, "email")
+ if err != nil {
+ log.Errorf("Error occurred in UserExists: %v", err)
+ cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
+ }
+ if !exist {
+ cc.CustomAbort(http.StatusNotFound, "email_does_not_exist")
+ }
+
+ messageTemplate, err := template.ParseFiles("views/ng/reset-password-mail.tpl")
+ if err != nil {
+ log.Errorf("Parse email template file failed: %v", err)
+ cc.CustomAbort(http.StatusInternalServerError, err.Error())
+ }
+
+ message := new(bytes.Buffer)
+
+ harborURL := os.Getenv("HARBOR_URL")
+ if harborURL == "" {
+ harborURL = "localhost"
+ }
+ uuid, err := dao.GenerateRandomString()
+ if err != nil {
+ log.Errorf("Error occurred in GenerateRandomString: %v", err)
+ cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
+ }
+ err = messageTemplate.Execute(message, messageDetail{
+ Hint: cc.Tr("reset_email_hint"),
+ URL: harborURL,
+ UUID: uuid,
+ })
+
+ if err != nil {
+ log.Errorf("Message template error: %v", err)
+ cc.CustomAbort(http.StatusInternalServerError, "internal_error")
+ }
+
+ config, err := beego.AppConfig.GetSection("mail")
+ if err != nil {
+ log.Errorf("Can not load app.conf: %v", err)
+ cc.CustomAbort(http.StatusInternalServerError, "internal_error")
+ }
+
+ mail := utils.Mail{
+ From: config["from"],
+ To: []string{email},
+ Subject: cc.Tr("reset_email_subject"),
+ Message: message.String()}
+
+ err = mail.SendMail()
+
+ if err != nil {
+ log.Errorf("Send email failed: %v", err)
+ cc.CustomAbort(http.StatusInternalServerError, "send_email_failed")
+ }
+
+ user := models.User{ResetUUID: uuid, Email: email}
+ dao.UpdateUserResetUUID(user)
+
+ }
+
+}
+
+type ForgotPasswordController struct {
+ BaseController
+}
+
+func (fpc *ForgotPasswordController) Get() {
+ fpc.Forward("Forgot Password", "forgot-password.htm")
+}
+
+// ResetPasswordController handles request to /resetPassword
+type ResetPasswordController struct {
+ BaseController
+}
+
+// Get checks if reset_uuid in the reset link is valid and render the result page for user to reset password.
+func (rpc *ResetPasswordController) Get() {
+
+ resetUUID := rpc.GetString("reset_uuid")
+ if resetUUID == "" {
+ log.Error("Reset uuid is blank.")
+ rpc.Redirect("/", http.StatusFound)
+ return
+ }
+
+ queryUser := models.User{ResetUUID: resetUUID}
+ user, err := dao.GetUser(queryUser)
+ if err != nil {
+ log.Errorf("Error occurred in GetUser: %v", err)
+ rpc.CustomAbort(http.StatusInternalServerError, "Internal error.")
+ }
+
+ if user != nil {
+ rpc.Data["ResetUuid"] = user.ResetUUID
+ rpc.Forward("Reset Password", "reset-password.htm")
+ } else {
+ rpc.Redirect("/", http.StatusFound)
+ }
+}
+
+// ResetPassword handles request from the reset page and reset password
+func (cc *CommonController) ResetPassword() {
+
+ resetUUID := cc.GetString("reset_uuid")
+ if resetUUID == "" {
+ cc.CustomAbort(http.StatusBadRequest, "Reset uuid is blank.")
+ }
+
+ queryUser := models.User{ResetUUID: resetUUID}
+ user, err := dao.GetUser(queryUser)
+ if err != nil {
+ log.Errorf("Error occurred in GetUser: %v", err)
+ cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
+ }
+ if user == nil {
+ log.Error("User does not exist")
+ cc.CustomAbort(http.StatusBadRequest, "User does not exist")
+ }
+
+ password := cc.GetString("password")
+
+ if password != "" {
+ user.Password = password
+ err = dao.ResetUserPassword(*user)
+ if err != nil {
+ log.Errorf("Error occurred in ResetUserPassword: %v", err)
+ cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
+ }
+ } else {
+ cc.CustomAbort(http.StatusBadRequest, "password_is_required")
+ }
+}
diff --git a/ngrouter.go b/ngrouter.go
index 1af19231e..45ac58f69 100644
--- a/ngrouter.go
+++ b/ngrouter.go
@@ -14,4 +14,6 @@ func initNgRouters() {
beego.Router("/ng/repository", &ng.RepositoryController{})
beego.Router("/ng/sign_up", &ng.SignUpController{})
beego.Router("/ng/account_setting", &ng.AccountSettingController{})
+ beego.Router("/ng/forgot_password", &ng.ForgotPasswordController{})
+ beego.Router("/ng/reset_password", &ng.ResetPasswordController{})
}
diff --git a/static/ng/resources/css/header.css b/static/ng/resources/css/header.css
index 0a1bfe866..7d2fabcdf 100644
--- a/static/ng/resources/css/header.css
+++ b/static/ng/resources/css/header.css
@@ -55,4 +55,68 @@ nav .container-custom {
.nav-custom .active {
border-bottom: 3px solid #EFEFEF;
+}
+
+.dropdown {
+ float: left;
+}
+
+.dropdown .btn-link:hover,
+.dropdown .btn-link:visited,
+.dropdown .btn-link:link {
+ display:inline-block;
+ text-decoration: none;
+ color: #FFFFFF;
+}
+
+.dropdown-menu {
+ left: 9%;
+}
+
+.dropdown-submenu {
+ position: relative;
+}
+
+.dropdown-submenu>.dropdown-menu {
+ top: 0;
+ left: 100%;
+ margin-top: -6px;
+ margin-left: -1px;
+ -webkit-border-radius: 0 6px 6px 6px;
+ -moz-border-radius: 0 6px 6px;
+ border-radius: 0 6px 6px 6px;
+}
+
+.dropdown-submenu:hover>.dropdown-menu {
+ display: block;
+}
+
+.dropdown-submenu>a:after {
+ display: block;
+ content: " ";
+ float: right;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+ border-width: 5px 0 5px 5px;
+ border-left-color: #ccc;
+ margin-top: 5px;
+ margin-right: -10px;
+}
+
+.dropdown-submenu:hover>a:after {
+ border-left-color: #fff;
+}
+
+.dropdown-submenu.pull-left {
+ float: none;
+}
+
+.dropdown-submenu.pull-left>.dropdown-menu {
+ left: -100%;
+ margin-left: 10px;
+ -webkit-border-radius: 6px 0 6px 6px;
+ -moz-border-radius: 6px 0 6px 6px;
+ border-radius: 6px 0 6px 6px;
}
\ No newline at end of file
diff --git a/static/ng/resources/css/index.css b/static/ng/resources/css/index.css
index dfcc78269..f191674ce 100644
--- a/static/ng/resources/css/index.css
+++ b/static/ng/resources/css/index.css
@@ -27,6 +27,7 @@ body {
.thumbnail {
display: inline-block;
border: none;
+ padding: 2px;
box-shadow: none;
}
diff --git a/static/ng/resources/js/components/log/list-log.directive.js b/static/ng/resources/js/components/log/list-log.directive.js
index f1229669b..c92ddc11c 100644
--- a/static/ng/resources/js/components/log/list-log.directive.js
+++ b/static/ng/resources/js/components/log/list-log.directive.js
@@ -92,6 +92,7 @@
restrict: 'E',
templateUrl: '/static/ng/resources/js/components/log/list-log.directive.html',
replace: true,
+ scope: true,
controller: ListLogController,
controllerAs: 'vm',
bindToController: true
diff --git a/static/ng/resources/js/components/optional-menu/optional-menu.directive.html b/static/ng/resources/js/components/optional-menu/optional-menu.directive.html
index 87343e82f..ef4da29d6 100644
--- a/static/ng/resources/js/components/optional-menu/optional-menu.directive.html
+++ b/static/ng/resources/js/components/optional-menu/optional-menu.directive.html
@@ -1,22 +1,30 @@
-
-
+
\ No newline at end of file
+
+
+
+
\ No newline at end of file
diff --git a/static/ng/resources/js/components/optional-menu/optional-menu.directive.js b/static/ng/resources/js/components/optional-menu/optional-menu.directive.js
index cc0629847..8d4c01c06 100644
--- a/static/ng/resources/js/components/optional-menu/optional-menu.directive.js
+++ b/static/ng/resources/js/components/optional-menu/optional-menu.directive.js
@@ -5,44 +5,33 @@
angular
.module('harbor.optional.menu')
.directive('optionalMenu', optionalMenu);
-
+
OptionalMenuController.$inject = ['$scope'];
-
- function OptionalMenuController($scope, CurrentUserService) {
+
+ function OptionalMenuController($scope, $timeout) {
var vm = this;
- vm.username = 'abcde';
- $scope.$watch('vm.username', function(current) {
- if(current) {
- vm.username = current;
- console.log('vm.username:' + current);
- }
- });
}
function optionalMenu() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/optional-menu/optional-menu.directive.html',
- 'scope': {
- 'isLoggedIn': '=',
- 'username': '='
- },
'link': link,
+ 'scope': true,
'controller': OptionalMenuController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
-
function link(scope, element, attrs, ctrl) {
- scope.$watch('vm.isLoggedIn', function(current) {
- if(current) {
- ctrl.isLoggedIn = current;
- console.log('vm.isLoggedIn:' + current);
+ ctrl.isLoggedIn = false;
+ scope.$on('currentUser', function(e, val) {
+ if(val != null) {
+ ctrl.isLoggedIn = true;
+ ctrl.username = val.username;
}
+ scope.$apply();
});
-
-
}
}
diff --git a/static/ng/resources/js/components/project-member/add-project-member.directive.js b/static/ng/resources/js/components/project-member/add-project-member.directive.js
index c5abca081..9eb8bbec1 100644
--- a/static/ng/resources/js/components/project-member/add-project-member.directive.js
+++ b/static/ng/resources/js/components/project-member/add-project-member.directive.js
@@ -6,9 +6,9 @@
.module('harbor.project.member')
.directive('addProjectMember', addProjectMember);
- AddProjectMemberController.$inject = ['roles', 'AddProjectMemberService'];
+ AddProjectMemberController.$inject = ['$scope', 'roles', 'AddProjectMemberService'];
- function AddProjectMemberController(roles, AddProjectMemberService) {
+ function AddProjectMemberController($scope, roles, AddProjectMemberService) {
var vm = this;
vm.username = "";
vm.roles = roles();
@@ -16,8 +16,8 @@
vm.save = save;
vm.cancel = cancel;
- function save() {
- AddProjectMemberService(2, vm.optRole, vm.username)
+ function save() {
+ AddProjectMemberService(vm.projectId, vm.optRole, vm.username)
.success(addProjectMemberComplete)
.error(addProjectMemberFailed);
vm.username = "";
@@ -46,6 +46,7 @@
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/project-member/add-project-member.directive.html',
'scope': {
+ 'projectId': '@',
'isOpen': '=',
'reload': '&'
},
diff --git a/static/ng/resources/js/components/project-member/list-project-member.directive.html b/static/ng/resources/js/components/project-member/list-project-member.directive.html
index 3fae44756..379950dd2 100644
--- a/static/ng/resources/js/components/project-member/list-project-member.directive.html
+++ b/static/ng/resources/js/components/project-member/list-project-member.directive.html
@@ -11,7 +11,7 @@
Add Member
-
+
diff --git a/static/ng/resources/js/components/project-member/list-project-member.directive.js b/static/ng/resources/js/components/project-member/list-project-member.directive.js
index 4b4dc9643..9c6d97b0e 100644
--- a/static/ng/resources/js/components/project-member/list-project-member.directive.js
+++ b/static/ng/resources/js/components/project-member/list-project-member.directive.js
@@ -6,10 +6,17 @@
.module('harbor.project.member')
.directive('listProjectMember', listProjectMember);
- ListProjectMemberController.$inject = ['$scope', 'CurrentUserService', 'ListProjectMemberService', '$routeParams'];
+ ListProjectMemberController.$inject = ['$scope', 'ListProjectMemberService', '$routeParams'];
- function ListProjectMemberController($scope, CurrentUserService, ListProjectMemberService, $routeParams) {
+ function ListProjectMemberController($scope, ListProjectMemberService, $routeParams) {
var vm = this;
+ vm.currentUser = {};
+
+ $scope.$on('currentUser', function(e, val) {
+ vm.currentUser = val;
+ console.log('In list-project-member received current user:' + vm.currentUser);
+ $scope.$apply();
+ });
vm.isOpen = false;
@@ -19,7 +26,8 @@
vm.projectId = $routeParams.project_id;
vm.username = "";
-
+ vm.currentUser = {};
+
vm.retrieve();
function search(e) {
@@ -36,34 +44,21 @@
vm.isOpen = true;
}
}
-
+
function retrieve() {
- $.when(
- CurrentUserService()
- .success(getCurrentUserSuccess)
- .error(getCurrentUserFailed))
- .then(function(){
- ListProjectMemberService(vm.projectId, {'username': vm.username})
- .then(getProjectMemberComplete)
- .catch(getProjectMemberFailed);
- });
+
+ ListProjectMemberService(vm.projectId, {'username': vm.username})
+ .then(getProjectMemberComplete)
+ .catch(getProjectMemberFailed);
+
}
- function getCurrentUserSuccess(data, status) {
- vm.currentUser = data;
- }
-
- function getCurrentUserFailed(e) {
- console.log('Failed in getCurrentUser:' + e);
- }
-
-
function getProjectMemberComplete(response) {
vm.projectMembers = response.data;
}
function getProjectMemberFailed(response) {
-
+ console.log('Failed get project members:' + response);
}
}
@@ -73,6 +68,7 @@
restrict: 'E',
templateUrl: '/static/ng/resources/js/components/project-member/list-project-member.directive.html',
replace: true,
+ scope: true,
link: link,
controller: ListProjectMemberController,
controllerAs: 'vm',
diff --git a/static/ng/resources/js/components/sign-in/sign-in.directive.js b/static/ng/resources/js/components/sign-in/sign-in.directive.js
index 52044976c..0ea51c99b 100644
--- a/static/ng/resources/js/components/sign-in/sign-in.directive.js
+++ b/static/ng/resources/js/components/sign-in/sign-in.directive.js
@@ -38,6 +38,7 @@
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/sign-in/sign-in.directive.html',
+ 'scope': true,
'controller': SignInController,
'controllerAs': 'vm',
'bindToController': true
diff --git a/static/ng/resources/js/components/validator/password.validator.js b/static/ng/resources/js/components/validator/password.validator.js
index efe2cf5d7..75dd2716e 100644
--- a/static/ng/resources/js/components/validator/password.validator.js
+++ b/static/ng/resources/js/components/validator/password.validator.js
@@ -25,9 +25,6 @@
}
}
-
-
-
}
})();
\ No newline at end of file
diff --git a/static/ng/resources/js/harbor.module.js b/static/ng/resources/js/harbor.module.js
index a6c2efa35..15d485695 100644
--- a/static/ng/resources/js/harbor.module.js
+++ b/static/ng/resources/js/harbor.module.js
@@ -4,10 +4,13 @@
.module('harbor.app', [
'ngRoute',
'ngMessages',
+ 'harbor.session',
'harbor.layout.header',
'harbor.layout.navigation',
'harbor.layout.sign.up',
'harbor.layout.account.setting',
+ 'harbor.layout.forgot.password',
+ 'harbor.layout.reset.password',
'harbor.layout.index',
'harbor.layout.project',
'harbor.layout.repository',
@@ -18,7 +21,6 @@
'harbor.services.user',
'harbor.services.repository',
'harbor.services.project.member',
- 'harbor.session',
'harbor.optional.menu',
'harbor.sign.in',
'harbor.search',
diff --git a/static/ng/resources/js/layout/account-setting/account-setting.controller.js b/static/ng/resources/js/layout/account-setting/account-setting.controller.js
index fb85cbec6..85a9e274d 100644
--- a/static/ng/resources/js/layout/account-setting/account-setting.controller.js
+++ b/static/ng/resources/js/layout/account-setting/account-setting.controller.js
@@ -6,9 +6,9 @@
.module('harbor.layout.account.setting')
.controller('AccountSettingController', AccountSettingController);
- AccountSettingController.$inject = ['CurrentUserService', 'ChangePasswordService', '$window'];
+ AccountSettingController.$inject = ['ChangePasswordService', '$scope', '$window'];
- function AccountSettingController(CurrentUserService, ChangePasswordService, $window) {
+ function AccountSettingController(ChangePasswordService, $scope, $window) {
var vm = this;
vm.isOpen = false;
vm.user = {};
@@ -18,9 +18,9 @@
vm.changePassword= changePassword;
vm.cancel = cancel;
- CurrentUserService()
- .success(getCurrentUserSuccess)
- .error(getCurrentUserFailed);
+ $scope.$on('currentUser', function(e, val) {
+ vm.user = val;
+ });
function toggleChangePassword() {
if(vm.isOpen) {
@@ -30,12 +30,7 @@
}
console.log('vm.isOpen:' + vm.isOpen);
}
-
- function getCurrentUserSuccess(data, status) {
- vm.user = angular.copy(data);
- console.log(data);
- }
-
+
function getCurrentUserFailed(data) {
console.log('Failed get current user:' + data);
}
diff --git a/static/ng/resources/js/layout/forgot-password/forgot-password.controller.js b/static/ng/resources/js/layout/forgot-password/forgot-password.controller.js
new file mode 100644
index 000000000..7944704b9
--- /dev/null
+++ b/static/ng/resources/js/layout/forgot-password/forgot-password.controller.js
@@ -0,0 +1,36 @@
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.forgot.password')
+ .controller('ForgotPasswordController', ForgotPasswordController);
+
+ ForgotPasswordController.$inject = ['SendMailService'];
+
+ function ForgotPasswordController(SendMailService) {
+ var vm = this;
+ vm.hasError = false;
+ vm.errorMessage = '';
+ vm.sendMail = sendMail;
+
+ function sendMail(user) {
+ vm.hasError = false;
+ console.log('Email address:' + user.email);
+ SendMailService(user.email)
+ .success(sendMailSuccess)
+ .error(sendMailFailed);
+ }
+
+ function sendMailSuccess(data, status) {
+ console.log('Successful send mail:' + data);
+ }
+
+ function sendMailFailed(data) {
+ vm.hasError = true;
+ vm.errorMessage = data;
+ console.log('Failed send mail:' + data);
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/ng/resources/js/layout/forgot-password/forgot-password.module.js b/static/ng/resources/js/layout/forgot-password/forgot-password.module.js
new file mode 100644
index 000000000..c23b732ef
--- /dev/null
+++ b/static/ng/resources/js/layout/forgot-password/forgot-password.module.js
@@ -0,0 +1,10 @@
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.forgot.password', [
+ 'harbor.services.user'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/ng/resources/js/layout/header/header.controller.js b/static/ng/resources/js/layout/header/header.controller.js
index 638b75135..c106134f6 100644
--- a/static/ng/resources/js/layout/header/header.controller.js
+++ b/static/ng/resources/js/layout/header/header.controller.js
@@ -6,27 +6,10 @@
.module('harbor.layout.header')
.controller('HeaderController', HeaderController);
- HeaderController.$inject = ['CurrentUserService', '$scope'];
+ HeaderController.$inject = ['$scope'];
- function HeaderController(CurrentUserService, $scope) {
+ function HeaderController($scope) {
var vm = this;
-
- vm.isLoggedIn = true;
- vm.currentUser = {};
-
- CurrentUserService()
- .then(currentUserSucess)
- .catch(currentUserFailed);
-
- function currentUserSucess(response) {
- vm.isLoggedIn = true;
- vm.currentUser.username = response.data.username;
- console.log('vm.currentUser.username:' + vm.currentUser.username);
- }
-
- function currentUserFailed(e) {
-// vm.isLoggedIn = false;
- }
}
})();
\ No newline at end of file
diff --git a/static/ng/resources/js/layout/navigation/navigation-header.directive.js b/static/ng/resources/js/layout/navigation/navigation-header.directive.js
index 9b22dbd81..2bd08c80b 100644
--- a/static/ng/resources/js/layout/navigation/navigation-header.directive.js
+++ b/static/ng/resources/js/layout/navigation/navigation-header.directive.js
@@ -18,7 +18,7 @@
restrict: 'E',
templateUrl: '/static/ng/resources/js/layout/navigation/navigation-header.directive.html',
link: link,
- replace: true,
+ scope: true,
controller: NavigationHeaderController,
controllerAs: 'vm',
bindToController: true
diff --git a/static/ng/resources/js/layout/project/project.controller.js b/static/ng/resources/js/layout/project/project.controller.js
index de844f690..f54fbc3b5 100644
--- a/static/ng/resources/js/layout/project/project.controller.js
+++ b/static/ng/resources/js/layout/project/project.controller.js
@@ -23,16 +23,15 @@
vm.retrieve();
- function retrieve() {
- $.when(
- CurrentUserService()
- .success(getCurrentUserSuccess)
- .error(getCurrentUserFailed))
- .then(function(){
- ListProjectService(vm.projectName, vm.publicity)
- .success(listProjectSuccess)
- .error(listProjectFailed);
+ function retrieve() {
+
+ $scope.$on('currentUser', function(e, val) {
+ vm.currentUser = val;
});
+
+ ListProjectService(vm.projectName, vm.publicity)
+ .success(listProjectSuccess)
+ .error(listProjectFailed);
}
function listProjectSuccess(data, status) {
@@ -42,15 +41,7 @@
function listProjectFailed(e) {
console.log('Failed to list Project:' + e);
}
-
- function getCurrentUserSuccess(data, status) {
- vm.currentUser = data;
- }
-
- function getCurrentUserFailed(e) {
- console.log('Failed in getCurrentUser:' + e);
- }
-
+
$scope.$on('addedSuccess', function(e, val) {
vm.retrieve();
});
diff --git a/static/ng/resources/js/layout/reset-password/reset-password.controller.js b/static/ng/resources/js/layout/reset-password/reset-password.controller.js
new file mode 100644
index 000000000..4665d0d41
--- /dev/null
+++ b/static/ng/resources/js/layout/reset-password/reset-password.controller.js
@@ -0,0 +1,44 @@
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.reset.password')
+ .controller('ResetPasswordController', ResetPasswordController);
+
+ ResetPasswordController.$inject = ['$location', 'ResetPasswordService'];
+
+ function getParameterByName(name, url) {
+ name = name.replace(/[\[\]]/g, "\\$&");
+ var regex = new RegExp("[?&]" + name + "(=([^]*)|&|#|$)"),
+ results = regex.exec(url);
+ if (!results) return null;
+ if (!results[2]) return '';
+ return decodeURIComponent(results[2].replace(/\+/g, " "));
+ }
+
+ function ResetPasswordController($location, ResetPasswordService) {
+ var vm = this;
+ vm.resetUuid = getParameterByName('reset_uuid', $location.absUrl());
+ vm.resetPassword = resetPassword;
+ console.log(vm.resetUuid);
+ function resetPassword(user) {
+ console.log('rececived password:' + user.password + ', reset_uuid:' + vm.resetUuid);
+ ResetPasswordService(vm.resetUuid, user.password)
+ .success(resetPasswordSuccess)
+ .error(resetPasswordFailed);
+ }
+
+ function resetPasswordSuccess(data, status) {
+ console.log('Successful reset password:' + data);
+ }
+
+ function resetPasswordFailed(data) {
+ console.log('Failed reset password:' + data);
+ }
+
+
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/ng/resources/js/layout/reset-password/reset-password.module.js b/static/ng/resources/js/layout/reset-password/reset-password.module.js
new file mode 100644
index 000000000..17229e5db
--- /dev/null
+++ b/static/ng/resources/js/layout/reset-password/reset-password.module.js
@@ -0,0 +1,10 @@
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.layout.reset.password', [
+ 'harbor.services.user'
+ ]);
+
+})();
\ No newline at end of file
diff --git a/static/ng/resources/js/services/user/services.forgot-password.js b/static/ng/resources/js/services/user/services.forgot-password.js
deleted file mode 100644
index 3c5027094..000000000
--- a/static/ng/resources/js/services/user/services.forgot-password.js
+++ /dev/null
@@ -1,21 +0,0 @@
-(function() {
-
- 'use strict';
-
- angular
- .module('harbor.services.user')
- .factory('ForgotPasswordService', ForgotPasswordService);
-
- ForgotPasswordService.$inject = ['$http', '$log'];
-
- function ForgotPasswordService($http, $log) {
-
- return ForgotPassword;
-
- function ForgotPassword(user) {
-
- }
-
- }
-
-})();
\ No newline at end of file
diff --git a/static/ng/resources/js/services/user/services.reset-password.js b/static/ng/resources/js/services/user/services.reset-password.js
new file mode 100644
index 000000000..0aede9911
--- /dev/null
+++ b/static/ng/resources/js/services/user/services.reset-password.js
@@ -0,0 +1,29 @@
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('ResetPasswordService', ResetPasswordService);
+
+ ResetPasswordService.$inject = ['$http', '$log'];
+
+ function ResetPasswordService($http, $log) {
+ return resetPassword;
+ function resetPassword(uuid, password) {
+ return $http({
+ method: 'POST',
+ url: '/reset',
+ headers: {'Content-Type': 'application/x-www-form-urlencoded'},
+ transformRequest: function(obj) {
+ var str = [];
+ for(var p in obj)
+ str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
+ return str.join("&");
+ },
+ data: {'reset_uuid': uuid, 'password': password}
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/static/ng/resources/js/services/user/services.send-mail.js b/static/ng/resources/js/services/user/services.send-mail.js
new file mode 100644
index 000000000..4ebe6cbbd
--- /dev/null
+++ b/static/ng/resources/js/services/user/services.send-mail.js
@@ -0,0 +1,26 @@
+(function() {
+
+ 'use strict';
+
+ angular
+ .module('harbor.services.user')
+ .factory('SendMailService', SendMailService);
+
+ SendMailService.$inject = ['$http', '$log'];
+
+ function SendMailService($http, $log) {
+
+ return SendMail;
+
+ function SendMail(email) {
+ return $http
+ .get('/ng/sendEmail', {
+ 'params': {
+ 'email': email
+ }
+ });
+ }
+
+ }
+
+})();
\ No newline at end of file
diff --git a/static/ng/resources/js/session/session.current-user.js b/static/ng/resources/js/session/session.current-user.js
index 36803a07c..8e19b38f9 100644
--- a/static/ng/resources/js/session/session.current-user.js
+++ b/static/ng/resources/js/session/session.current-user.js
@@ -5,34 +5,30 @@
angular
.module('harbor.session')
.controller('CurrentUserController', CurrentUserController)
- .directive('currentUser', currentUser);
+
+ CurrentUserController.$inject = ['CurrentUserService', '$scope', '$timeout', '$window'];
- CurrentUserController.$inject = ['CurrentUserService', '$log', '$window'];
-
- function CurrentUserController(CurrentUserService, $log, $window) {
+ function CurrentUserController(CurrentUserService, $scope, $timeout, $window) {
+
+ var vm = this;
CurrentUserService()
.then(getCurrentUserComplete)
.catch(getCurrentUserFailed);
- function getCurrentUserComplete(data) {
- $log.info('login success');
+ function getCurrentUserComplete(response) {
+ console.log('Successful logged in.');
+ $timeout(function(){
+ $scope.$broadcast('currentUser', response.data);
+ }, 50);
}
function getCurrentUserFailed(e){
- if(e.status == 401) {
- $window.location = '/ng';
- }
+ console.log('Have not logged in yet.');
+ $timeout(function(){
+ $scope.$broadcast('currentUser', null);
+ });
}
}
- function currentUser() {
- var directive = {
- restrict: 'A',
- controller: CurrentUserController,
- bindToController: true
- }
- return directive;
- }
-
})();
\ No newline at end of file
diff --git a/ui/ngrouter.go b/ui/ngrouter.go
index 108cc5bc3..4e72fe19c 100644
--- a/ui/ngrouter.go
+++ b/ui/ngrouter.go
@@ -13,4 +13,10 @@ func initNgRouters() {
beego.Router("/ng/repository", &ng.RepositoryController{})
beego.Router("/ng/sign_up", &ng.SignUpController{})
beego.Router("/ng/account_setting", &ng.AccountSettingController{})
+ beego.Router("/ng/forgot_password", &ng.ForgotPasswordController{})
+ beego.Router("/ng/reset_password", &ng.ResetPasswordController{})
+
+ beego.Router("/ng/reset", &ng.CommonController{}, "post:ResetPassword")
+ beego.Router("/ng/sendEmail", &ng.CommonController{}, "get:SendEmail")
+
}
diff --git a/views/ng/dashboard.htm b/views/ng/dashboard.htm
index 052174efb..71fdcecf0 100644
--- a/views/ng/dashboard.htm
+++ b/views/ng/dashboard.htm
@@ -1,4 +1,4 @@
-
+
diff --git a/views/ng/forgot-password.htm b/views/ng/forgot-password.htm
new file mode 100644
index 000000000..a7657093a
--- /dev/null
+++ b/views/ng/forgot-password.htm
@@ -0,0 +1,38 @@
+
\ No newline at end of file
diff --git a/views/ng/layout.htm b/views/ng/layout.htm
index 8815b19f0..5bcd3eeb1 100644
--- a/views/ng/layout.htm
+++ b/views/ng/layout.htm
@@ -4,7 +4,7 @@
{{.Title}}
-
+
{{.HeaderContent}}
{{.LayoutContent}}
{{.FooterContent}}
diff --git a/views/ng/reset-password-mail.tpl b/views/ng/reset-password-mail.tpl
new file mode 100644
index 000000000..213124846
--- /dev/null
+++ b/views/ng/reset-password-mail.tpl
@@ -0,0 +1,21 @@
+
+
+
+
+
{{.Hint}}:
+
{{.URL}}/ng/reset_password?reset_uuid={{.UUID}}
+
+
\ No newline at end of file
diff --git a/views/ng/reset-password.htm b/views/ng/reset-password.htm
new file mode 100644
index 000000000..682bd608f
--- /dev/null
+++ b/views/ng/reset-password.htm
@@ -0,0 +1,48 @@
+
+
\ No newline at end of file
diff --git a/views/ng/sections/header-content.htm b/views/ng/sections/header-content.htm
index 91508bf80..9b7e36ac6 100644
--- a/views/ng/sections/header-content.htm
+++ b/views/ng/sections/header-content.htm
@@ -1,4 +1,4 @@
-
+
-
-
+
+
diff --git a/views/ng/sections/header-include.htm b/views/ng/sections/header-include.htm
index db4e988d1..ca4ede474 100644
--- a/views/ng/sections/header-include.htm
+++ b/views/ng/sections/header-include.htm
@@ -54,6 +54,12 @@
+
+
+
+
+
+
@@ -90,6 +96,9 @@
+
+
+