Merge branch 'job-service' into new-ui-with-sync-image

This commit is contained in:
kunw 2016-06-14 11:22:03 +08:00
commit 91eacb895c
52 changed files with 1834 additions and 1252 deletions

View File

@ -1,3 +1,5 @@
sudo: true
language: go language: go
go: go:
@ -5,10 +7,32 @@ go:
go_import_path: github.com/vmware/harbor go_import_path: github.com/vmware/harbor
#service: services:
# - mysql - docker
- mysql
env: DB_HOST=127.0.0.1 DB_PORT=3306 DB_USR=root DB_PWD= dist: trusty
addons:
apt:
packages:
- mysql-server-5.6
- mysql-client-core-5.6
- mysql-client-5.6
env:
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_USR: root
DB_PWD:
DOCKER_COMPOSE_VERSION: 1.7.1
HARBOR_ADMIN: admin
HARBOR_ADMIN_PASSWD: Harbor12345
before_install:
- ./tests/hostcfg.sh
- cd Deploy
- ./prepare
- cd ..
install: install:
- sudo apt-get update && sudo apt-get install -y libldap2-dev - sudo apt-get update && sudo apt-get install -y libldap2-dev
@ -29,12 +53,29 @@ install:
- go get -d github.com/go-sql-driver/mysql - go get -d github.com/go-sql-driver/mysql
- go get github.com/golang/lint/golint - go get github.com/golang/lint/golint
- go get github.com/GeertJohan/fgt - go get github.com/GeertJohan/fgt
- sudo apt-get install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" docker-engine=1.11.1-0~trusty
- sudo rm /usr/local/bin/docker-compose
- curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
- chmod +x docker-compose
- sudo mv docker-compose /usr/local/bin
- go get github.com/dghubble/sling
- go get github.com/stretchr/testify
before_script: before_script:
# create tables and load data # create tables and load data
- mysql < ./Deploy/db/registry.sql -uroot --verbose - mysql < ./Deploy/db/registry.sql -uroot --verbose
script: script:
- go list ./... | grep -v /vendor/ | xargs -L1 fgt golint - go list ./... | grep -v 'tests' | grep -v /vendor/ | xargs -L1 fgt golint
- go list ./... | grep -v 'vendor' | xargs -L1 go vet - go list ./... | grep -v 'tests' | grep -v 'vendor' | xargs -L1 go vet
- go list ./... | grep -v 'vendor' | xargs -L1 go test -v - go list ./... | grep -v 'tests' | grep -v 'vendor' | xargs -L1 go test -v
- docker-compose -f Deploy/docker-compose.yml up -d
- docker ps
- go run tests/startuptest.go http://localhost/
- go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD}
# test for API
- go test -v ./tests/apitests

View File

@ -33,7 +33,7 @@ type ProjectMemberAPI struct {
} }
type memberReq struct { type memberReq struct {
Username string `json:"user_name"` Username string `json:"username"`
UserID int `json:"user_id"` UserID int `json:"user_id"`
Roles []int `json:"roles"` Roles []int `json:"roles"`
} }
@ -104,7 +104,7 @@ func (pma *ProjectMemberAPI) Get() {
log.Errorf("Error occurred in GetUser, error: %v", err) log.Errorf("Error occurred in GetUser, error: %v", err)
pma.CustomAbort(http.StatusInternalServerError, "Internal error.") pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
} }
result["user_name"] = user.Username result["username"] = user.Username
result["user_id"] = pma.memberID result["user_id"] = pma.memberID
result["roles"] = roleList result["roles"] = roleList
pma.Data["json"] = result pma.Data["json"] = result

View File

@ -14,8 +14,6 @@ import (
// RepPolicyAPI handles /api/replicationPolicies /api/replicationPolicies/:id/enablement // RepPolicyAPI handles /api/replicationPolicies /api/replicationPolicies/:id/enablement
type RepPolicyAPI struct { type RepPolicyAPI struct {
BaseAPI BaseAPI
policyID int64
policy *models.RepPolicy
} }
// Prepare validates whether the user has system admin role // Prepare validates whether the user has system admin role
@ -214,11 +212,11 @@ func (pa *RepPolicyAPI) UpdateEnablement() {
return return
} }
if pa.policy.Enabled == e.Enabled { if policy.Enabled == e.Enabled {
return return
} }
if err := dao.UpdateRepPolicyEnablement(pa.policyID, e.Enabled); err != nil { if err := dao.UpdateRepPolicyEnablement(id, e.Enabled); err != nil {
log.Errorf("Failed to update policy enablement in DB, error: %v", err) log.Errorf("Failed to update policy enablement in DB, error: %v", err)
pa.RenderError(http.StatusInternalServerError, "Internal Error") pa.RenderError(http.StatusInternalServerError, "Internal Error")
return return
@ -226,18 +224,18 @@ func (pa *RepPolicyAPI) UpdateEnablement() {
if e.Enabled == 1 { if e.Enabled == 1 {
go func() { go func() {
if err := TriggerReplication(pa.policyID, "", nil, models.RepOpTransfer); err != nil { if err := TriggerReplication(id, "", nil, models.RepOpTransfer); err != nil {
log.Errorf("failed to trigger replication of %d: %v", pa.policyID, err) log.Errorf("failed to trigger replication of %d: %v", id, err)
} else { } else {
log.Infof("replication of %d triggered", pa.policyID) log.Infof("replication of %d triggered", id)
} }
}() }()
} else { } else {
go func() { go func() {
if err := postReplicationAction(pa.policyID, "stop"); err != nil { if err := postReplicationAction(id, "stop"); err != nil {
log.Errorf("failed to stop replication of %d: %v", pa.policyID, err) log.Errorf("failed to stop replication of %d: %v", id, err)
} else { } else {
log.Infof("try to stop replication of %d", pa.policyID) log.Infof("try to stop replication of %d", id)
} }
}() }()
} }

View File

@ -4,7 +4,7 @@ swagger: '2.0'
info: info:
title: Harbor API title: Harbor API
description: These APIs provide services for manipulating Harbor project. description: These APIs provide services for manipulating Harbor project.
version: "0.1.0" version: "0.1.1"
# the domain of the service # the domain of the service
host: localhost host: localhost
# array of all schemes that your API supports # array of all schemes that your API supports
@ -167,7 +167,7 @@ paths:
- name: access_log - name: access_log
in: body in: body
schema: schema:
$ref: '#/definitions/AccessLog' $ref: '#/definitions/AccessLogFilter'
description: Search results of access logs. description: Search results of access logs.
tags: tags:
- Products - Products
@ -204,7 +204,7 @@ paths:
schema: schema:
type: array type: array
items: items:
$ref: '#/definitions/Role' $ref: '#/definitions/User'
400: 400:
description: Illegal format of provided ID value. description: Illegal format of provided ID value.
401: 401:
@ -567,7 +567,7 @@ paths:
schema: schema:
type: array type: array
items: items:
$ref: '#/definitions/Repository' type: string
400: 400:
description: Invalid project ID. description: Invalid project ID.
403: 403:
@ -719,12 +719,41 @@ definitions:
description: Search results of the projects that matched the filter keywords. description: Search results of the projects that matched the filter keywords.
type: array type: array
items: items:
$ref: '#/definitions/Project' $ref: '#/definitions/SearchProject'
repositories: repositories:
description: Search results of the repositories that matched the filter keywords. description: Search results of the repositories that matched the filter keywords.
type: array type: array
items: items:
$ref: '#/definitions/Repository' $ref: '#/definitions/SearchRepository'
SearchProject:
type: object
properties:
id:
type: integer
format: int64
description: The ID of project
name:
type: string
description: The name of the project
public:
type: integer
format: int
description: The flag to indicate the publicity of the project (1 is public, 0 is non-public)
SearchRepository:
type: object
properties:
repository_name:
type: string
description: The name of the repository
project_name:
type: string
description: The name of the project that the repository belongs to
project_id:
type: integer
description: The ID of the project that the repository belongs to
project_public:
type: integer
description: The flag to indicate the publicity of the project that the repository belongs to (1 is public, 0 is not)
Project: Project:
type: object type: object
properties: properties:
@ -736,30 +765,39 @@ definitions:
type: integer type: integer
format: int32 format: int32
description: The owner ID of the project always means the creator of the project. description: The owner ID of the project always means the creator of the project.
project_name: name:
type: string type: string
description: The name of the project. description: The name of the project.
creation_time: creation_time:
type: string type: string
description: The creation time of the project. description: The creation time of the project.
update_time:
type: string
description: The update time of the project.
deleted: deleted:
type: integer type: integer
format: int32 format: int32
description: A deletion mark of the project. description: A deletion mark of the project (1 means it's deleted, 0 is not)
user_id: user_id:
type: integer type: integer
format: int32 format: int32
description: A relation field to the user table. description: A relation field to the user table.
owner_name: owner_name:
type: string type: string
description: The owner name of tthe project always means the creator of the project. description: The owner name of the project.
public: public:
type: boolean type: boolean
format: boolean format: boolean
description: The public status of the project. description: The public status of the project.
togglable: togglable:
type: boolean type: boolean
description: Correspond to the UI about showing the public status of the project. description: Correspond to the UI about whether the project's publicity is updatable (for UI)
current_user_role_id:
type: integer
description: The role ID of the current user who triggered the API (for UI)
repo_count:
type: integer
description: The number of the repositories under this project.
Repository: Repository:
type: object type: object
properties: properties:
@ -794,6 +832,7 @@ definitions:
user_id: user_id:
type: integer type: integer
format: int32 format: int32
description: The ID of the user.
username: username:
type: string type: string
email: email:
@ -816,7 +855,7 @@ definitions:
new_password: new_password:
type: string type: string
description: New password for marking as to be updated. description: New password for marking as to be updated.
AccessLog: AccessLogFilter:
type: object type: object
properties: properties:
username: username:
@ -825,14 +864,32 @@ definitions:
keywords: keywords:
type: string type: string
description: Operation name specified when project created. description: Operation name specified when project created.
beginTimestamp: begin_timestamp:
type: integer type: integer
format: int32 format: int64
description: Begin timestamp for querying access logs. description: Begin timestamp for querying access logs.
endTimestamp: end_timestamp:
type: integer type: integer
format: int32 format: int64
description: End timestamp for querying accessl logs. description: End timestamp for querying accessl logs.
AccessLog:
type: object
properties:
log_id:
type: integer
description: The ID of the log entry.
repo_name:
type: string
description: Name of the repository in this log entry.
repo_tag:
type: string
description: Tag of the repository in this log entry.
operation:
type: string
description: The operation against the repository in this log entry.
op_time:
type: time
description: The time when this operation is triggered.
Role: Role:
type: object type: object
properties: properties:
@ -855,7 +912,7 @@ definitions:
type: integer type: integer
format: int32 format: int32
description: Role ID for updating project role member. description: Role ID for updating project role member.
user_name: username:
type: string type: string
description: Username relevant to a project role member. description: Username relevant to a project role member.
TopRepo: TopRepo:

View File

@ -21,19 +21,18 @@ import (
// AccessLog holds information about logs which are used to record the actions that user take to the resourses. // AccessLog holds information about logs which are used to record the actions that user take to the resourses.
type AccessLog struct { type AccessLog struct {
LogID int `orm:"pk;column(log_id)" json:"LogId"` LogID int `orm:"column(log_id)" json:"log_id"`
UserID int `orm:"column(user_id)" json:"UserId"` UserID int `orm:"column(user_id)" json:"user_id"`
ProjectID int64 `orm:"column(project_id)" json:"ProjectId"` ProjectID int64 `orm:"column(project_id)" json:"project_id"`
RepoName string `orm:"column(repo_name)"` RepoName string `orm:"column(repo_name)" json:"repo_name"`
RepoTag string `orm:"column(repo_tag)"` RepoTag string `orm:"column(repo_tag)" json:"repo_tag"`
GUID string `orm:"column(GUID)" json:"Guid"` GUID string `orm:"column(GUID)" json:"guid"`
Operation string `orm:"column(operation)"` Operation string `orm:"column(operation)" json:"operation"`
OpTime time.Time `orm:"column(op_time)"` OpTime time.Time `orm:"column(op_time)" json:"op_time"`
Username string Username string `json:"username"`
Keywords string Keywords string `json:"keywords"`
BeginTime time.Time BeginTime time.Time
BeginTimestamp int64 BeginTimestamp int64 `json:"begin_timestamp"`
EndTime time.Time EndTime time.Time
EndTimestamp int64 EndTimestamp int64 `json:"end_timestamp"`
} }

View File

@ -21,19 +21,19 @@ import (
// Project holds the details of a project. // Project holds the details of a project.
type Project struct { type Project struct {
ProjectID int64 `orm:"pk;column(project_id)" json:"ProjectId"` ProjectID int64 `orm:"column(project_id)" json:"project_id"`
OwnerID int `orm:"column(owner_id)" json:"OwnerId"` OwnerID int `orm:"column(owner_id)" json:"owner_id"`
Name string `orm:"column(name)"` Name string `orm:"column(name)" json:"name"`
CreationTime time.Time `orm:"column(creation_time)"` CreationTime time.Time `orm:"column(creation_time)" json:"creation_time"`
CreationTimeStr string CreationTimeStr string `json:"creation_time_str"`
Deleted int `orm:"column(deleted)"` Deleted int `orm:"column(deleted)" json:"deleted"`
UserID int `json:"UserId"` //UserID int `json:"UserId"`
OwnerName string OwnerName string `json:"owner_name"`
Public int `orm:"column(public)"` Public int `orm:"column(public)" json:"public"`
//This field does not have correspondent column in DB, this is just for UI to disable button //This field does not have correspondent column in DB, this is just for UI to disable button
Togglable bool Togglable bool
UpdateTime time.Time `orm:"update_time" json:"update_time"` UpdateTime time.Time `orm:"update_time" json:"update_time"`
Role int `json:"role_id"` Role int `json:"current_user_role_id"`
RepoCount int `json:"repo_count"` RepoCount int `json:"repo_count"`
} }

View File

@ -21,20 +21,19 @@ import (
// User holds the details of a user. // User holds the details of a user.
type User struct { type User struct {
UserID int `orm:"pk;column(user_id)" json:"UserId"` UserID int `orm:"column(user_id)" json:"user_id"`
Username string `orm:"column(username)" json:"username"` Username string `orm:"column(username)" json:"username"`
Email string `orm:"column(email)" json:"email"` Email string `orm:"column(email)" json:"email"`
Password string `orm:"column(password)" json:"password"` Password string `orm:"column(password)" json:"password"`
Realname string `orm:"column(realname)" json:"realname"` Realname string `orm:"column(realname)" json:"realname"`
Comment string `orm:"column(comment)" json:"comment"` Comment string `orm:"column(comment)" json:"comment"`
Deleted int `orm:"column(deleted)"` Deleted int `orm:"column(deleted)" json:"deleted"`
Rolename string Rolename string `json:"role_name"`
RoleID int `json:"RoleId"` RoleID int `json:"role_id"`
RoleList []*Role `orm:"rel(m2m)"` // RoleList []Role `json:"role_list"`
HasAdminRole int `orm:"column(sysadmin_flag)"` HasAdminRole int `orm:"column(sysadmin_flag)" json:"has_admin_role"`
ResetUUID string `orm:"column(reset_uuid)" json:"ResetUuid"` ResetUUID string `orm:"column(reset_uuid)" json:"reset_uuid"`
Salt string `orm:"column(salt)"` Salt string `orm:"column(salt)"`
CreationTime time.Time `orm:"creation_time" json:"creation_time"` CreationTime time.Time `orm:"creation_time" json:"creation_time"`
UpdateTime time.Time `orm:"update_time" json:"update_time"` UpdateTime time.Time `orm:"update_time" json:"update_time"`
} }

View File

@ -13,7 +13,6 @@
limitations under the License. limitations under the License.
*/ */
.footer { .footer {
margin-top: 60px;
width: 100%; width: 100%;
/* Set the fixed height of the footer here */ /* Set the fixed height of the footer here */
height: 60px; height: 60px;

View File

@ -24,7 +24,7 @@ jQuery(function(){
error: function(jqXhr){ error: function(jqXhr){
if(jqXhr && jqXhr.status == 401){ if(jqXhr && jqXhr.status == 401){
document.location = "/signIn"; document.location = "/signIn";
} }
} }
}).exec(); }).exec();
@ -36,12 +36,12 @@ jQuery(function(){
function bindEnterKey(){ function bindEnterKey(){
$(document).on("keydown", function(e){ $(document).on("keydown", function(e){
if(e.keyCode == 13){ if(e.keyCode == 13){
e.preventDefault(); e.preventDefault();
if($("#txtCommonSearch").is(":focus")){ if($("#txtCommonSearch").is(":focus")){
document.location = "/search?q=" + $("#txtCommonSearch").val(); document.location = "/search?q=" + $("#txtCommonSearch").val();
}else{ }else{
$("#btnSubmit").trigger("click"); $("#btnSubmit").trigger("click");
} }
} }
}); });
} }
@ -61,35 +61,35 @@ jQuery(function(){
type: "put", type: "put",
data: {"old_password": oldPassword, "new_password" : password}, data: {"old_password": oldPassword, "new_password" : password},
beforeSend: function(e){ beforeSend: function(e){
unbindEnterKey(); unbindEnterKey();
$("h1").append(spinner.el); $("h1").append(spinner.el);
$("#btnSubmit").prop("disabled", true); $("#btnSubmit").prop("disabled", true);
}, },
complete: function(xhr, status){ complete: function(xhr, status){
spinner.stop(); spinner.stop();
$("#btnSubmit").prop("disabled", false); $("#btnSubmit").prop("disabled", false);
if(xhr && xhr.status == 200){ if(xhr && xhr.status == 200){
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
"title": i18n.getMessage("title_change_password"), "title": i18n.getMessage("title_change_password"),
"content": i18n.getMessage("change_password_successfully"), "content": i18n.getMessage("change_password_successfully"),
"callback": function(){ "callback": function(){
window.close(); window.close();
} }
}); });
} }
}, },
error: function(jqXhr, status, error){ error: function(jqXhr, status, error){
if(jqXhr && jqXhr.responseText.length){ if(jqXhr && jqXhr.responseText.length){
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
"title": i18n.getMessage("title_change_password"), "title": i18n.getMessage("title_change_password"),
"content": i18n.getMessage(jqXhr.responseText), "content": i18n.getMessage(jqXhr.responseText),
"callback": function(){ "callback": function(){
bindEnterKey(); bindEnterKey();
return; return;
} }
}); });
} }
} }
}).exec(); }).exec();

