Merge branch 'newer-ui' into new-version-of-ui

This commit is contained in:
kunw 2016-05-17 11:55:34 +08:00
commit b7ca941ca0
202 changed files with 53747 additions and 28 deletions

View File

@ -1,15 +1,35 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIICWDCCAcGgAwIBAgIJAN1nLuloDeHNMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV MIIGBzCCA++gAwIBAgIJAKB8CNqCxhr7MA0GCSqGSIb3DQEBCwUAMIGZMQswCQYD
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX VQQGEwJDTjEOMAwGA1UECAwFU3RhdGUxCzAJBgNVBAcMAkNOMRUwEwYDVQQKDAxv
aWRnaXRzIFB0eSBMdGQwHhcNMTYwMTI3MDQyMDM1WhcNNDMwNjE0MDQyMDM1WjBF cmdhbml6YXRpb24xHDAaBgNVBAsME29yZ2FuaXphdGlvbmFsIHVuaXQxFDASBgNV
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 BAMMC2V4YW1wbGUuY29tMSIwIAYJKoZIhvcNAQkBFhNleGFtcGxlQGV4YW1wbGUu
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB Y29tMB4XDTE2MDUxNjAyNDY1NVoXDTI2MDUxNDAyNDY1NVowgZkxCzAJBgNVBAYT
gQClak/4HO7EeLU0w/BhtVENPLOqU0AP2QjVUdg1qhNiDWVrbWx9KYHqz5Kn0n2+ AkNOMQ4wDAYDVQQIDAVTdGF0ZTELMAkGA1UEBwwCQ04xFTATBgNVBAoMDG9yZ2Fu
fxdZo3o7ZY5/2+hhgkKh1z6Kge9XGgune6z4fx2J/X2Se8WsGeQUTiND8ngSnsCA aXphdGlvbjEcMBoGA1UECwwTb3JnYW5pemF0aW9uYWwgdW5pdDEUMBIGA1UEAwwL
NtYFwW50SbUZPtyf5XjAfKRofZem51OxbxzN3217L/ubKwIDAQABo1AwTjAdBgNV ZXhhbXBsZS5jb20xIjAgBgkqhkiG9w0BCQEWE2V4YW1wbGVAZXhhbXBsZS5jb20w
HQ4EFgQU5EG2VrB3I6G/TudUpz+kBgQXSvYwHwYDVR0jBBgwFoAU5EG2VrB3I6G/ ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2ky/K/XneJKbCbpOsWlQ7
TudUpz+kBgQXSvYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAx+2eo OwgYEQNsa044RkwSbTwPwgLafUZ3r9c5nkXE8APqAikTQQBwyiNjk7QeXgIOjJXd
oOm0YNy9KQ81+7GQkKVWoPQXjAGGgZuZj8WCFepYqUSJ4q5qbuVCY8WbGcHVk2Rx 7+IpwGoU6Bi2miA21qfvJPknyDAqw9tT/ycGQrvkY6rnqd++ri30ZUByUgO0du6+
Jg1XDCmMjBgYP6S0ikezBRqSmNA3G6oFiydTKBfPs6RNalsB0C78Xk5l5+PIyd2R aWHo7af5/G1HQz0tu6i1tIF1dhSHNeqJKwxyUG8vIiT/PfbtU/mXSdQ07M+4ojBC
jFKOKoMpkjwfeJv2j64WNGoBgqj7XRBoJ11a4g== O7FgoOS+rWgbL3yhWUTrCXSV2HZlhksYBhtWGoFVRPVSf89iqL02h9rZEjmfVY6R
QlCnzu9v49Q8WFU528f+gDNXr9v13PKEDmloMzTqWPaCyD2FBbEKBsWHXHf1zqlI
jyGZV7rHZ3i0C1LI6bdDDP7M7aVs8O+RjxK+HmfFRg5us2t6g7zAevwwLpMZRAud
S39F91Up7l9g8WXpViok/8vcsOdePvvWcWro8qJhuEHAnDdMzj2Cko1L85/vRM/a
budWXK7Ix0TlPWPfHJc2SLFeqqcm5Iypf/cGabQ6f0oRt6bCfspFgX9upznT5FwZ
R0o1w6Q3q+4xVl6LgZvEAudWppyz79RACJA/jbXZQ7uJkXAxoI0nev9vgY6XJqUj
XIQDih2hmi/uTnNU7Me7w7pCYKPdHlNU652kaJSH6W6ZFGk2rEOCOeAuWO9pZTq2
3IhuOcDAKOcmimlkzaWRGQIDAQABo1AwTjAdBgNVHQ4EFgQUPJF++WMsv1OJvf7F
oCew37JTnfQwHwYDVR0jBBgwFoAUPJF++WMsv1OJvf7FoCew37JTnfQwDAYDVR0T
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAb5LvqukMxWd5Zajbh3orfYsXmhWn
UWiwG176+bd3b5xMlG9iLd4vQ11lTZoIhFOfprRQzbizQ8BzR2JBQckpLcy+5hyA
D3M9vLL37OwA0wT6kxFnd6LtlFaH5gG++huw2ts2PDXFz0jqw+0YE/R8ov2+YdaZ
aPSEMunmAuEY1TbYWzz4u6PxycxhQzDQ34ZmJZ34Elvw1NYMfPMGTKp34PsxIcgT
ao5jqb9RMU6JAumfXrOvXRjjl573vX2hgMZzEU6OF2/+uyg95chn6nO1GUQrT2+F
/1xIqfHfFCm8+jujSDgqfBtGI+2C7No+Dq8LEyEINZe6wSQ81+ryt5jy5SZmAsnj
V4OsSIwlpR5fLUwrFStVoUWHEKl1DflkYki/cAC1TL0Om+ldJ219kcOnaXDNaq66
3I75BvRY7/88MYLl4Fgt7sn05Mn3uNPrCrci8d0R1tlXIcwMdCowIHeZdWHX43f7
NsVk/7VSOxJ343csgaQc+3WxEFK0tBxGO6GP+Xj0XmdVGLhalVBsEhPjnmx+Yyrn
oMsTA1Yrs88C8ItQn7zuO/30eKNGTnby0gptHiS6sa/c3O083Mpi8y33GPVZDvBl
l9PfSZT8LG7SvpjsdgdNZlyFvTY4vsB+Vd5Howh7gXYPVXdCs4k7HMyo7zvzliZS
ekCw9NGLoNqQqnA=
-----END CERTIFICATE----- -----END CERTIFICATE-----

View File

@ -1,15 +1,51 @@
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----

View File

@ -46,6 +46,8 @@ services:
volumes: volumes:
- ./config/ui/app.conf:/etc/ui/app.conf - ./config/ui/app.conf:/etc/ui/app.conf
- ./config/ui/private_key.pem:/etc/ui/private_key.pem - ./config/ui/private_key.pem:/etc/ui/private_key.pem
- ../static:/go/bin/static
- ../views:/go/bin/views
depends_on: depends_on:
- log - log
logging: logging:

View File

@ -0,0 +1,9 @@
package ng
type AccountSettingController struct {
BaseController
}
func (asc *AccountSettingController) Get() {
asc.Forward("Account Settings", "account-settings.htm")
}

View File

@ -0,0 +1,9 @@
package ng
type AdminOptionController struct {
BaseController
}
func (aoc *AdminOptionController) Get() {
aoc.Forward("Admin Options", "admin-options.htm")
}

157
controllers/ng/base.go Normal file
View File

@ -0,0 +1,157 @@
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"
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)
bc.Data["Title"] = title
bc.LayoutSections = make(map[string]string)
bc.LayoutSections["HeaderInclude"] = filepath.Join(prefixNg, viewPath, "header-include.htm")
bc.LayoutSections["FooterInclude"] = filepath.Join(prefixNg, viewPath, "footer-include.htm")
bc.LayoutSections["HeaderContent"] = filepath.Join(prefixNg, viewPath, "header-content.htm")
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())
}
}
}

View File

@ -0,0 +1,9 @@
package ng
type DashboardController struct {
BaseController
}
func (dc *DashboardController) Get() {
dc.Forward("Dashboard", "dashboard.htm")
}

9
controllers/ng/index.go Normal file
View File

@ -0,0 +1,9 @@
package ng
type IndexController struct {
BaseController
}
func (ic *IndexController) Get() {
ic.Forward("Index", "index.htm")
}

179
controllers/ng/password.go Normal file
View File

@ -0,0 +1,179 @@
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
}
func (cc *CommonController) LogOut() {
cc.DestroySession()
}
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")
}
}

View File

@ -0,0 +1,9 @@
package ng
type ProjectController struct {
BaseController
}
func (pc *ProjectController) Get() {
pc.Forward("My Projects", "project.htm")
}

View File

@ -0,0 +1,12 @@
package ng
import "os"
type RepositoryController struct {
BaseController
}
func (rc *RepositoryController) Get() {
rc.Data["HarborRegUrl"] = os.Getenv("HARBOR_REG_URL")
rc.Forward("Repository", "repository.htm")
}

9
controllers/ng/signup.go Normal file
View File

@ -0,0 +1,9 @@
package ng
type SignUpController struct {
BaseController
}
func (suc *SignUpController) Get() {
suc.Forward("Sign Up", "sign-up.htm")
}

BIN
static/ng/resources/.DS_Store vendored Normal file

Binary file not shown.

BIN
static/ng/resources/css/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,18 @@
.up-section .up-table-pane {
overflow-y: scroll;
height: 180px;
margin-top: -10px;
}
.up-section .dl-horizontal dt{
line-height: 25px;
}
.up-section .dl-horizontal dt {
text-align: left;
}
.single {
margin-right: 0;
}

View File

@ -0,0 +1,19 @@
.footer{
position: absolute;
bottom: 0;
left: 0;
padding: 0;
margin: 0;
width: 100%;
clear: both;
background-color: #A8A8A8;
height: 35px;
z-index: 2;
}
.footer p {
margin-top: 8px;
color: #FFFFFF;
margin-left: auto;
margin-right: auto;
width: 385px;
}

