diff --git a/.travis.yml b/.travis.yml index b060c6939..85e5f396e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,13 @@ language: go go: - 1.2 - 1.3 + - 1.4 + - tip sudo: false + +script: go build -v + +notifications: + email: + - u@gogs.io \ No newline at end of file diff --git a/README.md b/README.md index ce53a85e8..3d87e3a1f 100644 --- a/README.md +++ b/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 **u@gogs.io**, 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 diff --git a/README_ZH.md b/README_ZH.md index a91b0ce57..be3a2b1bc 100644 --- a/README_ZH.md +++ b/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))) ## 系统要求 diff --git a/cmd/fix.go b/cmd/fix.go index 5122ff32c..eff85d628 100644 --- a/cmd/fix.go +++ b/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 { diff --git a/cmd/web.go b/cmd/web.go index 241abf2c9..55b6bf087 100644 --- a/cmd/web.go +++ b/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) diff --git a/conf/app.ini b/conf/app.ini index 1af480a82..b7a0f1a7c 100644 --- a/conf/app.ini +++ b/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,Русский,日本语 diff --git a/conf/locale/TRANSLATORS b/conf/locale/TRANSLATORS index 38e4ddc27..6c72f3342 100644 --- a/conf/locale/TRANSLATORS +++ b/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 +Christoph Kisfeld Thomas Fanninger Łukasz Jan Niemier -Lafriks \ No newline at end of file +Lafriks +Miguel de la Cruz \ No newline at end of file diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index 7db7ca0d4..660fb253b 100644 --- a/conf/locale/locale_en-US.ini +++ b/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 - - - - - - - - - - - - - diff --git a/conf/locale/locale_ja-JP.ini b/conf/locale/locale_ja-JP.ini new file mode 100755 index 000000000..1d1d61493 --- /dev/null +++ b/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 アカウント連携 +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=新しい確認メールを %s に送りました。登録を完了させるために、%d時間以内にあなたのメールボックスを確認してください。 +sign_in_email=E-mailでサイイン +active_your_account=アカウントをアクティブ +resent_limit_prompt=申し訳ありませんが、アクティベーションメールは頻繁に送信しています。3 分お待ちください。 +has_unconfirmed_mail=こんにちは %s さん、あなたの電子メール アドレス (%s) は未確認です。もし確認メールをまだ確認できていないか、改めて再送信する場合は、下のボタンをクリックしてください。 +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=ヘルプが必要ですか? 我々のガイドをご覧ください。 SSH キーを生成 SSH の一般的な問題 +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=この操作はあなたのアカウントを完全に削除し、復旧できない ! +confirm_delete_account=削除の確認 +delete_account_title=アカウントの削除 +delete_account_desc=このアカウントは永久に削除しようとしている、継続しますか? + +[repo] +owner=オーナー +repo_name=リポジトリ名 +repo_name_helper=偉大なリポジトリ名は短い。思い出に残り、そして一意だ。 +visibility=ビジビリティ +visiblity_helper=このリポジトリは プライベート です。 +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=このリポジトリは Go-Getable になります + +need_auth=認証が必要 +migrate_type=マイグレーションの種類 +migrate_type_helper=このリポジトリは ミラー になります +migrate_repo=リポジトリを移行 + +copy_link=コピー +click_to_copy=クリップボードにコピー +copied=コピー成功 +clone_helper=クローニングのヘルプが必要ですか? ヘルプ を参照してください! +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=

-新オーナーは個人ユーザの場合、あなたはにアクセスできなくなります。

-新オーナーは組織であり、かつあなたが組織のオーナーに所属する場合、あなたはアクセス権を維持します。