View File

@ -12,6 +12,7 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
var AjaxUtil = function(params){ var AjaxUtil = function(params){
this.url = params.url; this.url = params.url;
@ -31,39 +32,39 @@ AjaxUtil.prototype.exec = function(){
var self = this; var self = this;
return $.ajax({ return $.ajax({
url: self.url, url: self.url,
contentType: (self.dataRaw ? "application/x-www-form-urlencoded; charset=UTF-8" : "application/json; charset=utf-8"), contentType: (self.dataRaw ? "application/x-www-form-urlencoded; charset=UTF-8" : "application/json; charset=utf-8"),
data: JSON.stringify(self.data) || self.dataRaw, data: JSON.stringify(self.data) || self.dataRaw,
type: self.type, type: self.type,
dataType: "json", dataType: "json",
success: function(data, status, xhr){ success: function(data, status, xhr){
if(self.success != null){ if(self.success != null){
self.success(data, status, xhr); self.success(data, status, xhr);
} }
}, },
complete: function(jqXhr, status) { complete: function(jqXhr, status) {
if(self.complete != null){ if(self.complete != null){
self.complete(jqXhr, status); self.complete(jqXhr, status);
} }
}, },
error: function(jqXhr){ error: function(jqXhr){
if(self.error != null){ if(self.error != null){
self.error(jqXhr); self.error(jqXhr);
}else{ }else{
var errorMessage = self.errors[jqXhr.status] || jqXhr.responseText; var errorMessage = self.errors[jqXhr.status] || jqXhr.responseText;
if(jqXhr.status == 401){ if(jqXhr.status == 401){
var lastUri = location.pathname + location.search; var lastUri = location.pathname + location.search;
if(lastUri != ""){ if(lastUri != ""){
document.location = "/signIn?uri=" + encodeURIComponent(lastUri); document.location = "/signIn?uri=" + encodeURIComponent(lastUri);
}else{ }else{
document.location = "/signIn"; document.location = "/signIn";
}
}else if($.trim(errorMessage).length > 0){
$("#dlgModal").dialogModal({"title": i18n.getMessage("operation_failed"), "content": errorMessage});
} }
}else if($.trim(errorMessage).length > 0){
$("#dlgModal").dialogModal({"title": i18n.getMessage("operation_failed"), "content": errorMessage});
} }
} }
} });
});
}; };
var SUPPORT_LANGUAGES = { var SUPPORT_LANGUAGES = {
@ -142,9 +143,9 @@ jQuery(function(){
var lines = ['<form class="form-horizontal">']; var lines = ['<form class="form-horizontal">'];
for(var item in settings.content){ for(var item in settings.content){
lines.push('<div class="form-group">'+ lines.push('<div class="form-group">'+
'<label class="col-sm-2 control-label">'+ item +'</label>' + '<label class="col-sm-2 control-label">'+ item +'</label>' +
'<div class="col-sm-10"><p class="form-control-static">' + settings.content[item] + '</p></div>' + '<div class="col-sm-10"><p class="form-control-static">' + settings.content[item] + '</p></div>' +
'</div>'); '</div>');
} }
lines.push('</form>'); lines.push('</form>');
$("#dlgBody", self).html(lines.join("")); $("#dlgBody", self).html(lines.join(""));
@ -154,8 +155,13 @@ jQuery(function(){
} }
if(settings.callback != null){ if(settings.callback != null){
$("#dlgConfirm").on("click", function(){ var hasEntered = false;
settings.callback(); $("#dlgConfirm").on("click", function(e){
if(!hasEntered) {
hasEntered = true;
settings.callback();
}
}); });
} }
$(self).modal('show'); $(self).modal('show');

View File

@ -20,19 +20,19 @@ jQuery(function(){
function bindEnterKey(){ function bindEnterKey(){
$(document).on("keydown", function(e){ $(document).on("keydown", function(e){
if(e.keyCode == 13){ if(e.keyCode == 13){
e.preventDefault(); e.preventDefault();
if($("#txtCommonSearch").is(":focus")){ if($("#txtCommonSearch").is(":focus")){
document.location = "/search?q=" + $("#txtCommonSearch").val(); document.location = "/search?q=" + $("#txtCommonSearch").val();
}else{ }else{
$("#btnSubmit").trigger("click"); $("#btnSubmit").trigger("click");
} }
} }
}); });
} }
function unbindEnterKey(){ function unbindEnterKey(){
$(document).off("keydown"); $(document).off("keydown");
} }
bindEnterKey(); bindEnterKey();
var spinner = new Spinner({scale:1}).spin(); var spinner = new Spinner({scale:1}).spin();
$("#btnSubmit").on("click", function(){ $("#btnSubmit").on("click", function(){
@ -44,20 +44,20 @@ jQuery(function(){
"type": "get", "type": "get",
"data": {"username": username, "email": email}, "data": {"username": username, "email": email},
"beforeSend": function(e){ "beforeSend": function(e){
unbindEnterKey(); unbindEnterKey();
$("h1").append(spinner.el); $("h1").append(spinner.el);
$("#btnSubmit").prop("disabled", true); $("#btnSubmit").prop("disabled", true);
}, },
"success": function(data, status, xhr){ "success": function(data, status, xhr){
if(xhr && xhr.status == 200){ if(xhr && xhr.status == 200){
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
"title": i18n.getMessage("title_forgot_password"), "title": i18n.getMessage("title_forgot_password"),
"content": i18n.getMessage("email_has_been_sent"), "content": i18n.getMessage("email_has_been_sent"),
"callback": function(){ "callback": function(){
document.location="/"; document.location="/";
} }
}); });
} }
}, },
@ -68,14 +68,14 @@ jQuery(function(){
"error": function(jqXhr, status, error){ "error": function(jqXhr, status, error){
if(jqXhr){ if(jqXhr){
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
"title": i18n.getMessage("title_forgot_password"), "title": i18n.getMessage("title_forgot_password"),
"content": i18n.getMessage(jqXhr.responseText), "content": i18n.getMessage(jqXhr.responseText),
"callback": function(){ "callback": function(){
bindEnterKey(); bindEnterKey();
return; return;
} }
}); });
} }
} }
}); });

View File

@ -13,20 +13,20 @@
limitations under the License. limitations under the License.
*/ */
jQuery(function(){ jQuery(function(){
$("#btnSignUp").css({"visibility": "visible"}); $("#btnSignUp").css({"visibility": "visible"});
$(document).on("keydown", function(e){ $(document).on("keydown", function(e){
if(e.keyCode == 13){ if(e.keyCode == 13){
e.preventDefault(); e.preventDefault();
if($("#txtCommonSearch").is(":focus")){ if($("#txtCommonSearch").is(":focus")){
document.location = "/search?q=" + $("#txtCommonSearch").val(); document.location = "/search?q=" + $("#txtCommonSearch").val();
} }
} }
}); });
$("#btnSignIn").on("click", function(){ $("#btnSignIn").on("click", function(){
document.location = "/signIn"; document.location = "/signIn";
}); });
$("#btnSignUp").on("click", function(){ $("#btnSignUp").on("click", function(){
document.location = "/register"; document.location = "/register";
}); });
}); });

View File

@ -23,461 +23,451 @@ jQuery(function(){
if(jqXhr.status == 403){ if(jqXhr.status == 403){
return false; return false;
} }
} }
} }
}).exec() }).exec()
).then(function(){ ).then(function(){
noNeedToLoginCallback(); noNeedToLoginCallback();
needToLoginCallback(); needToLoginCallback();
}).fail(function(){ }).fail(function(){
noNeedToLoginCallback(); noNeedToLoginCallback();
}); });
function noNeedToLoginCallback(){ function noNeedToLoginCallback(){
$("#tabItemDetail a:first").tab("show");
$("#btnFilterOption button:first").addClass("active");
$("#divErrMsg").hide();
if($("#public").val() == 1){
$("#tabItemDetail li:eq(1)").hide();
$("#tabItemDetail li:eq(2)").hide();
}
listRepo($("#repoName").val());
function listRepo(repoName){
$("#tabItemDetail a:first").tab("show");
$("#btnFilterOption button:first").addClass("active");
$("#divErrMsg").hide(); $("#divErrMsg").hide();
new AjaxUtil({ if($("#public").val() == 1){
url: "/api/repositories?project_id=" + $("#projectId").val() + "&q=" + repoName, $("#tabItemDetail li:eq(1)").hide();
type: "get", $("#tabItemDetail li:eq(2)").hide();
success: function(data, status, xhr){ }
if(xhr && xhr.status == 200){
$("#accordionRepo").children().remove(); listRepo($("#repoName").val());
if(data == null){
$("#divErrMsg").show(); function listRepo(repoName){
$("#divErrMsg center").html(i18n.getMessage("no_repo_exists"));
return; $("#divErrMsg").hide();
}
$.each(data, function(i, e){ new AjaxUtil({
var targetId = e.replace(/\//g, "------").replace(/\./g, "---"); url: "/api/repositories?project_id=" + $("#projectId").val() + "&q=" + repoName,
var row = '<div class="panel panel-default" targetId="' + targetId + '">' + type: "get",
success: function(data, status, xhr){
if(xhr && xhr.status == 200){
$("#accordionRepo").children().remove();
if(data == null){
$("#divErrMsg").show();
$("#divErrMsg center").html(i18n.getMessage("no_repo_exists"));
return;
}
$.each(data, function(i, e){
var targetId = e.replace(/\//g, "------").replace(/\./g, "---");
var row = '<div class="panel panel-default" targetId="' + targetId + '">' +
'<div class="panel-heading" role="tab" id="heading' + i + '"+ >' + '<div class="panel-heading" role="tab" id="heading' + i + '"+ >' +
'<h4 class="panel-title">' + '<h4 class="panel-title">' +
'<a data-toggle="collapse" data-parent="#accordion" href="#collapse'+ i + '" aria-expanded="true" aria-controls="collapse' + i + '">' + '<a data-toggle="collapse" data-parent="#accordion" href="#collapse'+ i + '" aria-expanded="true" aria-controls="collapse' + i + '">' +
'<span class="list-group-item-heading"> <span class="glyphicon glyphicon-book blue"></span> ' + e + ' </span>' + '<span class="list-group-item-heading"> <span class="glyphicon glyphicon-book blue"></span> ' + e + ' </span>' +
'</a>' + '</a>' +
'</h4>' + '</h4>' +
'</div>' + '</div>' +
'<div id="collapse' + i + '" targetId="' + targetId + '" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading' + i + '">' + '<div id="collapse' + i + '" targetId="' + targetId + '" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading' + i + '">' +
'<div class="panel-body" id="' + targetId + '">' + '<div class="panel-body" id="' + targetId + '">' +
'<div class="table-responsive" style="height: auto;">' + '<div class="table-responsive" style="height: auto;">' +
'<table class="table table-striped table-bordered table-condensed">' + '<table class="table table-striped table-bordered table-condensed">' +
'<thead>' + '<thead>' +
'<tr>' + '<tr>' +
'<th class="st-sort-ascent" st-sort="name" st-sort-default=""><span class="glyphicon glyphicon-tag blue"></span> ' + i18n.getMessage("tag")+ ' </th>' + '<th class="st-sort-ascent" st-sort="name" st-sort-default=""><span class="glyphicon glyphicon-tag blue"></span> ' + i18n.getMessage("tag")+ ' </th>' +
'<th class="st-sort-ascent" st-sort="name" st-sort-default=""><span class="glyphicon glyphicon-tag blue"></span> ' + i18n.getMessage("pull_command") + ' </th>' + '<th class="st-sort-ascent" st-sort="name" st-sort-default=""><span class="glyphicon glyphicon-tag blue"></span> ' + i18n.getMessage("pull_command") + ' </th>' +
'</tr>' + '</tr>' +
'</thead>' + '</thead>' +
'<tbody>' + '<tbody>' +
'</tbody>' + '</tbody>' +
'</table>' '</table>'
'</div>' + '</div>' +
'</div>' + '</div>' +
'</div>' + '</div>' +
'</div>'; '</div>';
$("#accordionRepo").append(row); $("#accordionRepo").append(row);
}); });
if(repoName != ""){ if(repoName != ""){
$("#txtRepoName").val(repoName); $("#txtRepoName").val(repoName);
$("#accordionRepo #heading0 a").trigger("click"); $("#accordionRepo #heading0 a").trigger("click");
}
} }
} }
} }).exec();
}).exec(); }
} $("#btnSearchRepo").on("click", function(){
$("#btnSearchRepo").on("click", function(){ listRepo($.trim($("#txtRepoName").val()));
listRepo($.trim($("#txtRepoName").val())); });
});
$('#accordionRepo').on('show.bs.collapse', function (e) { $('#accordionRepo').on('show.bs.collapse', function (e) {
$('#accordionRepo .in').collapse('hide'); $('#accordionRepo .in').collapse('hide');
var targetId = $(e.target).attr("targetId"); var targetId = $(e.target).attr("targetId");
var repoName = targetId.replace(/[-]{6}/g, "/").replace(/[-]{3}/g, '.'); var repoName = targetId.replace(/[-]{6}/g, "/").replace(/[-]{3}/g, ".");
new AjaxUtil({ new AjaxUtil({
url: "/api/repositories/tags?repo_name=" + repoName, url: "/api/repositories/tags?repo_name=" + repoName,
type: "get", type: "get",
success: function(data, status, xhr){ success: function(data, status, xhr){
$('#' + targetId +' table tbody tr').remove(); $('#' + targetId +' table tbody tr').remove();
var row = []; var row = [];
for(var i in data){ for(var i in data){
var tagName = data[i]; var tagName = data[i]
row.push('<tr><td><a href="#" imageId="' + tagName + '" repoName="' + repoName + '">' + tagName + '</a></td><td><input type="text" style="width:100%" readonly value=" docker pull '+ $("#harborRegUrl").val() +'/'+ repoName + ':' + tagName +'"></td></tr>'); row.push('<tr><td><a href="#" imageId="' + tagName + '" repoName="' + repoName + '">' + tagName + '</a></td><td><input type="text" style="width:100%" readonly value=" docker pull '+ $("#harborRegUrl").val() +'/'+ repoName + ':' + tagName +'"></td></tr>');
} }
$('#' + targetId +' table tbody').append(row.join("")); $('#' + targetId +' table tbody').append(row.join(""));
$('#' + targetId +' table tbody tr a').on("click", function(e){ $('#' + targetId +' table tbody tr a').on("click", function(e){
var imageId = $(this).attr("imageId"); var imageId = $(this).attr("imageId");
var repoName = $(this).attr("repoName"); var repoName = $(this).attr("repoName");
new AjaxUtil({ new AjaxUtil({
url: "/api/repositories/manifests?repo_name=" + repoName + "&tag=" + imageId, url: "/api/repositories/manifests?repo_name=" + repoName + "&tag=" + imageId,
type: "get", type: "get",
success: function(data, status, xhr){ success: function(data, status, xhr){
if(data){ if(data){
for(var i in data){ for(var i in data){
if(data[i] == ""){ if(data[i] == ""){
data[i] = "N/A"; data[i] = "N/A";
}
}
data.Created = moment(new Date(data.Created)).format("YYYY-MM-DD HH:mm:ss");
$("#dlgModal").dialogModal({"title": i18n.getMessage("image_details"), "content": data});
} }
} }
data.Created = moment(new Date(data.Created)).format("YYYY-MM-DD HH:mm:ss"); }).exec();
});
$("#dlgModal").dialogModal({"title": i18n.getMessage("image_details"), "content": data});
}
}
}).exec();
});
}
}).exec();
});
}
function needToLoginCallback(){
var hasAuthorization = false;
$.when(
new AjaxUtil({
url: "/api/projects/" + $("#projectId").val() + "/members/current",
type: "get",
success: function(data, status, xhr){
if(xhr && xhr.status == 200 && data.roles != null && data.roles.length > 0){
hasAuthorization = true;
} }
}
}).exec())
.done(function(){
if(!hasAuthorization) return false;
$("#tabItemDetail a:eq(1)").css({"visibility": "visible"});
$("#tabItemDetail a:eq(2)").css({"visibility": "visible"});
$(".glyphicon .glyphicon-pencil", "#tblUser").on("click", function(e){
$("#txtUserName").hide();
$("#lblUserName").show();
$("#dlgUserTitle").text(i18n.getMessage("edit_members"));
});
$("#btnAddUser").on("click", function(){
$("#operationType").val("add");
$("#spnSearch").show();
$("#txtUserName").prop("disabled", false)
$("#txtUserName").val("");
$("#lstRole input[name=chooseRole]:radio").prop("checked", false);
$("#dlgUserTitle").text(i18n.getMessage("add_members"));
});
$("#btnSave").on("click", function(){
var username = $("#txtUserName").val();
if($.trim(username).length == 0){
$("#dlgModal").dialogModal({"title": i18n.getMessage("add_member_failed"), "content": i18n.getMessage("please_input_username")});
return;
}
var projectId = $("#projectId").val();
var operationType = $("#operationType").val();
var userId = $("#editUserId").val();
var checkedRole = $("#lstRole input[name='chooseRole']:checked")
if(checkedRole.length == 0){
$("#dlgModal").dialogModal({"title": i18n.getMessage("add_member_failed"), "content": i18n.getMessage("please_assign_a_role_to_user")});
return;
}
var checkedRoleItemList = [];
$.each(checkedRole, function(i, e){
checkedRoleItemList.push(new Number($(this).val()));
});
var ajaxOpts = {};
if(operationType == "add"){
ajaxOpts.url = "/api/projects/" + projectId + "/members/";
ajaxOpts.type = "post";
ajaxOpts.data = {"roles" : checkedRoleItemList, "user_name": username};
}else if(operationType == "edit"){
ajaxOpts.url = "/api/projects/" + projectId + "/members/" + userId;
ajaxOpts.type = "put";
ajaxOpts.data = {"roles" : checkedRoleItemList};
}
new AjaxUtil({
url: ajaxOpts.url,
data: ajaxOpts.data,
type: ajaxOpts.type,
complete: function(jqXhr, status){
if(jqXhr && jqXhr.status == 200){
$("#btnCancel").trigger("click");
listUser(null);
}
},
errors: {
404: i18n.getMessage("user_id_does_not_exist"),
409: i18n.getMessage("user_id_exists"),
403: i18n.getMessage("insufficient_privileges")
}
}).exec(); }).exec();
}); });
}
var name_mapping = { function needToLoginCallback(){
"projectAdmin": "Project Admin",
"developer": "Developer",
"guest": "Guest"
}
function listUserByProjectCallback(userList){ var hasAuthorization = false;
var loginedUserId = $("#userId").val();
var loginedUserRoleId = $("#roleId").val();
var ownerId = $("#ownerId").val();
$("#tblUser tbody tr").remove(); $.when(
for(var i = 0; i < userList.length; ){ new AjaxUtil({
url: "/api/projects/" + $("#projectId").val() + "/members/current",
var userId = userList[i].UserId; type: "get",
var roleId = userList[i].RoleId; success: function(data, status, xhr){
var username = userList[i].username; if(xhr && xhr.status == 200 && data.roles != null && data.roles.length > 0){
var roleNameList = []; hasAuthorization = true;
for(var j = i; j < userList.length; i++, j++){
if(userList[j].UserId == userId){
roleNameList.push(name_mapping[userList[j].Rolename]);
}else{
break;
} }
} }
}).exec())
.done(function(){
var row = '<tr>' + if(!hasAuthorization) return false;
'<td>' + username + '</td>' +
'<td>' + roleNameList.join(",") + '</td>' + $("#tabItemDetail a:eq(1)").css({"visibility": "visible"});
'<td>'; $("#tabItemDetail a:eq(2)").css({"visibility": "visible"});
var isShowOperations = true;
if(loginedUserRoleId >= 3 /*role: developer guest*/){ $(".glyphicon .glyphicon-pencil", "#tblUser").on("click", function(e){
isShowOperations = false; $("#txtUserName").hide();
}else if(ownerId == userId){ $("#lblUserName").show();
isShowOperations = false; $("#dlgUserTitle").text(i18n.getMessage("edit_members"));
}else if (loginedUserId == userId){ });
isShowOperations = false;
} $("#btnAddUser").on("click", function(){
if(isShowOperations){ $("#operationType").val("add");
row += '<a href="#" userid="' + userId + '" class="glyphicon glyphicon-pencil" data-toggle="modal" data-target="#dlgUser"></a>&nbsp;' + $("#spnSearch").show();
'<a href="#" userid="' + userId + '" roleid="' + roleId + '" class="glyphicon glyphicon-trash"></a>'; $("#txtUserName").prop("disabled", false)
$("#txtUserName").val("");
$("#lstRole input[name=chooseRole]:radio").prop("checked", false);
$("#dlgUserTitle").text(i18n.getMessage("add_members"));
});
$("#btnSave").on("click", function(){
var username = $("#txtUserName").val();
if($.trim(username).length == 0){
$("#dlgModal").dialogModal({"title": i18n.getMessage("add_member_failed"), "content": i18n.getMessage("please_input_username")});
return;
}
var projectId = $("#projectId").val();
var operationType = $("#operationType").val();
var userId = $("#editUserId").val();
var checkedRole = $("#lstRole input[name='chooseRole']:checked")
if(checkedRole.length == 0){
$("#dlgModal").dialogModal({"title": i18n.getMessage("add_member_failed"), "content": i18n.getMessage("please_assign_a_role_to_user")});
return;
} }
row += '</td></tr>'; var checkedRoleItemList = [];
$("#tblUser tbody").append(row); $.each(checkedRole, function(i, e){
checkedRoleItemList.push(new Number($(this).val()));
}
}
function searchAccessLogCallback(LogList){
$("#tabOperationLog tbody tr").remove();
$.each(LogList || [], function(i, e){
$("#tabOperationLog tbody").append(
'<tr>' +
'<td>' + e.Username + '</td>' +
'<td>' + e.RepoName + '</td>' +
'<td>' + e.RepoTag + '</td>' +
'<td>' + e.Operation + '</td>' +
'<td>' + moment(new Date(e.OpTime)).format("YYYY-MM-DD HH:mm:ss") + '</td>' +
'</tr>');
});
}
function getUserRoleCallback(userId){
new AjaxUtil({
url: "/api/projects/" + $("#projectId").val() + "/members/" + userId,
type: "get",
success: function(data, status, xhr){
var user = data;
$("#operationType").val("edit");
$("#editUserId").val(user.user_id);
$("#spnSearch").hide();
$("#txtUserName").val(user.user_name);
$("#txtUserName").prop("disabled", true);
$("#btnSave").removeClass("disabled");
$("#dlgUserTitle").text(i18n.getMessage("edit_members"));
$("#lstRole input[name=chooseRole]:radio").not('[value=' + user.role_id + ']').prop("checked", false)
$.each(user.roles, function(i, e){
$("#lstRole input[name=chooseRole]:radio").filter('[value=' + e.role_id + ']').prop("checked", "checked");
});
}
}).exec();
}
function listUser(username){
$.when(
new AjaxUtil({
url: "/api/projects/" + $("#projectId").val() + "/members?username=" + (username == null ? "" : username),
type: "get",
errors: {
403: ""
},
success: function(data, status, xhr){
return data || [];
}
}).exec()
).done(function(userList){
listUserByProjectCallback(userList || []);
$("#tblUser .glyphicon-pencil").on("click", function(e){
var userId = $(this).attr("userid")
getUserRoleCallback(userId);
}); });
$("#tblUser .glyphicon-trash").on("click", function(){
var userId = $(this).attr("userid"); var ajaxOpts = {};
if(operationType == "add"){
ajaxOpts.url = "/api/projects/" + projectId + "/members/";
ajaxOpts.type = "post";
ajaxOpts.data = {"roles" : checkedRoleItemList, "username": username};
}else if(operationType == "edit"){
ajaxOpts.url = "/api/projects/" + projectId + "/members/" + userId;
ajaxOpts.type = "put";
ajaxOpts.data = {"roles" : checkedRoleItemList};
}
new AjaxUtil({
url: ajaxOpts.url,
data: ajaxOpts.data,
type: ajaxOpts.type,
complete: function(jqXhr, status){
if(jqXhr && jqXhr.status == 200){
$("#btnCancel").trigger("click");
listUser(null);
}
},
errors: {
404: i18n.getMessage("user_id_does_not_exist"),
409: i18n.getMessage("user_id_exists"),
403: i18n.getMessage("insufficient_privileges")
}
}).exec();
});
var name_mapping = {
"projectAdmin": "Project Admin",
"developer": "Developer",
"guest": "Guest"
}
function listUserByProjectCallback(userList){
var loginedUserId = $("#userId").val();
var loginedUserRoleId = $("#roleId").val();
var ownerId = $("#ownerId").val();
$("#tblUser tbody tr").remove();
for(var i = 0; i < userList.length; i++){
var userId = userList[i].user_id;
var roleId = userList[i].role_id;
var username = userList[i].username;
var row = '<tr>' +
'<td>' + username + '</td>' +
'<td>' + name_mapping[userList[i].role_name] + '</td>' +
'<td>';
var isShowOperations = true;
if(loginedUserRoleId >= 3 /*role: developer guest*/){
isShowOperations = false;
}else if(ownerId == userId){
isShowOperations = false;
}else if (loginedUserId == userId){
isShowOperations = false;
}
if(isShowOperations){
row += '<a href="#" userid="' + userId + '" class="glyphicon glyphicon-pencil" data-toggle="modal" data-target="#dlgUser"></a>&nbsp;' +
'<a href="#" userid="' + userId + '" roleid="' + roleId + '" class="glyphicon glyphicon-trash"></a>';
}
row += '</td></tr>';
$("#tblUser tbody").append(row);
}
}
function searchAccessLogCallback(LogList){
$("#tabOperationLog tbody tr").remove();
$.each(LogList || [], function(i, e){
$("#tabOperationLog tbody").append(
'<tr>' +
'<td>' + e.username + '</td>' +
'<td>' + e.repo_name + '</td>' +
'<td>' + e.repo_tag + '</td>' +
'<td>' + e.operation + '</td>' +
'<td>' + moment(new Date(e.op_time)).format("YYYY-MM-DD HH:mm:ss") + '</td>' +
'</tr>');
});
}
function getUserRoleCallback(userId){
new AjaxUtil({
url: "/api/projects/" + $("#projectId").val() + "/members/" + userId,
type: "get",
success: function(data, status, xhr){
var user = data;
$("#operationType").val("edit");
$("#editUserId").val(user.user_id);
$("#spnSearch").hide();
$("#txtUserName").val(user.username);
$("#txtUserName").prop("disabled", true);
$("#btnSave").removeClass("disabled");
$("#dlgUserTitle").text(i18n.getMessage("edit_members"));
$("#lstRole input[name=chooseRole]:radio").not('[value=' + user.role_id + ']').prop("checked", false)
$.each(user.roles, function(i, e){
$("#lstRole input[name=chooseRole]:radio").filter('[value=' + e.role_id + ']').prop("checked", "checked");
});
}
}).exec();
}
function listUser(username){
$.when(
new AjaxUtil({ new AjaxUtil({
url: "/api/projects/" + $("#projectId").val() + "/members/" + userId, url: "/api/projects/" + $("#projectId").val() + "/members?username=" + (username == null ? "" : username),
type: "delete", type: "get",
complete: function(jqXhr, status){ errors: {
if(jqXhr && jqXhr.status == 200){ 403: ""
listUser(null); },
} success: function(data, status, xhr){
} return data || [];
}).exec(); }
}); }).exec()
}); ).done(function(userList){
} listUserByProjectCallback(userList || []);
listUser(null); $("#tblUser .glyphicon-pencil").on("click", function(e){
listOperationLogs(); var userId = $(this).attr("userid")
getUserRoleCallback(userId);
});
$("#tblUser .glyphicon-trash").on("click", function(){
var userId = $(this).attr("userid");
new AjaxUtil({
url: "/api/projects/" + $("#projectId").val() + "/members/" + userId,
type: "delete",
complete: function(jqXhr, status){
if(jqXhr && jqXhr.status == 200){
listUser(null);
}
}
}).exec();
});
});
}
listUser(null);
listOperationLogs();
function listOperationLogs(){ function listOperationLogs(){
var projectId = $("#projectId").val(); var projectId = $("#projectId").val();
$.when( $.when(
new AjaxUtil({ new AjaxUtil({
url : "/api/projects/" + projectId + "/logs/filter", url : "/api/projects/" + projectId + "/logs/filter",
data: {}, data: {},
type: "post", type: "post",
success: function(data){ success: function(data){
return data || []; return data || [];
}
}).exec()
).done(function(operationLogs){
searchAccessLogCallback(operationLogs);
});
} }
}).exec()
).done(function(operationLogs){
searchAccessLogCallback(operationLogs);
});
}
$("#btnSearchUser").on("click", function(){ $("#btnSearchUser").on("click", function(){
var username = $("#txtSearchUser").val(); var username = $("#txtSearchUser").val();
if($.trim(username).length == 0){ if($.trim(username).length == 0){
username = null; username = null;
} }
listUser(username); listUser(username);
}); });
function toUTCSeconds(date, hour, min, sec) { function toUTCSeconds(date, hour, min, sec) {
var t = new Date(date); var t = new Date(date);
t.setHours(hour); t.setHours(hour);
t.setMinutes(min); t.setMinutes(min);
t.setSeconds(sec); t.setSeconds(sec);
var utcTime = new Date(t.getUTCFullYear(), var utcTime = new Date(t.getUTCFullYear(),
t.getUTCMonth(), t.getUTCMonth(),
t.getUTCDate(), t.getUTCDate(),
t.getUTCHours(), t.getUTCHours(),
t.getUTCMinutes(), t.getUTCMinutes(),
t.getUTCSeconds()); t.getUTCSeconds());
return utcTime.getTime() / 1000; return utcTime.getTime() / 1000;
}
$("#btnFilterLog").on("click", function(){
var projectId = $("#projectId").val();
var username = $("#txtSearchUserName").val();
var beginTimestamp = 0;
var endTimestamp = 0;
if($("#begindatepicker").val() != ""){
beginTimestamp = toUTCSeconds($("#begindatepicker").val(), 0, 0, 0);
}
if($("#enddatepicker").val() != ""){
endTimestamp = toUTCSeconds($("#enddatepicker").val(), 23, 59, 59);
}
new AjaxUtil({
url: "/api/projects/" + projectId + "/logs/filter",
data:{"username":username, "project_id" : projectId, "keywords" : getKeyWords() , "beginTimestamp" : beginTimestamp, "endTimestamp" : endTimestamp},
type: "post",
success: function(data, status, xhr){
if(xhr && xhr.status == 200){
searchAccessLogCallback(data);
} }
}
}).exec();
});
$("#spnFilterOption input[name=chkAll]").on("click", function(){ $("#btnFilterLog").on("click", function(){
$("#spnFilterOption input[name=chkOperation]").prop("checked", $(this).prop("checked"));
});
$("#spnFilterOption input[name=chkOperation]").on("click", function(){ var projectId = $("#projectId").val();
if(!$(this).prop("checked")){ var username = $("#txtSearchUserName").val();
$("#spnFilterOption input[name=chkAll]").prop("checked", false);
}
var selectedAll = true; var beginTimestamp = 0;
var endTimestamp = 0;
$("#spnFilterOption input[name=chkOperation]").each(function(i, e){ if($("#begindatepicker").val() != ""){
if(!$(e).prop("checked")){ beginTimestamp = toUTCSeconds($("#begindatepicker").val(), 0, 0, 0);
selectedAll = false; }
} if($("#enddatepicker").val() != ""){
}); endTimestamp = toUTCSeconds($("#enddatepicker").val(), 23, 59, 59);
}
if(selectedAll){ new AjaxUtil({
$("#spnFilterOption input[name=chkAll]").prop("checked", true); url: "/api/projects/" + projectId + "/logs/filter",
} data:{"username":username, "project_id" : Number(projectId), "keywords" : getKeyWords() , "begin_timestamp" : beginTimestamp, "end_timestamp" : endTimestamp},
}); type: "post",
success: function(data, status, xhr){
if(xhr && xhr.status == 200){
searchAccessLogCallback(data);
}
}
}).exec();
});
function getKeyWords(){ $("#spnFilterOption input[name=chkAll]").on("click", function(){
var keywords = ""; $("#spnFilterOption input[name=chkOperation]").prop("checked", $(this).prop("checked"));
var checkedItemList=$("#spnFilterOption input[name=chkOperation]:checked"); });
var keywords = [];
$.each(checkedItemList, function(i, e){
var itemValue = $(e).val();
if(itemValue == "others" && $.trim($("#txtOthers").val()).length > 0){
keywords.push($("#txtOthers").val());
}else{
keywords.push($(e).val());
}
});
return keywords.join("/");
}
$('#datetimepicker1').datetimepicker({ $("#spnFilterOption input[name=chkOperation]").on("click", function(){
locale: i18n.getLocale(), if(!$(this).prop("checked")){
ignoreReadonly: true, $("#spnFilterOption input[name=chkAll]").prop("checked", false);
format: 'L', }
showClear: true
});
$('#datetimepicker2').datetimepicker({
locale: i18n.getLocale(),
ignoreReadonly: true,
format: 'L',
showClear: true
});
});
}
$(document).on("keydown", function(e){ var selectedAll = true;
if(e.keyCode == 13){
e.preventDefault(); $("#spnFilterOption input[name=chkOperation]").each(function(i, e){
if($("#tabItemDetail li:eq(0)").is(":focus") || $("#txtRepoName").is(":focus")){ if(!$(e).prop("checked")){
$("#btnSearchRepo").trigger("click"); selectedAll = false;
}else if($("#tabItemDetail li:eq(1)").is(":focus") || $("#txtSearchUser").is(":focus")){ }
$("#btnSearchUser").trigger("click"); });
}else if($("#tabItemDetail li:eq(2)").is(":focus") || $("#txtSearchUserName").is(":focus")){
$("#btnFilterLog").trigger("click"); if(selectedAll){
}else if($("#txtUserName").is(":focus") || $("#lstRole :radio").is(":focus")){ $("#spnFilterOption input[name=chkAll]").prop("checked", true);
$("#btnSave").trigger("click"); }
} });
function getKeyWords(){
var keywords = "";
var checkedItemList=$("#spnFilterOption input[name=chkOperation]:checked");
var keywords = [];
$.each(checkedItemList, function(i, e){
var itemValue = $(e).val();
if(itemValue == "others" && $.trim($("#txtOthers").val()).length > 0){
keywords.push($("#txtOthers").val());
}else{
keywords.push($(e).val());
}
});
return keywords.join("/");
}
$('#datetimepicker1').datetimepicker({
locale: i18n.getLocale(),
ignoreReadonly: true,
format: 'L',
showClear: true
});
$('#datetimepicker2').datetimepicker({
locale: i18n.getLocale(),
ignoreReadonly: true,
format: 'L',
showClear: true
});
});
}
$(document).on("keydown", function(e){
if(e.keyCode == 13){
e.preventDefault();
if($("#tabItemDetail li:eq(0)").is(":focus") || $("#txtRepoName").is(":focus")){
$("#btnSearchRepo").trigger("click");
}else if($("#tabItemDetail li:eq(1)").is(":focus") || $("#txtSearchUser").is(":focus")){
$("#btnSearchUser").trigger("click");
}else if($("#tabItemDetail li:eq(2)").is(":focus") || $("#txtSearchUserName").is(":focus")){
$("#btnFilterLog").trigger("click");
}else if($("#txtUserName").is(":focus") || $("#lstRole :radio").is(":focus")){
$("#btnSave").trigger("click");
} }
}); }
});
}) })