View File

@ -0,0 +1,118 @@
.navbar-custom {
background-image: none;
background-color: #057ac9;
height: 66px;
margin-bottom: 0;
}
nav .container {
margin-top: 15px;
}
nav .container-custom {
min-width: 1024px;
}
.navbar-custom .navbar-nav > li > a,
.navbar-custom .navbar-nav > li > a:hover,
.navbar-custom .navbar-nav > li > a:focus,
.navbar-custom .navbar-nav > li > a:active {
color: white; /*Change active text color here*/
}
.navbar-brand > img {
height: 60px;
width: 100px;
margin-top: -30px;
filter: brightness(0) invert(1);
-webkit-filter: brightness(0) invert(1);
}
.navbar-form {
margin-top: 0;
padding-right: 0;
}
.search-icon {
background: url("/static/ng/resources/img/magnitude-glass.jpg") no-repeat 97% 6px;
background-size: 1.5em;
background-color: #FFFFFF;
}
.nav-custom li {
float: left;
padding: 10px 0 0 0;
margin-right: 12px;
list-style: none;
display: inline-block;
}
.nav-custom li a {
font-size: 14px;
color: #FFFFFF;
text-decoration: none;
}
.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-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;
}

View File

@ -0,0 +1,87 @@
body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
position: fixed;
}
.container-fluid-custom {
background-color: #EFEFEF;
height: 100%;
}
.up-section {
position: relative;
padding: 15px 15px 15px;
margin: 20px 0 20px 0;
background-color: #FFFFFF;
height: 277px;
}
.up-section h4 label {
margin-left: 5px;
}
.thumbnail {
margin-top: 10px;
display: inline-block;
border: none;
padding: 2px;
box-shadow: none;
width: 30%;
}
.down-section {
position: relative;
padding: 15px 15px 15px;
margin: 0 0 20px 0;
background-color: #FFFFFF;
height: 350px;
}
.down-section ul {
padding: 0;
margin-left: 30px;
margin-right: 20px;
}
.long-line {
overflow: hidden;
width: 100%;
white-space: nowrap;
text-overflow: ellipsis;
}
.down-table-pane {
overflow-y: auto;
height: auto;
padding-left: 10px;
padding-right: 10px;
}
.page-header {
padding-bottom: 10px;
margin: 10px;
border-bottom: none;
}
.underlined {
border-bottom: 2px solid #EFEFEF;
}
.step-content {
text-align: center;
}
.display-inline-block {
display: inline-block;
}
.title-color {
color: #057ac9;
}
.page-content {
margin: 0 20px 0 20px;
}

View File

@ -0,0 +1,50 @@
.container-custom {
position: relative;
height: 100%;
}
.extend-height {
height: 100%;
}
.section {
position: relative;
padding: 15px 15px 0;
margin: 20px 0 20px 0;
background-color: #FFFFFF;
height: 100%;
width: 100%;
}
.search-pane {
margin: 0 10px 0 10px;
height: 3em;
}
.table>tbody>tr>td, .table>tbody>tr>th {
vertical-align: middle;
}
.gutter {
margin: 0 1em 0 1em;
}
.project-pane {
margin: 0 10px 0 10px;
}
.pane {
min-height: 500px;
overflow-y: auto;
}
.sub-pane {
margin: 0 10px 0 10px;
min-height: 500px;
overflow-y: auto;
}
.well-custom {
margin-top: 10px;
}

View File

@ -0,0 +1,110 @@
.panel {
margin-bottom: 0;
}
.pane-container {
overflow-y: auto;
}
.pane-container .list-group-item {
border: none;
}
.item-line {
margin: 0 0 10px 0;
}
.switch-pane {
padding: 10px 15px 0 15px;
margin: 0 10px 10px 10px;
height: 3em;
background-color: rgb(231,244,254);
}
.switch-pane-projects {
float: left;
}
.switch-pane-tabs {
width: 25%;
float: right;
list-style-type: none;
}
.switch-pane-tabs a,.switch-pane-tabs span {
display: inline-block;
text-decoration: none;
float: left;
}
.switch-pane-tabs li .active {
border-bottom: 2px solid rgb(0, 84, 190);
}
.switch-pane-drop-down {
display: block;
position: absolute;
margin: -10px 0 0 10px;
z-index: 1000;
}
.search-projects {
padding: 15px 10px 10px;
}
.project-list {
height: 440px;
}
.project-selected {
margin-left: -1.2em;
}
.list-group {
box-shadow: none;
}
.list-group-item {
text-align: left;
margin-left: 20%;
}
.panel-group {
margin-top: 10px;
}
.repository-table {
width: 100%;
}
.repository-table th, .repository-table td {
padding: 10px;
text-align: left;
}
.each-tab-pane {
padding: 0 10px;
}
.inline-block {
display: inline-block;
}
.datetime-picker-title {
float: left;
line-height: 2.5em;
margin: 0 0.5em 0 0;
}
.input-group .form-control {
z-index: 1;
}
.well {
padding: 12px;
}
.popover{
max-width: 500px;
}

View File

@ -0,0 +1,48 @@
.main-title {
margin-top: 20px;
margin-left: 180px;
}
.main-content {
width: 60%;
margin-top: 40px;
}
.form-horizontal .control-label {
text-align: left;
}
.row {
margin-left: 0;
margin-right: 0;
}
.form-horizontal .form-group {
margin-left: -15px;
margin-right: -15px;
}
.small-size-fonts {
font-size: 10pt;
}
.asterisk {
display: inline-block;
margin-left: -25px;
padding-top: 8px;
color: red;
font-size: 14pt;
}
.css-form input.ng-invalid.ng-dirty {
border-color: red;
}
.error-message {
color: red;
width: 100%;
margin-right: auto;
margin-left: auto;
}

BIN
static/ng/resources/img/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,21 @@
<div class="switch-pane-drop-down" ng-show="vm.isOpen">
<div class="row">
<div class="col-xs-12 col-md-12">
<div class="panel panel-default">
<div class="form-inline search-projects">
<div class="input-group">
<input type="text" class="form-control search-icon" placeholder="" ng-model="vm.filterInput" size="30">
</div>
</div>
<h5 class="page-header">//vm.projectType | tr//: <span class="badge">//vm.resultCount//</span></h5>
<div class="project-list pane-container">
<ul class="list-group">
<li class="list-group-item" ng-repeat="item in vm.projects | name: vm.filterInput: 'Name'" ng-click="vm.selectItem(item)">
<span ng-show="item.ProjectId == vm.selectedId" class="glyphicon glyphicon-ok project-selected"></span> <a href="#/repositories?project_id=//item.ProjectId//">//item.Name//</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,128 @@
(function() {
'use strict';
angular
.module('harbor.details')
.directive('retrieveProjects', retrieveProjects);
RetrieveProjectsController.$inject = ['$scope', 'nameFilter', '$filter', 'CurrentProjectMemberService', 'ListProjectService', '$routeParams', '$route', '$location'];
function RetrieveProjectsController($scope, nameFilter, $filter, CurrentProjectMemberService, ListProjectService, $routeParams, $route, $location) {
var vm = this;
vm.projectName = '';
if($route.current.params.is_public) {
vm.isPublic = $route.current.params.is_public === 'true' ? 1 : 0;
vm.publicity = (vm.isPublic === 1) ? true : false;
}
vm.retrieve = retrieve;
vm.retrieve();
vm.checkProjectMember = checkProjectMember;
$scope.$watch('vm.selectedProject', function(current, origin) {
if(current) {
vm.selectedId = current.ProjectId;
}
});
vm.filterInput = "";
vm.selectItem = selectItem;
$scope.$watch('vm.publicity', function(current, origin) {
vm.publicity = current ? 1 : 0;
vm.isPublic = vm.publicity ? 1 : 0;
vm.projectType = (vm.isPublic === 1) ? 'public_projects' : 'my_projects';
vm.retrieve();
});
function retrieve() {
ListProjectService(vm.projectName, vm.isPublic)
.success(getProjectSuccess)
.error(getProjectFailed);
}
function getProjectSuccess(data, status) {
vm.projects = data;
if(!angular.isDefined(vm.projects)) {
vm.isPublic = 1;
vm.publicity = 1;
vm.projectType = 'public_projects';
console.log('vm.projects is undefined, load public projects.');
}
vm.selectedProject = vm.projects[0];
if($routeParams.project_id){
angular.forEach(vm.projects, function(value, index) {
if(value['ProjectId'] == $routeParams.project_id) {
vm.selectedProject = value;
}
});
}
$location.search('project_id', vm.selectedProject.ProjectId);
vm.checkProjectMember(vm.selectedProject.ProjectId);
vm.resultCount = vm.projects.length;
$scope.$watch('vm.filterInput', function(current, origin) {
vm.resultCount = $filter('name')(vm.projects, vm.filterInput, 'Name').length;
});
}
function getProjectFailed(response) {
console.log('Failed to list projects:' + response);
}
function checkProjectMember(projectId) {
CurrentProjectMemberService(projectId)
.success(getCurrentProjectMemberSuccess)
.error(getCurrentProjectMemberFailed);
}
function getCurrentProjectMemberSuccess(data, status) {
console.log('Successful get current project member:' + status);
vm.isProjectMember = true;
}
function getCurrentProjectMemberFailed(data, status) {
console.log('Failed get current project member:' + status);
vm.isProjectMember = false;
}
function selectItem(item) {
vm.selectedId = item.ProjectId;
vm.selectedProject = item;
vm.checkProjectMember(vm.selectedProject.ProjectId);
vm.isOpen = false;
$location.search('project_id', vm.selectedProject.ProjectId);
}
}
function retrieveProjects() {
var directive = {
restrict: 'E',
templateUrl: '/static/ng/resources/js/components/details/retrieve-projects.directive.html',
scope: {
'isOpen': '=',
'selectedProject': '=',
'publicity': '=',
'isProjectMember': '='
},
replace: true,
controller: RetrieveProjectsController,
bindToController: true,
controllerAs: 'vm'
}
return directive;
}
})();

