Browse Source

Merge branch 'dev' into newcollaboration

Peter Smit 10 years ago
parent
commit
03af37554e

+ 8 - 0
.travis.yml

@@ -3,5 +3,13 @@ language: go
 go:
   - 1.2
   - 1.3
+  - 1.4
+  - tip
 
 sudo: false
+
+script: go build -v
+
+notifications:
+  email:
+    - [email protected]

+ 6 - 5
README.md

@@ -1,18 +1,19 @@
-Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs)
+Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs)
 =====================
 
 [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 
 Gogs(Go Git Service) is a painless self-hosted Git Service written in Go.
 
-![Demo](https://gowalker.org/public/gogs_demo.gif)
+![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
 
-##### Current version: 0.5.11 Beta
+##### Current version: 0.5.12 Beta
 
 ### NOTICES
 
-- Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **June 21, 2014** and will reset multiple times after. Please do **NOT** put your important data on the site.
+- Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
 - Demo site [try.gogs.io](https://try.gogs.io) is running under `dev` branch.
+- If you think there are vulnerabilities in the project, please talk private to **[email protected]**, thanks!
 
 #### Other language version
 
@@ -50,7 +51,7 @@ The goal of this project is to make the easiest, fastest and most painless way t
 - Drone CI integration
 - Supports MySQL, PostgreSQL and SQLite3
 - Social account login(GitHub, Google, QQ, Weibo)
-- Multi-language support([7 languages](https://crowdin.com/project/gogs))
+- Multi-language support([9 languages](https://crowdin.com/project/gogs))
 
 ## System Requirements
 

+ 3 - 3
README_ZH.md

@@ -3,9 +3,9 @@ Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0b
 
 Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
 
-![Demo](https://gowalker.org/public/gogs_demo.gif)
+![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
 
-##### 当前版本:0.5.11 Beta
+##### 当前版本:0.5.12 Beta
 
 ## 开发目的
 
@@ -39,7 +39,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
 - Drone CI 持续部署集成
 - 支持 MySQL、PostgreSQL 以及 SQLite3 数据库
 - 社交帐号登录(GitHub、Google、QQ、微博)
-- 多语言支持([7 种语言]([more](https://crowdin.com/project/gogs)))
+- 多语言支持([9 种语言]([more](https://crowdin.com/project/gogs)))
 
 ## 系统要求
 

+ 1 - 1
cmd/fix.go

@@ -164,7 +164,7 @@ func runFixLocation(ctx *cli.Context) {
 	fmt.Scanln()
 
 	// Fix in authorized_keys file.
-	sshPath := path.Join(models.SshPath, "authorized_keys")
+	sshPath := path.Join(models.SSHPath, "authorized_keys")
 	if com.IsFile(sshPath) {
 		fmt.Printf("Fixing pathes in file: %s\n", sshPath)
 		if err := rewriteAuthorizedKeys(sshPath, oldPath, execPath); err != nil {

+ 16 - 8
cmd/web.go

@@ -53,7 +53,9 @@ var CmdWeb = cli.Command{
 	Description: `Gogs web server is the only thing you need to run,
 and it takes care of all the other things for you`,
 	Action: runWeb,
-	Flags:  []cli.Flag{},
+	Flags: []cli.Flag{
+		cli.StringFlag{"port, p", "3000", "Temporary port number to prevent conflict", ""},
+	},
 }
 
 type VerChecker struct {
@@ -75,13 +77,13 @@ func checkVersion() {
 
 	// Check dependency version.
 	checkers := []VerChecker{
-		{"github.com/Unknwon/macaron", macaron.Version, "0.5.0"},
+		{"github.com/Unknwon/macaron", macaron.Version, "0.5.1"},
 		{"github.com/macaron-contrib/binding", binding.Version, "0.0.4"},
 		{"github.com/macaron-contrib/cache", cache.Version, "0.0.7"},
-		{"github.com/macaron-contrib/csrf", csrf.Version, "0.0.1"},
+		{"github.com/macaron-contrib/csrf", csrf.Version, "0.0.3"},
 		{"github.com/macaron-contrib/i18n", i18n.Version, "0.0.5"},
 		{"github.com/macaron-contrib/session", session.Version, "0.1.6"},
-		{"gopkg.in/ini.v1", ini.Version, "1.0.1"},
+		{"gopkg.in/ini.v1", ini.Version, "1.2.0"},
 	}
 	for _, c := range checkers {
 		ver := strings.Join(strings.Split(c.Version(), ".")[:3], ".")
@@ -162,7 +164,7 @@ func newMacaron() *macaron.Macaron {
 	return m
 }
 
-func runWeb(*cli.Context) {
+func runWeb(ctx *cli.Context) {
 	routers.GlobalInit()
 	checkVersion()
 
@@ -179,9 +181,9 @@ func runWeb(*cli.Context) {
 	// Routers.
 	m.Get("/", ignSignIn, routers.Home)
 	m.Get("/explore", ignSignIn, routers.Explore)
-	// FIXME: when i'm binding form here???
-	m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install)
-	m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost)
+	m.Combo("/install", routers.InstallInit).
+		Get(routers.Install).
+		Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)
 	m.Group("", func() {
 		m.Get("/pulls", user.Pulls)
 		m.Get("/issues", user.Issues)
@@ -460,6 +462,12 @@ func runWeb(*cli.Context) {
 	// Not found handler.
 	m.NotFound(routers.NotFound)
 
+	// Flag for port number in case first time run conflict.
+	if ctx.IsSet("port") {
+		setting.AppUrl = strings.Replace(setting.AppUrl, setting.HttpPort, ctx.String("port"), 1)
+		setting.HttpPort = ctx.String("port")
+	}
+
 	var err error
 	listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
 	log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)

+ 5 - 2
conf/app.ini

@@ -1,3 +1,6 @@
+# NEVER EVER MODIFY THIS FILE
+# PLEASE MAKE CHANGES ON CORRESPONDING CUSTOM CONFIG FILE
+
 ; App name that shows on every page title
 APP_NAME = Gogs: Go Git Service
 ; Change it if you run locally
@@ -275,5 +278,5 @@ INTERVAL = 24
 ARGS = 
 
 [i18n]
-LANGS = en-US,zh-CN,zh-HK,de-DE,fr-CA,nl-NL,lv-LV,ru-RU
-NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands,Latviešu,Русский
+LANGS = en-US,zh-CN,zh-HK,de-DE,fr-CA,nl-NL,lv-LV,ru-RU,ja-JP
+NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands,Latviešu,Русский,日本语

+ 4 - 1
conf/locale/TRANSLATORS

@@ -1,6 +1,9 @@
 # This file lists all PUBLIC individuals having contributed content to the translation.
 # Order of name is meaningless.
 
+Akihiro YAGASAKI <[email protected]>
+Christoph Kisfeld <[email protected]>
 Thomas Fanninger <[email protected]>
 Łukasz Jan Niemier <[email protected]>
-Lafriks <[email protected]>
+Lafriks <[email protected]>
+Miguel de la Cruz <[email protected]>

+ 5 - 14
conf/locale/locale_en-US.ini

@@ -59,12 +59,14 @@ run_user = Run User
 run_user_helper = The user must have access to Repository Root Path and run Gogs.
 domain = Domain
 domain_helper = This affects SSH clone URLs.
+http_port = HTTP Port
+http_port_helper = Port number which application will listen on.
 app_url = Application URL
 app_url_helper = This affects HTTP/HTTPS clone URL and somewhere in e-mail.
 email_title = E-mail Service Settings (Optional)
 smtp_host = SMTP Host
 mailer_user = Sender E-mail
-mailer_password = Sender Password 
+mailer_password = Sender Password
 notify_title = Notification Settings(Optional)
 register_confirm = Enable Register Confirmation
 mail_notify = Enable Mail Notification
@@ -512,6 +514,8 @@ dashboard.delete_repo_archives = Delete all repositories archives
 dashboard.delete_repo_archives_success = All repositories archives have been deleted successfully.
 dashboard.git_gc_repos = Do garbage collection on repositories
 dashboard.git_gc_repos_success = All repositories have done garbage collection successfully.
+dashboard.resync_all_sshkeys = Rewrite '.ssh/autorized_key' file(caution: non-Gogs keys will be lost)
+dashboard.resync_all_sshkeys_success = All public keys have been rewritten successfully.
 dashboard.server_uptime = Server Uptime
 dashboard.current_goroutine = Current Goroutines
 dashboard.current_memory_usage = Current Memory Usage
@@ -712,16 +716,3 @@ months = %d months %s
 years = %d years %s
 raw_seconds = seconds
 raw_minutes = minutes
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 728 - 0
conf/locale/locale_ja-JP.ini

@@ -0,0 +1,728 @@
+app_desc=Go言語で実装したセルフホストGitサーバ
+
+home=ホーム
+dashboard=ダッシュボード
+explore=エスクプローラ
+help=ヘルプ
+sign_in=サインイン
+social_sign_in=SNSでサインイン: ステップ2 <small>アカウント連携</small>
+sign_out=サインアウト
+sign_up=サインアップ
+register=登録
+website=WEBサイト
+version=バージョン
+page=ページ
+template=テンプレート
+language=言語
+
+username=ユーザ名
+email=E-mail
+password=パスワード
+re_type=再入力
+captcha=キャプチャ
+
+repository=リポジトリ
+organization=組織
+mirror=ミラー
+new_repo=新しいリポジトリ
+new_migrate=新しい移行
+new_fork=新しいフォークのリポジトリ
+new_org=新しい組織
+manage_org=組織を管理
+admin_panel=管理者パネル
+account_settings=アカウント設定
+settings=設定
+
+news_feed=ニュースのフィード
+pull_requests=プルリクエスト
+issues=課題
+
+cancel=キャンセル
+
+[install]
+install=インストール
+title=初回実行のインストール手順
+requite_db_desc=Gogs には、MySQL や PostgreSQL 、SQLite3 が必要です。
+db_type=データベースの種類
+host=ホスト
+user=ユーザ
+password=パスワード
+db_name=データベース名
+db_helper=Mysql INNODB エンジン utf8_general_ci の文字セットを使用してください。
+ssl_mode=SSL モード
+path=パス
+sqlite_helper=SQLite3 データベースのファイル パス
+general_title=Gogs の全般設定
+repo_path=リポジトリのルートパス
+repo_path_helper=すべての Git リモート リポジトリはこのディレクトリに保存されます。
+run_user=実行ユーザ
+run_user_helper=ユーザーはリポジトリ ルートパスへのアクセス、及びGogs を実行する権限を所有する必要があります。
+domain=ドメイン
+domain_helper=これはSSHクローンURLに影響する。
+app_url=アプリケーションの URL
+app_url_helper=この設定は、HTTP / HTTPSのクローンURLおよび、一部のメールボックスへのリンクに影響を与えます。
+email_title=E-mailサービス設定(Optional)
+smtp_host=SMTP ホスト
+mailer_user=送信者の電子メール
+mailer_password=送信者のパスワード
+notify_title=通知 Settings(Optional)
+register_confirm=登録の確認を有効にする
+mail_notify=メール通知を有効にする
+admin_title=管理者アカウントの設定
+admin_name=ユーザ名
+admin_password=パスワード
+confirm_password=パスワード確認
+admin_email=E-mail
+install_gogs=Gogs をインストール
+test_git_failed='Git' コマンドテストに失敗: %v
+sqlite3_not_available=このリリース バージョンは SQLite3 をサポートしていません。gobuild バージョンではない、公式のバイナリ バージョンを %s からダウンロードしてください。
+invalid_db_setting=データベースの設定が正しくありません: %v
+invalid_repo_path=リポジトリのルート パスが無効です: %v
+run_user_not_match=実行ユーザーは、現在のユーザーではない: %s-> %s
+save_config_failed=構成の保存に失敗した: %v
+invalid_admin_setting=管理者アカウントの設定が無効です: %v
+install_success=ようこそ!我々はあなたが Gogs を選んでくれて嬉しいです!楽しみましょう!
+
+[home]
+uname_holder=ユーザー名またはEメール
+password_holder=パスワード
+switch_dashboard_context=ダッシュ ボードのコンテキストを切替
+my_repos=私のリポジトリ
+collaborative_repos=共同リポジトリ
+my_orgs=私の組織
+my_mirrors=私のミラー
+
+[explore]
+repos=リポジトリ
+
+[auth]
+create_new_account=新規アカウントを作成
+register_hepler_msg=すでにアカウントをお持ちですか?今すぐログイン !
+social_register_hepler_msg=すでにアカウントをお持ちですか?今すぐバインド !
+disable_register_prompt=申し訳ありませんが、登録が無効になっています。サイト管理者に問い合わせてください。
+disable_register_mail=申し訳ありませんが、登録メールの確認機能が無効になっています。
+remember_me=ログイン状態を保持する
+forgot_password=パスワードを忘れた
+forget_password=パスワードを忘れた?
+sign_up_now=アカウントが必要ですか?今すぐサインアップ
+confirmation_mail_sent_prompt=新しい確認メールを <b>%s</b> に送りました。登録を完了させるために、%d時間以内にあなたのメールボックスを確認してください。
+sign_in_email=E-mailでサイイン
+active_your_account=アカウントをアクティブ
+resent_limit_prompt=申し訳ありませんが、アクティベーションメールは頻繁に送信しています。3 分お待ちください。
+has_unconfirmed_mail=こんにちは %s さん、あなたの電子メール アドレス (<b>%s</b>) は未確認です。もし確認メールをまだ確認できていないか、改めて再送信する場合は、下のボタンをクリックしてください。
+resend_mail=アクティベーションメールを再送信するにはここをクリック
+email_not_associate=この電子メール アドレスは、アカウントには関連付けられません。
+send_reset_mail=パスワードリセットのメールを再送するにはここをクリック
+reset_password=パスワードリセット
+invalid_code=申し訳ありませんが、確認用コードが期限切れまたは無効です。
+reset_password_helper=パスワードをリセットするにはここをクリック
+password_too_short=6文字未満のパスワードは設定できません。
+
+[form]
+UserName=ユーザ名
+RepoName=リポジトリ名
+Email=Eメールアドレス
+Password=パスワード
+Retype=パスワードを再入力
+SSHTitle=SSH キーの名前
+HttpsUrl=HTTPS URL
+PayloadUrl=ペイロードの URL
+TeamName=チーム名
+AuthName=承認名
+AdminEmail=管理者の電子メール
+
+require_error=空にできません
+alpha_dash_error=アルファベット、数字、ハイフン"-"、アンダースコア"_"のいずれかの必要があります
+alpha_dash_dot_error=' アルファベット、数値、ダッシュ(-)、アンダースコア(_) 、ドット(.)のいずれかを入力する必要があります。 '
+min_size_error=' 少なくとも %s 文字の必要があります '
+max_size_error=' %s 文字以下の必要があります '
+email_error=' は有効な電子メール アドレスではない '
+url_error=' は有効な URL はありません。 '
+unknown_error=不明なエラー:
+captcha_incorrect=Captcha が一致しませんでした。
+password_not_match=パスワードと確認用パスワードが一致同していません。
+
+username_been_taken=ユーザー名は既に使用されています。
+repo_name_been_taken=リポジトリ名は既に使用されています。
+org_name_been_taken=組織名は既に使用されています。
+team_name_been_taken=チーム名は既に使用されています。
+email_been_used=電子メール アドレスは既に使用されています。
+ssh_key_been_used=パブリック キー名が使用されています。
+illegal_username=あなたのユーザ名に無効な文字が含まれます。
+illegal_repo_name=リポジトリ名には無効な文字が含まれています。
+illegal_org_name=組織名に無効な文字が含まれています。
+illegal_team_name=チーム名に無効な文字が含まれています。
+username_password_incorrect=ユーザー名またはパスワードが正しくありません。
+enterred_invalid_repo_name=入力したリポジトリの名前が正しいかどうかを確認してください。
+enterred_invalid_owner_name=入力された所有者名が正しいかどうかを確認してください。
+enterred_invalid_password=入力したパスワードが正しいかを確認してください。
+user_not_exist=指定されたユーザーは存在しません。
+last_org_owner=削除するユーザーはチームの最後のメンバーです。別の所有者設定が必要です。
+
+invalid_ssh_key=SSHを確認できません:%s
+unable_verify_ssh_key=GogsはあなたのSSH keyを確認できません。しかし、我々は有効とみなしますので、自分自身で確認してください。
+auth_failed=認証に失敗しました: %v
+
+still_own_repo=アカウント所有のリポジトリがあり、リポジトリの削除または所有者の移譲が必要です。
+still_has_org=アカウントはまだ組織のメンバーであり、組織から退出するか削除する必要があります。
+org_still_own_repo=この組織はまだリポジトリの所有しています、リポジトリを削除または転送する必要があります。
+
+still_own_user=この認証はまだ一部のユーザーによって使用されています。一部のユーザを移動させてから、もう一度削除してください。
+
+target_branch_not_exist=ターゲットブランチが存在しない
+
+[user]
+change_avatar=gravatar.com で自分のアバターを変更
+change_custom_avatar=設定で自分のアバターを変更
+join_on=参加しました
+repositories=リポジトリ
+activity=パブリック・アクティビティ
+followers=フォロワー
+starred=スター
+following=フォロー
+
+[settings]
+profile=プロフィール
+password=パスワード
+ssh_keys=SSH キー
+social=SNSアカウント
+applications=アプリケーション
+orgs=組織
+delete=アカウントを削除
+uid=Uid
+
+public_profile=パブリック プロフィール
+profile_desc=あなたのメールアドレスは公開され、任意のアカウント関連の通知に使用されます。また、Webベースの操作はサイトを介して行います。
+full_name=フルネーム
+website=WEBサイト
+location=ロケーション
+update_profile=プロファイル更新
+update_profile_success=あなたのプロフィールが更新されました。
+change_username=ユーザー名が変更されました
+change_username_desc=ユーザー名が変更されている、継続したいですか?これはあなたのアカウントに関連するすべてのリンクに影響を与える。
+continue=続行
+cancel=キャンセル
+
+enable_custom_avatar=カスタムのアバターを有効にする
+enable_custom_avatar_helper=Gravatarからのフェッチを無効にするのを、有効にします
+choose_new_avatar=新しいアバターを選択
+update_avatar=アバターの設定を更新
+uploaded_avatar_not_a_image=アップロードされたファイルは画像ではない。
+no_custom_avatar_available=利用可能なカスタム アバターがないため、有効にできません。
+update_avatar_success=あなたのアバターの設定が更新されました。
+
+change_password=パスワードを変更
+old_password=現在のパスワード
+new_password=新しいパスワード
+password_incorrect=現在のパスワードが正しくありません。
+change_password_success=パスワードが正常に変更されました。今すぐ新しいパスワード経由でサインインすることができます。
+
+emails=E-mail アドレス
+manage_emails=E-mail アドレスを管理
+email_desc=あなたのプライマリメールアドレスは、通知やその他の操作に使用されます。
+primary=プライマリー
+primary_email=プライマリに設定
+delete_email=削除
+add_new_email=新しいe-mailアドレスを追加
+add_email=電子メールを追加します。
+add_email_success=新しいe-mail アドレスが追加されました。
+
+manage_ssh_keys=SSH キーを管理
+add_key=キーを追加
+ssh_desc=これはあなたのアカウントに関連付けられている SSH キーの一覧です。あなたが認識していないキーを削除します。
+ssh_helper=<strong>ヘルプが必要ですか?</strong> 我々のガイドをご覧ください。 <a href="%s"> SSH キーを生成</a>  <a href="%s"> SSH の一般的な問題</a>
+add_new_key=SSH キーを追加
+key_name=キーの名前
+key_content=コンテンツ
+add_key_success=新しい SSH キーが追加されました !
+delete_key=削除
+add_on=追加された
+last_used=最終使用日
+no_activity=最近の活動なし
+
+manage_social=関連付けられているSNSアカウントを管理
+social_desc=これは関連付けられたソーシャルアカウントのリストです。あなたが認識していない結び付けを削除します。
+unbind=バインド解除
+unbind_success=SNSアカウントがバインドされていない。
+
+manage_access_token=個人のアクセス トークンを管理
+generate_new_token=新しいトークンを生成
+tokens_desc=生成したトークンを利用して Gogs の API にアクセスすることができます。
+new_token_desc=今のところ、全てのトークンはあなたのアカウントにフルアクセスできます。
+token_name=トークン名
+generate_token=トークンを生成
+generate_token_succees=新しいアクセス トークンは正常に生成されました !今すぐあなたの新しいアクセス トークンをコピーしておいてください。二度と見ることはできませんので確認してください!
+delete_token=削除
+delete_token_success=個人のアクセス トークンは正常に削除されました!同時にあなたのアプリケーションを更新することを忘れないでください。
+
+delete_account=アカウントを削除
+delete_prompt=この操作はあなたのアカウントを完全に削除し、復旧<strong>できない</strong> !
+confirm_delete_account=削除の確認
+delete_account_title=アカウントの削除
+delete_account_desc=このアカウントは永久に削除しようとしている、継続しますか?
+
+[repo]
+owner=オーナー
+repo_name=リポジトリ名
+repo_name_helper=偉大なリポジトリ名は短い。思い出に残り、そして<strong>一意</strong>だ。
+visibility=ビジビリティ
+visiblity_helper=このリポジトリは <span class="label label-red label-radius"> プライベート</span> です。
+fork_repo=フォークのリポジトリ
+fork_from=フォーク元
+fork_visiblity_helper=フォークされたリポジトリは可視状態を変更できません
+repo_desc=説明
+repo_lang=言語
+repo_lang_helper=.gitignore ファイルを選択
+license=ライセンス
+license_helper=ライセンス ファイルを選択
+init_readme=README.md 付きでリポジトリを初期化
+create_repo=リポジトリを作成
+default_branch=デフォルトのブランチ
+mirror_interval=ミラー 間隔(時)
+goget_meta=Go-Get メタ
+goget_meta_helper=このリポジトリは <span class="label label-blue label-radius"> Go-Getable </span> になります
+
+need_auth=認証が必要
+migrate_type=マイグレーションの種類
+migrate_type_helper=このリポジトリは <span class="label label-blue label-radius"> ミラー</span> になります
+migrate_repo=リポジトリを移行
+
+copy_link=コピー
+click_to_copy=クリップボードにコピー
+copied=コピー成功
+clone_helper=クローニングのヘルプが必要ですか?<a target="_blank"href="%s"> ヘルプ</a> を参照してください!
+unwatch=Unwatch
+watch=Watch
+unstar=Unstar
+star=Star
+fork=Fork
+
+no_desc=説明なし
+quick_guide=クイック ガイド
+clone_this_repo=このリポジトリのクローンを作成
+create_new_repo_command=コマンドラインで新しいリポジトリを作成します。
+push_exist_repo=コマンド ・ ラインから既存のリポジトリをプッシュ
+
+branch=ブランチ
+tree=ツリー
+branch_and_tags=ブランチ& タグ
+branches=ブランチ
+tags=タグ
+issues=課題
+commits=コミット
+releases=リリース
+file_raw=生データ
+file_history=履歴
+file_view_raw=生データを見る
+
+commits.commits=コミット
+commits.search=コミットの検索
+commits.find=検索
+commits.author=作者
+commits.message=メッセージ
+commits.date=日付
+commits.older=古い
+commits.newer=新しい
+
+settings=設定
+settings.options=オプション
+settings.collaboration=コラボレーション
+settings.hooks=Webhooks
+settings.githooks=Git のフック
+settings.deploy_keys=デプロイキー
+settings.basic_settings=基本設定
+settings.danger_zone=危険地帯
+settings.site=公式サイト
+settings.update_settings=設定の更新
+settings.change_reponame=リポジトリ名が変更されました
+settings.change_reponame_desc=リポジトリの名前が変更されています、継続しますか?このリポジトリ関連すべてのリンクに影響を与えます。
+settings.transfer=オーナー移転
+settings.transfer_desc=リポジトリをあなたが管理者権限を持っている別のユーザーまた組織に移譲します。
+settings.new_owner_has_same_repo=新しいオーナーは、既に同じ名前のリポジトリを持っています。
+settings.delete=このリポジトリを削除
+settings.delete_desc=リポジトリを削除すると元に戻せません。確実に確認してください。
+settings.transfer_notices=<p>-新オーナーは個人ユーザの場合、あなたはにアクセスできなくなります。</p><p>-新オーナーは組織であり、かつあなたが組織のオーナーに所属する場合、あなたはアクセス権を維持します。</p>
+settings.update_settings_success=リポジトリ オプションが更新されました。
+settings.transfer_owner=新しいオーナー
+settings.make_transfer=転送
+settings.transfer_succeed=リポジトリの所有権は正常に転送されました。
+settings.confirm_delete=削除の確認
+settings.add_collaborator=新しい共同編集者を追加
+settings.add_collaborator_success=新しい共同編集者が追加されました。
+settings.remove_collaborator_success=共同編集者が削除されました。
+settings.user_is_org_member=ユーザーは組織の一員なので、共同編集者として追加することはできません。
+settings.add_webhook=Webhook を追加
+settings.hooks_desc=Webhooksは、Gogsで特定のイベントの発生時に指定された外部サービスに通知を許可します。イベントが発生すると、それぞれ指定されたUrlに、POSTリクエストが送られます。詳細はこちらのの <a target="_blank"href="%s"> Webhooks ガイド</a>をご覧ください。
+settings.githooks_desc=Git のフックは Git 自体によって提供されています。以下のリストのファイルを編集して、サポートされているフックのカスタム操作を適用することができます。
+settings.githook_edit_desc=もしフックがアクティブではない場合は、サンプルコンテンツが表示されます。コンテンツを空白にするにはこのフックを無効にします。
+settings.githook_name=フックの名前
+settings.githook_content=コンテンツをフック
+settings.update_githook=フックを更新
+settings.remove_hook_success=Webhookが削除されました。
+settings.add_webhook_desc=私たちは、指定されたURLに購読されたイベントの詳細を <code>POST</code>リクエストとして送信します。あなたは、異なるデータ受信モード(JSONまたは, <code>x-www-form-urlencoded</code>, <em>その他</em>) を設定することができます。詳細については、<a target="_blank" href="%s">Webhookガイド</a>を参照してください。
+settings.payload_url=ペイロードの URL
+settings.content_type=コンテンツ タイプ
+settings.secret=秘密
+settings.event_desc=どのイベントをこのWEBフックのトリガーにしますか?
+settings.event_push_only=<code>push</code> イベントのみ
+settings.active=アクティブ
+settings.active_helper=このフックのトリガーが引かれた時に、イベントの詳細を配信します。
+settings.add_hook_success=新しい webhook が追加されました。
+settings.update_webhook=Webhookを更新
+settings.update_hook_success=Webhook を更新しました。
+settings.delete_webhook=Webhook を削除
+settings.recent_deliveries=最近のデリバリー
+settings.hook_type=フックタイプ
+settings.add_slack_hook_desc=<a href="%s"> Slack</a> インテグレーションをリポジトリに追加します。
+settings.slack_token=トークン
+settings.slack_domain=ドメイン
+settings.slack_channel=チャンネル
+
+diff.browse_source=ソースを参照
+diff.parent=親
+diff.commit=コミット
+diff.data_not_available=差分データは利用できません。
+diff.show_diff_stats=差分情報を表示
+diff.stats_desc=共有<strong>%d 個のファイルを変更した</strong>、<strong>%d 個の追加</strong> と <strong>%d 個の削除</strong>を含む
+diff.bin=BIN
+diff.view_file=ファイルの表示
+
+release.releases=リリース
+release.new_release=新しいリリース
+release.draft=ドラフト
+release.prerelease=プレリリース
+release.stable=安定
+release.edit=編集
+release.ahead=このリリース以降 %s へ <strong>%d</strong> コミット
+release.source_code=ソース コード
+release.tag_name=タグ名
+release.target=ターゲット
+release.tag_helper=既存のタグを選択するか、新しいタグを作成し発行します。
+release.release_title=リリース タイトル
+release.content_with_md=<a href="%s"> Markdown</a> コンテンツ
+release.write=書込み
+release.preview=プレビュー
+release.content_placeholder=コンテンツを書く
+release.loading=読み込み中…
+release.prerelease_desc=これはリリース前のものです
+release.prerelease_helper=このリリースは非プロダクション利用として識別します。
+release.publish=リリースを発行
+release.save_draft=下書きを保存
+release.edit_release=リリースを編集
+release.tag_name_already_exist=このタグ名には既にリリースが存在します。
+
+[org]
+org_name_holder=組織名
+org_name_helper=偉大な組織の名は短く覚えやすいです。
+org_email_helper=組織の電子メールはすべての通知や確認を受け取ります。
+create_org=組織を作成
+repo_updated=更新した
+people=人々
+invite_someone=誰かを招待
+teams=チーム
+lower_members=メンバー
+lower_repositories=リポジトリ
+create_new_team=新しいチームを作成
+org_desc=説明
+team_name=チーム名
+team_desc=説明
+team_name_helper=会話の時、この名前を使用しチーム名を表明します。
+team_desc_helper=このチームに関する全ての情報は?
+team_permission_desc=このチームに必要な権限レベルは?
+
+settings=設定
+settings.options=オプション
+settings.full_name=フルネーム
+settings.website=WEBサイト
+settings.location=ロケーション
+settings.update_settings=設定の更新
+settings.change_orgname=組織名が変更されました
+settings.change_orgname_desc=組織名が変更されています、継続しますか?これはすべての関連リンクに影響を与えます。
+settings.update_setting_success=組織の設定が更新されました。
+settings.delete=組織を削除
+settings.delete_account=この組織を削除
+settings.delete_prompt=操作はこの組織を完全に削除し、復旧<strong>できない</strong>!
+settings.confirm_delete_account=削除の確認
+settings.delete_org_title=組織の削除
+settings.delete_org_desc=この組織は完全に削除されます、継続しますか?
+settings.hooks_desc=この組織のもとで <strong>すべてのリポジトリ</strong> に対してトリガーされる webhook を追加します。
+
+members.public=パブリック
+members.public_helper=プライベートにする
+members.private=プライベート
+members.private_helper=公開する
+members.owner=オーナー
+members.member=メンバー
+members.conceal=隠す
+members.remove=削除
+members.leave=退出
+members.invite_desc=%s に招待する新しいメンバーをユーザ名を入力してください:
+members.invite_now=今すぐ招待
+
+teams.join=参加
+teams.leave=退出
+teams.read_access=読み取りアクセス権
+teams.read_access_helper=このチームはリポジトリの閲覧とクローンをすることができます。
+teams.write_access=書き込みアクセス権
+teams.write_access_helper=このチームはリポジトリを読むだけではなく、プッシュすることもできます。
+teams.admin_access=管理者のアクセス権
+teams.admin_access_helper=このチームはリポジトリにプッシュ/プル、及び他の共同編集者を追加することができます。
+teams.no_desc=このチームは説明がありません。
+teams.settings=設定
+teams.owners_permission_desc=オーナーは<strong>すべてのリポジトリ</strong> へのフルアクセス権、組織の <strong>管理権限</strong>を持ちます。
+teams.members=チーム メンバー
+teams.update_settings=設定の更新
+teams.delete_team=このチームを削除
+teams.add_team_member=チーム メンバーを追加
+teams.delete_team_title=チームの削除
+teams.delete_team_desc=このチームを削除します、継続しますか?このチームのメンバーはいくつかのリポジトリへのアクセスを失う可能性があります。
+teams.delete_team_success=指定のチームが正常に削除されました。
+teams.read_permission_desc=このチームは<strong>読み取り</strong>権限を持ち: メンバーはリポジトリの表示及びクローンの作成ができます。
+teams.write_permission_desc=このチームは<strong>書き込み</strong>権限を持ち: メンバーはリポジトリの表示及リポジトリへのプッシュができます。
+teams.admin_permission_desc=このチームは<strong>管理者</strong>の権限を持ち: メンバーはチームのリポジトリに対して、読み取り、プッシュや共同編集者の追加ができます。
+teams.repositories=チームのリポジトリ
+teams.add_team_repository=チームのリポジトリを追加
+teams.remove_repo=削除(Remove)
+teams.add_nonexistent_repo=追加しようとしているリポジトリは存在しません。まずはじめに作成してください。
+
+[admin]
+dashboard=ダッシュボード
+users=ユーザ
+organizations=組織
+repositories=リポジトリ
+authentication=認証
+config=コンフィギュレーション
+notices=システム通知
+monitor=モニタリング
+prev=前へ
+next=次へ
+
+dashboard.statistic=統計
+dashboard.operations=操作
+dashboard.system_status=システム モニターのステータス
+dashboard.statistic_info=Gogs データベースは <b>%d</b> ユーザ, <b>%d</b> 組織, <b>%d</b> 公開鍵, <b>%d</b> リポジトリ, <b>%d</b> ウォッチ, <b>%d</b> スター, <b>%d</b> 行動, <b>%d</b> アクセス, <b>%d</b> 問題, <b>%d</b> コメント, <b>%d</b> ソーシャルアカウント, <b>%d</b> フォロー, <b>%d</b> ミラー, <b>%d</b> リリース, <b>%d</b> ログイン元, <b>%d</b> webhook, <b>%d</b> マイルストーン, <b>%d</b> ラベル, <b>%d</b> フックタスク, <b>%d</b> チーム, <b>%d</b> アップデートタスク, <b>%d</b> 添付ファイル の情報を持っています。
+dashboard.operation_name=操作の名前
+dashboard.operation_switch=スイッチ
+dashboard.operation_run=実行
+dashboard.clean_unbind_oauth=結び付けられていない OAuth をクリーン
+dashboard.clean_unbind_oauth_success=結び付けられていない全ての OAuth を正常に削除しました。
+dashboard.delete_inactivate_accounts=非アクティブのアカウントをすべて削除
+dashboard.delete_inactivate_accounts_success=すべての非アクティブアカウントは正常に削除されました。
+dashboard.delete_repo_archives=リポジトリのすべてのアーカイブを削除
+dashboard.delete_repo_archives_success=リポジトリのすべてのアーカイブが正常に削除されました。
+dashboard.git_gc_repos=リポジトリでのガベージコレクションを実行します。
+dashboard.git_gc_repos_success=すべてのリポジトリは正常にガベージ コレクションを行いました。
+dashboard.server_uptime=サーバーの稼働時間
+dashboard.current_goroutine=現在のGoroutine
+dashboard.current_memory_usage=現在のメモリ使用量
+dashboard.total_memory_allocated=割り当てられたメモリの合計
+dashboard.memory_obtained=配分されたメモリ量
+dashboard.pointer_lookup_times=ポインタ参照回数
+dashboard.memory_allocate_times=メモリ割当回数
+dashboard.memory_free_times=メモリ解放回数
+dashboard.current_heap_usage=現在のヒープ使用量
+dashboard.heap_memory_obtained=配分されたヒープ メモリ量
+dashboard.heap_memory_idle=アイドルのヒープ メモリ量
+dashboard.heap_memory_in_use=使用中のヒープ メモリ
+dashboard.heap_memory_released=ヒープ メモリが解放されました
+dashboard.heap_objects=ヒープ オブジェクト
+dashboard.bootstrap_stack_usage=ブートストラップスタック使用量
+dashboard.stack_memory_obtained=配分されたスタック メモリ量
+dashboard.mspan_structures_usage=MSpan 構造体の使用量
+dashboard.mspan_structures_obtained=配分されたMSpan 構造体
+dashboard.mcache_structures_usage=MCache 構造体の使用量
+dashboard.mcache_structures_obtained=分配されたMCache 構造体
+dashboard.profiling_bucket_hash_table_obtained=ハッシュテーブル分析に割り当てられたメモリ
+dashboard.gc_metadata_obtained=GCメタデータ取得
+dashboard.other_system_allocation_obtained=他のシステムに割り当てられたメモリ
+dashboard.next_gc_recycle=次回のGCリサイクル
+dashboard.last_gc_time=前回GCからの時間
+dashboard.total_gc_time=GC一時停止の合計
+dashboard.total_gc_pause=GC一時停止の合計
+dashboard.last_gc_pause=直近のGC一時停止
+dashboard.gc_times=GC実行回数
+
+users.user_manage_panel=ユーザー管理パネル
+users.new_account=新規アカウントを作成
+users.name=名前
+users.activated=アクティブ化
+users.admin=アドミン
+users.repos=リポジトリ
+users.created=作成されました
+users.edit=編集
+users.auth_source=認証元
+users.local=ローカル
+users.auth_login_name=認証ログイン名
+users.update_profile_success=アカウントのプロファイルが更新されました。
+users.edit_account=アカウントの編集
+users.is_activated=アカウントがアクティブされました
+users.is_admin=このアカウントには管理者の権限を持つ
+users.allow_git_hook=このアカウントには Git のフックを作成する権限を持つ
+users.update_profile=アカウント ・ プロファイルを更新
+users.delete_account=このアカウントを削除
+users.still_own_repo=アカウント所有のリポジトリがあり、リポジトリの削除または所有者の移譲が必要です。
+users.still_has_org=アカウントはまだ組織のメンバーであり、組織から退出するか削除する必要があります。
+
+orgs.org_manage_panel=組織の管理パネル
+orgs.name=名前
+orgs.teams=チーム
+orgs.members=メンバー
+
+repos.repo_manage_panel=リポジトリの管理パネル
+repos.owner=オーナー
+repos.name=名前
+repos.private=プライベート
+repos.watches=Watches
+repos.stars=Stars
+repos.issues=課題
+
+auths.auth_manage_panel=承認の管理パネル
+auths.new=新しい認証元を追加
+auths.name=名前
+auths.type=タイプ
+auths.enabled=Enabled
+auths.updated=Updated
+auths.auth_type=認証の種類
+auths.auth_name=認証名
+auths.domain=ドメイン
+auths.host=ホスト
+auths.port=ポート
+auths.base_dn=ベースのドメイン名
+auths.attributes=属性検索
+auths.filter=検索フィルター
+auths.ms_ad_sa=Ms Ad SA
+auths.smtp_auth=SMTP 認証の種類
+auths.smtphost=SMTP ホスト
+auths.smtpport=SMTP ポート
+auths.enable_tls=TLS 暗号化を有効にする
+auths.enable_auto_register=自動登録を有効にする
+auths.tips=ヒント
+auths.edit=認証設定を編集
+auths.activated=認証がアクティブされました
+auths.update_success=認証の設定が正常に更新されました。
+auths.update=認証設定の更新
+auths.delete=この権限を削除
+auths.delete_auth_title=認証の削除
+auths.delete_auth_desc=認証を削除します、継続しますか?
+
+config.server_config=サーバーの構成
+config.app_name=アプリケーション名
+config.app_ver=アプリケーションのバージョン
+config.app_url=アプリケーションの URL
+config.domain=ドメイン
+config.offline_mode=オフラインモード
+config.disable_router_log=ルーターのログを無効にする
+config.run_user=実行ユーザ
+config.run_mode=実行モード
+config.repo_root_path=リポジトリのルートパス
+config.static_file_root_path=静的ファイルのルートパス
+config.log_file_root_path=ログ ファイルのルート パス
+config.script_type=スクリプトの種類
+config.reverse_auth_user=リバース認証ユーザ
+config.db_config=データベースの構成
+config.db_type=タイプ
+config.db_host=ホスト
+config.db_name=名前
+config.db_user=ユーザ
+config.db_ssl_mode=SSL モード
+config.db_ssl_mode_helper=(「postgres」のみ)
+config.db_path=パス
+config.db_path_helper=(「sqlite3」のみ)
+config.service_config=サービスの構成
+config.register_email_confirm=電子メールの確認を必要
+config.disable_register=登録を無効にする
+config.require_sign_in_view=サインインを要求
+config.mail_notify=メール通知
+config.enable_cache_avatar=アバターのキャッシュを有効にします。
+config.active_code_lives=コードリンクの有効期限をアクティブ
+config.reset_password_code_lives=パスワードリンクの有効期限をリセット
+config.webhook_config=Webhook設定
+config.task_interval=タスクの間隔
+config.deliver_timeout=送信タイムアウト
+config.mailer_config=メーラーの構成
+config.mailer_enabled=有効にした
+config.mailer_name=名前
+config.mailer_host=ホスト
+config.mailer_user=ユーザ
+config.oauth_config=OAuth 構成
+config.oauth_enabled=Enabled
+config.cache_config=キャッシュの構成
+config.cache_adapter=キャッシュ アダプター
+config.cache_interval=キャッシュ間隔
+config.cache_conn=キャッシュ接続
+config.session_config=セッションの構成
+config.session_provider=セッション プロバイダー
+config.provider_config=プロバイダーの構成
+config.cookie_name=クッキー名
+config.enable_set_cookie=クッキーの設定を有効にする
+config.gc_interval_time=GC 間隔
+config.session_life_time=セッションのライフタイム
+config.https_only=HTTPS のみ
+config.cookie_life_time=クッキーのライフタイム
+config.picture_config=画像構成
+config.picture_service=画像サービス
+config.disable_gravatar=グラバターを無効にする
+config.log_config=ログの構成
+config.log_mode=ログ モード
+
+monitor.cron=Cron タスク
+monitor.name=名前
+monitor.schedule=スケジュール
+monitor.next=次回
+monitor.previous=前回
+monitor.execute_times=実行回数
+monitor.process=実行中のプロセス
+monitor.desc=説明
+monitor.start=開始日時
+monitor.execute_time=実行時間:
+
+notices.system_notice_list=システム通知
+notices.type=タイプ
+notices.type_1=リポジトリ
+notices.desc=説明
+notices.op=Op。
+notices.delete_success=システム通知が正常に削除されました。
+
+[action]
+create_repo=リポジトリ <a href="%s/%s"> %s</a>を作成しました
+commit_repo=<a href="%s/%s">%s</a>を<a href="%s/%s/src/%s">%s</a>にプッシュしました
+create_issue=問題 <a href="%s/%s/issues/%s"> %s #%s</a> を開きました
+comment_issue=問題 <a href="%s/%s/issues/%s"> %s #%s</a> のコメント
+transfer_repo=リポジトリ <code>%s</code> を <a href="/%s%s">%s</a> へ転送しました
+push_tag=<a href="%s/%s">%s</a> に タグ <a href="%s/%s/src/%s">%s</a> をプッシュしました
+compare_2_commits=これら 2 のコミットの比較を閲覧する
+
+[tool]
+ago=前
+from_now=今から
+now=今
+1s=1 秒 %s
+1m=1 分 %s
+1h=1 時間 %s
+1d=1 日 %s
+1w=1 週間 %s
+1mon=1 ヶ月 %s
+1y=1 年間 %s
+seconds=%d 秒 %s
+minutes=%d 分の %s
+hours=%d 時間 %s
+days=%d 日 %s
+weeks=%d 週間 %s
+months=%d ヶ月 %s
+years=%d 年 %s
+raw_seconds=秒
+raw_minutes=分
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 62 - 62
conf/locale/locale_ru-RU.ini

@@ -61,7 +61,7 @@ domain=Домен
 domain_helper=This affects SSH clone URLs.
 app_url=URL приложения
 app_url_helper=This affects HTTP/HTTPS clone URL and somewhere in e-mail.
-email_title=E-mail Service Settings (Optional)
+email_title=Настройки службы электронной почты (опционально)
 smtp_host=Узел SMTP
 mailer_user=Электронная почта отправителя
 mailer_password=Пароль отправителя
@@ -75,7 +75,7 @@ confirm_password=Подтвердить пароль
 admin_email=Эл. почта
 install_gogs=Установить Gogs
 test_git_failed=Не удалось проверить 'git' команду: %v
-sqlite3_not_available=Your release version does not support SQLite3, please download the official binary version from %s, NOT the gobuild version.
+sqlite3_not_available=Ваша версия не поддерживает SQLite3, пожалуйста скачайте официальную бинарную версию от %s, а не версию gobuild.
 invalid_db_setting=Настройки базы данных не правильные: %v
 invalid_repo_path=Недопустимый путь к корню репозитория: %v
 run_user_not_match=Run user isn't the current user: %s -> %s
@@ -98,9 +98,9 @@ repos=Репозитории
 [auth]
 create_new_account=Создать новый аккаунт
 register_hepler_msg=Уже есть аккаунт? Авторизуйтесь!
-social_register_hepler_msg=Уже есть учетная запись? Свяжите ее соцсетью!
+social_register_hepler_msg=Уже есть учетная запись? Свяжите ее с соцсетью!
 disable_register_prompt=Извините,  возможность регистрации отключена. Пожалуйста, свяжитесь с администратором сайта.
-disable_register_mail=Sorry, Register Mail Confirmation has been disabled.
+disable_register_mail=К сожалению подтверждение регистрации по почте отключено.
 remember_me=Запомнить меня
 forgot_password=Забыли пароль
 forget_password=Забыли пароль?
@@ -109,7 +109,7 @@ confirmation_mail_sent_prompt=Новое письмо для подтвержд
 sign_in_email=Войдите в свой адрес электронной почты
 active_your_account=Активируйте свой аккаунт
 resent_limit_prompt=Вы слишком часто отправляете письмо с активацией. Подождите 3 минуты, пожалуйста.
-has_unconfirmed_mail=Hi %s, you have an unconfirmed e-mail address <b>%s</b>). If you haven't received a confirmation e-mail or need to resend a new one, please click on the button below.
+has_unconfirmed_mail=Здравствуйте, %s! У вас есть неподтвержденный адрес электронной почты (<b>%s</b>). Если вам не приходило письмо с подтверждением или нужно выслать новое письмо, нажмите на кнопку ниже.
 resend_mail=Нажмите здесь, чтобы переотправить активационное письмо
 email_not_associate=Этот адрес электронной почты не связан ни с одной учетной записью.
 send_reset_mail=Нажмите сюда, чтобы отправить письмо для сброса пароля
@@ -157,17 +157,17 @@ enterred_invalid_repo_name=Пожалуйста, убедитесь, что вв
 enterred_invalid_owner_name=Убедитесь, что введенное имя владельца верное.
 enterred_invalid_password=Убедитесь, что введенный пароль верен.
 user_not_exist=Данный пользователь не существует.
-last_org_owner=The user to remove is the last member in owner team. There must be another owner.
+last_org_owner=Удаляемый пользователь является последним в команде владельцев. Должен быть хотя бы один владелец.
 
 invalid_ssh_key=К сожалению, мы не смогли проверить ваш SSH-ключ: %s
 unable_verify_ssh_key=Gogs не может проверить ваш SSH-ключ, но мы допускаем, что он действителен. Пожалуйста, удостоверьтесь самостоятельно, что ключ действителен.
 auth_failed=Ошибка аутентификации: %v
 
-still_own_repo=Your account still have ownership of repository, you have to delete or transfer them first.
-still_has_org=Your account still have membership of organization, you have to left or delete them first.
+still_own_repo=На вашем аккаунте все еще остается как минимум один репозиторий, сначала вам нужно удалить или передать его.
+still_has_org=Вы находитесь в организации, сперва Вам необходимо покинуть ее или удалить.
 org_still_own_repo=Данная организация все еще является владельцем репозиториев, необходимо удалить или переместить их в начале.
 
-still_own_user=This authentication still has used by some users, you should move them and then delete again.
+still_own_user=Эта проверка подлинности по-прежнему используется некоторыми пользователями, вы должны переместить их и затем снова удалить.
 
 target_branch_not_exist=Целевая ветка не существует
 
@@ -192,7 +192,7 @@ delete=Удалить аккаунт
 uid=UID
 
 public_profile=Открытый профиль
-profile_desc=Your E-mail address is public and will be used for any account related notifications, and any web based operations made via the site.
+profile_desc=Адрес вашей электронной почты является публичным и будет использован для любых уведомлений, связанных с аккаунтом, а также для любых действий, совершенных через сайт.
 full_name=ФИО
 website=Веб-сайт
 location=Местоположение
@@ -217,15 +217,15 @@ new_password=Новый пароль
 password_incorrect=Текущий пароль не правильный.
 change_password_success=Пароль сменен успешно. Теперь вы можете войти с новым паролем.
 
-emails=E-mail Addresses
-manage_emails=Manage e-mail addresses
-email_desc=Your primary e-mail address will be used for notifications and other operations.
-primary=Primary
-primary_email=Set as primary
-delete_email=Delete
-add_new_email=Add new e-mail address
-add_email=Add e-mail
-add_email_success=Your new E-mail address was successfully added.
+emails=Адреса электронной почты
+manage_emails=Управление адресами электронной почты
+email_desc=Ваш основной адрес электронной почты будет использован для уведомлений и других операций.
+primary=Основной
+primary_email=Установить как основной
+delete_email=Удалить
+add_new_email=Добавить новый адрес электронной почты
+add_email=Добавить электронную почту
+add_email_success=Новый адрес электронной почты успешно добавлен.
 
 manage_ssh_keys=Управление SSH ключами
 add_key=Добавить ключ
@@ -240,20 +240,20 @@ add_on=Добавлено
 last_used=Последний раз использовался
 no_activity=Еще не применялся
 
-manage_social=Manage Associated Social Accounts
-social_desc=This is a list of associated social accounts. Remove any binding that you do not recognize.
+manage_social=Управление привязанными учетными записями в соцсетях
+social_desc=Это список привязанных учетных записей в соцсетях. Удаляйте любые неизвестные вам привязки.
 unbind=Отвязать
 unbind_success=Социальная учетная запись отвязана.
 
-manage_access_token=Manage Personal Access Tokens
+manage_access_token=Управление Токенами Персонального Доступа
 generate_new_token=Создать новый token
-tokens_desc=Tokens you have generated that can be used to access the Gogs API.
-new_token_desc=As for now, every token will have full access to your account.
+tokens_desc=Созданные вами токены могут использоваться для доступа к Gogs API.
+new_token_desc=Пока что каждый токен будет иметь полный доступ к вашей учетной записи.
 token_name=Имя маркера
 generate_token=Генерировать маркер
-generate_token_succees=New access token has been generated successfully! Make sure to copy your new personal access token now. You won't be able to see it again!
+generate_token_succees=Успешно создан новый токен доступа! Пожалуйста сделайте копию вашего нового токена персонального доступа. Вы не сможете увидеть его снова!
 delete_token=Удалить
-delete_token_success=Personal access token has been deleted successfully! Don't forget to update your applications as well.
+delete_token_success=Персональный токен доступа был успешно удален! Не забудьте так же обновить ваши приложения.
 
 delete_account=Удалить свой аккаунт
 delete_prompt=Этим действием вы удалите свою учетную запись навсегда и <strong>НЕ СМОЖЕТЕ</strong> ее вернуть!
@@ -285,7 +285,7 @@ goget_meta_helper=This repository will be <span class="label label-blue label-ra
 need_auth=Требуется авторизация
 migrate_type=Тип миграции
 migrate_type_helper=Этот репозиторий будет <span class="label label-blue label-radius">зеркалом</span>
-migrate_repo=Migrate Repository
+migrate_repo=Перенос репозитория
 
 copy_link=Копировать
 click_to_copy=Скопировать в буфер обмена
@@ -322,7 +322,7 @@ commits.author=Автор
 commits.message=Сообщение
 commits.date=Дата
 commits.older=Раньше
-commits.newer=Newer
+commits.newer=Новее
 
 settings=Настройки
 settings.options=Опции
@@ -347,9 +347,9 @@ settings.transfer_owner=Новый владелец
 settings.make_transfer=Выполнить передачу
 settings.transfer_succeed=Repository ownership has been transferred successfully.
 settings.confirm_delete=Подтвердить удаление
-settings.add_collaborator=Add New Collaborator
-settings.add_collaborator_success=New collaborator has been added.
-settings.remove_collaborator_success=Collaborator has been removed.
+settings.add_collaborator=Добавить нового соавтора
+settings.add_collaborator_success=Был добавлен новый соавтор.
+settings.remove_collaborator_success=Соавтор был удален.
 settings.user_is_org_member=User is organization member who cannot be added as a collaborator.
 settings.add_webhook=Добавить Webhook
 settings.hooks_desc=Webhooks allow external services to be notified when certain events happen on Gogs. When the specified events happen, we'll send a POST request to each of the URLs you provide. Learn more in our <a target="_blank" href="%s">Webhooks Guide</a>.
@@ -462,14 +462,14 @@ members.invite_now=Пригласите сейчас
 teams.join=Объединить
 teams.leave=Выйти
 teams.read_access=Доступ на чтение
-teams.read_access_helper=This team will be able to view and clone its repositories.
+teams.read_access_helper=Эта команда будет иметь возможность просматривать и клонировать ее репозитории.
 teams.write_access=Доступ на запись
-teams.write_access_helper=This team will be able to read its repositories, as well as push to them.
+teams.write_access_helper=Эта команда будет в состоянии прочитать ее репозитории, а также посылать изменения.
 teams.admin_access=Доступ администратора
 teams.admin_access_helper=This team will be able to push/pull to its repositories, as well as add other collaborators to them.
 teams.no_desc=Эта группа не имеет описания
 teams.settings=Настройки
-teams.owners_permission_desc=Owners have full access to <strong>all repositories</strong> and have <strong>admin rights</strong> to the organization.
+teams.owners_permission_desc=Владельцы имеют полный доступ ко <strong>всем репозиториям</strong> и имеют <strong>права администратора</strong> организации.
 teams.members=Члены группы разработки
 teams.update_settings=Обновить настройки
 teams.delete_team=Удалить эту группу разработки
@@ -486,7 +486,7 @@ teams.remove_repo=Удалить
 teams.add_nonexistent_repo=Вы добавляете в отсутствующий репозиторий, пожалуйста сначала его создайте.
 
 [admin]
-dashboard=Dashboard
+dashboard=Панель управления
 users=Пользователи
 organizations=Организации
 repositories=Репозитории
@@ -508,14 +508,14 @@ dashboard.clean_unbind_oauth=Clean unbound OAuthes
 dashboard.clean_unbind_oauth_success=All unbind OAuthes have been deleted successfully.
 dashboard.delete_inactivate_accounts=Удалить все неактивированные учетные записи
 dashboard.delete_inactivate_accounts_success=Все неактивированные учетные записи удалены успешно.
-dashboard.delete_repo_archives=Delete all repositories archives
-dashboard.delete_repo_archives_success=All repositories archives have been deleted successfully.
-dashboard.git_gc_repos=Do garbage collection on repositories
-dashboard.git_gc_repos_success=All repositories have done garbage collection successfully.
+dashboard.delete_repo_archives=Удаление всех архивов репозиториев
+dashboard.delete_repo_archives_success=Все архивы репозиториев были успешно удалены.
+dashboard.git_gc_repos=Выполнить сборку мусора на репозиториях
+dashboard.git_gc_repos_success=Сборка мусора на всех репозиториях успешно выполнена.
 dashboard.server_uptime=Время непрерывной работы сервера
 dashboard.current_goroutine=Current Goroutines
 dashboard.current_memory_usage=Текущее использование памяти
-dashboard.total_memory_allocated=Total Memory Allocated
+dashboard.total_memory_allocated=Всего памяти выделено
 dashboard.memory_obtained=Memory Obtained
 dashboard.pointer_lookup_times=Pointer Lookup Times
 dashboard.memory_allocate_times=Memory Allocate Times
@@ -543,7 +543,7 @@ dashboard.last_gc_pause=Last GC Pause
 dashboard.gc_times=GC Times
 
 users.user_manage_panel=User Manage Panel
-users.new_account=Create New Account
+users.new_account=Создать новый аккаунт
 users.name=Имя
 users.activated=Активирован
 users.admin=Администратор
@@ -560,7 +560,7 @@ users.is_admin=У этой учетной записи есть права ад
 users.allow_git_hook=Пользователь имеет право создать Git перехватчик
 users.update_profile=Обновить профиль учетной записи
 users.delete_account=Удалить эту учетную запись
-users.still_own_repo=This account still have ownership of repository, you have to delete or transfer them first.
+users.still_own_repo=На вашем аккаунте все еще остается как минимум один репозиторий, сначала вам нужно удалить или передать его.
 users.still_has_org=This account still have membership of organization, you have to left or delete them first.
 
 orgs.org_manage_panel=Управление группами
@@ -630,30 +630,30 @@ config.db_path=Path
 config.db_path_helper=(for "sqlite3" only)
 config.service_config=Service Configuration
 config.register_email_confirm=Require E-mail Confirmation
-config.disable_register=Disable Registration
-config.require_sign_in_view=Require Sign In View
-config.mail_notify=Mail Notification
-config.enable_cache_avatar=Enable Cache Avatar
+config.disable_register=Отключить регистрацию
+config.require_sign_in_view=Для просмотра необходима авторизация
+config.mail_notify=Почтовые уведомления
+config.enable_cache_avatar=Кешировать аватар
 config.active_code_lives=Active Code Lives
 config.reset_password_code_lives=Reset Password Code Lives
 config.webhook_config=Настройка автоматического обновления репозиции
-config.task_interval=Task Interval
-config.deliver_timeout=Deliver Timeout
-config.mailer_config=Mailer Configuration
-config.mailer_enabled=Enabled
-config.mailer_name=Name
-config.mailer_host=Host
-config.mailer_user=User
-config.oauth_config=OAuth Configuration
-config.oauth_enabled=Enabled
-config.cache_config=Cache Configuration
+config.task_interval=Интервал задания
+config.deliver_timeout=Задержка доставки
+config.mailer_config=Настройки почты
+config.mailer_enabled=Включено
+config.mailer_name=Имя
+config.mailer_host=Сервер
+config.mailer_user=Пользователь
+config.oauth_config=Конфигурация OAuth
+config.oauth_enabled=Включено
+config.cache_config=Настройки кеша
 config.cache_adapter=Cache Adapter
 config.cache_interval=Cache Interval
 config.cache_conn=Cache Connection
 config.session_config=Session Configuration
 config.session_provider=Session Provider
 config.provider_config=Provider Config
-config.cookie_name=Cookie Name
+config.cookie_name=Имя файла cookie
 config.enable_set_cookie=Enable Set Cookie
 config.gc_interval_time=GC Interval Time
 config.session_life_time=Время жизни сессии
@@ -674,7 +674,7 @@ monitor.execute_times=Execute Times
 monitor.process=Запущенные процессы
 monitor.desc=Описание
 monitor.start=Start Time
-monitor.execute_time=Execution Time
+monitor.execute_time=Время выполнения
 
 notices.system_notice_list=Система уведомлений
 notices.type=Тип
@@ -684,7 +684,7 @@ notices.op=Op.
 notices.delete_success=System notice has been deleted successfully.
 
 [action]
-create_repo=created repository <a href="%s/%s">%s</a>
+create_repo=создан репозиторий <a href="%s/%s"> %s</a>
 commit_repo=pushed to <a href="%s/%s/src/%s">%s</a> at <a href="%s/%s">%s</a>
 create_issue=opened issue <a href="%s/%s/issues/%s">%s#%s</a>
 comment_issue=commented on issue <a href="%s/%s/issues/%s">%s#%s</a>
@@ -703,9 +703,9 @@ now=сейчас
 1w=1 week %s
 1mon=1 month %s
 1y=1 year %s
-seconds=%d seconds %s
-minutes=%d minutes %s
-hours=%d hours %s
+seconds=%d секунд %s
+minutes=%d минут %s
+hours=%d часов %s
 days=%d days %s
 weeks=%d weeks %s
 months=%d months %s

+ 1 - 1
gogs.go

@@ -17,7 +17,7 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.5.12.0120 Beta"
+const APP_VER = "0.5.12.0204 Beta"
 
 func init() {
 	runtime.GOMAXPROCS(runtime.NumCPU())

+ 40 - 8
models/action.go

@@ -41,12 +41,14 @@ var (
 
 var (
 	// Same as Github. See https://help.github.com/articles/closing-issues-via-commit-messages
-	IssueKeywords    = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
-	IssueKeywordsPat *regexp.Regexp
+	IssueCloseKeywords    = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
+	IssueCloseKeywordsPat *regexp.Regexp
+	IssueReferenceKeywordsPat *regexp.Regexp
 )
 
 func init() {
-	IssueKeywordsPat = regexp.MustCompile(fmt.Sprintf(`(?i)(?:%s) \S+`, strings.Join(IssueKeywords, "|")))
+	IssueCloseKeywordsPat = regexp.MustCompile(fmt.Sprintf(`(?i)(?:%s) \S+`, strings.Join(IssueCloseKeywords, "|")))
+	IssueReferenceKeywordsPat = regexp.MustCompile(fmt.Sprintf(`(?i)(?:) \S+`))
 }
 
 // Action represents user operation type and other information to repository.,
@@ -110,13 +112,13 @@ func (a Action) GetIssueInfos() []string {
 
 func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, commits []*base.PushCommit) error {
 	for _, c := range commits {
-		refs := IssueKeywordsPat.FindAllString(c.Message, -1)
-
-		for _, ref := range refs {
+		references := IssueReferenceKeywordsPat.FindAllString(c.Message, -1)
+		
+		for _, ref := range references {
 			ref := ref[strings.IndexByte(ref, byte(' '))+1:]
 			ref = strings.TrimRightFunc(ref, func(c rune) bool {
-				return !unicode.IsDigit(c)
-			})
+					return !unicode.IsDigit(c)
+				})
 
 			if len(ref) == 0 {
 				continue
@@ -144,6 +146,35 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
 			if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message, nil); err != nil {
 				return err
 			}
+		}
+
+		closes := IssueCloseKeywordsPat.FindAllString(c.Message, -1)
+
+		for _, ref := range closes {
+			ref := ref[strings.IndexByte(ref, byte(' '))+1:]
+			ref = strings.TrimRightFunc(ref, func(c rune) bool {
+					return !unicode.IsDigit(c)
+				})
+
+			if len(ref) == 0 {
+				continue
+			}
+
+			// Add repo name if missing
+			if ref[0] == '#' {
+				ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
+			} else if strings.Contains(ref, "/") == false {
+				// We don't support User#ID syntax yet
+				// return ErrNotImplemented
+
+				continue
+			}
+
+			issue, err := GetIssueByRef(ref)
+
+			if err != nil {
+				return err
+			}
 
 			if issue.RepoId == repoId {
 				if issue.IsClosed {
@@ -168,6 +199,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
 				}
 			}
 		}
+		
 	}
 
 	return nil

+ 8 - 8
models/models.go

@@ -33,7 +33,7 @@ var (
 	HasEngine bool
 
 	DbCfg struct {
-		Type, Host, Name, User, Pwd, Path, SslMode string
+		Type, Host, Name, User, Passwd, Path, SSLMode string
 	}
 
 	EnableSQLite3 bool
@@ -59,10 +59,10 @@ func LoadModelsConfig() {
 	DbCfg.Host = sec.Key("HOST").String()
 	DbCfg.Name = sec.Key("NAME").String()
 	DbCfg.User = sec.Key("USER").String()
-	if len(DbCfg.Pwd) == 0 {
-		DbCfg.Pwd = sec.Key("PASSWD").String()
+	if len(DbCfg.Passwd) == 0 {
+		DbCfg.Passwd = sec.Key("PASSWD").String()
 	}
-	DbCfg.SslMode = sec.Key("SSL_MODE").String()
+	DbCfg.SSLMode = sec.Key("SSL_MODE").String()
 	DbCfg.Path = sec.Key("PATH").MustString("data/gogs.db")
 }
 
@@ -71,7 +71,7 @@ func getEngine() (*xorm.Engine, error) {
 	switch DbCfg.Type {
 	case "mysql":
 		cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8",
-			DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name)
+			DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name)
 	case "postgres":
 		var host, port = "127.0.0.1", "5432"
 		fields := strings.Split(DbCfg.Host, ":")
@@ -82,7 +82,7 @@ func getEngine() (*xorm.Engine, error) {
 			port = fields[1]
 		}
 		cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s",
-			DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode)
+			DbCfg.User, DbCfg.Passwd, host, port, DbCfg.Name, DbCfg.SSLMode)
 	case "sqlite3":
 		if !EnableSQLite3 {
 			return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
@@ -98,7 +98,7 @@ func getEngine() (*xorm.Engine, error) {
 func NewTestEngine(x *xorm.Engine) (err error) {
 	x, err = getEngine()
 	if err != nil {
-		return fmt.Errorf("models.init(fail to connect to database): %v", err)
+		return fmt.Errorf("connect to database: %v", err)
 	}
 
 	x.SetMapper(core.GonicMapper{})
@@ -108,7 +108,7 @@ func NewTestEngine(x *xorm.Engine) (err error) {
 func SetEngine() (err error) {
 	x, err = getEngine()
 	if err != nil {
-		return fmt.Errorf("models.init(fail to connect to database): %v", err)
+		return fmt.Errorf("connect to database: %v", err)
 	}
 
 	x.SetMapper(core.GonicMapper{})

+ 50 - 11
models/publickey.go

@@ -33,7 +33,7 @@ const (
 )
 
 var (
-	ErrKeyAlreadyExist = errors.New("Public key already exist")
+	ErrKeyAlreadyExist = errors.New("Public key already exists")
 	ErrKeyNotExist     = errors.New("Public key does not exist")
 	ErrKeyUnableVerify = errors.New("Unable to verify public key")
 )
@@ -41,7 +41,7 @@ var (
 var sshOpLocker = sync.Mutex{}
 
 var (
-	SshPath string // SSH directory.
+	SSHPath string // SSH directory.
 	appPath string // Execution(binary) path.
 )
 
@@ -72,9 +72,9 @@ func init() {
 	appPath = strings.Replace(appPath, "\\", "/", -1)
 
 	// Determine and create .ssh path.
-	SshPath = filepath.Join(homeDir(), ".ssh")
-	if err = os.MkdirAll(SshPath, 0700); err != nil {
-		log.Fatal(4, "fail to create SshPath(%s): %v\n", SshPath, err)
+	SSHPath = filepath.Join(homeDir(), ".ssh")
+	if err = os.MkdirAll(SSHPath, 0700); err != nil {
+		log.Fatal(4, "fail to create '%s': %v", SSHPath, err)
 	}
 }
 
@@ -244,16 +244,17 @@ func CheckPublicKeyString(content string) (bool, error) {
 }
 
 // saveAuthorizedKeyFile writes SSH key content to authorized_keys file.
-func saveAuthorizedKeyFile(key *PublicKey) error {
+func saveAuthorizedKeyFile(keys ...*PublicKey) error {
 	sshOpLocker.Lock()
 	defer sshOpLocker.Unlock()
 
-	fpath := filepath.Join(SshPath, "authorized_keys")
+	fpath := filepath.Join(SSHPath, "authorized_keys")
 	f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
 	if err != nil {
 		return err
 	}
 	defer f.Close()
+
 	finfo, err := f.Stat()
 	if err != nil {
 		return err
@@ -269,8 +270,12 @@ func saveAuthorizedKeyFile(key *PublicKey) error {
 		}
 	}
 
-	_, err = f.WriteString(key.GetAuthorizedString())
-	return err
+	for _, key := range keys {
+		if _, err = f.WriteString(key.GetAuthorizedString()); err != nil {
+			return err
+		}
+	}
+	return nil
 }
 
 // AddPublicKey adds new public key to database and authorized_keys file.
@@ -413,8 +418,8 @@ func DeletePublicKey(key *PublicKey) error {
 		return err
 	}
 
-	fpath := filepath.Join(SshPath, "authorized_keys")
-	tmpPath := filepath.Join(SshPath, "authorized_keys.tmp")
+	fpath := filepath.Join(SSHPath, "authorized_keys")
+	tmpPath := filepath.Join(SSHPath, "authorized_keys.tmp")
 	if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil {
 		return err
 	} else if err = os.Remove(fpath); err != nil {
@@ -422,3 +427,37 @@ func DeletePublicKey(key *PublicKey) error {
 	}
 	return os.Rename(tmpPath, fpath)
 }
+
+// RewriteAllPublicKeys removes any authorized key and rewrite all keys from database again.
+func RewriteAllPublicKeys() error {
+	sshOpLocker.Lock()
+	defer sshOpLocker.Unlock()
+
+	tmpPath := filepath.Join(SSHPath, "authorized_keys.tmp")
+	f, err := os.Create(tmpPath)
+	if err != nil {
+		return err
+	}
+	defer os.Remove(tmpPath)
+
+	err = x.Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
+		_, err = f.WriteString((bean.(*PublicKey)).GetAuthorizedString())
+		return err
+	})
+	f.Close()
+	if err != nil {
+		return err
+	}
+
+	fpath := filepath.Join(SSHPath, "authorized_keys")
+	if com.IsExist(fpath) {
+		if err = os.Remove(fpath); err != nil {
+			return err
+		}
+	}
+	if err = os.Rename(tmpPath, fpath); err != nil {
+		return err
+	}
+
+	return nil
+}

+ 7 - 5
models/repo.go

@@ -7,7 +7,6 @@ package models
 import (
 	"errors"
 	"fmt"
-	"html"
 	"html/template"
 	"io/ioutil"
 	"os"
@@ -218,11 +217,9 @@ func (repo *Repository) HasAccess(uname string) bool {
 // DescriptionHtml does special handles to description and return HTML string.
 func (repo *Repository) DescriptionHtml() template.HTML {
 	sanitize := func(s string) string {
-		// TODO(nuss-justin): Improve sanitization. Strip all tags?
-		ss := html.EscapeString(s)
-		return fmt.Sprintf(`<a href="%s" target="_blank">%s</a>`, ss, ss)
+		return fmt.Sprintf(`<a href="%[1]s" target="_blank">%[1]s</a>`, s)
 	}
-	return template.HTML(DescPattern.ReplaceAllStringFunc(base.XSSString(repo.Description), sanitize))
+	return template.HTML(DescPattern.ReplaceAllStringFunc(base.Sanitizer.Sanitize(repo.Description), sanitize))
 }
 
 // IsRepositoryExist returns true if the repository with given name under user has already existed.
@@ -507,6 +504,11 @@ func initRepository(f string, u *User, repo *Repository, initReadme bool, repoLa
 	}
 
 	if len(fileName) == 0 {
+		// Re-fetch the repository from database before updating it (else it would
+		// override changes that were done earlier with sql)
+		if repo, err = GetRepositoryById(repo.Id); err != nil {
+			return err
+		}
 		repo.IsBare = true
 		repo.DefaultBranch = "master"
 		return UpdateRepository(repo)

+ 1 - 0
models/user.go

@@ -477,6 +477,7 @@ func UpdateUser(u *User) error {
 	}
 	u.Avatar = avatar.HashEmail(u.AvatarEmail)
 
+	u.FullName = base.Sanitizer.Sanitize(u.FullName)
 	_, err = x.Id(u.Id).AllCols().Update(u)
 	return err
 }

+ 7 - 0
modules/auth/auth.go

@@ -9,6 +9,7 @@ import (
 	"reflect"
 	"strings"
 
+	"github.com/Unknwon/com"
 	"github.com/Unknwon/macaron"
 	"github.com/macaron-contrib/binding"
 	"github.com/macaron-contrib/session"
@@ -135,6 +136,10 @@ type Form interface {
 	binding.Validator
 }
 
+func init() {
+	binding.SetNameMapper(com.ToSnakeCase)
+}
+
 // AssignForm assign form values back to the template data.
 func AssignForm(form interface{}, data map[string]interface{}) {
 	typ := reflect.TypeOf(form)
@@ -152,6 +157,8 @@ func AssignForm(form interface{}, data map[string]interface{}) {
 		// Allow ignored fields in the struct
 		if fieldName == "-" {
 			continue
+		} else if len(fieldName) == 0 {
+			fieldName = com.ToSnakeCase(field.Name)
 		}
 
 		data[fieldName] = val.Field(i).Interface()

+ 21 - 20
modules/auth/user_form.go

@@ -12,26 +12,27 @@ import (
 )
 
 type InstallForm struct {
-	Database        string `form:"database" binding:"Required"`
-	DbHost          string `form:"host"`
-	DbUser          string `form:"user"`
-	DbPasswd        string `form:"passwd"`
-	DatabaseName    string `form:"database_name"`
-	SslMode         string `form:"ssl_mode"`
-	DatabasePath    string `form:"database_path"`
-	RepoRootPath    string `form:"repo_path" binding:"Required"`
-	RunUser         string `form:"run_user" binding:"Required"`
-	Domain          string `form:"domain" binding:"Required"`
-	AppUrl          string `form:"app_url" binding:"Required"`
-	SmtpHost        string `form:"smtp_host"`
-	SmtpEmail       string `form:"mailer_user"`
-	SmtpPasswd      string `form:"mailer_pwd"`
-	RegisterConfirm string `form:"register_confirm"`
-	MailNotify      string `form:"mail_notify"`
-	AdminName       string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"`
-	AdminPasswd     string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(255)"`
-	ConfirmPasswd   string `form:"confirm_passwd" binding:"Required;MinSize(6);MaxSize(255)"`
-	AdminEmail      string `form:"admin_email" binding:"Required;Email;MaxSize(50)"`
+	DbType             string `binding:"Required"`
+	DbHost             string
+	DbUser             string
+	DbPasswd           string
+	DbName             string
+	SSLMode            string
+	DbPath             string
+	RepoRootPath       string `binding:"Required"`
+	RunUser            string `binding:"Required"`
+	Domain             string `binding:"Required"`
+	HTTPPort           string `binding:"Required"`
+	AppUrl             string `binding:"Required"`
+	SMTPHost           string
+	SMTPEmail          string
+	SMTPPasswd         string
+	RegisterConfirm    string
+	MailNotify         string
+	AdminName          string `binding:"Required;AlphaDashDot;MaxSize(30)"`
+	AdminPasswd        string `binding:"Required;MinSize(6);MaxSize(255)"`
+	AdminConfirmPasswd string `binding:"Required;MinSize(6);MaxSize(255)"`
+	AdminEmail         string `binding:"Required;Email;MaxSize(50)"`
 }
 
 func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

+ 9 - 3
modules/base/markdown.go

@@ -63,12 +63,18 @@ func IsImageFile(data []byte) (string, bool) {
 	return contentType, false
 }
 
+// IsReadmeFile returns true if given file name suppose to be a README file.
 func IsReadmeFile(name string) bool {
 	name = strings.ToLower(name)
 	if len(name) < 6 {
 		return false
+	} else if len(name) == 6 {
+		if name == "readme" {
+			return true
+		}
+		return false
 	}
-	if name[:6] == "readme" {
+	if name[:7] == "readme." {
 		return true
 	}
 	return false
@@ -103,7 +109,7 @@ var (
 	MentionPattern     = regexp.MustCompile(`@[0-9a-zA-Z_]{1,}`)
 	commitPattern      = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
 	issueFullPattern   = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
-	issueIndexPattern  = regexp.MustCompile(`#[0-9]+`)
+	issueIndexPattern  = regexp.MustCompile(`( |^)#[0-9]+`)
 	sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`)
 )
 
@@ -212,7 +218,7 @@ func RenderRawMarkdown(body []byte, urlPrefix string) []byte {
 func RenderMarkdown(rawBytes []byte, urlPrefix string) []byte {
 	body := RenderSpecialLink(rawBytes, urlPrefix)
 	body = RenderRawMarkdown(body, urlPrefix)
-	body = XSS(body)
+	body = Sanitizer.SanitizeBytes(body)
 	return body
 }
 

+ 7 - 5
modules/base/template.go

@@ -13,7 +13,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/microcosm-cc/bluemonday"
 	"golang.org/x/net/html/charset"
 	"golang.org/x/text/transform"
 
@@ -21,11 +20,8 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-// FIXME: use me to Markdown API renders
-var p = bluemonday.UGCPolicy()
-
 func Str2html(raw string) template.HTML {
-	return template.HTML(p.Sanitize(raw))
+	return template.HTML(Sanitizer.Sanitize(raw))
 }
 
 func Range(l int) []int {
@@ -90,6 +86,11 @@ func ToUtf8(content string) string {
 	return res
 }
 
+// RenderCommitMessage renders commit message with XSS-safe and special links.
+func RenderCommitMessage(msg, urlPrefix string) template.HTML {
+	return template.HTML(string(RenderIssueIndexPattern([]byte(template.HTMLEscapeString(msg)), urlPrefix)))
+}
+
 var mailDomains = map[string]string{
 	"gmail.com": "gmail.com",
 }
@@ -163,6 +164,7 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
 	"EscapePound": func(str string) string {
 		return strings.Replace(str, "#", "%23", -1)
 	},
+	"RenderCommitMessage": RenderCommitMessage,
 }
 
 type Actioner interface {

+ 3 - 27
modules/base/tool.go

@@ -15,17 +15,19 @@ import (
 	"hash"
 	"html/template"
 	"math"
-	"regexp"
 	"strings"
 	"time"
 
 	"github.com/Unknwon/com"
 	"github.com/Unknwon/i18n"
+	"github.com/microcosm-cc/bluemonday"
 
 	"github.com/gogits/gogs/modules/avatar"
 	"github.com/gogits/gogs/modules/setting"
 )
 
+var Sanitizer = bluemonday.UGCPolicy()
+
 // Encode string to md5 hex value.
 func EncodeMd5(str string) string {
 	m := md5.New()
@@ -473,29 +475,3 @@ func DateFormat(t time.Time, format string) string {
 	format = replacer.Replace(format)
 	return t.Format(format)
 }
-
-type xssFilter struct {
-	reg  *regexp.Regexp
-	repl []byte
-}
-
-var (
-	whiteSpace = []byte(" ")
-	xssFilters = []xssFilter{
-		{regexp.MustCompile(`\ [ONon]\w*=["]*`), whiteSpace},
-		{regexp.MustCompile(`<[SCRIPTscript]{6}`), whiteSpace},
-		{regexp.MustCompile(`=[` + "`" + `'"]*[JAVASCRIPTjavascript \t\0&#x0D;]*:`), whiteSpace},
-	}
-)
-
-// XSS goes through all the XSS filters to make user input content as safe as possible.
-func XSS(in []byte) []byte {
-	for _, filter := range xssFilters {
-		in = filter.reg.ReplaceAll(in, filter.repl)
-	}
-	return in
-}
-
-func XSSString(in string) string {
-	return string(XSS([]byte(in)))
-}

+ 1 - 1
modules/middleware/auth.go

@@ -54,7 +54,7 @@ func Toggle(options *ToggleOptions) macaron.Handler {
 				if strings.HasSuffix(ctx.Req.RequestURI, "watch") {
 					return
 				}
-				ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
+				ctx.SetCookie("redirect_to", url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
 				ctx.Redirect(setting.AppSubUrl + "/user/login")
 				return
 			} else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {

+ 1 - 1
modules/setting/setting.go

@@ -178,7 +178,7 @@ func NewConfigContext() {
 			log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err)
 		}
 	} else {
-		log.Warn("No custom 'conf/app.ini' found, please go to '/install'")
+		log.Warn("No custom 'conf/app.ini' found, ignore this if you're running first time")
 	}
 	Cfg.NameMapper = ini.AllCapsUnderscore
 

+ 0 - 16
public/js/app.js

@@ -1052,22 +1052,6 @@ function initRepoSetting() {
             return;
         }
         Gogits.getUsers($this.val(), $this.next());
-        /*$.ajax({
-         url: '/api/v1/users/search?q=' + $this.val(),
-         dataType: "json",
-         success: function (json) {
-         if (json.ok && json.data.length) {
-         var html = '';
-         $.each(json.data, function (i, item) {
-         html += '<li><img src="' + item.avatar + '">' + item.username + '</li>';
-         });
-         $this.next().toggleShow();
-         $this.next().find('ul').html(html);
-         } else {
-         $this.next().toggleHide();
-         }
-         }
-         });*/
     }).on('focus', function () {
         if (!$(this).val()) {
             $(this).next().toggleHide();

+ 23 - 0
public/ng/css/gogs.css

@@ -1630,6 +1630,10 @@ The register and sign-in page style
   background-color: #d1ffd6 !important;
   border-color: #b4e2b4 !important;
 }
+.diff-file-box .code-diff tbody tr.add-code td.selected-line,
+.diff-file-box .code-diff tbody tr.add-code td.selected-line pre {
+  background-color: #ffffdd !important;
+}
 .diff-file-box .code-diff tbody tr:hover td,
 .diff-file-box .code-diff tbody tr:hover pre {
   background-color: #FFF8D2 !important;
@@ -1761,6 +1765,7 @@ The register and sign-in page style
 #org-setting-form,
 #repo-setting-form,
 #user-profile-form,
+#add-email-form,
 .repo-setting-form {
   background-color: #FFF;
   padding: 30px 0;
@@ -1769,6 +1774,7 @@ The register and sign-in page style
 #org-setting-form textarea,
 #repo-setting-form textarea,
 #user-profile-form textarea,
+#add-email-form textarea,
 .repo-setting-form textarea {
   margin-left: 4px;
   height: 100px;
@@ -1777,11 +1783,13 @@ The register and sign-in page style
 #org-setting-form label,
 #repo-setting-form label,
 #user-profile-form label,
+#add-email-form label,
 .repo-setting-form label,
 #auth-setting-form .form-label,
 #org-setting-form .form-label,
 #repo-setting-form .form-label,
 #user-profile-form .form-label,
+#add-email-form .form-label,
 .repo-setting-form .form-label {
   width: 240px;
 }
@@ -1789,6 +1797,7 @@ The register and sign-in page style
 #org-setting-form .ipt,
 #repo-setting-form .ipt,
 #user-profile-form .ipt,
+#add-email-form .ipt,
 .repo-setting-form .ipt {
   width: 360px;
 }
@@ -1796,6 +1805,7 @@ The register and sign-in page style
 #org-setting-form .field,
 #repo-setting-form .field,
 #user-profile-form .field,
+#add-email-form .field,
 .repo-setting-form .field {
   margin-bottom: 24px;
 }
@@ -1813,6 +1823,7 @@ The register and sign-in page style
 #repo-hooks-history-panel,
 #user-social-panel,
 #user-applications-panel,
+#user-email-panel,
 #user-ssh-panel {
   margin-bottom: 20px;
 }
@@ -1820,6 +1831,7 @@ The register and sign-in page style
 #repo-hooks-history-panel .setting-list,
 #user-social-panel .setting-list,
 #user-applications-panel .setting-list,
+#user-email-panel .setting-list,
 #user-ssh-panel .setting-list {
   background-color: #FFF;
 }
@@ -1827,6 +1839,7 @@ The register and sign-in page style
 #repo-hooks-history-panel .setting-list li,
 #user-social-panel .setting-list li,
 #user-applications-panel .setting-list li,
+#user-email-panel .setting-list li,
 #user-ssh-panel .setting-list li {
   padding: 8px 20px;
   border-bottom: 1px solid #eaeaea;
@@ -1835,6 +1848,7 @@ The register and sign-in page style
 #repo-hooks-history-panel .setting-list li.ssh:hover,
 #user-social-panel .setting-list li.ssh:hover,
 #user-applications-panel .setting-list li.ssh:hover,
+#user-email-panel .setting-list li.ssh:hover,
 #user-ssh-panel .setting-list li.ssh:hover {
   background-color: #ffffEE;
 }
@@ -1842,6 +1856,7 @@ The register and sign-in page style
 #repo-hooks-history-panel .setting-list li i,
 #user-social-panel .setting-list li i,
 #user-applications-panel .setting-list li i,
+#user-email-panel .setting-list li i,
 #user-ssh-panel .setting-list li i {
   padding-right: 5px;
 }
@@ -1849,6 +1864,7 @@ The register and sign-in page style
 #repo-hooks-history-panel .active-icon,
 #user-social-panel .active-icon,
 #user-applications-panel .active-icon,
+#user-email-panel .active-icon,
 #user-ssh-panel .active-icon {
   width: 10px;
   height: 10px;
@@ -1861,6 +1877,7 @@ The register and sign-in page style
 #repo-hooks-history-panel .ssh-content,
 #user-social-panel .ssh-content,
 #user-applications-panel .ssh-content,
+#user-email-panel .ssh-content,
 #user-ssh-panel .ssh-content {
   margin-left: 24px;
 }
@@ -1868,6 +1885,7 @@ The register and sign-in page style
 #repo-hooks-history-panel .ssh-content .octicon,
 #user-social-panel .ssh-content .octicon,
 #user-applications-panel .ssh-content .octicon,
+#user-email-panel .ssh-content .octicon,
 #user-ssh-panel .ssh-content .octicon {
   margin-right: 4px;
 }
@@ -1875,16 +1893,19 @@ The register and sign-in page style
 #repo-hooks-history-panel .ssh-content .print,
 #user-social-panel .ssh-content .print,
 #user-applications-panel .ssh-content .print,
+#user-email-panel .ssh-content .print,
 #user-ssh-panel .ssh-content .print,
 #repo-hooks-panel .ssh-content .access,
 #repo-hooks-history-panel .ssh-content .access,
 #user-social-panel .ssh-content .access,
 #user-applications-panel .ssh-content .access,
+#user-email-panel .ssh-content .access,
 #user-ssh-panel .ssh-content .access,
 #repo-hooks-panel .ssh-content .activity,
 #repo-hooks-history-panel .ssh-content .activity,
 #user-social-panel .ssh-content .activity,
 #user-applications-panel .ssh-content .activity,
+#user-email-panel .ssh-content .activity,
 #user-ssh-panel .ssh-content .activity {
   color: #888;
 }
@@ -1892,6 +1913,7 @@ The register and sign-in page style
 #repo-hooks-history-panel .ssh-content .access,
 #user-social-panel .ssh-content .access,
 #user-applications-panel .ssh-content .access,
+#user-email-panel .ssh-content .access,
 #user-ssh-panel .ssh-content .access {
   max-width: 500px;
 }
@@ -1899,6 +1921,7 @@ The register and sign-in page style
 #repo-hooks-history-panel .ssh-btn,
 #user-social-panel .ssh-btn,
 #user-applications-panel .ssh-btn,
+#user-email-panel .ssh-btn,
 #user-ssh-panel .ssh-btn {
   margin-top: 6px;
 }

+ 80 - 3
public/ng/js/gogs.js

@@ -202,6 +202,78 @@ var Gogs = {};
         }).trigger('hashchange');
     };
 
+    // Render diff view.
+    Gogs.renderDiffView = function () {
+        function selectRange($list, $select, $from) {
+            $list.removeClass('active');
+            $list.parents('tr').find('td').removeClass('selected-line');
+            if ($from) {
+                var a = parseInt($select.attr('rel').substr(1));
+                var b = parseInt($from.attr('rel').substr(1));
+                var c;
+                if (a != b) {
+                    if (a > b) {
+                        c = a;
+                        a = b;
+                        b = c;
+                    }
+                    var classes = [];
+                    for (i = a; i <= b; i++) {
+                        classes.push('[rel=L' + i + ']');
+                    }
+                    $list.filter(classes.join(',')).addClass('active');
+                    $list.filter(classes.join(',')).parents('tr').find('td').addClass('selected-line');
+                    $.changeHash('#L' + a + '-' + 'L' + b);
+                    return
+                }
+            }
+            $select.addClass('active');
+            $select.parents('tr').find('td').addClass('selected-line');
+            $.changeHash('#' + $select.attr('rel'));
+        }
+
+        $(document).on('click', '.code-diff .lines-num span', function (e) {
+            var $select = $(this);
+            var $list = $select.parent().siblings('.lines-code').parents().find('td.lines-num > span');
+            selectRange(
+                $list,
+                $list.filter('[rel=' + $select.attr('rel') + ']'),
+                (e.shiftKey && $list.filter('.active').length ? $list.filter('.active').eq(0) : null)
+            );
+            $.deSelect();
+        });
+
+        $('.code-diff .lines-code > pre').each(function () {
+            var $pre = $(this);
+            var $lineCode = $pre.parent();
+            var $lineNums = $lineCode.siblings('.lines-num');
+            if ($lineNums.length > 0) {
+                var nums = $pre.find('ol.linenums > li').length;
+                for (var i = 1; i <= nums; i++) {
+                    $lineNums.append('<span id="L' + i + '" rel="L' + i + '">' + i + '</span>');
+                }
+            }
+        });
+
+        $(window).on('hashchange', function (e) {
+            var m = window.location.hash.match(/^#(L\d+)\-(L\d+)$/);
+            var $list = $('.code-diff td.lines-num > span');
+            var $first;
+            if (m) {
+                $first = $list.filter('[rel=' + m[1] + ']');
+                selectRange($list, $first, $list.filter('[rel=' + m[2] + ']'));
+                $("html, body").scrollTop($first.offset().top - 200);
+                return;
+            }
+            m = window.location.hash.match(/^#(L\d+)$/);
+            if (m) {
+                $first = $list.filter('[rel=' + m[1] + ']');
+                selectRange($list, $first);
+                $("html, body").scrollTop($first.offset().top - 200);
+            }
+        }).trigger('hashchange');
+    };
+
     // Search users by keyword.
     Gogs.searchUsers = function (val, $target) {
         var notEmpty = function (str) {
@@ -287,7 +359,12 @@ var Gogs = {};
 
 function initCore() {
     Gogs.renderMarkdown();
-    Gogs.renderCodeView();
+
+    if ($('.code-diff').length == 0) {
+        Gogs.renderCodeView();
+    } else {
+        Gogs.renderDiffView();
+    }
 
     // Switch list.
     $('.js-tab-nav').click(function (e) {
@@ -508,7 +585,7 @@ function initRepoSetting() {
             $ul.toggleShow();
         }
     }).next().next().find('ul').on("click", 'li', function () {
-        $('#repo-collaborator').val($(this).text());
+        $('#repo-collaborator').val($(this).find('.username').text());
         $ul.toggleHide();
     });
 }
@@ -608,7 +685,7 @@ function initTeamMembersList() {
             $ul.toggleShow();
         }
     }).next().next().find('ul').on("click", 'li', function () {
-        $('#org-team-members-add').val($(this).text());
+        $('#org-team-members-add').val($(this).find('.username').text());
         $ul.toggleHide();
     });
 }

File diff suppressed because it is too large
+ 0 - 0
public/ng/js/min/gogs-min.js


+ 3 - 0
public/ng/less/gogs/repository.less

@@ -665,6 +665,9 @@
                         background-color: #d1ffd6 !important;
                         border-color: #b4e2b4 !important;
                     }
+                    td.selected-line, td.selected-line pre {
+                        background-color: #ffffdd !important;
+                    }
                 }
                 &:hover {
                     td, pre {

+ 2 - 0
public/ng/less/gogs/settings.less

@@ -35,6 +35,7 @@
 #org-setting-form,
 #repo-setting-form,
 #user-profile-form,
+#add-email-form,
 .repo-setting-form {
     background-color: #FFF;
     padding: 30px 0;
@@ -69,6 +70,7 @@
 #repo-hooks-history-panel,
 #user-social-panel,
 #user-applications-panel,
+#user-email-panel,
 #user-ssh-panel {
     margin-bottom: 20px;
     .setting-list {

+ 4 - 0
routers/admin/admin.go

@@ -118,6 +118,7 @@ const (
 	CLEAN_INACTIVATE_USER
 	CLEAN_REPO_ARCHIVES
 	GIT_GC_REPOS
+	SYNC_SSH_AUTHORIZED_KEY
 )
 
 func Dashboard(ctx *middleware.Context) {
@@ -144,6 +145,9 @@ func Dashboard(ctx *middleware.Context) {
 		case GIT_GC_REPOS:
 			success = ctx.Tr("admin.dashboard.git_gc_repos_success")
 			err = models.GitGcRepos()
+		case SYNC_SSH_AUTHORIZED_KEY:
+			success = ctx.Tr("admin.dashboard.resync_all_sshkeys_success")
+			err = models.RewriteAllPublicKeys()
 		}
 
 		if err != nil {

+ 68 - 88
routers/install.go

@@ -14,6 +14,7 @@ import (
 	"github.com/Unknwon/com"
 	"github.com/Unknwon/macaron"
 	"github.com/go-xorm/xorm"
+	"gopkg.in/ini.v1"
 
 	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/modules/auth"
@@ -73,12 +74,7 @@ func GlobalInit() {
 	checkRunMode()
 }
 
-func renderDbOption(ctx *middleware.Context) {
-	ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"}
-}
-
-// @router /install [get]
-func Install(ctx *middleware.Context, form auth.InstallForm) {
+func InstallInit(ctx *middleware.Context) {
 	if setting.InstallLock {
 		ctx.Handle(404, "Install", errors.New("Installation is prohibited"))
 		return
@@ -87,46 +83,35 @@ func Install(ctx *middleware.Context, form auth.InstallForm) {
 	ctx.Data["Title"] = ctx.Tr("install.install")
 	ctx.Data["PageIsInstall"] = true
 
-	// FIXME: when i'm ckeching length here? should they all be 0 no matter when?
-	// Get and assign values to install form.
-	if len(form.DbHost) == 0 {
-		form.DbHost = models.DbCfg.Host
-	}
-	if len(form.DbUser) == 0 {
-		form.DbUser = models.DbCfg.User
-	}
-	if len(form.DbPasswd) == 0 {
-		form.DbPasswd = models.DbCfg.Pwd
-	}
-	if len(form.DatabaseName) == 0 {
-		form.DatabaseName = models.DbCfg.Name
-	}
-	if len(form.DatabasePath) == 0 {
-		form.DatabasePath = models.DbCfg.Path
-	}
+	ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"}
+}
 
-	if len(form.RepoRootPath) == 0 {
-		form.RepoRootPath = setting.RepoRootPath
-	}
-	if len(form.RunUser) == 0 {
-		// Note: it's not normall to use SSH in windows so current user can be first option(not git).
-		if setting.IsWindows && setting.RunUser == "git" {
-			form.RunUser = os.Getenv("USER")
-			if len(form.RunUser) == 0 {
-				form.RunUser = os.Getenv("USERNAME")
-			}
-		} else {
-			form.RunUser = setting.RunUser
+func Install(ctx *middleware.Context) {
+	form := auth.InstallForm{}
+
+	form.DbHost = models.DbCfg.Host
+	form.DbUser = models.DbCfg.User
+	form.DbPasswd = models.DbCfg.Passwd
+	form.DbName = models.DbCfg.Name
+	form.DbPath = models.DbCfg.Path
+
+	form.RepoRootPath = setting.RepoRootPath
+
+	// Note(unknwon): it's hard for Windows users change a running user,
+	// 	so just use current one if config says default.
+	if setting.IsWindows && setting.RunUser == "git" {
+		form.RunUser = os.Getenv("USER")
+		if len(form.RunUser) == 0 {
+			form.RunUser = os.Getenv("USERNAME")
 		}
-	}
-	if len(form.Domain) == 0 {
-		form.Domain = setting.Domain
-	}
-	if len(form.AppUrl) == 0 {
-		form.AppUrl = setting.AppUrl
+	} else {
+		form.RunUser = setting.RunUser
 	}
 
-	renderDbOption(ctx)
+	form.Domain = setting.Domain
+	form.HTTPPort = setting.HttpPort
+	form.AppUrl = setting.AppUrl
+
 	curDbOp := ""
 	if models.EnableSQLite3 {
 		curDbOp = "SQLite3" // Default when enabled.
@@ -138,16 +123,7 @@ func Install(ctx *middleware.Context, form auth.InstallForm) {
 }
 
 func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
-	if setting.InstallLock {
-		ctx.Handle(404, "InstallPost", errors.New("Installation is prohibited"))
-		return
-	}
-
-	ctx.Data["Title"] = ctx.Tr("install.install")
-	ctx.Data["PageIsInstall"] = true
-
-	renderDbOption(ctx)
-	ctx.Data["CurDbOption"] = form.Database
+	ctx.Data["CurDbOption"] = form.DbType
 
 	if ctx.HasError() {
 		ctx.HTML(200, INSTALL)
@@ -162,18 +138,17 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
 	// Pass basic check, now test configuration.
 	// Test database setting.
 	dbTypes := map[string]string{"MySQL": "mysql", "PostgreSQL": "postgres", "SQLite3": "sqlite3"}
-	models.DbCfg.Type = dbTypes[form.Database]
+	models.DbCfg.Type = dbTypes[form.DbType]
 	models.DbCfg.Host = form.DbHost
 	models.DbCfg.User = form.DbUser
-	models.DbCfg.Pwd = form.DbPasswd
-	models.DbCfg.Name = form.DatabaseName
-	models.DbCfg.SslMode = form.SslMode
-	models.DbCfg.Path = form.DatabasePath
+	models.DbCfg.Passwd = form.DbPasswd
+	models.DbCfg.Name = form.DbName
+	models.DbCfg.SSLMode = form.SSLMode
+	models.DbCfg.Path = form.DbPath
 
 	// Set test engine.
 	var x *xorm.Engine
 	if err := models.NewTestEngine(x); err != nil {
-		// FIXME: should use core.QueryDriver (github.com/go-xorm/core)
 		if strings.Contains(err.Error(), `Unknown database type: sqlite3`) {
 			ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "http://gogs.io/docs/installation/install_from_binary.html"), INSTALL, &form)
 		} else {
@@ -194,7 +169,6 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
 	if len(curUser) == 0 {
 		curUser = os.Getenv("USERNAME")
 	}
-	// Does not check run user when the install lock is off.
 	if form.RunUser != curUser {
 		ctx.Data["Err_RunUser"] = true
 		ctx.RenderWithErr(ctx.Tr("install.run_user_not_match", form.RunUser, curUser), INSTALL, &form)
@@ -202,47 +176,53 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
 	}
 
 	// Check admin password.
-	if form.AdminPasswd != form.ConfirmPasswd {
+	if form.AdminPasswd != form.AdminConfirmPasswd {
 		ctx.Data["Err_AdminPasswd"] = true
 		ctx.RenderWithErr(ctx.Tr("form.password_not_match"), INSTALL, form)
 		return
 	}
 
+	if form.AppUrl[len(form.AppUrl)-1] != '/' {
+		form.AppUrl += "/"
+	}
+
 	// Save settings.
-	setting.Cfg.Section("database").Key("DB_TYPE").SetValue(models.DbCfg.Type)
-	setting.Cfg.Section("database").Key("HOST").SetValue(models.DbCfg.Host)
-	setting.Cfg.Section("database").Key("NAME").SetValue(models.DbCfg.Name)
-	setting.Cfg.Section("database").Key("USER").SetValue(models.DbCfg.User)
-	setting.Cfg.Section("database").Key("PASSWD").SetValue(models.DbCfg.Pwd)
-	setting.Cfg.Section("database").Key("SSL_MODE").SetValue(models.DbCfg.SslMode)
-	setting.Cfg.Section("database").Key("PATH").SetValue(models.DbCfg.Path)
-
-	setting.Cfg.Section("repository").Key("ROOT").SetValue(form.RepoRootPath)
-	setting.Cfg.Section("").Key("RUN_USER").SetValue(form.RunUser)
-	setting.Cfg.Section("server").Key("DOMAIN").SetValue(form.Domain)
-	setting.Cfg.Section("server").Key("ROOT_URL").SetValue(form.AppUrl)
-
-	if len(strings.TrimSpace(form.SmtpHost)) > 0 {
-		setting.Cfg.Section("mailer").Key("ENABLED").SetValue("true")
-		setting.Cfg.Section("mailer").Key("HOST").SetValue(form.SmtpHost)
-		setting.Cfg.Section("mailer").Key("USER").SetValue(form.SmtpEmail)
-		setting.Cfg.Section("mailer").Key("PASSWD").SetValue(form.SmtpPasswd)
-
-		setting.Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").SetValue(com.ToStr(form.RegisterConfirm == "on"))
-		setting.Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(com.ToStr(form.MailNotify == "on"))
+	cfg := ini.Empty()
+	cfg.Section("database").Key("DB_TYPE").SetValue(models.DbCfg.Type)
+	cfg.Section("database").Key("HOST").SetValue(models.DbCfg.Host)
+	cfg.Section("database").Key("NAME").SetValue(models.DbCfg.Name)
+	cfg.Section("database").Key("USER").SetValue(models.DbCfg.User)
+	cfg.Section("database").Key("PASSWD").SetValue(models.DbCfg.Passwd)
+	cfg.Section("database").Key("SSL_MODE").SetValue(models.DbCfg.SSLMode)
+	cfg.Section("database").Key("PATH").SetValue(models.DbCfg.Path)
+
+	cfg.Section("repository").Key("ROOT").SetValue(form.RepoRootPath)
+	cfg.Section("").Key("RUN_USER").SetValue(form.RunUser)
+	cfg.Section("server").Key("DOMAIN").SetValue(form.Domain)
+	cfg.Section("server").Key("HTTP_PORT").SetValue(form.HTTPPort)
+	cfg.Section("server").Key("ROOT_URL").SetValue(form.AppUrl)
+
+	if len(strings.TrimSpace(form.SMTPHost)) > 0 {
+		cfg.Section("mailer").Key("ENABLED").SetValue("true")
+		cfg.Section("mailer").Key("HOST").SetValue(form.SMTPHost)
+		cfg.Section("mailer").Key("USER").SetValue(form.SMTPEmail)
+		cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd)
+
+		cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").SetValue(com.ToStr(form.RegisterConfirm == "on"))
+		cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(com.ToStr(form.MailNotify == "on"))
 	}
 
-	setting.Cfg.Section("").Key("RUN_MODE").SetValue("prod")
+	cfg.Section("").Key("RUN_MODE").SetValue("prod")
 
-	setting.Cfg.Section("session").Key("PROVIDER").SetValue("file")
+	cfg.Section("session").Key("PROVIDER").SetValue("file")
 
-	setting.Cfg.Section("log").Key("MODE").SetValue("file")
+	cfg.Section("log").Key("MODE").SetValue("file")
 
-	setting.Cfg.Section("security").Key("INSTALL_LOCK").SetValue("true")
-	setting.Cfg.Section("security").Key("SECRET_KEY").SetValue(base.GetRandomString(15))
+	cfg.Section("security").Key("INSTALL_LOCK").SetValue("true")
+	cfg.Section("security").Key("SECRET_KEY").SetValue(base.GetRandomString(15))
 
 	os.MkdirAll("custom/conf", os.ModePerm)
-	if err := setting.Cfg.SaveTo(path.Join(setting.CustomPath, "conf/app.ini")); err != nil {
+	if err := cfg.SaveTo(path.Join(setting.CustomPath, "conf/app.ini")); err != nil {
 		ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), INSTALL, &form)
 		return
 	}
@@ -264,5 +244,5 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
 
 	log.Info("First-time run install finished!")
 	ctx.Flash.Success(ctx.Tr("install.install_success"))
-	ctx.Redirect(setting.AppSubUrl + "/user/login")
+	ctx.Redirect(form.AppUrl + "user/login")
 }

+ 2 - 2
routers/repo/commit.go

@@ -37,7 +37,7 @@ func RenderIssueLinks(oldCommits *list.List, repoLink string) *list.List {
 	newCommits := list.New()
 	for e := oldCommits.Front(); e != nil; e = e.Next() {
 		c := e.Value.(*git.Commit)
-		c.CommitMessage = string(base.RenderIssueIndexPattern([]byte(c.CommitMessage), repoLink))
+		c.CommitMessage = c.CommitMessage
 		newCommits.PushBack(c)
 	}
 	return newCommits
@@ -206,7 +206,7 @@ func Diff(ctx *middleware.Context) {
 	commitId := ctx.Repo.CommitId
 
 	commit := ctx.Repo.Commit
-	commit.CommitMessage = string(base.RenderIssueIndexPattern([]byte(commit.CommitMessage), ctx.Repo.RepoLink))
+	commit.CommitMessage = commit.CommitMessage
 	diff, err := models.GetDiffCommit(models.RepoPath(userName, repoName),
 		commitId, setting.Git.MaxGitDiffLines)
 	if err != nil {

+ 4 - 4
routers/repo/download.go

@@ -25,16 +25,16 @@ func ServeBlob(ctx *middleware.Context, blob *git.Blob) error {
 		buf = buf[:n]
 	}
 
-	contentType, isTextFile := base.IsTextFile(buf)
+	_, isTextFile := base.IsTextFile(buf)
 	_, isImageFile := base.IsImageFile(buf)
-	ctx.Resp.Header().Set("Content-Type", contentType)
+	ctx.Resp.Header().Set("Content-Type", "text/plain")
 	if !isTextFile && !isImageFile {
 		ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+path.Base(ctx.Repo.TreeName))
 		ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
 	}
 	ctx.Resp.Write(buf)
-	io.Copy(ctx.Resp, dataRc)
-	return nil
+	_, err = io.Copy(ctx.Resp, dataRc)
+	return err
 }
 
 func SingleDownload(ctx *middleware.Context) {

+ 2 - 2
routers/repo/view.go

@@ -156,9 +156,9 @@ func Home(ctx *middleware.Context) {
 		for _, f := range files {
 			switch c := f[1].(type) {
 			case *git.Commit:
-				c.CommitMessage = string(base.RenderIssueIndexPattern([]byte(c.CommitMessage), ctx.Repo.RepoLink))
+				c.CommitMessage = c.CommitMessage
 			case *git.SubModuleFile:
-				c.CommitMessage = string(base.RenderIssueIndexPattern([]byte(c.CommitMessage), ctx.Repo.RepoLink))
+				c.CommitMessage = c.CommitMessage
 			}
 		}
 		ctx.Data["Files"] = files

+ 46 - 0
scripts/init/freebsd/gogs

@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+# PROVIDE: gogs
+# REQUIRE: NETWORKING SYSLOG
+# KEYWORD: shutdown
+#
+# Add the following lines to /etc/rc.conf to enable gogs:
+#
+#gogs_enable="YES"
+
+. /etc/rc.subr
+
+name="gogs"
+rcvar="gogs_enable"
+
+load_rc_config $name
+
+: ${gogs_user:="git"}
+: ${gogs_enable:="NO"}
+: ${gogs_directory:="/home/git"}
+
+command="${gogs_directory}/scripts/start.sh"
+
+pidfile="${gogs_directory}/${name}.pid"
+
+start_cmd="${name}_start"
+stop_cmd="${name}_stop"
+
+gogs_start() {
+	cd ${gogs_directory}
+	export USER=${gogs_user}
+	export HOME=${gogs_directory}
+	/usr/sbin/daemon -f -u ${gogs_user} -p ${pidfile} $command
+}
+
+gogs_stop() {
+	if [ ! -f $pidfile ]; then
+		echo "GOGS PID File not found. Maybe GOGS is not running?"
+	else
+		kill $(cat $pidfile)
+	fi
+}
+
+run_rc_command "$1"

+ 0 - 2
scripts/start.bat

@@ -1,2 +0,0 @@
-@echo off
-..\\gogs.exe web

+ 1 - 1
scripts/start.sh

@@ -3,7 +3,7 @@
 # Use of this source code is governed by a MIT-style
 # license that can be found in the LICENSE file.
 #
-# start gogs web
+# MUST EXECUTE THIS AT ROOT DIRECTORY: ./scripts/start.sh
 #
 IFS='
 	'

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.5.12.0120 Beta
+0.5.12.0204 Beta

+ 1 - 1
templates/admin/config.tmpl

@@ -61,7 +61,7 @@
                                     <dt>{{.i18n.Tr "admin.config.db_user"}}</dt>
                                     <dd>{{.DbCfg.User}}</dd>
                                     <dt>{{.i18n.Tr "admin.config.db_ssl_mode"}}</dt>
-                                    <dd>{{.DbCfg.SslMode}} {{.i18n.Tr "admin.config.db_ssl_mode_helper"}}</dd>
+                                    <dd>{{.DbCfg.SSLMode}} {{.i18n.Tr "admin.config.db_ssl_mode_helper"}}</dd>
                                     <dt>{{.i18n.Tr "admin.config.db_path"}}</dt>
                                     <dd>{{.DbCfg.Path}} {{.i18n.Tr "admin.config.db_path_helper"}}</dd>
                                 </dl>

+ 5 - 0
templates/admin/dashboard.tmpl

@@ -48,6 +48,11 @@
                                                 <td>{{.i18n.Tr "admin.dashboard.git_gc_repos"}}</td>
                                                 <td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubUrl}}/admin?op=4">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
                                             </tr>
+                                            <tr>
+                                                <td>{{.i18n.Tr "admin.dashboard.resync_all_sshkeys"}}</td>
+                                                <td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubUrl}}/admin?op=5">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
+                                            </tr>
+
                                         </tbody>
                                     </table>
                                 </div>

+ 3 - 3
templates/home.tmpl

@@ -28,7 +28,7 @@
         <div class="grid-1-2 left">
             <i class="octicon octicon-flame"></i>
             <b>Einfach zu installieren</b>
-            <p>Starte einfach <a target="_blank" href="http://gogs.io/docs/installation/install_from_binary.html">die Anwendung</a> für deine Plattform. Gogs gibt es auch für <a target="_blank" href="https://github.com/gogits/gogs/tree/master/dockerfiles">Docker</a>, <a target="_blank" href="https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs">Vagrant</a> oder als <a target="_blank" href="http://gogs.io/docs/installation/install_from_packages.html">Installationspaket</a>.</p>
+            <p>Starte einfach <a target="_blank" href="http://gogs.io/docs/installation/install_from_binary.html">die Anwendung</a> für deine Plattform. Gogs gibt es auch für <a target="_blank" href="https://github.com/gogits/gogs/tree/master/docker">Docker</a>, <a target="_blank" href="https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs">Vagrant</a> oder als <a target="_blank" href="http://gogs.io/docs/installation/install_from_packages.html">Installationspaket</a>.</p>
         </div>
         <div class="grid-1-2 left">
             <i class="octicon octicon-device-desktop"></i>
@@ -49,7 +49,7 @@
         <div class="grid-1-2 left">
             <i class="octicon octicon-flame"></i>
             <b>易安装</b>
-            <p>您除了可以根据操作系统平台通过 <a target="_blank" href="http://gogs.io/docs/installation/install_from_binary.html">二进制运行</a>,还可以通过 <a target="_blank" href="https://github.com/gogits/gogs/tree/master/dockerfiles">Docker</a> 或 <a target="_blank" href="https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs">Vagrant</a>,以及 <a target="_blank" href="http://gogs.io/docs/installation/install_from_packages.html">包管理</a> 安装。</p>
+            <p>您除了可以根据操作系统平台通过 <a target="_blank" href="http://gogs.io/docs/installation/install_from_binary.html">二进制运行</a>,还可以通过 <a target="_blank" href="https://github.com/gogits/gogs/tree/master/docker">Docker</a> 或 <a target="_blank" href="https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs">Vagrant</a>,以及 <a target="_blank" href="http://gogs.io/docs/installation/install_from_packages.html">包管理</a> 安装。</p>
         </div>
         <div class="grid-1-2 left">
             <i class="octicon octicon-device-desktop"></i>
@@ -70,7 +70,7 @@
         <div class="grid-1-2 left">
             <i class="octicon octicon-flame"></i>
             <b>Easy to install</b>
-            <p>Simply <a target="_blank" href="http://gogs.io/docs/installation/install_from_binary.html">run the binary</a> for your platform. Or ship Gogs with <a target="_blank" href="https://github.com/gogits/gogs/tree/master/dockerfiles">Docker</a> or <a target="_blank" href="https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs">Vagrant</a>, or get it <a target="_blank" href="http://gogs.io/docs/installation/install_from_packages.html">packaged</a>.</p>
+            <p>Simply <a target="_blank" href="http://gogs.io/docs/installation/install_from_binary.html">run the binary</a> for your platform. Or ship Gogs with <a target="_blank" href="https://github.com/gogits/gogs/tree/master/docker">Docker</a> or <a target="_blank" href="https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs">Vagrant</a>, or get it <a target="_blank" href="http://gogs.io/docs/installation/install_from_packages.html">packaged</a>.</p>
         </div>
         <div class="grid-1-2 left">
             <i class="octicon octicon-device-desktop"></i>

+ 27 - 21
templates/install.tmpl

@@ -13,7 +13,7 @@
                         <div class="text-center panel-desc">{{.i18n.Tr "install.requite_db_desc"}}</div>
                         <div class="field">
                             <label class="req">{{.i18n.Tr "install.db_type"}}</label>
-                            <select name="database" id="install-database" class="form-control">
+                            <select name="db_type" id="install-database" class="form-control">
                                 {{range .DbOptions}}
                                 <option value="{{.}}"{{if eq $.CurDbOption .}}selected{{end}}>{{.}}</option>
                                 {{end}}
@@ -22,20 +22,20 @@
 
                         <div class="server-sql {{if eq .CurDbOption "SQLite3"}}hide{{end}}">
                             <div class="field">
-                                <label class="req" for="host">{{.i18n.Tr "install.host"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_DbHost}}ipt-error{{end}}" id="host" name="host" value="{{.host}}" />
+                                <label class="req" for="db_host">{{.i18n.Tr "install.host"}}</label>
+                                <input class="ipt ipt-large ipt-radius {{if .Err_DbHost}}ipt-error{{end}}" id="db_host" name="db_host" value="{{.db_host}}" />
                             </div>
                             <div class="field">
-                                <label class="req" for="user">{{.i18n.Tr "install.user"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_DbUser}}ipt-error{{end}}" id="user" name="user" value="{{.user}}" />
+                                <label class="req" for="db_user">{{.i18n.Tr "install.user"}}</label>
+                                <input class="ipt ipt-large ipt-radius {{if .Err_DbUser}}ipt-error{{end}}" id="db_user" name="db_user" value="{{.db_user}}" />
                             </div>
                             <div class="field">
-                                <label class="req" for="passwd">{{.i18n.Tr "install.password"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_DbPasswd}}ipt-error{{end}}" id="passwd" name="passwd" type="password" value="{{.passwd}}" />
+                                <label class="req" for="db_passwd">{{.i18n.Tr "install.password"}}</label>
+                                <input class="ipt ipt-large ipt-radius {{if .Err_DbPasswd}}ipt-error{{end}}" id="db_passwd" name="db_passwd" type="password" value="{{.db_passwd}}" />
                             </div>
                             <div class="field">
-                                <label class="req" for="database_name">{{.i18n.Tr "install.db_name"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_DatabaseName}}ipt-error{{end}}" id="database_name" name="database_name" value="{{.database_name}}" />
+                                <label class="req" for="db_name">{{.i18n.Tr "install.db_name"}}</label>
+                                <input class="ipt ipt-large ipt-radius {{if .Err_DbName}}ipt-error{{end}}" id="db_name" name="db_name" value="{{.db_name}}" />
                                 <label></label>
                                 <span class="help">{{.i18n.Tr "install.db_helper"}}</span>
                             </div>
@@ -51,8 +51,8 @@
                         </div>
 
                         <div class="field sqlite-setting {{if not (eq .CurDbOption "SQLite3")}}hide{{end}}">
-                            <label class="req" for="database_path">{{.i18n.Tr "install.path"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_DatabasePath}}ipt-error{{end}}" id="database_path" name="database_path" value="{{.database_path}}" />
+                            <label class="req" for="db_path">{{.i18n.Tr "install.path"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_DbPath}}ipt-error{{end}}" id="db_path" name="db_path" value="{{.db_path}}" />
                             <label></label>
                             <span class="help">{{.i18n.Tr "install.sqlite_helper"}}</span>
                         </div>
@@ -61,8 +61,8 @@
 
                         <div class="text-center panel-desc">{{.i18n.Tr "install.general_title"}}</div>
                         <div class="field">
-                            <label class="req" for="repo_path">{{.i18n.Tr "install.repo_path"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_RepoRootPath}}ipt-error{{end}}" id="repo_path" name="repo_path" value="{{.repo_path}}" required />
+                            <label class="req" for="repo_root_path">{{.i18n.Tr "install.repo_path"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_RepoRootPath}}ipt-error{{end}}" id="repo_root_path" name="repo_root_path" value="{{.repo_root_path}}" required />
                             <label></label>
                             <span class="help">{{.i18n.Tr "install.repo_path_helper"}}</span>
                         </div>
@@ -78,6 +78,12 @@
                             <label></label>
                             <span class="help">{{.i18n.Tr "install.domain_helper"}}</span>
                         </div>
+                        <div class="field">
+                            <label class="req" for="http_port">{{.i18n.Tr "install.http_port"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_HttpPort}}ipt-error{{end}}" id="http_port" name="http_port" value="{{.http_port}}" required />
+                            <label></label>
+                            <span class="help">{{.i18n.Tr "install.http_port_helper"}}</span>
+                        </div>
                         <div class="field">
                             <label class="req" for="app_url">{{.i18n.Tr "install.app_url"}}</label>
                             <input class="ipt ipt-large ipt-radius {{if .Err_AppUrl}}ipt-error{{end}}" id="app_url" name="app_url" value="{{.app_url}}" required />
@@ -93,12 +99,12 @@
                             <input class="ipt ipt-large ipt-radius {{if .Err_SmtpHost}}ipt-error{{end}}" id="smtp_host" name="smtp_host" value="{{.smtp_host}}" />
                         </div>
                         <div class="field">
-                            <label for="mailer_user">{{.i18n.Tr "install.mailer_user"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_SmtpEmail}}ipt-error{{end}}" id="mailer_user" name="mailer_user" value="{{.mailer_user}}" />
+                            <label for="smtp_user">{{.i18n.Tr "install.mailer_user"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_SMTPEmail}}ipt-error{{end}}" id="smtp_user" name="smtp_user" value="{{.smtp_user}}" />
                         </div>
                         <div class="field">
-                            <label for="mailer_pwd">{{.i18n.Tr "install.mailer_password"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_SmtpPasswd}}ipt-error{{end}}" id="mailer_pwd" name="mailer_pwd" type="password" value="{{.mailer_pwd}}" />
+                            <label for="smtp_pwd">{{.i18n.Tr "install.mailer_password"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_SMTPPasswd}}ipt-error{{end}}" id="smtp_pwd" name="smtp_pwd" type="password" value="{{.smtp_pwd}}" />
                         </div>
 
                         <hr>
@@ -122,12 +128,12 @@
                             <input class="ipt ipt-large ipt-radius {{if .Err_AdminName}}ipt-error{{end}}" id="admin_name" name="admin_name" value="{{.admin_name}}" required />
                         </div>
                         <div class="field">
-                            <label class="req" for="admin_pwd">{{.i18n.Tr "install.admin_password"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_AdminPasswd}}ipt-error{{end}}" id="admin_pwd" name="admin_pwd" type="password" value="{{.admin_pwd}}" required />
+                            <label class="req" for="admin_passwd">{{.i18n.Tr "install.admin_password"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_AdminPasswd}}ipt-error{{end}}" id="admin_passwd" name="admin_passwd" type="password" value="{{.admin_passwd}}" required />
                         </div>
                         <div class="field">
-                            <label class="req" for="confirm_passwd">{{.i18n.Tr "install.confirm_password"}}</label>
-                            <input class="ipt ipt-large ipt-radius {{if .Err_AdminPasswd}}ipt-error{{end}}" id="confirm_passwd" name="confirm_passwd" type="password" required />
+                            <label class="req" for="admin_confirm_passwd">{{.i18n.Tr "install.confirm_password"}}</label>
+                            <input class="ipt ipt-large ipt-radius {{if .Err_AdminPasswd}}ipt-error{{end}}" id="admin_confirm_passwd" name="admin_confirm_passwd" type="password" required />
                         </div>
                         <div class="field">
                             <label class="req" for="admin_email">{{.i18n.Tr "install.admin_email"}}</label>

+ 1 - 1
templates/repo/commits_table.tmpl

@@ -32,7 +32,7 @@
                     {{end}}
                 </td>
                 <td class="sha"><a rel="nofollow" class="label label-green" href="{{AppSubUrl}}/{{$username}}/{{$reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 10}} </a></td>
-                <td class="message"><span class="text-truncate">{{Str2html .Summary}}</span></td>
+                <td class="message"><span class="text-truncate">{{RenderCommitMessage .Summary $.RepoLink}}</span></td>
                 <td class="date">{{TimeSince .Author.When $.Lang}}</td>
             </tr>
             {{end}}

+ 11 - 11
templates/repo/diff.tmpl

@@ -17,7 +17,7 @@
         <div class="panel panel-info panel-radius diff-head-box">
             <div class="panel-header">
                 <a class="pull-right btn btn-blue btn-header btn-medium btn-radius" rel="nofollow" href="{{.SourcePath}}">{{.i18n.Tr "repo.diff.browse_source"}}</a>
-                <h4 class="commit-message">{{Str2html .Commit.Message}}</h4>
+                <h4 class="commit-message">{{RenderCommitMessage .Commit.Message $.RepoLink}}</h4>
             </div>
             <div class="panel-body">
                 <span class="pull-right">
@@ -74,11 +74,11 @@
             </ol>
         </div>
 
-        {{range .Diff.Files}}
+        {{range $i, $file := .Diff.Files}}
         <div class="panel panel-radius diff-file-box diff-box file-content" id="diff-{{.Index}}">
             <div class="panel-header">
                 <div class="diff-counter count pull-left">
-                    {{if not .IsBin}}
+                    {{if not $file.IsBin}}
                     <span class="add" data-line="{{.Addition}}">+ {{.Addition}}</span>
                     <span class="bar">
                         <span class="pull-left add"></span>
@@ -90,9 +90,9 @@
                     {{end}}
                 </div>
                 <a class="btn btn-gray btn-header btn-radius text-black pull-right" rel="nofollow" href="{{$.SourcePath}}/{{.Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
-                <span class="file">{{.Name}}</span>
+                <span class="file">{{$file.Name}}</span>
             </div>
-            {{$isImage := (call $.IsImageFile .Name)}}
+            {{$isImage := (call $.IsImageFile $file.Name)}}
             <div class="panel-body file-body file-code code-view code-diff">
                 {{if $isImage}}
                     <div class="text-center">
@@ -101,18 +101,18 @@
                 {{else}}
                 <table>
                     <tbody>
-                        {{range .Sections}}
-                        {{range .Lines}}
-                        <tr class="{{DiffLineTypeToStr .Type}}-code nl-1 ol-1">
+                        {{range $j, $section := $file.Sections}}
+                        {{range $k, $line := $section.Lines}}                        
+                        <tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$i}} ol-{{$i}}">
                             <td class="lines-num lines-num-old">
-                                <span rel="L1">{{if .LeftIdx}}{{.LeftIdx}}{{end}}</span>
+                                <span rel="diff-{{Add $i 1}}L{{$j}}{{$k}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span>
                             </td>
                             <td class="lines-num lines-num-new">
-                                <span rel="L1">{{if .RightIdx}}{{.RightIdx}}{{end}}</span>
+                                <span rel="diff-{{Add $i 1}}L{{$j}}{{$k}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span>
                             </td>
                             
                             <td class="lines-code">
-                                <pre>{{.Content}}</pre>
+                                <pre>{{$line.Content}}</pre>
                             </td>
                         </tr>
                         {{end}}

+ 2 - 2
templates/repo/view_list.tmpl

@@ -14,7 +14,7 @@
             </span>
             <span class="last-commit"><a href="{{.RepoLink}}/commit/{{.LastCommit.Id}}" rel="nofollow">
                 <strong>{{ShortSha .LastCommit.Id.String}}</strong></a>
-                <span class="text-truncate">{{Str2html .LastCommit.Summary}}</span>
+                <span class="text-truncate">{{RenderCommitMessage .LastCommit.Summary .RepoLink}}</span>
             </span>
             <span class="age right">{{TimeSince .LastCommit.Author.When $.Lang}}</span>
         </th>
@@ -53,7 +53,7 @@
                   <a rel="nofollow" class="label label-green" href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/commit/{{$commit.Id}} ">{{SubStr $commit.Id.String 0 10}} </a>
                 </td>
                 <td class="message">
-                  <span class="text-truncate">{{Str2html $commit.Summary}}</span>
+                  <span class="text-truncate">{{RenderCommitMessage $commit.Summary $.RepoLink}}</span>
                 </td>
                 <td class="age">{{TimeSince $commit.Committer.When $.Lang}}</td>
             </tr>

+ 10 - 8
templates/user/settings/email.tmpl

@@ -36,19 +36,21 @@
 							   {{end}}
                             </li>
                             {{end}}
-                             <form action="{{AppSubUrl}}/user/settings/email" method="post">
-							{{.CsrfTokenHtml}}
-							<p class="panel-header"><strong>{{.i18n.Tr "settings.add_new_email"}}</strong></p>
-							<p class="field">
+                        </ul>
+                        <div class="panel-header">
+                             <strong>{{.i18n.Tr "settings.add_new_email"}}</strong>
+                        </div>
+                        <form class="form form-align panel-body" id="add-email-form" action="{{AppSubUrl}}/user/settings/email" method="post">
+                            {{.CsrfTokenHtml}}                            
+                            <p class="field">
                                 <label class="req" for="email">{{.i18n.Tr "email"}}</label>
-                                <input class="ipt ipt-radius" id="email" name="email" type="text" required />
+                                <input class="ipt ipt-large ipt-radius" id="email" name="email" type="text" required />
                             </p>
                             <p class="field">
                                 <label></label>
-                                <button class="btn btn-green btn-radius" id="email-add-btn">{{.i18n.Tr "settings.add_email"}}</button>
+                                <button class="btn btn-green btn-large btn-radius" id="email-add-btn">{{.i18n.Tr "settings.add_email"}}</button>
                             </p>
-							</form>
-                        </ul>
+                        </form>
                     </div>                    
                 </div>
             </div>

+ 1 - 1
templates/user/settings/nav.tmpl

@@ -4,7 +4,7 @@
         <ul class="menu menu-vertical switching-list grid-1-5 left">
             <li {{if .PageIsSettingsProfile}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings">{{.i18n.Tr "settings.profile"}}</a></li>
             <li {{if .PageIsSettingsPassword}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/password">{{.i18n.Tr "settings.password"}}</a></li>
-            <li {{if .PageIsSettingsEmail}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/email">{{.i18n.Tr "settings.emails"}}</a></li>
+            <li {{if .PageIsSettingsEmails}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/email">{{.i18n.Tr "settings.emails"}}</a></li>
             <li {{if .PageIsSettingsSSHKeys}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/ssh">{{.i18n.Tr "settings.ssh_keys"}}</a></li>
             <li {{if .PageIsSettingsSocial}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/social">{{.i18n.Tr "settings.social"}}</a></li>
             <li {{if .PageIsSettingsApplications}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/applications">{{.i18n.Tr "settings.applications"}}</a></li>

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