View File

@ -24,7 +24,7 @@ jQuery(function(){
}, },
error: function(jqXhr){ error: function(jqXhr){
if(jqXhr.status == 401) if(jqXhr.status == 401)
return false; return false;
} }
}).exec(); }).exec();

View File

@ -19,7 +19,7 @@ jQuery(function(){
type: "get", type: "get",
success: function(data, status, xhr){ success: function(data, status, xhr){
if(xhr && xhr.status == 200){ if(xhr && xhr.status == 200){
if(data.HasAdminRole == 1) { if(data.has_admin_role == 1) {
renderForAdminRole(); renderForAdminRole();
} }
renderForAnyRole(); renderForAnyRole();
@ -32,16 +32,16 @@ jQuery(function(){
$(document).on("keydown", function(e){ $(document).on("keydown", function(e){
if(e.keyCode == 13){ if(e.keyCode == 13){
e.preventDefault(); e.preventDefault();
if($("#tabProject li:eq(0)").is(":focus") || $("#txtSearchProject").is(":focus")){ if($("#tabProject li:eq(0)").is(":focus") || $("#txtSearchProject").is(":focus")){
$("#btnSearch").trigger("click"); $("#btnSearch").trigger("click");
}else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchPublicProjects").is(":focus")){ }else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchPublicProjects").is(":focus")){
$("#btnSearchPublicProjects").trigger("click"); $("#btnSearchPublicProjects").trigger("click");
}else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchUsername").is(":focus")){ }else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchUsername").is(":focus")){
$("#btnSearchUsername").trigger("click"); $("#btnSearchUsername").trigger("click");
}else if($("#dlgAddProject").is(":focus") || $("#projectName").is(":focus")){ }else if($("#dlgAddProject").is(":focus") || $("#projectName").is(":focus")){
$("#btnSave").trigger("click"); $("#btnSave").trigger("click");
} }
} }
}); });
@ -49,37 +49,37 @@ jQuery(function(){
currentPublic = isPublic; currentPublic = isPublic;
$.when( $.when(
new AjaxUtil({ new AjaxUtil({
url: "/api/projects?is_public=" + isPublic + "&project_name=" + (projectName == null ? "" : projectName) + "&timestamp=" + new Date().getTime(), url: "/api/projects?is_public=" + isPublic + "&project_name=" + (projectName == null ? "" : projectName) + "&timestamp=" + new Date().getTime(),
type: "get", type: "get",
success: function(data, status, xhr){ success: function(data, status, xhr){
$("#tblProject tbody tr").remove(); $("#tblProject tbody tr").remove();
$.each(data || [], function(i, e){ $.each(data || [], function(i, e){
var row = '<tr>' + var row = '<tr>' +
'<td style="vertical-align: middle;"><a href="/registry/detail?project_id=' + e.ProjectId + '">' + e.Name + '</a></td>' + '<td style="vertical-align: middle;"><a href="/registry/detail?project_id=' + e.project_id + '">' + e.name + '</a></td>' +
'<td style="vertical-align: middle;">' + moment(new Date(e.CreationTime)).format("YYYY-MM-DD HH:mm:ss") + '</td>'; '<td style="vertical-align: middle;">' + moment(new Date(e.creation_time)).format("YYYY-MM-DD HH:mm:ss") + '</td>';
if(e.Public == 1 && e.Togglable){ if(e.public == 1 && e.Togglable){
row += '<td><button type="button" class="btn btn-success" projectid="' + e.ProjectId + '">' + i18n.getMessage("button_on")+ '</button></td>' row += '<td><button type="button" class="btn btn-success" projectid="' + e.project_id + '">' + i18n.getMessage("button_on")+ '</button></td>'
} else if (e.Public == 1) { } else if (e.public == 1) {
row += '<td><button type="button" class="btn btn-success" projectid="' + e.ProjectId + '" disabled>' + i18n.getMessage("button_on")+ '</button></td>'; row += '<td><button type="button" class="btn btn-success" projectid="' + e.project_id + '" disabled>' + i18n.getMessage("button_on")+ '</button></td>';
} else if (e.Public == 0 && e.Togglable) { } else if (e.public == 0 && e.Togglable) {
row += '<td><button type="button" class="btn btn-danger" projectid="' + e.ProjectId + '">' + i18n.getMessage("button_off")+ '</button></td>'; row += '<td><button type="button" class="btn btn-danger" projectid="' + e.project_id + '">' + i18n.getMessage("button_off")+ '</button></td>';
} else if (e.Public == 0) { } else if (e.public == 0) {
row += '<td><button type="button" class="btn btn-danger" projectid="' + e.ProjectId + '" disabled>' + i18n.getMessage("button_off")+ '</button></td>'; row += '<td><button type="button" class="btn btn-danger" projectid="' + e.project_id + '" disabled>' + i18n.getMessage("button_off")+ '</button></td>';
row += '</tr>'; row += '</tr>';
} }
$("#tblProject tbody").append(row); $("#tblProject tbody").append(row);
}); });
} }
}).exec()) }).exec())
.done(function() { .done(function() {
$("#tblProject tbody tr :button").on("click", function(){ $("#tblProject tbody tr :button").on("click", function(){
var projectId = $(this).attr("projectid"); var projectId = $(this).attr("projectid");
var self = this; var self = this;
new AjaxUtil({ new AjaxUtil({
url: "/api/projects/" + projectId, url: "/api/projects/" + projectId,
data: {"public": ($(self).hasClass("btn-success") ? false : true)}, data: {"public": ($(self).hasClass("btn-success") ? false : true)},
type: "put", type: "put",
complete: function(jqXhr, status) { complete: function(jqXhr, status) {
if($(self).hasClass("btn-success")){ if($(self).hasClass("btn-success")){
$(self).removeClass("btn-success").addClass("btn-danger"); $(self).removeClass("btn-success").addClass("btn-danger");
$(self).html(i18n.getMessage("button_off")); $(self).html(i18n.getMessage("button_off"));
@ -88,9 +88,9 @@ jQuery(function(){
$(self).html(i18n.getMessage("button_on")); $(self).html(i18n.getMessage("button_on"));
} }
} }
}).exec(); }).exec();
}); });
}); });
} }
listProject(null, 0); listProject(null, 0);
var currentPublic = 0; var currentPublic = 0;
@ -119,7 +119,7 @@ jQuery(function(){
$("#projectName").val(""); $("#projectName").val("");
$("#projectName").parent().addClass("has-feedback"); $("#projectName").parent().addClass("has-feedback");
$("#projectName").siblings("span").removeClass("glyphicon-warning-sign").removeClass("glyphicon-ok"); $("#projectName").siblings("span").removeClass("glyphicon-warning-sign").removeClass("glyphicon-ok");
$("#isPublic").prop('checked', false); $("#isPublic").prop('checked', false);
}); });
$("#btnSave").on("click", function(){ $("#btnSave").on("click", function(){
@ -161,47 +161,47 @@ jQuery(function(){
$("#tblUser tbody tr").remove(); $("#tblUser tbody tr").remove();
$.each(data || [], function(i, e){ $.each(data || [], function(i, e){
var row = '<tr>' + var row = '<tr>' +
'<td style="vertical-align: middle;">' + e.username + '</td>' + '<td style="vertical-align: middle;">' + e.username + '</td>' +
'<td style="vertical-align: middle;">' + e.email + '</td>'; '<td style="vertical-align: middle;">' + e.email + '</td>';
if(e.HasAdminRole == 1){ if(e.has_admin_role == 1){
row += '<td style="padding-left: 30px;"><button type="button" class="btn btn-success" userid="' + e.UserId + '">' + i18n.getMessage("button_on") + '</button></td>'; row += '<td style="padding-left: 30px;"><button type="button" class="btn btn-success" userid="' + e.user_id + '">' + i18n.getMessage("button_on") + '</button></td>';
} else { } else {
row += '<td style="padding-left: 30px;"><button type="button" class="btn btn-danger" userid="' + e.UserId + '">' + i18n.getMessage("button_off") + '</button></td>'; row += '<td style="padding-left: 30px;"><button type="button" class="btn btn-danger" userid="' + e.user_id + '">' + i18n.getMessage("button_off") + '</button></td>';
} }
row += '<td style="padding-left: 30px; vertical-align: middle;"><a href="#" style="visibility: hidden;" class="tdDeleteUser" userid="' + e.UserId + '" username="' + e.Username + '"><span class="glyphicon glyphicon-trash"></span></a></td>'; row += '<td style="padding-left: 30px; vertical-align: middle;"><a href="#" style="visibility: hidden;" class="tdDeleteUser" userid="' + e.user_id + '" username="' + e.username + '"><span class="glyphicon glyphicon-trash"></span></a></td>';
row += '</tr>'; row += '</tr>';
$("#tblUser tbody").append(row); $("#tblUser tbody").append(row);
}); });
} }
}).exec() }).exec()
).done(function(){ ).done(function(){
$("#tblUser tbody tr :button").on("click",function(){ $("#tblUser tbody tr :button").on("click",function(){
var userId = $(this).attr("userid"); var userId = $(this).attr("userid");
var self = this; var self = this;
new AjaxUtil({ new AjaxUtil({
url: "/api/users/" + userId, url: "/api/users/" + userId,
type: "put", type: "put",
complete: function(jqXhr, status){ complete: function(jqXhr, status){
if(jqXhr && jqXhr.status == 200){ if(jqXhr && jqXhr.status == 200){
if($(self).hasClass("btn-success")){ if($(self).hasClass("btn-success")){
$(self).removeClass("btn-success").addClass("btn-danger"); $(self).removeClass("btn-success").addClass("btn-danger");
$(self).html(i18n.getMessage("button_off")); $(self).html(i18n.getMessage("button_off"));
}else{ }else{
$(self).removeClass("btn-danger").addClass("btn-success"); $(self).removeClass("btn-danger").addClass("btn-success");
$(self).html(i18n.getMessage("button_on")); $(self).html(i18n.getMessage("button_on"));
}
} }
} }
} }).exec();
}).exec(); });
}); $("#tblUser tbody tr").on("mouseover", function(e){
$("#tblUser tbody tr").on("mouseover", function(){ $(".tdDeleteUser", this).css({"visibility":"visible"});
$(".tdDeleteUser", this).css({"visibility":"visible"}); }).on("mouseout", function(e){
}).on("mouseout", function(){ $(".tdDeleteUser", this).css({"visibility":"hidden"});
$(".tdDeleteUser", this).css({"visibility":"hidden"}); });
}); $("#tblUser tbody tr .tdDeleteUser").on("click", function(e){
$("#tblUser tbody tr .tdDeleteUser").on("click", function(){ var userId = $(this).attr("userid");
var userId = $(this).attr("userid"); $("#dlgModal")
$("#dlgModal")
.dialogModal({ .dialogModal({
"title": i18n.getMessage("delete_user"), "title": i18n.getMessage("delete_user"),
"content": i18n.getMessage("are_you_sure_to_delete_user") + $(this).attr("username") + " ?", "content": i18n.getMessage("are_you_sure_to_delete_user") + $(this).attr("username") + " ?",
@ -218,17 +218,18 @@ jQuery(function(){
error: function(jqXhr){} error: function(jqXhr){}
}).exec(); }).exec();
} }
});
}); });
}); });
}
listUserAdminRole(null);
$("#btnSearchUsername").on("click", function(){
var username = $("#txtSearchUsername").val();
if($.trim(username).length == 0){
username = null;
}
listUserAdminRole(username);
}); });
} }
listUserAdminRole(null); })
$("#btnSearchUsername").on("click", function(){
var username = $("#txtSearchUsername").val();
if($.trim(username).length == 0){
username = null;
}
listUserAdminRole(username);
});
}
})