View File

@ -0,0 +1,5 @@
<div class="switch-pane-projects" ng-switch="vm.isOpen">
<a href="javascript:void(0);" ng-click="vm.switchPane()">//vm.projectName//</a>
<span ng-switch-default class="glyphicon glyphicon-triangle-right" style="font-size: 12px;"></span>
<span ng-switch-when="true" class="glyphicon glyphicon-triangle-bottom" style="font-size: 12px;"></span>
</div>

View File

@ -0,0 +1,60 @@
(function() {
'use strict';
angular
.module('harbor.details')
.directive('switchPaneProjects', switchPaneProjects);
SwitchPaneProjectsController.$inject = ['$scope'];
function SwitchPaneProjectsController($scope) {
var vm = this;
$scope.$on('isOpen', function(e, val){
vm.isOpen = val;
});
$scope.$watch('vm.selectedProject', function(current, origin) {
if(current){
vm.projectName = current.Name;
vm.selectedProject = current;
}
});
vm.switchPane = switchPane;
function switchPane() {
if(vm.isOpen) {
vm.isOpen = false;
}else{
vm.isOpen = true;
}
}
}
function switchPaneProjects() {
var directive = {
restrict: 'E',
templateUrl: '/static/ng/resources/js/components/details/switch-pane-projects.directive.html',
replace: true,
scope: {
'selectedProject': '=',
'isOpen': '='
},
link: link,
controller: SwitchPaneProjectsController,
controllerAs: 'vm',
bindToController: true
}
return directive;
function link(scope, element, attrs, ctrl) {
}
}
})();

View File

@ -0,0 +1,53 @@
<div class="well panel-group">
<div class="row">
<div class="col-xs-10 col-md-10">
<form class="form">
<div class="form-group">
<label for="">// 'operation' | tr //:</label>
</div>
<div class="form-group">
<input type="checkbox" ng-model="vm.opAll" ng-checked="vm.opCreate && vm.opPull && vm.opPush && vm.opDelete && vm.opOthers" ng-click="vm.checkOperation({checked: 'all'})">&nbsp;// 'all' | tr //&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opCreate" ng-model="vm.opCreate" ng-click="vm.checkOperation({checked: 'create'})">&nbsp;Create&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opPull" ng-model="vm.opPull" ng-click="vm.checkOperation({checked: 'pull'})">&nbsp;Pull&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opPush" ng-model="vm.opPush" ng-click="vm.checkOperation({checked: 'push'})">&nbsp;Push&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opDelete" ng-model="vm.opDelete" ng-click="vm.checkOperation({checked: 'delete'})">&nbsp;Delete&nbsp;&nbsp;
<input type="checkbox" ng-checked="vm.opOthers" ng-model="vm.opOthers" ng-click="vm.checkOperation({checked: 'others'})">&nbsp;// 'others' | tr //&nbsp;&nbsp;
<input type="text" ng-model="vm.others" size="10">
</div>
<div class="form-group">
<label for="">// 'duration' | tr //:</label>
</div>
<div class="form-group inline-block col-md-5">
<span class="datetime-picker-title">// 'from' | tr //:</span>
<!--date-picker picked-date="vm.fromDate"></date-picker-->
<div class="input-group datetimepicker">
<input id="fromDatePicker" class="form-control" type="text" readonly="readonly" ng-model="vm.fromDate" ng-change="vm.pickUp({key:'fromDate', value: vm.fromDate})">
<span class="input-group-addon">
<a href="javascript:void(0);"><span class="glyphicon glyphicon-calendar"></span></a>
</span>
</div>
</div>
<div class="form-group inline-block col-md-5">
<span class="datetime-picker-title">// 'to' | tr //:</span>
<div class="input-group datetimepicker">
<input id="toDatePicker" class="form-control" type="text" readonly="readonly" ng-model="vm.toDate" ng-change="vm.pickUp({key:'toDate', value: vm.toDate})">
<span class="input-group-addon">
<a href="javascript:void(0);"><span class="glyphicon glyphicon-calendar"></span></a>
</span>
</div>
</div>
</form>
</div>
<div class="col-xs-2 col-md-2">
<form>
<div class="form-group" style="margin-top: 40%;">
<button type="button" class="btn btn-primary" ng-click="vm.search({op: vm.op})">// 'search' | tr //</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,107 @@
(function() {
'use strict';
angular
.module('harbor.log')
.directive('advancedSearch', advancedSearch);
AdvancedSearchController.$inject = ['$scope', 'ListLogService'];
function AdvancedSearchController($scope, ListLogService) {
var vm = this;
vm.checkOperation = checkOperation;
vm.opAll = true;
vm.opCreate = true;
vm.opPull = true;
vm.opPush = true;
vm.opDelete = true;
vm.opOthers = true;
vm.others = "";
vm.op = [];
vm.op.push('all');
function checkOperation(e) {
if(e.checked == 'all') {
vm.opCreate = vm.opAll;
vm.opPull = vm.opAll;
vm.opPush = vm.opAll;
vm.opDelete = vm.opAll;
vm.opOthers = vm.opAll;
}else {
vm.opAll = false;
}
vm.op = [];
if(vm.opCreate) {
vm.op.push('create');
}
if(vm.opPull) {
vm.op.push('pull');
}
if(vm.opPush) {
vm.op.push('push');
}
if(vm.opDelete) {
vm.op.push('delete');
}
if(vm.opOthers && vm.others != "") {
vm.op.push(vm.others);
}
}
vm.pickUp = pickUp;
function pickUp(e) {
switch(e.key){
case 'fromDate':
vm.fromDate = e.value;
break;
case 'toDate':
vm.toDate = e.value;
break;
}
$scope.$apply();
}
}
function advancedSearch() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/log/advanced-search.directive.html',
'scope': {
'isOpen': '=',
'op': '=',
'others': '=',
'fromDate': '=',
'toDate': '=',
'search': '&'
},
'link': link,
'controller': AdvancedSearchController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
element.find('.datetimepicker').datetimepicker({
locale: 'en-US',
ignoreReadonly: true,
format: 'L',
showClear: true
});
element.find('#fromDatePicker').on('blur', function(){
ctrl.pickUp({'key': 'fromDate', 'value': $(this).val()});
});
element.find('#toDatePicker').on('blur', function(){
ctrl.pickUp({'key': 'toDate', 'value': $(this).val()});
});
}
}
})();

View File

@ -0,0 +1,30 @@
<div class="tab-pane" id="logs">
<div class="col-xs-12 col-md-12 each-tab-pane">
<div class="form-inline">
<div class="input-group">
<input type="text" class="form-control" placeholder="" ng-model="vm.username" size="30">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" ng-click="vm.search({op: vm.op, username: vm.username})"><span class="glyphicon glyphicon-search"></span></button>
</span>
<span class="input-group-btn">
<button class="btn btn-link" type="button" ng-click="vm.showAdvancedSearch()">// 'advanced_search' | tr //</button>
</span>
</div>
</div>
<div class="pane">
<advanced-search ng-show="vm.isOpen" is-open="vm.isOpen" op="vm.op" others="vm.others" search='vm.search({op: vm.op, username: vm.username})' from-date="vm.fromDate" to-date="vm.toDate"></advanced-search>
<div class="sub-pane">
<table class="table table-pane">
<thead>
<th>// 'username' | tr //</th><th>// 'repository_name' | tr //</th><th>// 'operation' | tr //</th><th>// 'timestamp' | tr //</th>
</thead>
<tbody>
<tr ng-repeat="log in vm.logs">
<td>//log.Username//</td><td>//log.RepoName//</td><td>//log.Operation//</td><td>//log.OpTime | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,104 @@
(function() {
'use strict';
angular
.module('harbor.log')
.directive('listLog', listLog);
ListLogController.$inject = ['$scope','ListLogService', '$routeParams'];
function ListLogController($scope, ListLogService, $routeParams) {
var vm = this;
vm.isOpen = false;
vm.beginTimestamp = 0;
vm.endTimestamp = 0;
vm.keywords = "";
vm.username = "";
vm.op = [];
vm.search = search;
vm.showAdvancedSearch = showAdvancedSearch;
vm.projectId = $routeParams.project_id;
vm.queryParams = {
'beginTimestamp' : vm.beginTimestamp,
'endTimestamp' : vm.endTimestamp,
'keywords' : vm.keywords,
'projectId': vm.projectId,
'username' : vm.username
};
retrieve(vm.queryParams);
function search(e) {
if(e.op[0] == 'all') {
vm.queryParams.keywords = '';
}else {
vm.queryParams.keywords = e.op.join('/') ;
}
vm.queryParams.username = e.username;
vm.queryParams.beginTimestamp = toUTCSeconds(vm.fromDate, 0, 0, 0);
vm.queryParams.endTimestamp = toUTCSeconds(vm.toDate, 23, 59, 59);
retrieve(vm.queryParams);
}
function showAdvancedSearch() {
if(vm.isOpen){
vm.isOpen = false;
}else{
vm.isOpen = true;
}
}
function retrieve(queryParams) {
ListLogService(queryParams)
.then(listLogComplete)
.catch(listLogFailed);
}
function listLogComplete(response) {
vm.logs = response.data;
}
function listLogFailed(e){
console.log('listLogFailed:' + e);
}
function toUTCSeconds(date, hour, min, sec) {
if(date == "") {
return 0;
}
var t = new Date(date);
t.setHours(hour);
t.setMinutes(min);
t.setSeconds(sec);
var utcTime = new Date(t.getUTCFullYear(),
t.getUTCMonth(),
t.getUTCDate(),
t.getUTCHours(),
t.getUTCMinutes(),
t.getUTCSeconds());
return utcTime.getTime() / 1000;
}
}
function listLog() {
var directive = {
restrict: 'E',
templateUrl: '/static/ng/resources/js/components/log/list-log.directive.html',
replace: true,
scope: true,
controller: ListLogController,
controllerAs: 'vm',
bindToController: true
}
return directive;
}
})();