+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リクエストが送られます。詳細はこちらのの Webhooks ガイドをご覧ください。 +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に購読されたイベントの詳細を POSTリクエストとして送信します。あなたは、異なるデータ受信モード(JSONまたは, x-www-form-urlencoded, その他) を設定することができます。詳細については、Webhookガイドを参照してください。 +settings.payload_url=ペイロードの URL +settings.content_type=コンテンツ タイプ +settings.secret=秘密 +settings.event_desc=どのイベントをこのWEBフックのトリガーにしますか? +settings.event_push_only=push イベントのみ +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= Slack インテグレーションをリポジトリに追加します。 +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=共有%d 個のファイルを変更した%d 個の追加%d 個の削除を含む +diff.bin=BIN +diff.view_file=ファイルの表示 + +release.releases=リリース +release.new_release=新しいリリース +release.draft=ドラフト +release.prerelease=プレリリース +release.stable=安定 +release.edit=編集 +release.ahead=このリリース以降 %s へ %d コミット +release.source_code=ソース コード +release.tag_name=タグ名 +release.target=ターゲット +release.tag_helper=既存のタグを選択するか、新しいタグを作成し発行します。 +release.release_title=リリース タイトル +release.content_with_md= Markdown コンテンツ +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=操作はこの組織を完全に削除し、復旧できない! +settings.confirm_delete_account=削除の確認 +settings.delete_org_title=組織の削除 +settings.delete_org_desc=この組織は完全に削除されます、継続しますか? +settings.hooks_desc=この組織のもとで すべてのリポジトリ に対してトリガーされる 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=オーナーはすべてのリポジトリ へのフルアクセス権、組織の 管理権限を持ちます。 +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=このチームは読み取り権限を持ち: メンバーはリポジトリの表示及びクローンの作成ができます。 +teams.write_permission_desc=このチームは書き込み権限を持ち: メンバーはリポジトリの表示及リポジトリへのプッシュができます。 +teams.admin_permission_desc=このチームは管理者の権限を持ち: メンバーはチームのリポジトリに対して、読み取り、プッシュや共同編集者の追加ができます。 +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 データベースは %d ユーザ, %d 組織, %d 公開鍵, %d リポジトリ, %d ウォッチ, %d スター, %d 行動, %d アクセス, %d 問題, %d コメント, %d ソーシャルアカウント, %d フォロー, %d ミラー, %d リリース, %d ログイン元, %d webhook, %d マイルストーン, %d ラベル, %d フックタスク, %d チーム, %d アップデートタスク, %d 添付ファイル の情報を持っています。 +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=リポジトリ %sを作成しました +commit_repo=%s%sにプッシュしました +create_issue=問題 %s #%s を開きました +comment_issue=問題 %s #%s のコメント +transfer_repo=リポジトリ %s%s へ転送しました +push_tag=%s に タグ %s をプッシュしました +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=分 + + + + + + + + + + + + + + diff --git a/conf/locale/locale_ru-RU.ini b/conf/locale/locale_ru-RU.ini index 6655342fe..b0da5c550 100755 --- a/conf/locale/locale_ru-RU.ini +++ b/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 %s). 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! У вас есть неподтвержденный адрес электронной почты (%s). Если вам не приходило письмо с подтверждением или нужно выслать новое письмо, нажмите на кнопку ниже. 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=Этим действием вы удалите свою учетную запись навсегда и НЕ СМОЖЕТЕ ее вернуть! @@ -285,7 +285,7 @@ goget_meta_helper=This repository will be зеркалом -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 Webhooks Guide. @@ -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 all repositories and have admin rights to the organization. +teams.owners_permission_desc=Владельцы имеют полный доступ ко всем репозиториям и имеют права администратора организации. 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 %s +create_repo=создан репозиторий %s commit_repo=pushed to %s at %s create_issue=opened issue %s#%s comment_issue=commented on issue %s#%s @@ -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 diff --git a/gogs.go b/gogs.go index 5711452d7..5e69bd68e 100644 --- a/gogs.go +++ b/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()) diff --git a/models/action.go b/models/action.go index de2cdd12c..318a5f6ad 100644 --- a/models/action.go +++ b/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 diff --git a/models/models.go b/models/models.go index cf4e291cf..1a67041b4 100644 --- a/models/models.go +++ b/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{}) diff --git a/models/publickey.go b/models/publickey.go index 566814e84..67ab4242f 100644 --- a/models/publickey.go +++ b/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 +} diff --git a/models/repo.go b/models/repo.go index 663e227ae..a06f1d3e4 100644 --- a/models/repo.go +++ b/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(`%s`, ss, ss) + return fmt.Sprintf(`%[1]s`, 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) diff --git a/models/user.go b/models/user.go index f16fbca34..2da0881c8 100644 --- a/models/user.go +++ b/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 } diff --git a/modules/auth/auth.go b/modules/auth/auth.go index 1dd96d8d4..ad7ce5b9a 100644 --- a/modules/auth/auth.go +++ b/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() diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go index becd5cbca..3c0ff6517 100644 --- a/modules/auth/user_form.go +++ b/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 { diff --git a/modules/base/markdown.go b/modules/base/markdown.go index b2f94c480..d3f3e5fea 100644 --- a/modules/base/markdown.go +++ b/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 } diff --git a/modules/base/template.go b/modules/base/template.go index 829999d1c..f3fa13857 100644 --- a/modules/base/template.go +++ b/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 { diff --git a/modules/base/tool.go b/modules/base/tool.go index ff5a4f4cd..5043364ce 100644 --- a/modules/base/tool.go +++ b/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 ]*:`), 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))) -} diff --git a/modules/middleware/auth.go b/modules/middleware/auth.go index 94bb1c14a..b0bcd87f5 100644 --- a/modules/middleware/auth.go +++ b/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 { diff --git a/modules/setting/setting.go b/modules/setting/setting.go index bc9da3c63..e7c44cdd4 100644 --- a/modules/setting/setting.go +++ b/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 diff --git a/public/js/app.js b/public/js/app.js index 23b629e3e..61539148e 100644 --- a/public/js/app.js +++ b/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 += '
  • ' + item.username + '
  • '; - }); - $this.next().toggleShow(); - $this.next().find('ul').html(html); - } else { - $this.next().toggleHide(); - } - } - });*/ }).on('focus', function () { if (!$(this).val()) { $(this).next().toggleHide(); diff --git a/public/ng/css/gogs.css b/public/ng/css/gogs.css index 12ae89ed4..dc451a705 100644 --- a/public/ng/css/gogs.css +++ b/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; } diff --git a/public/ng/js/gogs.js b/public/ng/js/gogs.js index ff38bda9d..e9b44d657 100644 --- a/public/ng/js/gogs.js +++ b/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('' + i + ''); + } + } + }); + + $(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(); }); } diff --git a/public/ng/js/min/gogs-min.js b/public/ng/js/min/gogs-min.js index a132e4a9a..77304d788 100644 --- a/public/ng/js/min/gogs-min.js +++ b/public/ng/js/min/gogs-min.js @@ -1,5 +1,5 @@ -function Tabs(e){function t(e){console.log("hide",e),e.removeClass("js-tab-nav-show"),$(e.data("tab-target")).removeClass("js-tab-show").hide()}function n(e){console.log("show",e),e.addClass("js-tab-nav-show"),$(e.data("tab-target")).addClass("js-tab-show").show()}var r=$(e);if(r.length){var i=r.find(".js-tab-nav-show");i.length&&$(i.data("tab-target")).addClass("js-tab-show"),r.on("click",".js-tab-nav",function(e){e.preventDefault();var o=$(this);o.hasClass("js-tab-nav-show")||(i=r.find(".js-tab-nav-show").eq(0),t(i),n(o))}),console.log("init tabs @",e)}}function Preview(e,t){function n(e){return e.find(".js-preview-input").eq(0)}function r(e){return e.hasClass("js-preview-container")?e:e.find(".js-preview-container").eq(0)}var i=$(e),o=$(t),a=n(o);if(!a.length)return void console.log("[preview]: no preview input");var s=r(o);return s.length?(i.on("click",function(){$.post("/api/v1/markdown",{text:a.val()},function(e){s.html(e)})}),void console.log("[preview]: init preview @",e,"&",t)):void console.log("[preview]: no preview container")}function initCore(){Gogs.renderMarkdown(),Gogs.renderCodeView(),$(".js-tab-nav").click(function(e){$(this).hasClass("js-tab-nav-show")||($(this).parent().find(".js-tab-nav-show").each(function(){$(this).removeClass("js-tab-nav-show"),$($(this).data("tab-target")).hide()}),$(this).addClass("js-tab-nav-show"),$($(this).data("tab-target")).show()),e.preventDefault()}),$(document).on("click",".popup-modal-dismiss",function(e){e.preventDefault(),$.magnificPopup.close()}),$(".collapse").hide(),$(".tipsy-tooltip").tipsy({fade:!0})}function initUserSetting(){var t=$("#username"),n=$("#user-profile-form");$("#change-username-btn").magnificPopup({modal:!0,callbacks:{open:function(){t.data("uname")==t.val()&&($.magnificPopup.close(),n.submit())}}}).click(function(){return t.data("uname")!=t.val()?(e.preventDefault(),!0):void 0}),$("#change-username-submit").click(function(){$.magnificPopup.close(),n.submit()}),$(".show-form-btn").click(function(){$($(this).data("target-form")).removeClass("hide")}),$("#delete-account-btn").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-account-submit").click(function(){$.magnificPopup.close(),$("#delete-account-form").submit()})}function initRepoCreate(){$("#repo-create-owner-list").on("click","li",function(){if(!$(this).hasClass("checked")){var e=$(this).data("uid");$("#repo-owner-id").val(e),$("#repo-owner-avatar").attr("src",$(this).find("img").attr("src")),$("#repo-owner-name").text($(this).text().trim()),$(this).parent().find(".checked").removeClass("checked"),$(this).addClass("checked"),console.log("set repo owner to uid :",e,$(this).text().trim())}}),$("#auth-button").click(function(e){$("#repo-migrate-auth").slideToggle("fast"),e.preventDefault()}),console.log("initRepoCreate")}function initRepo(){$("#repo-clone-ssh").click(function(){$(this).removeClass("btn-gray").addClass("btn-blue"),$("#repo-clone-https").removeClass("btn-blue").addClass("btn-gray"),$("#repo-clone-url").val($(this).data("link")),$(".clone-url").text($(this).data("link"))}),$("#repo-clone-https").click(function(){$(this).removeClass("btn-gray").addClass("btn-blue"),$("#repo-clone-ssh").removeClass("btn-blue").addClass("btn-gray"),$("#repo-clone-url").val($(this).data("link")),$(".clone-url").text($(this).data("link"))});var e=$("#repo-clone-copy");e.hover(function(){Gogs.bindCopy($(this))}),e.tipsy({fade:!0}),$(".markdown-preview").click(function(){var e=$(this);e.toggleAjax(function(t){$(e.data("preview")).html(t)},function(){$(e.data("preview")).html("no content")})})}function initHookTypeChange(){$("select#hook-type").on("change",function(){hookTypes=["Gogs","Slack"];var e=$(this).val();hookTypes.forEach(function(t){e===t?$("div#"+t.toLowerCase()).toggleShow():$("div#"+t.toLowerCase()).toggleHide()})})}function initRepoRelease(){$("#release-new-target-branch-list li").click(function(){$(this).hasClass("checked")||($("#repo-branch-current").text($(this).text()),$("#tag-target").val($(this).text()),$(this).parent().find(".checked").removeClass("checked"),$(this).addClass("checked"))})}function initRepoSetting(){var t=$("#repo_name"),n=$("#repo-setting-form");$("#change-reponame-btn").magnificPopup({modal:!0,callbacks:{open:function(){t.data("repo-name")==t.val()&&($.magnificPopup.close(),n.submit())}}}).click(function(){return t.data("repo-name")!=t.val()?(e.preventDefault(),!0):void 0}),$("#change-reponame-submit").click(function(){$.magnificPopup.close(),n.submit()}),initHookTypeChange(),$("#transfer-repo-btn").magnificPopup({modal:!0}),$("#transfer-repo-submit").click(function(){$.magnificPopup.close(),$("#transfer-repo-form").submit()}),$("#delete-repo-btn").magnificPopup({modal:!0}),$("#delete-repo-submit").click(function(){$.magnificPopup.close(),$("#delete-repo-form").submit()}),$("#repo-collab-list hr:last-child").remove();var r=$("#repo-collaborator").next().next().find("ul");$("#repo-collaborator").on("keyup",function(){var e=$(this);return e.val()?void Gogs.searchUsers(e.val(),r):void r.toggleHide()}).on("focus",function(){$(this).val()?r.toggleShow():r.toggleHide()}).next().next().find("ul").on("click","li",function(){$("#repo-collaborator").val($(this).text()),r.toggleHide()})}function initOrgSetting(){var t=$("#orgname"),n=$("#org-setting-form");$("#change-orgname-btn").magnificPopup({modal:!0,callbacks:{open:function(){t.data("orgname")==t.val()&&($.magnificPopup.close(),n.submit())}}}).click(function(){return t.data("orgname")!=t.val()?(e.preventDefault(),!0):void 0}),$("#change-orgname-submit").click(function(){$.magnificPopup.close(),n.submit()}),$("#delete-org-btn").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-org-submit").click(function(){$.magnificPopup.close(),$("#delete-org-form").submit()}),initHookTypeChange()}function initInvite(){var e=$("#org-member-invite-list");$("#org-member-invite").on("keyup",function(){var t=$(this);return t.val()?void Gogs.searchUsers(t.val(),e):void e.toggleHide()}).on("focus",function(){$(this).val()?e.toggleShow():e.toggleHide()}).next().next().find("ul").on("click","li",function(){$("#org-member-invite").val($(this).find(".username").text()),e.toggleHide()})}function initOrgTeamCreate(){$("#org-team-delete").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-team-submit").click(function(){$.magnificPopup.close();var e=$("#team-create-form");e.attr("action",e.data("delete-url"))})}function initTeamMembersList(){var e=$("#org-team-members-list");$("#org-team-members-add").on("keyup",function(){var t=$(this);return t.val()?void Gogs.searchUsers(t.val(),e):void e.toggleHide()}).on("focus",function(){$(this).val()?e.toggleShow():e.toggleHide()}).next().next().find("ul").on("click","li",function(){$("#org-team-members-add").val($(this).text()),e.toggleHide()})}function initTeamRepositoriesList(){var e=$("#org-team-repositories-list");$("#org-team-repositories-add").on("keyup",function(){var t=$(this);return t.val()?void Gogs.searchRepos(t.val(),e,"uid="+t.data("uid")):void e.toggleHide()}).on("focus",function(){$(this).val()?e.toggleShow():e.toggleHide()}).next().next().find("ul").on("click","li",function(){$("#org-team-repositories-add").val($(this).text()),e.toggleHide()})}function initAdmin(){$("#login-type").on("change",function(){var e=$(this).val();e.indexOf("0-")+1?($(".auth-name").toggleHide(),$(".pwd").find("input").attr("required","required").end().toggleShow()):($(".pwd").find("input").removeAttr("required").end().toggleHide(),$(".auth-name").toggleShow())}),$("#delete-account-btn").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-account-submit").click(function(){$.magnificPopup.close();var e=$("#user-profile-form");e.attr("action",e.data("delete-url"))}),$("#auth-type").on("change",function(){var e=$(this).val();2==e&&($(".ldap").toggleShow(),$(".smtp").toggleHide()),3==e&&($(".smtp").toggleShow(),$(".ldap").toggleHide())}),$("#delete-auth-btn").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-auth-submit").click(function(){$.magnificPopup.close();var e=$("#auth-setting-form");e.attr("action",e.data("delete-url"))})}function initInstall(){!function(){var e="127.0.0.1:3306",t="127.0.0.1:5432";$("#install-database").on("change",function(){var n=$(this).val();"SQLite3"!=n?($(".server-sql").show(),$(".sqlite-setting").addClass("hide"),"PostgreSQL"==n?($(".pgsql-setting").removeClass("hide"),$("#database-host").val()==e&&$("#database-host").val(t)):"MySQL"==n?($(".pgsql-setting").addClass("hide"),$("#database-host").val()==t&&$("#database-host").val(e)):$(".pgsql-setting").addClass("hide")):($(".server-sql").hide(),$(".pgsql-setting").hide(),$(".sqlite-setting").removeClass("hide"))})}()}function initProfile(){$("#profile-avatar").tipsy({fade:!0})}function initTimeSwitch(){$(".time-since[title]").on("click",function(){var e=$(this),t=e.attr("title"),n=e.text();e.text(t),e.attr("title",n)})}function initDiff(){$(".diff-detail-box>a").click(function(){$($(this).data("target")).slideToggle(100)});var e=$(".diff-counter");e.length<1||e.each(function(e,t){var n=$(t),r=n.find("span[data-line].add").data("line"),i=n.find("span[data-line].del").data("line"),o=parseFloat(r)/(parseFloat(r)+parseFloat(i))*100;n.find(".bar .add").css("width",o+"%")})}function homepage(){$("#promo-form").submit(function(e){return""===$("#username").val()?(e.preventDefault(),window.location.href=Gogs.AppSubUrl+"/user/login",!0):void 0}),$("#register-button").click(function(e){return""===$("#username").val()?(e.preventDefault(),window.location.href=Gogs.AppSubUrl+"/user/sign_up",!0):void $("#promo-form").attr("action",Gogs.AppSubUrl+"/user/sign_up")})}!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=e.length,n=ot.type(e);return"function"===n||ot.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e}function r(e,t,n){if(ot.isFunction(t))return ot.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return ot.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(pt.test(t))return ot.filter(t,e,n);t=ot.filter(t,e)}return ot.grep(e,function(e){return ot.inArray(e,t)>=0!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t=wt[e]={};return ot.each(e.match(xt)||[],function(e,n){t[n]=!0}),t}function a(){mt.addEventListener?(mt.removeEventListener("DOMContentLoaded",s,!1),e.removeEventListener("load",s,!1)):(mt.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(mt.addEventListener||"load"===event.type||"complete"===mt.readyState)&&(a(),ot.ready())}function l(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(Tt,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:St.test(n)?ot.parseJSON(n):n}catch(i){}ot.data(e,t,n)}else n=void 0}return n}function c(e){var t;for(t in e)if(("data"!==t||!ot.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function u(e,t,n,r){if(ot.acceptData(e)){var i,o,a=ot.expando,s=e.nodeType,l=s?ot.cache:e,c=s?e[a]:e[a]&&a;if(c&&l[c]&&(r||l[c].data)||void 0!==n||"string"!=typeof t)return c||(c=s?e[a]=V.pop()||ot.guid++:a),l[c]||(l[c]=s?{}:{toJSON:ot.noop}),("object"==typeof t||"function"==typeof t)&&(r?l[c]=ot.extend(l[c],t):l[c].data=ot.extend(l[c].data,t)),o=l[c],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[ot.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[ot.camelCase(t)])):i=o,i}}function d(e,t,n){if(ot.acceptData(e)){var r,i,o=e.nodeType,a=o?ot.cache:e,s=o?e[ot.expando]:ot.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){ot.isArray(t)?t=t.concat(ot.map(t,ot.camelCase)):t in r?t=[t]:(t=ot.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!c(r):!ot.isEmptyObject(r))return}(n||(delete a[s].data,c(a[s])))&&(o?ot.cleanData([e],!0):rt.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}function f(){return!0}function p(){return!1}function h(){try{return mt.activeElement}catch(e){}}function m(e){var t=Ht.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function g(e,t){var n,r,i=0,o=typeof e.getElementsByTagName!==kt?e.getElementsByTagName(t||"*"):typeof e.querySelectorAll!==kt?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||ot.nodeName(r,t)?o.push(r):ot.merge(o,g(r,t));return void 0===t||t&&ot.nodeName(e,t)?ot.merge([e],o):o}function v(e){Dt.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t){return ot.nodeName(e,"table")&&ot.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function b(e){return e.type=(null!==ot.find.attr(e,"type"))+"/"+e.type,e}function x(e){var t=Zt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function w(e,t){for(var n,r=0;null!=(n=e[r]);r++)ot._data(n,"globalEval",!t||ot._data(t[r],"globalEval"))}function C(e,t){if(1===t.nodeType&&ot.hasData(e)){var n,r,i,o=ot._data(e),a=ot._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)ot.event.add(t,n,s[n][r])}a.data&&(a.data=ot.extend({},a.data))}}function k(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!rt.noCloneEvent&&t[ot.expando]){i=ot._data(t);for(r in i.events)ot.removeEvent(t,r,i.handle);t.removeAttribute(ot.expando)}"script"===n&&t.text!==e.text?(b(t).text=e.text,x(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),rt.html5Clone&&e.innerHTML&&!ot.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Dt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function S(t,n){var r,i=ot(n.createElement(t)).appendTo(n.body),o=e.getDefaultComputedStyle&&(r=e.getDefaultComputedStyle(i[0]))?r.display:ot.css(i[0],"display");return i.detach(),o}function T(e){var t=mt,n=Jt[e];return n||(n=S(e,t),"none"!==n&&n||(Kt=(Kt||ot("