View File

@ -30,13 +30,13 @@ jQuery(function(){
$("#btnPageSignUp").on("click", function(){ $("#btnPageSignUp").on("click", function(){
validateOptions.Validate(function() { validateOptions.Validate(function() {
var username = $.trim($("#Username").val()); var username = $.trim($("#Username").val());
var email = $.trim($("#Email").val()); var email = $.trim($("#Email").val());
var password = $.trim($("#Password").val()); var password = $.trim($("#Password").val());
var confirmedPassword = $.trim($("#ConfirmedPassword").val()); var confirmedPassword = $.trim($("#ConfirmedPassword").val());
var realname = $.trim($("#Realname").val()); var realname = $.trim($("#Realname").val());
var comment = $.trim($("#Comment").val()); var comment = $.trim($("#Comment").val());
var isAdmin = $("#isAdmin").val(); var isAdmin = $("#isAdmin").val();
new AjaxUtil({ new AjaxUtil({
url : "/api/users", url : "/api/users",
@ -47,29 +47,29 @@ jQuery(function(){
}, },
error:function(jqxhr, status, error){ error:function(jqxhr, status, error){
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
"title": i18n.getMessage("title_sign_up"), "title": i18n.getMessage("title_sign_up"),
"content": i18n.getMessage("internal_error"), "content": i18n.getMessage("internal_error"),
"callback": function(){ "callback": function(){
return; return;
} }
}); });
}, },
complete: function(xhr, status){ complete: function(xhr, status){
$("#btnPageSignUp").prop("disabled", false); $("#btnPageSignUp").prop("disabled", false);
if(xhr && xhr.status == 201){ if(xhr && xhr.status == 201){
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
"title": isAdmin == "true" ? i18n.getMessage("title_add_user") : i18n.getMessage("title_sign_up"), "title": isAdmin == "true" ? i18n.getMessage("title_add_user") : i18n.getMessage("title_sign_up"),
"content": isAdmin == "true" ? i18n.getMessage("added_user_successfully") : i18n.getMessage("registered_successfully"), "content": isAdmin == "true" ? i18n.getMessage("added_user_successfully") : i18n.getMessage("registered_successfully"),
"callback": function(){ "callback": function(){
if(isAdmin == "true") { if(isAdmin == "true") {
document.location = "/registry/project"; document.location = "/registry/project";
}else{ }else{
document.location = "/signIn"; document.location = "/signIn";
}
} }
}); }
});
} }
} }
}).exec(); }).exec();

View File

@ -17,11 +17,11 @@ jQuery(function(){
$("#Password,#ConfirmedPassword").on("blur", validateCallback); $("#Password,#ConfirmedPassword").on("blur", validateCallback);
validateOptions.Items = ["#Password", "#ConfirmedPassword"]; validateOptions.Items = ["#Password", "#ConfirmedPassword"];
function bindEnterKey(){ function bindEnterKey(){
$(document).on("keydown", function(e){ $(document).on("keydown", function(e){
if(e.keyCode == 13){ if(e.keyCode == 13){
e.preventDefault(); e.preventDefault();
$("#btnSubmit").trigger("click"); $("#btnSubmit").trigger("click");
} }
}); });
} }
@ -30,7 +30,6 @@ jQuery(function(){
} }
bindEnterKey(); bindEnterKey();
var spinner = new Spinner({scale:1}).spin(); var spinner = new Spinner({scale:1}).spin();
$("#btnSubmit").on("click", function(){ $("#btnSubmit").on("click", function(){
@ -42,20 +41,20 @@ jQuery(function(){
"type": "post", "type": "post",
"data": {"reset_uuid": resetUuid, "password": password}, "data": {"reset_uuid": resetUuid, "password": password},
"beforeSend": function(e){ "beforeSend": function(e){
unbindEnterKey(); unbindEnterKey();
$("h1").append(spinner.el); $("h1").append(spinner.el);
$("#btnSubmit").prop("disabled", true); $("#btnSubmit").prop("disabled", true);
}, },
"success": function(data, status, xhr){ "success": function(data, status, xhr){
if(xhr && xhr.status == 200){ if(xhr && xhr.status == 200){
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
"title": i18n.getMessage("title_reset_password"), "title": i18n.getMessage("title_reset_password"),
"content": i18n.getMessage("reset_password_successfully"), "content": i18n.getMessage("reset_password_successfully"),
"callback": function(){ "callback": function(){
document.location="/signIn"; document.location="/signIn";
} }
}); });
} }
}, },
@ -66,14 +65,14 @@ jQuery(function(){
"error": function(jqXhr, status, error){ "error": function(jqXhr, status, error){
if(jqXhr){ if(jqXhr){
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
"title": i18n.getMessage("title_reset_password"), "title": i18n.getMessage("title_reset_password"),
"content": i18n.getMessage(jqXhr.responseText), "content": i18n.getMessage(jqXhr.responseText),
"callback": function(){ "callback": function(){
bindEnterKey(); bindEnterKey();
return; return;
} }
}); });
} }
} }
}); });