View File

@ -0,0 +1,9 @@
(function() {
'use strict';
angular
.module('harbor.log');
})();

View File

@ -0,0 +1,10 @@
(function() {
'use strict';
angular
.module('harbor.log', [
'harbor.services.log'
]);
})();

View File

@ -0,0 +1,16 @@
<div class="modal fade" id="myModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">//vm.title//</h4>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">// 'close' | tr //</button>
<button type="button" class="btn btn-primary" id="btnOk">// 'ok' | tr //</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View File

@ -0,0 +1,58 @@
(function() {
'use strict';
angular
.module('harbor.modal.dialog')
.directive('modalDialog', modalDialog);
ModalDialogController.$inject = ['$scope'];
function ModalDialogController($scope) {
var vm = this;
}
function modalDialog() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/modal-dialog/modal-dialog.directive.html',
'link': link,
'scope': {
'contentType': '@',
'title': '@',
'message': '@',
'action': '&'
},
'controller': ModalDialogController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
console.log('Received contentType in modal:' + ctrl.contentType);
scope.$watch('vm.message', function(current) {
if(current) {
switch(ctrl.contentType) {
case 'text/html':
element.find('.modal-body').html(current); break;
case 'text/plain':
element.find('.modal-body').text(current); break;
default:
element.find('.modal-body').text(current); break;
}
}
});
element.find('#btnOk').on('click', clickHandler);
function clickHandler(e) {
ctrl.action();
element.find('#myModal').modal('hide');
}
}
}
})();

View File

@ -0,0 +1,8 @@
(function() {
'use strict';
angular
.module('harbor.modal.dialog', []);
})();

View File

@ -0,0 +1,27 @@
<div ng-if="!vm.isLoggedIn" class="dropdown">
<a id="dLabel" role="button" data-toggle="dropdown" class="btn btn-link" data-target="#" href="">
<span class="glyphicon glyphicon-globe"></span> //vm.languageName//
</a>
<ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('en-US')">English</a></li>
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('zh-CN')">中文</a></li>
</ul>
</div>
<div ng-if="vm.isLoggedIn" class="dropdown">
<a id="dLabel" role="button" data-toggle="dropdown" class="btn btn-link" data-target="#" href="">
<span class="glyphicon glyphicon-user"></span> //vm.username//
</a>
<ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
<li><a href="/ng/account_setting"><span class="glyphicon glyphicon-pencil"></span> // 'account_setting' | tr //</a></li>
<li class="dropdown-submenu">
<a tabindex="-1" href="#"><span class="glyphicon glyphicon-globe"></span> //vm.languageName//</a>
<ul class="dropdown-menu">
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('en-US')">English</a></li>
<li><a href="javascript:void(0);" ng-click="vm.setLanguage('zh-CN')">中文</a></li>
</ul>
</li>
<li class="divider"></li>
<li><a href="javascript:void(0)" ng-click="vm.logOut()"><span class="glyphicon glyphicon-log-out"></span> // 'log_out' | tr //</a></li>
</ul>
</div>

View File

@ -0,0 +1,60 @@
(function() {
'use strict';
angular
.module('harbor.optional.menu')
.directive('optionalMenu', optionalMenu);
OptionalMenuController.$inject = ['$scope', '$window', '$cookies', 'I18nService', 'LogOutService'];
function OptionalMenuController($scope, $window, $cookies, I18nService, LogOutService) {
var vm = this;
vm.currentLanguage = I18nService().getCurrentLanguage();
vm.setLanguage = setLanguage;
vm.languageName = I18nService().getLanguageName(vm.currentLanguage);
console.log('current language:' + I18nService().getCurrentLanguage());
vm.logOut = logOut;
function setLanguage(name) {
I18nService().setCurrentLanguage(name);
}
function logOut() {
LogOutService()
.success(logOutSuccess)
.error(logOutFailed);
}
function logOutSuccess(data, status) {
$window.location.href= '/ng';
}
function logOutFailed(data, status) {
console.log('Failed to log out:' + data);
}
}
function optionalMenu() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/optional-menu/optional-menu.directive.html',
'link': link,
'scope': true,
'controller': OptionalMenuController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
ctrl.isLoggedIn = false;
scope.$on('currentUser', function(e, val) {
if(val != null) {
ctrl.isLoggedIn = true;
ctrl.username = val.username;
}
scope.$apply();
});
}
}
})();

View File

@ -0,0 +1,10 @@
(function() {
'use strict';
angular
.module('harbor.optional.menu', [
'harbor.services.user'
]);
})();

View File

@ -0,0 +1,33 @@
<div class="well panel-group">
<div class="row">
<div class="col-xs-10 col-md-10">
<form name="form" novalidate ng-submit="form.$valid">
<div class="form-group col-md-6">
<input type="text" class="form-control" id="addUsername" placeholder="// 'username' | tr //" ng-model="pm.username" name="uUsername" ng-model-options="{ debounce: 250 }" ng-change="vm.reset()" required>
<div class="error-message">
<div ng-messages="form.$dirty && form.uUsername.$error">
<span ng-message="required">// 'username_is_required' | tr //</span>
</div>
<span ng-show="vm.hasError">// vm.errorMessage | tr //</span>
</div>
</div>
</form>
<form class="form-inline clearfix">
<div class="form-group">
<label for="roleIdList">// 'role' | tr //:</label>&nbsp;&nbsp;
<span ng-repeat="role in vm.roles">
<input type="radio" name="role" ng-model="vm.optRole" value="//role.id//">&nbsp;//role.name//&nbsp;&nbsp;
</span>
</div>
</form>
</div>
<div class="col-xs-2 col-md-2">
<form>
<div class="form-group">
<button type="button" class="btn btn-default" id="btnCancel" ng-click="vm.cancel(form)">// 'cancel' | tr //</button>
<button type="button" class="btn btn-primary" id="btnSave" ng-disabled="form.$invalid" ng-click="vm.save(pm)">// 'save' | tr //</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,83 @@
(function() {
'use strict';
angular
.module('harbor.project.member')
.directive('addProjectMember', addProjectMember);
AddProjectMemberController.$inject = ['$scope', 'roles', 'AddProjectMemberService'];
function AddProjectMemberController($scope, roles, AddProjectMemberService) {
var vm = this;
vm.username = '';
vm.roles = roles();
vm.optRole = 1;
vm.reset = reset;
vm.save = save;
vm.cancel = cancel;
vm.hasError = false;
vm.errorMessage = '';
function reset() {
vm.hasError = false;
vm.errorMessage = '';
}
function save(pm) {
if(pm && angular.isDefined(pm.username)) {
AddProjectMemberService(vm.projectId, vm.optRole, pm.username)
.success(addProjectMemberComplete)
.error(addProjectMemberFailed);
vm.username = '';
vm.optRole = 1;
vm.reload();
}
}
function cancel(form) {
if(form) {
form.$setPristine();
}
vm.isOpen = false;
vm.username = '';
vm.optRole = 1;
}
function addProjectMemberComplete(data, status, header) {
console.log('addProjectMemberComplete: status:' + status + ', data:' + data);
}
function addProjectMemberFailed(data, status, headers) {
if(status === 409) {
vm.hasError = true;
vm.errorMessage = 'username_already_exist';
}
if(status == 404) {
vm.hasError = true;
vm.errorMessage = 'username_does_not_exist';
}
console.log('addProjectMemberFailed: status:' + status + ', data:' + data);
}
}
function addProjectMember() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/project-member/add-project-member.directive.html',
'scope': {
'projectId': '@',
'isOpen': '=',
'reload': '&'
},
'controller': AddProjectMemberController,
'controllerAs': 'vm',
'bindToController': true
}
return directive;
}
})();

View File

@ -0,0 +1,11 @@
<td style="padding: 15px;">//vm.username//</td>
<td><switch-role roles="vm.roles" edit-mode="vm.editMode" user-id="vm.userId" role-name="vm.roleName"></switch-role></td>
<td>
<a ng-show="vm.userId != vm.currentUserId" href="javascript:void(0);" ng-click="vm.updateProjectMember({projectId: vm.projectId, userId: vm.userId, roleId: vm.roleId})">
<span ng-if="!vm.editMode" class="glyphicon glyphicon-pencil" title="Edit"></span><span ng-if="vm.editMode" class="glyphicon glyphicon-ok" title="Confirm">
</a>
<a ng-show="vm.userId != vm.currentUserId" href="javascript:void(0);" ng-click="vm.cancelUpdate()" title="Cancel">
<span ng-if="vm.editMode" class="glyphicon glyphicon-repeat"></span>
</a>
<a ng-show="vm.userId != vm.currentUserId && !vm.editMode" href="javascript:void(0);" ng-click="vm.deleteProjectMember({projectId: vm.projectId, userId: vm.userId})" title="Delete"><span class="glyphicon glyphicon-trash"></span></a>
</td>

View File

@ -0,0 +1,89 @@
(function() {
'use strict';
angular
.module('harbor.project.member')
.directive('editProjectMember', editProjectMember);
EditProjectMemberController.$inject = ['$scope', 'roles', 'getRole','EditProjectMemberService', 'DeleteProjectMemberService'];
function EditProjectMemberController($scope, roles, getRole, EditProjectMemberService, DeleteProjectMemberService) {
var vm = this;
vm.roles = roles();
vm.editMode = false;
vm.lastRoleName = vm.roleName;
$scope.$watch('vm.roleName', function(current, origin) {
if(current) {
vm.currentRole = getRole({'key': 'roleName', 'value': current});
vm.roleId = vm.currentRole.id;
}
});
vm.updateProjectMember = updateProjectMember;
vm.deleteProjectMember = deleteProjectMember;
vm.cancelUpdate = cancelUpdate;
function updateProjectMember(e) {
if(vm.editMode) {
vm.editMode = false;
console.log('update project member, roleId:' + e.roleId);
EditProjectMemberService(e.projectId, e.userId, e.roleId)
.success(editProjectMemberComplete)
.error(editProjectMemberFailed);
}else {
vm.editMode = true;
}
}
function deleteProjectMember(e) {
DeleteProjectMemberService(e.projectId, e.userId)
.success(editProjectMemberComplete)
.error(editProjectMemberFailed);
vm.reload();
}
function editProjectMemberComplete(data, status, headers) {
console.log('edit project member complete: ' + status);
vm.lastRoleName = vm.roleName;
}
function editProjectMemberFailed(e) {
console.log('Failed to edit project member:' + e);
}
function cancelUpdate() {
vm.editMode = false;
console.log('lastRoleName:' + vm.lastRoleName);
vm.roleName = vm.lastRoleName;
}
}
function editProjectMember() {
var directive = {
'restrict': 'A',
'templateUrl': '/static/ng/resources/js/components/project-member/edit-project-member.directive.html',
'scope': {
'username': '=',
'userId': '=',
'currentUserId': '=',
'roleName': '=',
'projectId': '=',
'reload': '&'
},
'link': link,
'controller': EditProjectMemberController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
}
}
})();

View File

@ -0,0 +1,27 @@
<div class="tab-pane" id="users">
<div class="col-xs-12 col-md-12 each-tab-pane">
<div class="form-inline">
<div class="input-group">
<input type="text" class="form-control" placeholder="" ng-model="vm.username" size="30">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" ng-click="vm.search({projectId: vm.projectId, username: vm.username})"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
<button ng-if="!vm.isOpen" class="btn btn-success" type="button" ng-click="vm.addProjectMember()"><span class="glyphicon glyphicon-plus"></span>// 'add_member' | tr //</button>
<button ng-if="vm.isOpen" class="btn btn-default" disabled="disabled" type="button"><span class="glyphicon glyphicon-plus"></span>// 'add_member' | tr //</button>
</div>
<div class="pane">
<add-project-member ng-show="vm.isOpen" is-open="vm.isOpen" project-id="//vm.projectId//" reload='vm.search({projectId: vm.projectId, username: vm.username})'></add-project-member>
<div class="sub-pane">
<table class="table table-pane" >
<thead>
<th width="30%">// 'username' | tr //</th><th width="40%">// 'role' | tr //</th><th width="30%">// 'operation' | tr //</th>
</thead>
<tbody>
<tr ng-repeat="pr in vm.projectMembers" edit-project-member username="pr.username" project-id="vm.projectId" user-id="pr.UserId" current-user-id="vm.currentUser.UserId" role-name="pr.Rolename" reload='vm.search({projectId: vm.projectId, username: vm.username})'></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,78 @@
(function() {
'use strict';
angular
.module('harbor.project.member')
.directive('listProjectMember', listProjectMember);
ListProjectMemberController.$inject = ['$scope', 'ListProjectMemberService', '$routeParams', 'currentUser'];
function ListProjectMemberController($scope, ListProjectMemberService, $routeParams, currentUser) {
var vm = this;
vm.isOpen = false;
vm.search = search;
vm.addProjectMember = addProjectMember;
vm.retrieve = retrieve;
vm.projectId = $routeParams.project_id;
vm.username = "";
vm.currentUser = {};
vm.retrieve();
function search(e) {
vm.projectId = e.projectId;
vm.username = e.username;
console.log('project_id:' + e.projectId);
retrieve();
}
function addProjectMember() {
if(vm.isOpen) {
vm.isOpen = false;
}else{
vm.isOpen = true;
}
}
function retrieve() {
ListProjectMemberService(vm.projectId, {'username': vm.username})
.then(getProjectMemberComplete)
.catch(getProjectMemberFailed);
}
function getProjectMemberComplete(response) {
vm.currentUser = currentUser.get();
vm.projectMembers = response.data;
}
function getProjectMemberFailed(response) {
console.log('Failed get project members:' + response);
}
}
function listProjectMember() {
var directive = {
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',
bindToController: true
}
return directive;
function link(scope, element, attrs, ctrl) {
}
}
})();

View File

@ -0,0 +1,34 @@
(function() {
'use strict';
angular
.module('harbor.project.member')
.constant('roles', roles)
.factory('getRole', getRole);
function roles() {
return [
{'id': '1', 'name': 'Project Admin', 'roleName': 'projectAdmin'},
{'id': '2', 'name': 'Developer', 'roleName': 'developer'},
{'id': '3', 'name': 'Guest', 'roleName': 'guest'}
];
}
getRole.$inject = ['roles'];
function getRole(roles) {
var r = roles();
return get;
function get(query) {
for(var i = 0; i < r.length; i++) {
var role = r[i];
if(query.key == 'roleName' && role.roleName == query.value
|| query.key == 'roleId' && role.id == query.value) {
return role;
}
}
}
}
})();

View File

@ -0,0 +1,11 @@
(function() {
'use strict';
angular
.module('harbor.project.member', [
'harbor.services.project.member',
'harbor.services.user'
]);
})();

View File

@ -0,0 +1,5 @@
<ng-switch on="vm.editMode">
<span ng-switch-default>//vm.currentRole.name//</span>
<select ng-switch-when="true" ng-model="vm.currentRole" ng-options="role as role.name for role in vm.roles track by role.roleName" ng-click="vm.selectRole(vm.currentRole)">
</select>
</ng-switch>

View File

@ -0,0 +1,45 @@
(function() {
'use strict';
angular
.module('harbor.project.member')
.directive('switchRole', switchRole);
SwitchRoleController.$inject = ['getRole', '$scope'];
function SwitchRoleController(getRole, $scope) {
var vm = this;
$scope.$watch('vm.roleName', function(current,origin) {
if(current) {
vm.currentRole = getRole({'key': 'roleName', 'value': current});
}
});
vm.selectRole = selectRole;
function selectRole(role) {
vm.currentRole = getRole({'key': 'roleName', 'value': role.roleName});
vm.roleName = role.roleName;
}
}
function switchRole() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/project-member/switch-role.directive.html',
'scope': {
'roles': '=',
'editMode': '=',
'userId': '=',
'roleName': '='
},
'controller' : SwitchRoleController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
}
})();

View File

@ -0,0 +1,28 @@
<div class="well panel-group">
<div class="row">
<div class="col-xs-10 col-md-10">
<form name="form" ng-submit="form.$valid">
<div class="form-group col-md-6">
<input type="text" class="form-control" placeholder="// 'project_name' | tr //" ng-model="p.projectName" name="uProjectName" ng-model-options="{ debounce: 250 }" ng-change="vm.reset()" required>
<div class="error-message">
<div ng-messages="form.$dirty && form.uProjectName.$error" ng-if="form.uProjectName.$touched">
<span ng-message="required">// 'project_name_is_required' | tr //</span>
</div>
<span ng-show="vm.hasError">// vm.errorMessage | tr //</span>
</div>
</div>
<div class="form-group">
<input type="checkbox" ng-model="vm.isPublic">&nbsp;// 'public' | tr //
</div>
</form>
</div>
<div class="col-xs-2 col-md-2">
<form>
<div class="form-group">
<button type="button" class="btn btn-default" ng-click="vm.cancel(form)">// 'cancel' | tr //</button>
<button type="button" class="btn btn-primary" ng-disabled="form.$invalid" ng-click="vm.addProject(p)">// 'save' | tr //</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,83 @@
(function() {
'use strict';
angular
.module('harbor.project')
.directive('addProject', addProject);
AddProjectController.$inject = ['AddProjectService', '$scope'];
function AddProjectController(AddProjectService, $scope) {
var vm = this;
vm.projectName = "";
vm.isPublic = false;
vm.reset = reset;
vm.addProject = addProject;
vm.cancel = cancel;
vm.hasError = false;
vm.errorMessage = '';
function reset() {
vm.hasError = false;
vm.errorMessage = '';
}
function addProject(p) {
if(p && angular.isDefined(p.projectName)) {
AddProjectService(p.projectName, vm.isPublic)
.success(addProjectSuccess)
.error(addProjectFailed);
}
}
function addProjectSuccess(data, status) {
vm.projectName = "";
vm.isPublic = false;
$scope.$emit('addedSuccess', true);
}
function addProjectFailed(data, status) {
if(status === 409) {
vm.hasError = true;
vm.errorMessage = 'project_already_exist';
}
if(status == 500) {
vm.hasError = true;
vm.errorMessage = 'project_name_is_invalid';
}
console.log('Failed to add project:' + status);
}
function cancel(form){
if(form) {
form.$setPristine();
}
vm.isOpen = false;
vm.projectName = '';
vm.isPublic = false;
}
}
function addProject() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/project/add-project.directive.html',
'controller': AddProjectController,
'scope' : {
'isOpen': '='
},
'link': link,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
}
}
})();

View File

@ -0,0 +1,9 @@
(function() {
'use strict';
angular
.module('harbor.project', [
'harbor.services.project',
'harbor.services.user'
]);
})();

View File

@ -0,0 +1,2 @@
<button ng-if="vm.isPublic" ng-disabled="!vm.owned" class="btn btn-success" ng-click="vm.toggle()">// 'button_on' | tr //</button>
<button ng-if="!vm.isPublic" ng-disabled="!vm.owned" class="btn btn-danger" ng-click="vm.toggle()">// 'button_off' | tr //</button>

View File

@ -0,0 +1,64 @@
(function() {
'use strict';
angular
.module('harbor.project')
.directive('publicityButton', publicityButton);
PublicityButtonController.$inject = ['EditProjectService'];
function PublicityButtonController(EditProjectService) {
var vm = this;
vm.toggle = toggle;
if(vm.isPublic == 1) {
vm.isPublic = true;
}else{
vm.isPublic = false;
}
function toggle() {
if(vm.isPublic) {
vm.isPublic = false;
}else{
vm.isPublic = true;
}
EditProjectService(vm.projectId, vm.isPublic)
.success(editProjectSuccess)
.error(editProjectFailed);
}
function editProjectSuccess(data, status) {
console.log('edit project successfully:' + status);
}
function editProjectFailed(e) {
console.log('edit project failed:' + e);
}
}
function publicityButton() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/project/publicity-button.directive.html',
'scope': {
'isPublic': '=',
'owned': '=',
'projectId': '='
},
'link': link,
'controller': PublicityButtonController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attr, ctrl) {
}
}
})();