View File

@ -60,12 +60,12 @@ jQuery(function(){
$.each(data, function(i, e){ $.each(data, function(i, e){
var project, description, repoName; var project, description, repoName;
switch(discriminator){ switch(discriminator){
case "project": case "project":
project = new Project(e.id, e.name, e.public); project = new Project(e.id, e.name, e.public);
description = project.name; description = project.name;
repoName = ""; repoName = "";
break; break;
case "repository": case "repository":
project = new Project(e.project_id, e.project_name, e.project_public); project = new Project(e.project_id, e.project_name, e.project_public);
description = e.repository_name; description = e.repository_name;
repoName = e.repository_name.substring(e.repository_name.lastIndexOf("/") + 1); repoName = e.repository_name.substring(e.repository_name.lastIndexOf("/") + 1);

View File

@ -56,7 +56,7 @@ jQuery(function(){
success: function(jqXhr, status){ success: function(jqXhr, status){
var lastUri = location.search; var lastUri = location.search;
if(lastUri != "" && lastUri.indexOf("=") > 0){ if(lastUri != "" && lastUri.indexOf("=") > 0){
document.location = decodeURIComponent(lastUri.split("=")[1]); document.location = decodeURIComponent(lastUri.split("=")[1]);
}else{ }else{
document.location = "/registry/project"; document.location = "/registry/project";
} }
@ -69,10 +69,10 @@ jQuery(function(){
i18nKey = "check_your_username_or_password" i18nKey = "check_your_username_or_password"
} }
$("#dlgModal") $("#dlgModal")
.dialogModal({ .dialogModal({
"title": i18n.getMessage("title_login_failed"), "title": i18n.getMessage("title_login_failed"),
"content": i18n.getMessage(i18nKey) "content": i18n.getMessage(i18nKey)
}); });
} }
}); });
}); });

View File

@ -26,18 +26,18 @@ var validateOptions = {
"Username" :{ "Username" :{
"Required": { "value" : true, "errMsg" : i18n.getMessage("username_is_required")}, "Required": { "value" : true, "errMsg" : i18n.getMessage("username_is_required")},
"CheckExist": { "value" : function(value){ "CheckExist": { "value" : function(value){
var result = true; var result = true;
$.ajax({ $.ajax({
url: "/userExists", url: "/userExists",
data: {"target": "username", "value" : value}, data: {"target": "username", "value" : value},
dataType: "json", dataType: "json",
type: "post", type: "post",
async: false, async: false,
success: function(data){ success: function(data){
result = data; result = data;
} }
}); });
return result; return result;
}, "errMsg" : i18n.getMessage("username_has_been_taken")}, }, "errMsg" : i18n.getMessage("username_has_been_taken")},
"MaxLength": {"value" : 20, "errMsg" : i18n.getMessage("username_is_too_long")}, "MaxLength": {"value" : 20, "errMsg" : i18n.getMessage("username_is_too_long")},
"IllegalChar": {"value": [",","~","#", "$", "%"] , "errMsg": i18n.getMessage("username_contains_illegal_chars")} "IllegalChar": {"value": [",","~","#", "$", "%"] , "errMsg": i18n.getMessage("username_contains_illegal_chars")}
@ -45,40 +45,40 @@ var validateOptions = {
"Email" :{ "Email" :{
"Required": { "value" : true, "errMsg" : i18n.getMessage("email_is_required")}, "Required": { "value" : true, "errMsg" : i18n.getMessage("email_is_required")},
"RegExp": {"value": /^(([^<>()[\]\\.,;:\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,}))$/, "RegExp": {"value": /^(([^<>()[\]\\.,;:\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,}))$/,
"errMsg": i18n.getMessage("email_contains_illegal_chars")}, "errMsg": i18n.getMessage("email_contains_illegal_chars")},
"CheckExist": { "value" : function(value){ "CheckExist": { "value" : function(value){
var result = true; var result = true;
$.ajax({ $.ajax({
url: "/userExists", url: "/userExists",
data: {"target": "email", "value": value}, data: {"target": "email", "value": value},
dataType: "json", dataType: "json",
type: "post", type: "post",
async: false, async: false,
success: function(data){ success: function(data){
result = data; result = data;
} }
}); });
return result; return result;
}, "errMsg" : i18n.getMessage("email_has_been_taken")} }, "errMsg" : i18n.getMessage("email_has_been_taken")}
}, },
"EmailF" :{ "EmailF" :{
"Required": { "value" : true, "errMsg" : i18n.getMessage("email_is_required")}, "Required": { "value" : true, "errMsg" : i18n.getMessage("email_is_required")},
"RegExp": {"value": /^(([^<>()[\]\\.,;:\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,}))$/, "RegExp": {"value": /^(([^<>()[\]\\.,;:\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,}))$/,
"errMsg": i18n.getMessage("email_content_illegal")}, "errMsg": i18n.getMessage("email_content_illegal")},
"CheckIfNotExist": { "value" : function(value){ "CheckIfNotExist": { "value" : function(value){
var result = true; var result = true;
$.ajax({ $.ajax({
url: "/userExists", url: "/userExists",
data: {"target": "email", "value": value}, data: {"target": "email", "value": value},
dataType: "json", dataType: "json",
type: "post", type: "post",
async: false, async: false,
success: function(data){ success: function(data){
result = data; result = data;
} }
}); });
return result; return result;
}, "errMsg" : i18n.getMessage("email_does_not_exist")} }, "errMsg" : i18n.getMessage("email_does_not_exist")}
}, },
"Realname" :{ "Realname" :{
"Required": { "value" : true, "errMsg" : i18n.getMessage("realname_is_required")}, "Required": { "value" : true, "errMsg" : i18n.getMessage("realname_is_required")},
@ -119,7 +119,7 @@ function validateCallback(target){
var currentId = $(target).attr("id"); var currentId = $(target).attr("id");
var validateItem = validateOptions[currentId]; var validateItem = validateOptions[currentId];
var errMsg = ""; var errMsg = "";
for(var checkTitle in validateItem){ for(var checkTitle in validateItem){

View File

@ -0,0 +1,23 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package HarborAPI
type AccessLog struct {
Username string `json:"username,omitempty"`
Keywords string `json:"keywords,omitempty"`
BeginTimestamp int32 `json:"beginTimestamp,omitempty"`
EndTimestamp int32 `json:"endTimestamp,omitempty"`
}

View File

@ -0,0 +1,113 @@
//Package HarborAPI
//These APIs provide services for manipulating Harbor project.
package HarborAPI
import (
"encoding/json"
//"fmt"
"io/ioutil"
"net/http"
"github.com/dghubble/sling"
)
type HarborAPI struct {
basePath string
}
func NewHarborAPI() *HarborAPI {
return &HarborAPI{
basePath: "http://localhost",
}
}
func NewHarborAPIWithBasePath(basePath string) *HarborAPI {
return &HarborAPI{
basePath: basePath,
}
}
type UsrInfo struct {
Name string
Passwd string
}
//Search for projects and repositories
//Implementation Notes
//The Search endpoint returns information about the projects and repositories
//offered at public status or related to the current logged in user.
//The response includes the project and repository list in a proper display order.
//@param q Search parameter for project and repository name.
//@return []Search
//func (a HarborAPI) SearchGet (q string) (Search, error) {
func (a HarborAPI) SearchGet(q string) (Search, error) {
_sling := sling.New().Get(a.basePath)
// create path and map variables
path := "/api/search"
_sling = _sling.Path(path)
type QueryParams struct {
Query string `url:"q"`
}
_sling = _sling.QueryStruct(&QueryParams{q})
// accept header
accepts := []string{"application/json", "text/plain"}
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
req, err := _sling.Request()
client := &http.Client{}
httpResponse, err := client.Do(req)
defer httpResponse.Body.Close()
body, err := ioutil.ReadAll(httpResponse.Body)
if err != nil {
// handle error
}
var successPayload = new(Search)
err = json.Unmarshal(body, &successPayload)
return *successPayload, err
}
//Create a new project.
//Implementation Notes
//This endpoint is for user to create a new project.
//@param project New created project.
//@return void
//func (a HarborAPI) ProjectsPost (prjUsr UsrInfo, project Project) (int, error) {
func (a HarborAPI) ProjectsPost(prjUsr UsrInfo, project Project) (int, error) {
_sling := sling.New().Post(a.basePath)
// create path and map variables
path := "/api/projects"
_sling = _sling.Path(path)
// accept header
accepts := []string{"application/json", "text/plain"}
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
// body params
_sling = _sling.BodyJSON(project)
req, err := _sling.Request()
req.SetBasicAuth(prjUsr.Name, prjUsr.Passwd)
client := &http.Client{}
httpResponse, err := client.Do(req)
defer httpResponse.Body.Close()
return httpResponse.StatusCode, err
}

View File

@ -0,0 +1,15 @@
// HarborLogout.go
package HarborAPI
import (
"net/http"
)
func (a HarborAPI) HarborLogout() (int, error) {
response, err := http.Get(a.basePath + "/logout")
defer response.Body.Close()
return response.StatusCode, err
}

View File

@ -0,0 +1,28 @@
// HarborLogon.go
package HarborAPI
import (
"io/ioutil"
"net/http"
"net/url"
"strings"
)
func (a HarborAPI) HarborLogin(user UsrInfo) (int, error) {
v := url.Values{}
v.Set("principal", user.Name)
v.Set("password", user.Passwd)
body := ioutil.NopCloser(strings.NewReader(v.Encode())) //endode v:[body struce]
client := &http.Client{}
reqest, err := http.NewRequest("POST", a.basePath+"/login", body)
reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //setting post head
resp, err := client.Do(reqest)
defer resp.Body.Close() //close resp.Body
return resp.StatusCode, err
}

View File

@ -0,0 +1,15 @@
package HarborAPI
import ()
type Project struct {
ProjectId int32 `json:"id,omitempty"`
OwnerId int32 `json:"owner_id,omitempty"`
ProjectName string `json:"project_name,omitempty"`
CreationTime string `json:"creation_time,omitempty"`
Deleted int32 `json:"deleted,omitempty"`
UserId int32 `json:"user_id,omitempty"`
OwnerName string `json:"owner_name,omitempty"`
Public bool `json:"public,omitempty"`
Togglable bool `json:"togglable,omitempty"`
}

View File

@ -0,0 +1,9 @@
package HarborAPI
import ()
type Project4Search struct {
ProjectId int32 `json:"id,omitempty"`
ProjectName string `json:"name,omitempty"`
Public int32 `json:"public,omitempty"`
}

View File

@ -0,0 +1,16 @@
package HarborAPI
import (
"time"
)
type Repository struct {
Id string `json:"id,omitempty"`
Parent string `json:"parent,omitempty"`
Created time.Time `json:"created,omitempty"`
DurationDays string `json:"duration_days,omitempty"`
Author string `json:"author,omitempty"`
Architecture string `json:"architecture,omitempty"`
DockerVersion string `json:"docker_version,omitempty"`
Os string `json:"os,omitempty"`
}

View File

@ -0,0 +1,9 @@
package HarborAPI
type Repository4Search struct {
ProjectId int32 `json:"project_id,omitempty"`
ProjectName string `json:"project_name,omitempty"`
ProjectPublic int32 `json:"project_public,omitempty"`
RepoName string `json:"repository_name,omitempty"`
}

View File

@ -0,0 +1,7 @@
package HarborAPI
type Role struct {
RoleId int32 `json:"role_id,omitempty"`
RoleCode string `json:"role_code,omitempty"`
RoleName string `json:"role_name,omitempty"`
}

View File

@ -0,0 +1,6 @@
package HarborAPI
type RoleParam struct {
Roles []int32 `json:"roles,omitempty"`
UserName string `json:"user_name,omitempty"`
}

View File

@ -0,0 +1,8 @@
package HarborAPI
import ()
type Search struct {
Projects []Project4Search `json:"project,omitempty"`
Repositories []Repository4Search `json:"repository,omitempty"`
}

View File

@ -0,0 +1,11 @@
package HarborAPI
type User struct {
UserId int32 `json:"user_id,omitempty"`
Username string `json:"username,omitempty"`
Email string `json:"email,omitempty"`
Password string `json:"password,omitempty"`
Realname string `json:"realname,omitempty"`
Comment string `json:"comment,omitempty"`
Deleted int32 `json:"deleted,omitempty"`
}

View File

@ -0,0 +1,95 @@
package HarborAPItest
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
"github.com/vmware/harbor/tests/apitests/apilib"
)
func TestAddProject(t *testing.T) {
fmt.Println("Test for Project Add (ProjectsPost) API\n")
assert := assert.New(t)
apiTest := HarborAPI.NewHarborAPI()
//prepare for test
adminEr := &HarborAPI.UsrInfo{"admin", "Harbor1234"}
admin := &HarborAPI.UsrInfo{"admin", "Harbor12345"}
prjUsr := &HarborAPI.UsrInfo{"unknown", "unknown"}
var project HarborAPI.Project
project.ProjectName = "testProject"
project.Public = true
//case 1: admin login fail, expect project creation fail.
fmt.Println("case 1: admin login fail, expect project creation fail.")
resault, err := apiTest.HarborLogin(*adminEr)
if err != nil {
t.Error("Error while admin login", err.Error())
t.Log(err)
} else {
assert.Equal(resault, int(401), "Admin login status should be 401")
//t.Log(resault)
}
resault, err = apiTest.ProjectsPost(*prjUsr, project)
if err != nil {
t.Error("Error while creat project", err.Error())
t.Log(err)
} else {
assert.Equal(resault, int(401), "Case 1: Project creation status should be 401")
//t.Log(resault)
}
//case 2: admin successful login, expect project creation success.
fmt.Println("case 2: admin successful login, expect project creation success.")
resault, err = apiTest.HarborLogin(*admin)
if err != nil {
t.Error("Error while admin login", err.Error())
t.Log(err)
} else {
assert.Equal(resault, int(200), "Admin login status should be 200")
//t.Log(resault)
}
if resault != 200 {
t.Log(resault)
} else {
prjUsr = admin
}
resault, err = apiTest.ProjectsPost(*prjUsr, project)
if err != nil {
t.Error("Error while creat project", err.Error())
t.Log(err)
} else {
assert.Equal(resault, int(201), "Case 2: Project creation status should be 201")
//t.Log(resault)
}
//case 3: duplicate project name, create project fail
fmt.Println("case 3: duplicate project name, create project fail")
resault, err = apiTest.ProjectsPost(*prjUsr, project)
if err != nil {
t.Error("Error while creat project", err.Error())
t.Log(err)
} else {
assert.Equal(resault, int(409), "Case 3: Project creation status should be 409")
//t.Log(resault)
}
//resault1, err := apiTest.HarborLogout()
//if err != nil {
// t.Error("Error while admin logout", err.Error())
// t.Log(err)
//} else {
// assert.Equal(resault1, int(200), "Admin logout status")
// //t.Log(resault)
//}
//if resault1 != 200 {
// t.Log(resault)
//}
}

View File

@ -0,0 +1,31 @@
package HarborAPItest
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
"github.com/vmware/harbor/tests/apitests/apilib"
)
func TestSearch(t *testing.T) {
fmt.Println("Test for Search (SearchGet) API\n")
assert := assert.New(t)
apiTest := HarborAPI.NewHarborAPI()
var resault HarborAPI.Search
resault, err := apiTest.SearchGet("library")
//fmt.Printf("%+v\n", resault)
if err != nil {
t.Error("Error while search project or repository", err.Error())
t.Log(err)
} else {
assert.Equal(resault.Projects[0].ProjectId, int32(1), "Project id should be equal")
assert.Equal(resault.Projects[0].ProjectName, "library", "Project name should be library")
assert.Equal(resault.Projects[0].Public, int32(1), "Project public status should be 1 (true)")
//t.Log(resault)
}
//if resault.Response.StatusCode != 200 {
// t.Log(resault.Response)
//}
}

4
tests/hostcfg.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'`
#echo $IP
sed "s/reg.mydomain.com/$IP/" -i Deploy/harbor.cfg

36
tests/startuptest.go Normal file
View File

@ -0,0 +1,36 @@
// Fetch prints the content found at a URL.
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
)
func main() {
time.Sleep(60*time.Second)
for _, url := range os.Args[1:] {
resp, err := http.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
os.Exit(1)
}
// fmt.Printf("%s", b)
if strings.Contains(string(b), "Harbor") {
fmt.Printf("sucess!\n")
} else {
os.Exit(1)
}
}
}

11
tests/testprepare.sh Executable file
View File

@ -0,0 +1,11 @@
IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'`
#echo $IP
docker pull hello-world
docker pull docker
#docker login -u admin -p Harbor12345 $IP
docker tag hello-world $IP/library/hello-world
docker push $IP/library/hello-world
docker tag docker $IP/library/docker
docker push $IP/library/docker

55
tests/userlogintest.go Normal file
View File

@ -0,0 +1,55 @@
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"flag"
)
func main() {
usrNamePtr := flag.String("name","anaymous","user name")
usrPasswdPtr := flag.String("passwd","anaymous","user password")
flag.Parse()
v := url.Values{}
v.Set("principal", *usrNamePtr)
v.Set("password", *usrPasswdPtr)
body := ioutil.NopCloser(strings.NewReader(v.Encode())) //endode v:[body struce]
fmt.Println(v)
client := &http.Client{}
reqest, err := http.NewRequest("POST", "http://localhost/login", body)
if err != nil {
fmt.Println("Fatal error ", err.Error())
}
reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //setting post head
resp, err := client.Do(reqest)
defer resp.Body.Close() //close resp.Body
fmt.Println("login status: ", resp.StatusCode) //print status code
//content_post, err := ioutil.ReadAll(resp.Body)
//if err != nil {
// fmt.Println("Fatal error ", err.Error())
//}
//fmt.Println(string(content_post)) //print reply
response, err := http.Get("http://localhost/api/logout")
if err != nil {
fmt.Println("Fatal error ", err.Error())
}
defer response.Body.Close()
fmt.Println("logout status: ", resp.StatusCode) //print status code
//content_get, err := ioutil.ReadAll(response.Body)
//fmt.Println(string(content_get))
}

View File

@ -16,32 +16,32 @@
<div class="col-sm-4"></div> <div class="col-sm-4"></div>
<div class="col-sm-4"> <div class="col-sm-4">
<div class="page-header"> <div class="page-header">
<h1>{{i18n .Lang "title_change_password"}}</h1> <h1>{{i18n .Lang "title_change_password"}}</h1>
</div> </div>
<form class="form"> <form class="form">
<div class="alert alert-danger" role="alert" id="divErrMsg"></div> <div class="alert alert-danger" role="alert" id="divErrMsg"></div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="OldPassword" class="control-label">{{i18n .Lang "old_password"}}</label> <label for="OldPassword" class="control-label">{{i18n .Lang "old_password"}}</label>
<input type="password" class="form-control" id="OldPassword"> <input type="password" class="form-control" id="OldPassword">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="Password" class="control-label">{{i18n .Lang "new_password"}}</label> <label for="Password" class="control-label">{{i18n .Lang "new_password"}}</label>
<input type="password" class="form-control" id="Password"> <input type="password" class="form-control" id="Password">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "password_description"}}</h6> <h6>{{i18n .Lang "password_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="ConfirmedPassword" class="control-label">{{i18n .Lang "confirm_password"}}</label> <label for="ConfirmedPassword" class="control-label">{{i18n .Lang "confirm_password"}}</label>
<input type="password" class="form-control" id="ConfirmedPassword"> <input type="password" class="form-control" id="ConfirmedPassword">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "password_description"}}</h6> <h6>{{i18n .Lang "password_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<div class="text-center"> <div class="text-center">
<button type="button" class="btn btn-default" id="btnSubmit">{{i18n .Lang "button_submit"}}</button> <button type="button" class="btn btn-default" id="btnSubmit">{{i18n .Lang "button_submit"}}</button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div class="col-sm-4"></div> <div class="col-sm-4"></div>

View File

@ -19,19 +19,19 @@
<h1>{{i18n .Lang "title_forgot_password"}}</h1> <h1>{{i18n .Lang "title_forgot_password"}}</h1>
</div> </div>
<form class="form"> <form class="form">
<div id="waiting1" class="waiting-nonfluid"></div> <div id="waiting1" class="waiting-nonfluid"></div>
<div class="alert alert-danger" role="alert" id="divErrMsg"></div> <div class="alert alert-danger" role="alert" id="divErrMsg"></div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="EmailF" class="control-label">{{i18n .Lang "email"}}</label> <label for="EmailF" class="control-label">{{i18n .Lang "email"}}</label>
<input type="email" class="form-control" id="EmailF"> <input type="email" class="form-control" id="EmailF">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "forgot_password_description"}}</h6> <h6>{{i18n .Lang "forgot_password_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<div class="text-center"> <div class="text-center">
<button type="button" class="btn btn-default" id="btnSubmit">{{i18n .Lang "button_submit"}}</button> <button type="button" class="btn btn-default" id="btnSubmit">{{i18n .Lang "button_submit"}}</button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div class="col-sm-4"></div> <div class="col-sm-4"></div>

View File

@ -13,25 +13,25 @@
limitations under the License. limitations under the License.
--> -->
<!-- Main jumbotron for a primary marketing message or call to action --> <!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron"> <div class="jumbotron">
<div class="container"> <div class="container">
<img class="pull-left" src="static/resources/image/Harbor_Logo_rec.png" alt="Harbor's Logo" height="180" width="360"/> <img class="pull-left" src="static/resources/image/Harbor_Logo_rec.png" alt="Harbor's Logo" height="180" width="360"/>
<p class="pull-left" style="margin-top: 85px; color: #245580; font-size: 30pt; text-align: center; width: 60%;">{{i18n .Lang "index_title"}}</p> <p class="pull-left" style="margin-top: 85px; color: #245580; font-size: 30pt; text-align: center; width: 60%;">{{i18n .Lang "index_title"}}</p>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
<!-- Example row of columns --> <!-- Example row of columns -->
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<p>{{i18n .Lang "index_desc"}}</p> <p>{{i18n .Lang "index_desc"}}</p>
<p>{{i18n .Lang "index_desc_0"}}</p> <p>{{i18n .Lang "index_desc_0"}}</p>
<p>{{i18n .Lang "index_desc_1"}}</p> <p>{{i18n .Lang "index_desc_1"}}</p>
<p>{{i18n .Lang "index_desc_2"}}</p> <p>{{i18n .Lang "index_desc_2"}}</p>
<p>{{i18n .Lang "index_desc_3"}}</p> <p>{{i18n .Lang "index_desc_3"}}</p>
<p>{{i18n .Lang "index_desc_4"}}</p> <p>{{i18n .Lang "index_desc_4"}}</p>
<p>{{i18n .Lang "index_desc_5"}}</p> <p>{{i18n .Lang "index_desc_5"}}</p>
</div> </div>
</div> </div>
</div> <!-- /container --> </div> <!-- /container -->
<script src="static/resources/js/login.js"></script> <script src="static/resources/js/login.js"></script>

View File

@ -18,7 +18,7 @@
<li>{{.ProjectName}}</li> <li>{{.ProjectName}}</li>
</ol> </ol>
<div class="page-header" style="margin-top: -10px;"> <div class="page-header" style="margin-top: -10px;">
<h2>{{.ProjectName}} </h2></h4>{{i18n .Lang "owner"}}: {{.OwnerName}}</h4> <h2>{{.ProjectName}} </h2><h4>{{i18n .Lang "owner"}}: {{.OwnerName}}</h4>
</div> </div>
<div row="tabpanel"> <div row="tabpanel">
<div class="row"> <div class="row">
@ -29,152 +29,150 @@
<li role="presentation" style="visibility: hidden;"><a href="#tabOperationLog" aria-controls="tabOperationLog" role="tab" data-toggle="tab">{{i18n .Lang "logs"}}</a></li> <li role="presentation" style="visibility: hidden;"><a href="#tabOperationLog" aria-controls="tabOperationLog" role="tab" data-toggle="tab">{{i18n .Lang "logs"}}</a></li>
</ul> </ul>
</div> </div>
<div class="col-md-10"> <div class="col-md-10">
<input type="hidden" id="projectId" value="{{.ProjectId}}"> <input type="hidden" id="projectId" value="{{.ProjectId}}">
<input type="hidden" id="projectName" value="{{.ProjectName}}"> <input type="hidden" id="projectName" value="{{.ProjectName}}">
<input type="hidden" id="userId" value="{{.UserId}}"> <input type="hidden" id="userId" value="{{.UserId}}">
<input type="hidden" id="ownerId" value="{{.OwnerId}}"> <input type="hidden" id="ownerId" value="{{.OwnerId}}">
<input type="hidden" id="roleId" value="{{.RoleId}}"> <input type="hidden" id="roleId" value="{{.RoleId}}">
<input type="hidden" id="harborRegUrl" value="{{.HarborRegUrl}}"> <input type="hidden" id="harborRegUrl" value="{{.HarborRegUrl}}">
<input type="hidden" id="public" value="{{.Public}}"> <input type="hidden" id="public" value="{{.Public}}">
<input type="hidden" id="repoName" value="{{.RepoName}}"> <input type="hidden" id="repoName" value="{{.RepoName}}">
<!-- tab panes --> <!-- tab panes -->
<div class="tab-content"> <div class="tab-content">
<div role="tabpanel" class="tab-pane" id="tabRepoInfo"> <div role="tabpanel" class="tab-pane" id="tabRepoInfo">
<form class="form-inline"> <form class="form-inline">
<div class="form-group"> <div class="form-group">
<label class="sr-only" for="txtRepoName">{{i18n .Lang "repo_name"}}:</label> <label class="sr-only" for="txtRepoName">{{i18n .Lang "repo_name"}}:</label>
<div class="input-group">
<div class="input-group-addon">{{i18n .Lang "repo_name"}}:</div>
<input type="text" class="form-control" id="txtRepoName">
<span class="input-group-btn">
<button id="btnSearchRepo" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</div>
</form>
<p>
<div class="table-responsive div-height">
<div class="alert alert-danger" role="alert" id="divErrMsg"><center></center></div>
<div class="panel-group" id="accordionRepo" role="tablist" aria-multiselectable="true">
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabUserInfo">
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<label class="sr-only" for="txtSearchUser">{{i18n .Lang "username"}}:</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-addon">{{i18n .Lang "username"}}:</div> <div class="input-group-addon">{{i18n .Lang "repo_name"}}:</div>
<input type="text" class="form-control" id="txtSearchUser"> <input type="text" class="form-control" id="txtRepoName">
<span class="input-group-btn"> <span class="input-group-btn">
<button id="btnSearchUser" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span></button> <button id="btnSearchRepo" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span></button>
</span> </span>
</div> </div>
</div> </div>
</div>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#dlgUser" id="btnAddUser">{{i18n .Lang "add_members"}}</button>
</form>
<p>
<div class="table-responsive div-height">
<table id="tblUser" class="table table-hover">
<thead>
<tr>
<th>{{i18n .Lang "username"}}</th>
<th>{{i18n .Lang "role"}}</th>
<th>{{i18n .Lang "operation"}}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabOperationLog">
<form class="form-inline">
<div class="form-group">
<label for="txtUserName" class="sr-only">{{i18n .Lang "username"}}:</label>
<div class="input-group">
<div class="input-group-addon">{{i18n .Lang "username"}}:</div>
<input type="text" class="form-control" id="txtSearchUserName">
<span class="input-group-btn">
<button id="btnFilterLog" type="button" class="btn btn-primary" data-toggle="modal" data-target="#dlgSearch"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</div>
<div class="form-group">
<div class="input-group">
<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#collapseAdvance" aria-expanded="false" aria-controls="collapseAdvance">{{i18n .Lang "advance"}}</button>
</div>
</div>
<form>
<p></p>
<div class="collapse" id="collapseAdvance">
<form class="form">
<div class="form-group">
<label for="txtUserName" class="sr-only">{{i18n .Lang "operation"}}:</label>
<div class="input-group">
<div class="input-group-addon">{{i18n .Lang "operation"}}:</div>
<span class="input-group-addon" id="spnFilterOption">
<input type="checkbox" name="chkAll" value="0"> {{i18n .Lang "all"}}
<input type="checkbox" name="chkOperation" value="create"> Create
<input type="checkbox" name="chkOperation" value="pull"> Pull
<input type="checkbox" name="chkOperation" value="push"> Push
<input type="checkbox" name="chkOperation" value="delete"> Delete
<input type="checkbox" name="chkOperation" value="others"> {{i18n .Lang "others"}}:
<input type="text" id="txtOthers" size="10">
</span>
</div>
</div>
<p></p>
<div class="form-group">
<label for="begindatepicker" class="sr-only">{{i18n .Lang "start_date"}}:</label>
<div class="input-group">
<div class="input-group-addon">{{i18n .Lang "start_date"}}:</div>
<div class="input-group date" id="datetimepicker1">
<input type="text" class="form-control" id="begindatepicker" readonly="readonly">
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">{{i18n .Lang "end_date"}}:</div>
<div class="input-group date" id="datetimepicker2">
<input type="text" class="form-control" id="enddatepicker" readonly="readonly">
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
</form> </form>
<p/>
<div class="table-responsive div-height">
<div class="alert alert-danger" role="alert" id="divErrMsg"><center></center></div>
<div class="panel-group" id="accordionRepo" role="tablist" aria-multiselectable="true">
</div>
</div>
</div> </div>
<div class="table-responsive div-height"> <div role="tabpanel" class="tab-pane" id="tabUserInfo">
<table id="tblAccessLog" class="table table-hover" > <form class="form-inline">
<thead> <div class="form-group">
<tr> <div class="input-group">
<th width="15%">{{i18n .Lang "username"}}</th> <label class="sr-only" for="txtSearchUser">{{i18n .Lang "username"}}:</label>
<th width="30%">{{i18n .Lang "repo_name"}}</th> <div class="input-group">
<th width="15%">{{i18n .Lang "repo_tag"}}</th> <div class="input-group-addon">{{i18n .Lang "username"}}:</div>
<th width="15%">{{i18n .Lang "operation"}}</th> <input type="text" class="form-control" id="txtSearchUser">
<th width="15%">{{i18n .Lang "timestamp"}}</th> <span class="input-group-btn">
</tr> <button id="btnSearchUser" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span></button>
</thead> </span>
<tbody> </div>
</tbody> </div>
</table> </div>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#dlgUser" id="btnAddUser">{{i18n .Lang "add_members"}}</button>
</form>
<p/>
<div class="table-responsive div-height">
<table id="tblUser" class="table table-hover">
<thead>
<tr>
<th>{{i18n .Lang "username"}}</th>
<th>{{i18n .Lang "role"}}</th>
<th>{{i18n .Lang "operation"}}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabOperationLog">
<form class="form-inline">
<div class="form-group">
<label for="txtUserName" class="sr-only">{{i18n .Lang "username"}}:</label>
<div class="input-group">
<div class="input-group-addon">{{i18n .Lang "username"}}:</div>
<input type="text" class="form-control" id="txtSearchUserName">
<span class="input-group-btn">
<button id="btnFilterLog" type="button" class="btn btn-primary" data-toggle="modal" data-target="#dlgSearch"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</div>
<div class="form-group">
<div class="input-group">
<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#collapseAdvance" aria-expanded="false" aria-controls="collapseAdvance">{{i18n .Lang "advance"}}</button>
</div>
</div>
<p/>
<div class="collapse" id="collapseAdvance">
<form class="form">
<div class="form-group">
<label for="txtUserName" class="sr-only">{{i18n .Lang "operation"}}:</label>
<div class="input-group">
<div class="input-group-addon">{{i18n .Lang "operation"}}:</div>
<span class="input-group-addon" id="spnFilterOption">
<input type="checkbox" name="chkAll" value="0"> {{i18n .Lang "all"}}
<input type="checkbox" name="chkOperation" value="create"> Create
<input type="checkbox" name="chkOperation" value="pull"> Pull
<input type="checkbox" name="chkOperation" value="push"> Push
<input type="checkbox" name="chkOperation" value="delete"> Delete
<input type="checkbox" name="chkOperation" value="others"> {{i18n .Lang "others"}}:
<input type="text" id="txtOthers" size="10">
</span>
</div>
</div>
<p></p>
<div class="form-group">
<label for="begindatepicker" class="sr-only">{{i18n .Lang "start_date"}}:</label>
<div class="input-group">
<div class="input-group-addon">{{i18n .Lang "start_date"}}:</div>
<div class="input-group date" id="datetimepicker1">
<input type="text" class="form-control" id="begindatepicker" readonly="readonly">
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">{{i18n .Lang "end_date"}}:</div>
<div class="input-group date" id="datetimepicker2">
<input type="text" class="form-control" id="enddatepicker" readonly="readonly">
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
</form>
</div>
<div class="table-responsive div-height">
<table id="tblAccessLog" class="table table-hover" >
<thead>
<tr>
<th width="15%">{{i18n .Lang "username"}}</th>
<th width="30%">{{i18n .Lang "repo_name"}}</th>
<th width="15%">{{i18n .Lang "repo_tag"}}</th>
<th width="15%">{{i18n .Lang "operation"}}</th>
<th width="15%">{{i18n .Lang "timestamp"}}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="modal fade" id="dlgUser" tabindex="-1" role="dialog" aria-labelledby="User" aria-hidden="true"> <div class="modal fade" id="dlgUser" tabindex="-1" role="dialog" aria-labelledby="User" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">

View File

@ -25,24 +25,24 @@
<!-- tab panes --> <!-- tab panes -->
<div class="tab-content"> <div class="tab-content">
<div role="tabpanel" class="tab-pane" id="tabMyProject" style="margin-top: 15px;"> <div role="tabpanel" class="tab-pane" id="tabMyProject" style="margin-top: 15px;">
<form class="form-inline"> <form class="form-inline">
<label class="sr-only" for="txtProjectName">{{i18n .Lang "project_name"}}:</label> <label class="sr-only" for="txtProjectName">{{i18n .Lang "project_name"}}:</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-addon">{{i18n .Lang "project_name"}}:</div> <div class="input-group-addon">{{i18n .Lang "project_name"}}:</div>
<input type="text" class="form-control" id="txtSearchProject"> <input type="text" class="form-control" id="txtSearchProject">
<span class="input-group-btn"> <span class="input-group-btn">
<button id="btnSearch" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span></button> <button id="btnSearch" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span></button>
</span> </span>
</div> </div>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#dlgAddProject" id="btnAddProject">{{i18n .Lang "add_project"}}</button> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#dlgAddProject" id="btnAddProject">{{i18n .Lang "add_project"}}</button>
</form> </form>
<div class="table-responsive div-height"> <div class="table-responsive div-height">
<table id="tblProject" class="table table-hover"> <table id="tblProject" class="table table-hover">
<thead> <thead>
<tr> <tr>
<th width="35%">{{i18n .Lang "project_name"}}</th> <th width="35%">{{i18n .Lang "project_name"}}</th>
<th width="45%">{{i18n .Lang "creation_time"}}</th> <th width="45%">{{i18n .Lang "creation_time"}}</th>
<th width="20%">{{i18n .Lang "publicity"}}</th> <th width="20%">{{i18n .Lang "publicity"}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -51,65 +51,65 @@
</div> </div>
</div> </div>
<div role="tabpanel" class="tab-pane" id="tabAdminOption" style="visibility: hidden; margin-top: 15px;"> <div role="tabpanel" class="tab-pane" id="tabAdminOption" style="visibility: hidden; margin-top: 15px;">
<form class="form-inline"> <form class="form-inline">
<label class="sr-only" for="txtProjectName">{{i18n .Lang "username"}}:</label> <label class="sr-only" for="txtProjectName">{{i18n .Lang "username"}}:</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-addon">{{i18n .Lang "username"}}:</div> <div class="input-group-addon">{{i18n .Lang "username"}}:</div>
<input type="text" class="form-control" id="txtSearchUsername"> <input type="text" class="form-control" id="txtSearchUsername">
<span class="input-group-btn"> <span class="input-group-btn">
<button id="btnSearchUsername" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span></button> <button id="btnSearchUsername" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span></button>
</span> </span>
</div>
</form>
<div class="table-responsive div-height">
<table id="tblUser" class="table table-hover">
<thead>
<tr>
<th width="35%">{{i18n .Lang "username"}}</th>
<th width="45%">{{i18n .Lang "email"}}</th>
<th width="20%">{{i18n .Lang "system_admin"}}</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div> </div>
</div> </form>
</div> <div class="table-responsive div-height">
</div> <table id="tblUser" class="table table-hover">
</div> <thead>
<div class="modal fade" id="dlgAddProject" tabindex="-1" role="dialog" aria-labelledby="Add Project" aria-hidden="true"> <tr>
<div class="modal-dialog"> <th width="35%">{{i18n .Lang "username"}}</th>
<div class="modal-content"> <th width="45%">{{i18n .Lang "email"}}</th>
<div class="modal-header"> <th width="20%">{{i18n .Lang "system_admin"}}</th>
<a type="button" class="close" data-dismiss="modal" aria-label="Close" id="btnCancel"> <th></th>
<span aria-hidden="true">&times;</span> </tr>
</a> </thead>
<h4 class="modal-title" id="dlgAddProjectTitle">{{i18n .Lang "add_project"}}</h4> <tbody>
</div> </tbody>
<div class="modal-body"> </table>
<form role="form">
<div class="alert alert-danger" role="alert" id="divErrMsg"></div>
<div class="form-group has-feedback">
<label for="projectName" class="control-label">{{i18n .Lang "project_name"}}:</label>
<input type="text" class="form-control" id="projectName">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="isPublic" checked=false> {{i18n .Lang "check_for_publicity"}}
</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="btnSave">{{i18n .Lang "button_save"}}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{i18n .Lang "button_cancel"}}</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" id="dlgAddProject" tabindex="-1" role="dialog" aria-labelledby="Add Project" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<a type="button" class="close" data-dismiss="modal" aria-label="Close" id="btnCancel">
<span aria-hidden="true">&times;</span>
</a>
<h4 class="modal-title" id="dlgAddProjectTitle">{{i18n .Lang "add_project"}}</h4>
</div>
<div class="modal-body">
<form role="form">
<div class="alert alert-danger" role="alert" id="divErrMsg"></div>
<div class="form-group has-feedback">
<label for="projectName" class="control-label">{{i18n .Lang "project_name"}}:</label>
<input type="text" class="form-control" id="projectName">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="isPublic" checked=false> {{i18n .Lang "check_for_publicity"}}
</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="btnSave">{{i18n .Lang "button_save"}}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{i18n .Lang "button_cancel"}}</button>
</div>
</div>
</div>
</div>
</div>
<script src="static/resources/js/validate-options.js"></script> <script src="static/resources/js/validate-options.js"></script>
<script src="static/resources/js/project.js"></script> <script src="static/resources/js/project.js"></script>

View File

@ -16,65 +16,65 @@
<div class="col-sm-4"></div> <div class="col-sm-4"></div>
<div class="col-sm-4"> <div class="col-sm-4">
<div class="page-header"> <div class="page-header">
{{ if eq .IsAdmin true }} {{ if eq .IsAdmin true }}
<h1>{{i18n .Lang "add_user" }}</h1> <h1>{{i18n .Lang "add_user" }}</h1>
{{ else }} {{ else }}
<h1>{{i18n .Lang "registration"}}</h1> <h1>{{i18n .Lang "registration"}}</h1>
{{ end }} {{ end }}
</div> </div>
<form class="form"> <form class="form">
<div class="alert alert-danger" role="alert" id="divErrMsg"></div> <div class="alert alert-danger" role="alert" id="divErrMsg"></div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="username" class="control-label">{{i18n .Lang "username"}}</label> <label for="username" class="control-label">{{i18n .Lang "username"}}</label>
<p style="display:inline; color: red; font-size: 12pt;">*</p> <p style="display:inline; color: red; font-size: 12pt;">*</p>
<input type="text" class="form-control" id="Username"> <input type="text" class="form-control" id="Username">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "username_description"}}</h6> <h6>{{i18n .Lang "username_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="Email" class="control-label">{{i18n .Lang "email"}}</label> <label for="Email" class="control-label">{{i18n .Lang "email"}}</label>
<p style="display:inline; color: red; font-size: 12pt;">*</p> <p style="display:inline; color: red; font-size: 12pt;">*</p>
<input type="email" class="form-control" id="Email"> <input type="email" class="form-control" id="Email">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "email_description"}}</h6> <h6>{{i18n .Lang "email_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="Realname" class="control-label">{{i18n .Lang "full_name"}}</label> <label for="Realname" class="control-label">{{i18n .Lang "full_name"}}</label>
<p style="display:inline; color: red; font-size: 12pt;">*</p> <p style="display:inline; color: red; font-size: 12pt;">*</p>
<input type="text" class="form-control" id="Realname"> <input type="text" class="form-control" id="Realname">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "full_name_description"}}</h6> <h6>{{i18n .Lang "full_name_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="Password" class="control-label">{{i18n .Lang "password"}}</label> <label for="Password" class="control-label">{{i18n .Lang "password"}}</label>
<p style="display:inline; color: red; font-size: 12pt;">*</p> <p style="display:inline; color: red; font-size: 12pt;">*</p>
<input type="password" class="form-control" id="Password"> <input type="password" class="form-control" id="Password">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "password_description"}}</h6> <h6>{{i18n .Lang "password_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="ConfirmedPassword" class="control-label">{{i18n .Lang "confirm_password"}}</label> <label for="ConfirmedPassword" class="control-label">{{i18n .Lang "confirm_password"}}</label>
<p style="display:inline; color: red; font-size: 12pt;">*</p> <p style="display:inline; color: red; font-size: 12pt;">*</p>
<input type="password" class="form-control" id="ConfirmedPassword"> <input type="password" class="form-control" id="ConfirmedPassword">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "password_description"}}</h6> <h6>{{i18n .Lang "password_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="Comment" class="control-label">{{i18n .Lang "note_to_the_admin"}}</label> <label for="Comment" class="control-label">{{i18n .Lang "note_to_the_admin"}}</label>
<input type="text" class="form-control" id="Comment"> <input type="text" class="form-control" id="Comment">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<div class="text-center"> <div class="text-center">
<button type="button" class="btn btn-default" id="btnPageSignUp"> <button type="button" class="btn btn-default" id="btnPageSignUp">
{{ if eq .IsAdmin true }} {{ if eq .IsAdmin true }}
{{i18n .Lang "add_user" }} {{i18n .Lang "add_user" }}
{{ else }} {{ else }}
{{i18n .Lang "sign_up"}} {{i18n .Lang "sign_up"}}
{{ end }} {{ end }}
</button> </button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div class="col-sm-4"></div> <div class="col-sm-4"></div>

View File

@ -14,8 +14,8 @@
--> -->
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<body> <body>
<p>{{.Hint}}:</p> <p>{{.Hint}}:</p>
<a href="{{.URL}}/resetPassword?reset_uuid={{.UUID}}">{{.URL}}/resetPassword?reset_uuid={{.UUID}}</a> <a href="{{.URL}}/resetPassword?reset_uuid={{.UUID}}">{{.URL}}/resetPassword?reset_uuid={{.UUID}}</a>
</body> </body>
</html> </html>

View File

@ -17,27 +17,27 @@
<div class="col-sm-4"></div> <div class="col-sm-4"></div>
<div class="col-sm-4"> <div class="col-sm-4">
<div class="page-header"> <div class="page-header">
<h1>{{i18n .Lang "title_reset_password"}}</h1> <h1>{{i18n .Lang "title_reset_password"}}</h1>
</div> </div>
<form class="form"> <form class="form">
<div class="alert alert-danger" role="alert" id="divErrMsg"></div> <div class="alert alert-danger" role="alert" id="divErrMsg"></div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="Password" class="control-label">{{i18n .Lang "password"}}</label> <label for="Password" class="control-label">{{i18n .Lang "password"}}</label>
<input type="password" class="form-control" id="Password"> <input type="password" class="form-control" id="Password">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "password_description"}}</h6> <h6>{{i18n .Lang "password_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<label for="ConfirmedPassword" class="control-label">{{i18n .Lang "confirm_password"}}</label> <label for="ConfirmedPassword" class="control-label">{{i18n .Lang "confirm_password"}}</label>
<input type="password" class="form-control" id="ConfirmedPassword"> <input type="password" class="form-control" id="ConfirmedPassword">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span> <span class="glyphicon form-control-feedback" aria-hidden="true"></span>
<h6>{{i18n .Lang "password_description"}}</h6> <h6>{{i18n .Lang "password_description"}}</h6>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<div class="text-center"> <div class="text-center">
<button type="button" class="btn btn-default" id="btnSubmit">{{i18n .Lang "button_submit"}}</button> <button type="button" class="btn btn-default" id="btnSubmit">{{i18n .Lang "button_submit"}}</button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div class="col-sm-4"></div> <div class="col-sm-4"></div>

View File

@ -18,15 +18,15 @@
<li><a href="/">{{i18n .Lang "home"}}</a></li> <li><a href="/">{{i18n .Lang "home"}}</a></li>
<li>{{i18n .Lang "search"}}</li> <li>{{i18n .Lang "search"}}</li>
</ol> </ol>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading" id="panelCommonSearchProjectsHeader">{{i18n .Lang "projects"}}</div> <div class="panel-heading" id="panelCommonSearchProjectsHeader">{{i18n .Lang "projects"}}</div>
<div class="panel-body" id="panelCommonSearchProjectsBody"> <div class="panel-body" id="panelCommonSearchProjectsBody">
</div> </div>
</div> </div>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading" id="panelCommonSearchRepositoriesHeader">{{i18n .Lang "repositories"}}</div> <div class="panel-heading" id="panelCommonSearchRepositoriesHeader">{{i18n .Lang "repositories"}}</div>
<div class="panel-body" id="panelCommonSearchRepositoriesBody"> <div class="panel-body" id="panelCommonSearchRepositoriesBody">
</div> </div>
</div> </div>
</div> </div>
<script src="static/resources/js/search.js"></script> <script src="static/resources/js/search.js"></script>

View File

@ -14,15 +14,15 @@
--> -->
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
{{.HeaderInc}} {{.HeaderInc}}
<title>{{.PageTitle}}</title> <title>{{.PageTitle}}</title>
</head> </head>
<body> <body>
{{.HeaderContent}} {{.HeaderContent}}
{{.BodyContent}} {{.BodyContent}}
{{.FooterInc}} {{.FooterInc}}
{{.ModalDialog}} {{.ModalDialog}}
{{.FootContent}} {{.FootContent}}
</body> </body>
</html> </html>

View File

@ -13,11 +13,11 @@
limitations under the License. limitations under the License.
--> -->
<footer class="footer"> <footer class="footer">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-5 col-md-offset-4"> <div class="col-md-5 col-md-offset-4">
<p class="text-muted">{{i18n .Lang "copyright"}} © 2015-2016 VMware, Inc. {{i18n .Lang "all_rights_reserved"}}</p> <p class="text-muted">{{i18n .Lang "copyright"}} © 2015-2016 VMware, Inc. {{i18n .Lang "all_rights_reserved"}}</p>
</div> </div>
</div> </div>
</div> </div>
</footer> </footer>

View File

@ -11,80 +11,83 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<input type="hidden" id="currentLanguage" value="{{.Lang}}"> <input type="hidden" id="currentLanguage" value="{{.Lang}}">
<input type="hidden" id="isAdmin" value="{{.IsAdmin}}"> <input type="hidden" id="isAdmin" value="{{.IsAdmin}}">
<nav class="navbar navbar-default" role="navigation" style="margin-bottom: 0;"> <nav class="navbar navbar-default" role="navigation" style="margin-bottom: 0;">
<div class="navbar-header"> <div class="navbar-header">
<button aria-controls="navbar" aria-expanded="false" data-target="#navbar" data-toggle="collapse" class="navbar-toggle collapsed" type="button"> <button aria-controls="navbar" aria-expanded="false" data-target="#navbar" data-toggle="collapse" class="navbar-toggle collapsed" type="button">
<span class="sr-only">Toggle navigation</span> <span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="/"><img src="static/resources/image/Harbor_Logo_rec.png" height="40px" width="80px"/></a> <a class="navbar-brand" href="/"><img src="static/resources/image/Harbor_Logo_rec.png" height="40px" width="80px"/></a>
</div> </div>
<div id="navbar" class="navbar-collapse collapse"> <div id="navbar" class="navbar-collapse collapse">
<form class="navbar-form navbar-right"> <form class="navbar-form navbar-right">
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-globe"></span>
{{i18n .Lang "language"}}
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/language?lang=en-US">{{i18n .Lang "language_en-US"}}</a></li>
<li><a href="/language?lang=zh-CN">{{i18n .Lang "language_zh-CN"}}</a></li>
<li><a href="/language?lang=de-DE">{{i18n .Lang "language_de-DE"}}</a></li>
<li><a href="/language?lang=ru-RU">{{i18n .Lang "language_ru-RU"}}</a></li>
<li><a href="/language?lang=ja-JP">{{i18n .Lang "language_ja-JP"}}</a></li>
</ul>
</li>
</ul>
</div>
<div class="input-group" > <ul class="nav navbar-nav">
<span class="input-group-addon"><span class="input-group glyphicon glyphicon-search"></span></span> <li class="dropdown">
<input type="text" class="form-control" id="txtCommonSearch" size="50" placeholder="{{i18n .Lang "search_placeholder"}}"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
</div> <span class="glyphicon glyphicon-globe"></span>
</div> {{i18n .Lang "language"}}
{{ if .Username }} <span class="caret"></span></a>
<div class="input-group"> <ul class="dropdown-menu">
<ul class="nav navbar-nav"> <li><a href="/language?lang=en-US">{{i18n .Lang "language_en-US"}}</a></li>
<li class="dropdown"> <li><a href="/language?lang=zh-CN">{{i18n .Lang "language_zh-CN"}}</a></li>
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-user"></span> {{.Username}}<span class="caret"></span></a> <li><a href="/language?lang=de-DE">{{i18n .Lang "language_de-DE"}}</a></li>
<ul class="dropdown-menu"> <li><a href="/language?lang=ru-RU">{{i18n .Lang "language_ru-RU"}}</a></li>
{{ if eq .AuthMode "db_auth" }} <li><a href="/language?lang=ja-JP">{{i18n .Lang "language_ja-JP"}}</a></li>
<li><a id="aChangePassword" href="/changePassword" target="_blank"><span class="glyphicon glyphicon-pencil"></span>&nbsp;&nbsp;{{i18n .Lang "change_password"}}</a></li> </ul>
<li role="separator" class="divider"></li> </li>
{{ end }}
{{ if eq .IsLdapAdminUser true }}
<li><a id="aChangePassword" href="/changePassword" target="_blank"><span class="glyphicon glyphicon-pencil"></span>&nbsp;&nbsp;{{i18n .Lang "change_password"}}</a></li>
<li role="separator" class="divider"></li>
{{ end }}
{{ if eq .AuthMode "db_auth" }}
{{ if eq .IsAdmin true }}
<li><a id="aAddUser" href="/addUser" target="_blank"><span class="glyphicon glyphicon-plus"></span>&nbsp;&nbsp;{{i18n .Lang "add_user"}}</a></li>
{{ end }}
{{ end}}
<li><a id="aLogout" href="#"><span class="glyphicon glyphicon-log-in"></span>&nbsp;&nbsp;{{i18n .Lang "log_out"}}</a></li>
</ul> </ul>
</li> </div>
</ul>
</div> <div class="input-group" >
{{ else if eq .AuthMode "db_auth" }} <span class="input-group-addon"><span class="input-group glyphicon glyphicon-search"></span></span>
<div class="input-group"> <input type="text" class="form-control" id="txtCommonSearch" size="50" placeholder="{{i18n .Lang "search_placeholder"}}">
&nbsp;<button type="button" class="btn btn-default" id="btnSignIn">{{i18n .Lang "sign_in"}}</button> </div>
{{ if eq .SelfRegistration true }} </div>
&nbsp;<button type="button" class="btn btn-success" id="btnSignUp">{{i18n .Lang "sign_up"}}</button> {{ if .Username }}
<div class="input-group">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-user"></span> {{.Username}}<span class="caret"></span></a>
<ul class="dropdown-menu">
{{ if eq .AuthMode "db_auth" }}
<li><a id="aChangePassword" href="/changePassword" target="_blank"><span class="glyphicon glyphicon-pencil"></span>&nbsp;&nbsp;{{i18n .Lang "change_password"}}</a></li>
<li role="separator" class="divider"></li>
{{ end }}
{{ if eq .IsLdapAdminUser true }}
<li><a id="aChangePassword" href="/changePassword" target="_blank"><span class="glyphicon glyphicon-pencil"></span>&nbsp;&nbsp;{{i18n .Lang "change_password"}}</a></li>
<li role="separator" class="divider"></li>
{{ end }}
{{ if eq .AuthMode "db_auth" }}
{{ if eq .IsAdmin true }}
<li><a id="aAddUser" href="/addUser" target="_blank"><span class="glyphicon glyphicon-plus"></span>&nbsp;&nbsp;{{i18n .Lang "add_user"}}</a></li>
{{ end }}
{{ end}}
<li><a id="aLogout" href="#"><span class="glyphicon glyphicon-log-in"></span>&nbsp;&nbsp;{{i18n .Lang "log_out"}}</a></li>
</ul>
</li>
</ul>
</div>
{{ else if eq .AuthMode "db_auth" }}
<div class="input-group">
&nbsp;<button type="button" class="btn btn-default" id="btnSignIn">{{i18n .Lang "sign_in"}}</button>
{{ if eq .SelfRegistration true }}
&nbsp;<button type="button" class="btn btn-success" id="btnSignUp">{{i18n .Lang "sign_up"}}</button>
{{ end }}
</div>
{{ else }}
<div class="input-group">
&nbsp;<button type="button" class="btn btn-default" id="btnSignIn">{{i18n .Lang "sign_in"}}</button>
</div>
{{ end }} {{ end }}
</div> </form>
{{ else }} </div>
<div class="input-group"> </nav>
&nbsp;<button type="button" class="btn btn-default" id="btnSignIn">{{i18n .Lang "sign_in"}}</button>
</div>
{{ end }}
</form>
</div>
</nav>

View File

@ -13,26 +13,26 @@
limitations under the License. limitations under the License.
--> -->
<style> <style>
.center { .center {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
top: 10%; top: 10%;
} }
</style> </style>
<!-- Modal --> <!-- Modal -->
<div class="center modal fade" id="dlgModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="center modal fade" id="dlgModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="dlgLabel"></h4> <h4 class="modal-title" id="dlgLabel"></h4>
</div> </div>
<div class="modal-body" id="dlgBody"> <div class="modal-body" id="dlgBody">
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary" id="dlgConfirm" data-dismiss="modal">{{i18n .Lang "dlg_button_ok"}}</button> <button type="button" class="btn btn-primary" id="dlgConfirm" data-dismiss="modal">{{i18n .Lang "dlg_button_ok"}}</button>
<button type="button" class="btn btn-primary" id="dlgCancel" data-dismiss="modal" style="display: none;">{{i18n .Lang "dlg_button_cancel"}}</button> <button type="button" class="btn btn-primary" id="dlgCancel" data-dismiss="modal" style="display: none;">{{i18n .Lang "dlg_button_cancel"}}</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -13,28 +13,28 @@
limitations under the License. limitations under the License.
--> -->
<div class="container"> <div class="container">
<form class="form-signin form-horizontal"> <form class="form-signin form-horizontal">
<div class="form-group"> <div class="form-group">
<label for="Principal" class="col-md-4 control-label">{{i18n .Lang "username_email"}}</label> <label for="Principal" class="col-md-4 control-label">{{i18n .Lang "username_email"}}</label>
<div class="col-md-8"> <div class="col-md-8">
<input type="text" id="Principal" class="form-control" placeholder="{{i18n .Lang "username_email"}}"> <input type="text" id="Principal" class="form-control" placeholder="{{i18n .Lang "username_email"}}">
</div>
</div> </div>
</div> <div class="form-group">
<div class="form-group"> <label for="Password" class="col-md-4 control-label">{{i18n .Lang "password"}}</label>
<label for="Password" class="col-md-4 control-label">{{i18n .Lang "password"}}</label> <div class="col-md-8">
<div class="col-md-8"> <input type="password" id="Password" class="form-control" placeholder="{{i18n .Lang "password"}}">
<input type="password" id="Password" class="form-control" placeholder="{{i18n .Lang "password"}}"> </div>
</div> </div>
</div> <button class="btn btn-lg btn-primary btn-block" type="button" id="btnPageSignIn">{{i18n .Lang "sign_in"}}</button>
<button class="btn btn-lg btn-primary btn-block" type="button" id="btnPageSignIn">{{i18n .Lang "sign_in"}}</button> {{ if eq .AuthMode "db_auth" }}
{{ if eq .AuthMode "db_auth" }} <div class="form-group">
<div class="form-group"> <div class="col-md-12">
<div class="col-md-12"> <button type="button" class="btn btn-link pull-right" id="btnForgot">{{i18n .Lang "forgot_password"}}</button>
<button type="button" class="btn btn-link pull-right" id="btnForgot">{{i18n .Lang "forgot_password"}}</button> </div>
</div> </div>
</div> {{ end }}
{{ end }} </form>
</form>
</div> </div>
<link href="static/resources/css/sign-in.css" type="text/css" rel="stylesheet"> <link href="static/resources/css/sign-in.css" type="text/css" rel="stylesheet">
<script src="static/resources/js/sign-in.js"></script> <script src="static/resources/js/sign-in.js"></script>