View File

@ -0,0 +1,15 @@
(function() {
'use strict';
angular
.module('harbor.repository')
.controller('DeleteRepositoryController', DeleteRepositoryController);
DeleteRepositoryController.$inject = ['DeleteRepositoryService'];
function DeleteRepositoryController(DeleteRepositoryService) {
}
})();

View File

@ -0,0 +1,26 @@
<div class="tab-pane active" id="repositories" >
<div class="col-xs-12 col-md-12 each-tab-pane">
<div class="form-inline">
<div class="input-group">
<input type="text" class="form-control" placeholder="" ng-model="vm.filterInput" value="" size="30">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" ng-click="vm.retrieve()"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</div>
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<modal-dialog action="vm.deleteImage()" content-type="text/html" title="//vm.modalTitle//" message="//vm.modalMessage//"></modal-dialog>
<div class="panel panel-default" ng-repeat="repo in vm.repositories">
<div class="panel-heading" role="tab" id="heading//$index + 1//">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="" href="?project_id=//vm.projectId//#collapse//$index + 1//" aria-expanded="true" aria-controls="collapse//$index+1//">
<span class="glyphicon glyphicon-book"></span> &nbsp;//repo// &nbsp;&nbsp;<span class="badge">//vm.tagCount[repo]//</span>
</a>
<a class="pull-right" style="margin-right: 78px;" href="javascript:void(0)" data-toggle="modal" data-target="#myModal" ng-click="vm.deleteByRepo(repo)"><span class="glyphicon glyphicon-trash"></span></a>
</h4>
</div>
<list-tag associate-id="$index + 1" repo-name="repo" tag-count="vm.tagCount"></list-tag>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,101 @@
(function() {
'use strict';
angular
.module('harbor.repository')
.directive('listRepository', listRepository);
ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$routeParams', '$filter', 'trFilter'];
function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $routeParams, $filter, trFilter) {
var vm = this;
vm.filterInput = "";
vm.retrieve = retrieve;
vm.projectId = $routeParams.project_id;
vm.tagCount = {};
vm.retrieve();
$scope.$on('repoName', function(e, val) {
vm.repoName = val;
});
$scope.$on('tag', function(e, val){
vm.tag = val;
});
$scope.$on('tagCount', function(e, val) {
vm.tagCount = val;
});
$scope.$on('modalTitle', function(e, val) {
vm.modalTitle = val;
});
$scope.$on('modalMessage', function(e, val) {
vm.modalMessage = val;
});
vm.deleteByRepo = deleteByRepo;
vm.deleteImage = deleteImage;
function retrieve(){
ListRepositoryService(vm.projectId, vm.filterInput)
.success(getRepositoryComplete)
.error(getRepositoryFailed);
}
function getRepositoryComplete(data, status) {
vm.repositories = data;
}
function getRepositoryFailed(response) {
console.log('Failed list repositories:' + response);
}
function deleteByRepo(repoName) {
vm.repoName = repoName;
vm.tag = '';
vm.modalTitle = $filter('tr')('alert_delete_repo_title', [repoName]);
vm.modalMessage = $filter('tr')('alert_delete_repo', [repoName]);
}
function deleteImage() {
console.log('repoName:' + vm.repoName + ', tag:' + vm.tag);
DeleteRepositoryService(vm.repoName, vm.tag)
.success(deleteRepositorySuccess)
.error(deleteRepositoryFailed);
}
function deleteRepositorySuccess(data, status) {
vm.retrieve();
}
function deleteRepositoryFailed(data, status) {
console.log('Failed delete repository:' + data);
}
}
function listRepository() {
var directive = {
restrict: 'E',
templateUrl: '/static/ng/resources/js/components/repository/list-repository.directive.html',
replace: true,
link: 'link',
controller: ListRepositoryController,
controllerAs: 'vm',
bindToController: true
}
return directive;
function link(scope, element, attrs, ctrl) {
}
}
})();

View File

@ -0,0 +1,22 @@
<div id="collapse//vm.associateId//" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading//vm.associateId//">
<div class="panel-body">
<table class="repository-table">
<thead>
<th width="20%"><span class="glyphicon glyphicon-tags"></span> // 'tag' | tr //</th>
<th width="15%" style="text-align: center;">// 'image_details' | tr //</th>
<th width="40%">// 'pull_command' | tr //</th>
<th width="15%" style="text-align: center;">// 'operation' | tr //</th>
</thead>
<tbody>
<tr ng-repeat="tag in vm.tags">
<td>//tag//</td>
<td style="text-align: center;"><popup-details repo-name="//vm.repoName//" tag="//tag//"></popup-details></td>
<td>
<pull-command repo-name="//vm.repoName//" tag="//tag//"></pull-command>
</td>
<td style="text-align: center;"><a href="javascript:void(0);" data-toggle="modal" data-target="#myModal" ng-click="vm.deleteByTag({repoName: vm.repoName, tag: tag})"><span class="glyphicon glyphicon-trash"></span></a></td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,71 @@
(function() {
'use strict';
angular
.module('harbor.repository')
.directive('listTag', listTag);
ListTagController.$inject = ['$scope', 'ListTagService', '$filter', 'trFilter'];
function ListTagController($scope, ListTagService, $filter, trFilter) {
var vm = this;
vm.tags = [];
$scope.$watch('vm.repoName', function(current, origin) {
if(current) {
console.log('vm.repoName in tags:' + current);
ListTagService(current)
.then(getTagComplete)
.catch(getTagFailed);
}
});
vm.deleteByTag = deleteByTag;
function getTagComplete(response) {
vm.tags = response.data;
vm.tagCount[vm.repoName] = vm.tags.length;
$scope.$emit('tagCount', vm.tagCount);
}
function getTagFailed(response) {
console.log('Failed get tag:' + response);
}
function deleteByTag(e) {
$scope.$emit('tag', e.tag);
$scope.$emit('repoName', e.repoName);
$scope.$emit('modalTitle', $filter('tr')('alert_delete_tag_title', [e.tag]));
var message;
if(vm.tags.length == 1) {
message = $filter('tr')('alert_delete_last_tag', [e.tag]);
}else {
message = $filter('tr')('alert_delete_tag', [e.tag]);
}
$scope.$emit('modalMessage', message);
}
}
function listTag() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/repository/list-tag.directive.html',
'scope': {
'tagCount': '=',
'associateId': '=',
'repoName': '='
},
'replace': true,
'controller': ListTagController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
}
})();

View File

@ -0,0 +1,4 @@
<a href="javascript:void(0);">
<span tabindex="0" class="glyphicon glyphicon-info-sign" role="button" data-trigger="focus" data-toggle="popover" data-placement="right">
</span>
</a>

View File

@ -0,0 +1,84 @@
(function() {
'use strict';
angular
.module('harbor.repository')
.directive('popupDetails', popupDetails);
PopupDetailsController.$inject = ['ListManifestService', '$filter', 'dateLFilter'];
function PopupDetailsController(ListManifestService, $filter, dateLFilter) {
var vm = this;
vm.retrieve = retrieve;
function retrieve() {
ListManifestService(vm.repoName, vm.tag)
.success(getManifestSuccess)
.error(getManifestFailed);
}
function getManifestSuccess(data, status) {
console.log('Successful get manifest:' + data);
vm.manifest = data;
vm.manifest['Created'] = $filter('dateL')(vm.manifest['Created'], 'YYYY-MM-DD HH:mm:ss');
}
function getManifestFailed(data, status) {
console.log('Failed get manifest:' + data);
}
}
function popupDetails() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/repository/popup-details.directive.html',
'scope': {
'repoName': '@',
'tag': '@'
},
'link': link,
'controller': PopupDetailsController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
ctrl.retrieve();
scope.$watch('vm.manifest', function(current, origin) {
if(current) {
element.find('span').popover({
'content': generateContent,
'html': true
});
}
});
function generateContent() {
var content =
'<form class="form-horizontal">' +
'<div class="form-group">' +
'<label class="col-sm-3 control-label">Id</label>' +
'<div class="col-sm-9"><p class="form-control-static long-line">' + ctrl.manifest['Id'] + '</p></div></div>' +
'<div class="form-group"><label class="col-sm-3 control-label">Parent</label>' +
'<div class="col-sm-9"><p class="form-control-static long-line">' + ctrl.manifest['Parent'] + '</p></div></div>' +
'<div class="form-group"><label class="col-sm-3 control-label">Created</label>' +
'<div class="col-sm-9"><p class="form-control-static">' + ctrl.manifest['Created'] + '</p></div></div>' +
'<div class="form-group"><label class="col-sm-3 control-label">Duration Days</label>' +
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['Duration Days'] == '' ? 'N/A' : ctrl.manifest['Duration Days']) + ' days</p></div></div>' +
'<div class="form-group"><label class="col-sm-3 control-label">Author</label>' +
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['Author'] == '' ? 'N/A' : ctrl.manifest['Author']) + '</p></div></div>' +
'<div class="form-group"><label class="col-sm-3 control-label">Architecture</label>' +
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['Architecture'] == '' ? 'N/A' : ctrl.manifest['Architecture']) + '</p></div></div>' +
'<div class="form-group"><label class="col-sm-3 control-label">Docker Version</label>' +
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['Docker Version'] == '' ? 'N/A' : ctrl.manifest['Docker Version']) + '</p></div></div>' +
'<div class="form-group"><label class="col-sm-3 control-label">OS</label>' +
'<div class="col-sm-9"><p class="form-control-static">' + (ctrl.manifest['OS'] == '' ? 'N/A' : ctrl.manifest['OS']) + '</p></div></div>' +
'</form>';
return content;
}
}
}
})();

View File

@ -0,0 +1,10 @@
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" id="//vm.repoName//-//vm.tag//" value="docker pull //vm.harborRegUrl////vm.repoName//://vm.tag//" readonly="readonly" size="60">
<div class="input-group-addon">
<a href="javascript:void(0);" data-clipboard-target="//vm.repoName//-//vm.tag//"><span class="glyphicon glyphicon-duplicate" data-toggle="tooltip" data-placement="right" title="Copied!"></span></a>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,57 @@
(function() {
'use strict';
angular
.module('harbor.repository')
.directive('pullCommand', pullCommand);
function PullCommandController() {
}
function pullCommand() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/repository/pull-command.directive.html',
'scope': {
'repoName': '@',
'tag': '@'
},
'link': link,
'controller': PullCommandController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
ctrl.harborRegUrl = $('#HarborRegUrl').val() + '/';
ZeroClipboard.config( { swfPath: "/static/ng/vendors/zc/v2.2.0/ZeroClipboard.swf" } );
var clip = new ZeroClipboard(element.find('a'));
element.find('span').tooltip({'trigger': 'click'});
clip.on("ready", function() {
console.log("Flash movie loaded and ready.");
this.on("aftercopy", function(event) {
console.log("Copied text to clipboard: " + event.data["text/plain"]);
element.find('span').tooltip('show');
setTimeout(function(){
element.find('span').tooltip('hide');
}, 1000);
});
});
clip.on("error", function(event) {
console.log('error[name="' + event.name + '"]: ' + event.message);
ZeroClipboard.destroy();
element.find('span').tooltip('destroy');
});
}
}
})();

View File

@ -0,0 +1,7 @@
(function() {
'use strict';
angular
.module('harbor.repository', [
'harbor.services.repository']);
})();

View File

@ -0,0 +1,12 @@
<div class="down-table-pane">
<table class="table">
<thead>
<th>// 'project_repo_name' | tr //</th><th>// 'creation_time' | tr //</th><th>// 'author' | tr //</th>
</thead>
<tbody>
<tr ng-repeat="s in vm.searchResult">
<td>//s.repository_name//</td><td>N/A</td><td>N/A</td>
</tr>
</tbody>
</table>
</div>

View File

@ -0,0 +1,52 @@
(function() {
'use strict';
angular
.module('harbor.search')
.directive('search', search);
SearchController.$inject = ['SearchService', '$scope'];
function SearchController(SearchService, $scope) {
var vm = this;
vm.keywords = "";
vm.search = searchByFilter;
vm.filterBy = 'repository';
searchByFilter();
function searchByFilter() {
SearchService(vm.keywords)
.success(searchSuccess)
.error(searchFailed);
}
function searchSuccess(data, status) {
console.log('filterBy:' + vm.filterBy + ", data:" + data);
vm.searchResult = data[vm.filterBy];
}
function searchFailed(data, status) {
console.log('Failed search:' + data);
}
}
function search() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/search/search.directive.html',
'scope': {
'filterBy': '='
},
'controller': SearchController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
}
})();

View File

@ -0,0 +1,7 @@
(function() {
'use strict';
angular
.module('harbor.search', [
'harbor.services.search']);
})();

View File

@ -0,0 +1,38 @@
<form name="form" class="form-horizontal css-form" ng-submit="form.$valid" novalidate>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input id="username" type="text" class="form-control" placeholder="// 'username_email' | tr //" name="uPrincipal" ng-model-options="{ debounce: 250 }" ng-change="vm.reset()" ng-model="user.principal" required>
<div class="error-message">
<div ng-messages="form.uPrincipal.$error" ng-if="form.uPrincipal.$touched">
<span ng-message="required">// 'username_is_required' | tr //</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input type="password" class="form-control" placeholder="// 'password' | tr //" name="uPassword" ng-model-options="{ debounce: 250 }" ng-change="vm.reset()" ng-model="user.password" required>
<div class="error-message">
<div ng-messages="form.uPassword.$error" ng-if="form.uPassword.$touched">
<span ng-message="required">// 'password_is_required' | tr //</span>
</div>
<span ng-show="vm.hasError">// vm.errorMessage | tr //</span>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<div class="pull-right">
<button class="btn btn-default" ng-disabled="form.$invalid" ng-click="vm.doSignIn(user)">// 'sign_in' | tr //</button>
<button class="btn btn-success" ng-click="vm.doSignUp()">// 'sign_up' | tr //</button>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<div class="pull-right">
<a href="javascript:void(0)" ng-click="vm.doForgotPassword()">// 'forgot_password' | tr //</a>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,68 @@
(function() {
'use strict';
angular
.module('harbor.sign.in')
.directive('signIn', signIn);
SignInController.$inject = ['SignInService', '$window', '$scope'];
function SignInController(SignInService, $window, $scope) {
var vm = this;
vm.hasError = false;
vm.errorMessage = '';
vm.reset = reset;
vm.doSignIn = doSignIn;
vm.doSignUp = doSignUp;
vm.doForgotPassword = doForgotPassword;
function reset() {
vm.hasError = false;
vm.errorMessage = '';
}
function doSignIn(user) {
if(user && angular.isDefined(user.principal) && angular.isDefined(user.password)) {
SignInService(user.principal, user.password)
.success(signedInSuccess)
.error(signedInFailed);
}
}
function signedInSuccess(data, status) {
$window.location.href = "/ng/project";
}
function signedInFailed(data, status) {
if(status === 401) {
vm.hasError = true;
vm.errorMessage = 'username_or_password_is_incorrect';
}
console.log('Failed sign in:' + data + ', status:' + status);
}
function doSignUp() {
$window.location.href = '/ng/sign_up';
}
function doForgotPassword() {
$window.location.href = '/ng/forgot_password';
}
}
function signIn() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/sign-in/sign-in.directive.html',
'scope': true,
'controller': SignInController,
'controllerAs': 'vm',
'bindToController': true
}
return directive;
}
})();

View File

@ -0,0 +1,10 @@
(function() {
'use strict';
angular
.module('harbor.sign.in', [
'harbor.services.user'
]);
})();

View File

@ -0,0 +1,2 @@
<button ng-show="vm.isAdmin" class="btn btn-success" ng-click="vm.toggle()">// 'button_on' | tr //</button>
<button ng-show="!vm.isAdmin" class="btn btn-danger" ng-click="vm.toggle()">// 'button_off' | tr //</button>

View File

@ -0,0 +1,57 @@
(function() {
'use strict';
angular
.module('harbor.user')
.directive('toggleAdmin', toggleAdmin);
ToggleAdminController.$inject = ['$scope', 'ToggleAdminService'];
function ToggleAdminController($scope, ToggleAdminService) {
var vm = this;
vm.isAdmin = (vm.hasAdminRole == 1) ? true : false;
vm.toggle = toggle;
function toggle() {
ToggleAdminService(vm.userId)
.success(toggleAdminSuccess)
.error(toggleAdminFailed);
}
function toggleAdminSuccess(data, status) {
if(vm.isAdmin) {
vm.isAdmin = false;
}else{
vm.isAdmin = true;
}
console.log('Toggled userId:' + vm.userId + ' to admin:' + vm.isAdmin);
}
function toggleAdminFailed(data, status) {
console.log('Failed toggle admin:' + data);
}
}
function toggleAdmin() {
var directive = {
'restrict': 'E',
'templateUrl': '/static/ng/resources/js/components/user/toggle-admin.directive.html',
'scope': {
'hasAdminRole': '=',
'userId': '@'
},
'link': link,
'controller': ToggleAdminController,
'controllerAs': 'vm',
'bindToController': true
};
return directive;
function link(scope, element, attrs, ctrl) {
}
}
})();

View File

@ -0,0 +1,8 @@
(function() {
'use strict';
angular
.module('harbor.user', [
'harbor.services.user']);
})();

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular
.module('harbor.validator')
.directive('compareTo', compareTo);
function compareTo() {
var directive = {
'require' : 'ngModel',
'scope':{
'otherModelValue': '=compareTo'
},
'link': link
};
return directive;
function link (scope, element, attrs, ctrl) {
ctrl.$validators.compareTo = validator;
function validator(modelValue) {
return modelValue == scope.otherModelValue;
}
scope.$watch("otherModelValue", function(current, origin) {
ctrl.$validate();
});
}
}
})();

View File

@ -0,0 +1,41 @@
(function() {
'use strict';
angular
.module('harbor.validator')
.directive('invalidChars', invalidChars);
invalidChars.$inject = ['INVALID_CHARS'];
function invalidChars(INVALID_CHARS) {
var directive = {
'require': 'ngModel',
'link': link
};
return directive;
function link(scope, element, attrs, ctrl) {
ctrl.$validators.invalidChars = validator;
function validator(modelValue, viewValue) {
if(ctrl.$isEmpty(modelValue)) {
return true;
}
for(var i = 0; i < INVALID_CHARS.length; i++) {
if(modelValue.indexOf(INVALID_CHARS[i]) >= 0) {
return false;
}
}
return true;
}
}
}
})();

View File

@ -0,0 +1,30 @@
(function() {
'use strict';
angular
.module('harbor.validator')
.directive('password', password);
password.$inject = ['PASSWORD_REGEXP'];
function password(PASSWORD_REGEXP) {
var directive = {
'require' : 'ngModel',
'link': link
};
return directive;
function link (scope, element, attrs, ctrl) {
ctrl.$validators.password = validator;
function validator(modelValue, viewValue) {
return PASSWORD_REGEXP.test(modelValue);
}
}
}
})();

View File

@ -0,0 +1,56 @@
(function() {
'use strict';
angular
.module('harbor.validator')
.directive('userExists', userExists);
userExists.$inject = ['UserExistService'];
function userExists(UserExistService) {
var directive = {
'require': 'ngModel',
'scope': {
'target': '@'
},
'link': link
};
return directive;
function link(scope, element, attrs, ctrl) {
var valid = true;
ctrl.$validators.userExists = validator;
function validator(modelValue, viewValue) {
console.log('modelValue:' + modelValue + ', viewValue:' + viewValue);
if(ctrl.$isEmpty(modelValue)) {
console.log('Model value is empty.');
return true;
}
UserExistService(attrs.target, modelValue)
.success(userExistSuccess)
.error(userExistFailed);
function userExistSuccess(data, status) {
valid = !data;
if(!valid) {
console.log('Model value already exists');
}
}
function userExistFailed(data, status) {
console.log('Failed in retrieval:' + data);
}
return valid;
}
}
}
})();

View File

@ -0,0 +1,10 @@
(function() {
'use strict';
angular
.module('harbor.validator')
.constant('INVALID_CHARS', [",","~","#", "$", "%"])
.constant('PASSWORD_REGEXP', /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{7,20}$/);
})();

View File

@ -0,0 +1,10 @@
(function() {
'use strict';
angular
.module('harbor.validator', [
'harbor.services.user'
]);
})();

View File

@ -0,0 +1,42 @@
(function() {
'use strict';
angular
.module('harbor.app')
.config(function($interpolateProvider){
$interpolateProvider.startSymbol('//');
$interpolateProvider.endSymbol('//');
})
.config(function($httpProvider) {
$httpProvider.defaults.headers.common = {'Accept': 'application/json, text/javascript, */*; q=0.01'};
})
.filter('dateL', localizeDate)
.filter('tr', tr);
function localizeDate() {
return filter;
function filter(input, pattern) {
return moment(new Date(input || '')).format(pattern);
}
}
tr.$inject = ['I18nService'];
function tr(I18nService) {
return tr;
function tr(label, params) {
var currentLanguage = I18nService().getCurrentLanguage();
var result = '';
if(label && label.length > 0){
result = I18nService().getValue(label, currentLanguage);
}
if(angular.isArray(params)) {
angular.forEach(params, function(value, index) {
result = result.replace('$' + index, params[index]);
});
}
return result;
}
}
})();

View File

@ -0,0 +1,7 @@
(function() {
'use strict';
angular
.module('harbor.app');
})();

View File

@ -0,0 +1,38 @@
(function() {
angular
.module('harbor.app')
.factory('currentUser', currentUser)
.factory('projectMember', projectMember);
function currentUser() {
var currentUser;
return {
set: function(user) {
currentUser = user;
console.log('set currentUser:' + currentUser);
},
get: function() {
console.log('get currentUser:' + currentUser);
return currentUser;
}
}
}
function projectMember() {
var projectMember;
return {
set: function(member) {
projectMember = member;
console.log('set projectMember:');
console.log(projectMember);
},
get: function() {
console.log('get projectMember:');
console.log(projectMember);
return projectMember;
}
}
}
})();

View File

@ -0,0 +1,7 @@
(function() {
'use strict';
angular
.module('harbor.app');
})();

View File

@ -0,0 +1,41 @@
(function() {
'use strict';
angular
.module('harbor.app', [
'ngRoute',
'ngMessages',
'ngCookies',
'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.dashboard',
'harbor.layout.project',
'harbor.layout.repository',
'harbor.layout.project.member',
'harbor.layout.user',
'harbor.layout.system.management',
'harbor.layout.log',
'harbor.layout.admin.option',
'harbor.services.i18n',
'harbor.services.project',
'harbor.services.user',
'harbor.services.repository',
'harbor.services.project.member',
'harbor.optional.menu',
'harbor.modal.dialog',
'harbor.sign.in',
'harbor.search',
'harbor.project',
'harbor.details',
'harbor.repository',
'harbor.project.member',
'harbor.user',
'harbor.log',
'harbor.validator'
]);
})();

View File

@ -0,0 +1,79 @@
(function() {
'use strict';
angular
.module('harbor.layout.account.setting')
.controller('AccountSettingController', AccountSettingController);
AccountSettingController.$inject = ['ChangePasswordService', '$scope', '$window', 'currentUser'];
function AccountSettingController(ChangePasswordService, $scope, $window, currentUser) {
var vm = this;
vm.isOpen = false;
vm.user = {};
vm.hasError = false;
vm.errorMessage = '';
vm.reset = reset;
vm.toggleChangePassword = toggleChangePassword;
vm.changeProfile = changeProfile;
vm.changePassword= changePassword;
vm.cancel = cancel;
$scope.$on('currentUser', function(e, val) {
vm.user = val;
});
function reset() {
vm.hasError = false;
vm.errorMessage = '';
}
function toggleChangePassword() {
if(vm.isOpen) {
vm.isOpen = false;
}else{
vm.isOpen = true;
}
}
function getCurrentUserFailed(data) {
console.log('Failed get current user:' + data);
}
function changeProfile(user) {
console.log(user);
}
function changePassword(user) {
if(user && angular.isDefined(user.oldPassword) && angular.isDefined(user.password)) {
ChangePasswordService(vm.user.UserId, user.oldPassword, user.password)
.success(changePasswordSuccess)
.error(changePasswordFailed);
}
}
function changePasswordSuccess(data, status) {
$window.location.href = '/ng/project';
}
function changePasswordFailed(data, status) {
console.log('Failed changed password:' + data);
if(data === 'old_password_is_not_correct') {
vm.hasError = true;
vm.errorMessage = 'old_password_is_incorrect';
}
}
function cancel(form) {
if(form) {
form.$setPristine();
}
$window.location.href = '/ng/project';
}
}
})();

View File

@ -0,0 +1,9 @@
(function() {
'use strict';
angular
.module('harbor.layout.account.setting', [
'harbor.services.user']);
})();

View File

@ -0,0 +1,26 @@
(function() {
'use strict';
angular
.module('harbor.layout.admin.option')
.config(routeConfig);
function routeConfig($routeProvider) {
$routeProvider
.when('/all_user', {
'templateUrl': '/static/ng/resources/js/layout/user/user.controller.html',
'controller': 'UserController',
'controllerAs': 'vm'
})
.when('/system_management', {
'templateUrl': '/static/ng/resources/js/layout/system-management/system-management.controller.html',
'controller': 'SystemManagementController',
'controllerAs': 'vm'
})
.otherwise({
'redirectTo': '/'
});
}
})();

View File

@ -0,0 +1,23 @@
(function() {
'use strict';
angular
.module('harbor.layout.admin.option')
.controller('AdminOptionController', AdminOptionController);
function AdminOptionController() {
var vm = this;
vm.toggle = false;
vm.toggleAdminOption = toggleAdminOption;
function toggleAdminOption() {
if(vm.toggle) {
vm.toggle = false;
}else{
vm.toggle = true;
}
}
}
})();

View File

@ -0,0 +1,8 @@
(function() {
'use strict';
angular
.module('harbor.layout.admin.option', []);
})();

View File

@ -0,0 +1,49 @@
(function() {
'use strict';
angular
.module('harbor.layout.dashboard')
.controller('DashboardController', DashboardController);
DashboardController.$inject = ['StatProjectService', 'ListTop10RepositoryService', 'ListIntegratedLogService'];
function DashboardController(StatProjectService, ListTop10RepositoryService, ListIntegratedLogService) {
var vm = this;
StatProjectService()
.then(statProjectSuccess, statProjectFailed);
ListTop10RepositoryService()
.then(listTop10RepositorySuccess, listTop10RepositoryFailed);
ListIntegratedLogService()
.then(listIntegratedLogSuccess, listIntegratedLogFailed);
function statProjectSuccess(data) {
vm.statProjects = data;
}
function statProjectFailed(data) {
console.log('Failed stat project:' + data);
}
function listTop10RepositorySuccess(data) {
vm.top10Repositories = data;
}
function listTop10RepositoryFailed(data) {
console.log('Failed list top 10 repositories:' + data);
}
function listIntegratedLogSuccess(data) {
vm.integratedLogs = data;
}
function listIntegratedLogFailed(data) {
console.log('Failed list integrated logs:' + data);
}
}
})();

View File

@ -0,0 +1,12 @@
(function() {
'use strict';
angular
.module('harbor.layout.dashboard', [
'harbor.services.project',
'harbor.services.repository',
'harbor.services.log'
]);
})();

View File

@ -0,0 +1,53 @@
(function() {
'use strict';
angular
.module('harbor.details')
.config(routeConfig)
.filter('name', nameFilter);
function routeConfig($routeProvider) {
$routeProvider
.when('/repositories', {
templateUrl: '/static/ng/resources/js/layout/repository/repository.controller.html',
controller: 'RepositoryController',
controllerAs: 'vm'
})
.when('/users', {
templateUrl: '/static/ng/resources/js/layout/project-member/project-member.controller.html',
controller: 'ProjectMemberController',
controllerAs: 'vm'
})
.when('/logs', {
templateUrl: '/static/ng/resources/js/layout/log/log.controller.html',
controller: 'LogController',
controllerAs: 'vm'
});
}
function nameFilter() {
return filter;
function filter(input, filterInput, key) {
input = input || [];
var filteredResults = [];
if (filterInput != '') {
for(var i = 0; i < input.length; i++) {
var item = input[i];
if((key == "" && item.indexOf(filterInput) >= 0) || (key != "" && item[key].indexOf(filterInput) >= 0)) {
filteredResults.push(item);
continue;
}
}
input = filteredResults;
}
return input;
}
}
})();

View File

@ -0,0 +1,28 @@
(function() {
'use strict';
angular
.module('harbor.details')
.controller('DetailsController', DetailsController);
DetailsController.$inject = ['$scope', '$location', '$routeParams'];
function DetailsController($scope, $location, $routeParams) {
var vm = this;
vm.isOpen = false;
vm.publicity = false;
vm.isProjectMember = true;
vm.closeRetrievePane = closeRetrievePane;
vm.togglePublicity = togglePublicity;
function closeRetrievePane() {
$scope.$broadcast('isOpen', false);
}
function togglePublicity(e) {
vm.publicity = e.publicity;
console.log('current project publicity:' + vm.publicity);
}
}
})();

View File

@ -0,0 +1,11 @@
(function() {
'use strict';
angular
.module('harbor.details', [
'harbor.services.project',
'harbor.services.project.member'
]);
})();

Some files were not shown because too many files have changed in this diff Show More