Compare commits

...

27 Commits

Author SHA1 Message Date
Unknwon
348c75c91b issue: response wrong type of comment from AJAX 2017-04-03 20:06:15 -04:00
Unknwon
76ebdb265b css: update fonts
[CI SKIP]
2017-04-03 18:20:49 -04:00
Unknwon
88ae3510ff Prepare release 2017-04-03 16:26:43 -04:00
Unknwon
c07899701a vendor: update github.com/gogits/git-module 2017-04-03 15:09:53 -04:00
Unknwon
ab42671c63 repo: handle git.ErrUnsupportedVersion error type 2017-04-01 17:56:55 -04:00
Unknwon
b3ac33cbcf vendor: update github.com/gogits/git-module 2017-04-01 17:50:11 -04:00
Unknwon
3b94162803 modules/setting: set default session GC interval to 3600s 2017-03-31 23:55:42 -04:00
Unknwon
52aade232d modes: fix test compile error 2017-03-31 18:28:08 -04:00
Unknwon
3a9276307c modules/markup: move common functions to markup.go 2017-03-31 17:46:57 -04:00
Unknwon
ca6326c937 modules/base: move file-related functions to file.go 2017-03-31 17:20:05 -04:00
Unknwon
8da16ac302 modules/markup: rename Markdown render fucntions
The unified function 'Markdown' accepts both string or []byte type
input and renders to HTML with []byte type.
2017-03-31 16:37:30 -04:00
Unknwon
761bb3cf53 modules/markup: protect sanitizer from possible modification
Only expose public APIs for 'Sanitize' and 'SanitizeBytes' to
eliminate unintentional modifications to sanitizer policy. Also
use 'sync.Once' to make sure multiple calls of 'NewSanitizer' is
safe (although should never happen, but this is a better way).
2017-03-31 16:19:10 -04:00
Unknwon
c1c269d9ef modules: rename markdown -> markup
To further support more markup languages (e.g. Org-mode, AsciiDoc,
reStructuredText), the name 'markdown' is inappropriate.

This is the first step towards more markup language support.
2017-03-31 15:29:43 -04:00
Unknwon
9edac05e05 templates/repo: improve README icon and font 2017-03-31 15:11:40 -04:00
Unknwon
9c1620d49c css: font-family fixes for Chinese 2017-03-31 14:13:25 -04:00
John Behm
65bb6eb284 locale: update TRANSLATORS (#4372)
* Update TRANSLATORS

* Update TRANSLATORS
2017-03-31 12:37:46 -04:00
Unknwon
aff55ff105 vendor: update github.com/go-macaron/captcha
[CI SKIP]
2017-03-30 12:35:43 -04:00
Unknwon
4a67bb5806 migrations.v17: skip if table not exist 2017-03-30 11:37:27 -04:00
Unknwon
1afafde3b3 user/setting: preserve user input with validation error (#1123) 2017-03-30 01:03:44 -04:00
Unknwon
ab634ce61a cmd/web: fix routes requires sign in (#4359)
Redirect user to sign in page when visit private repository with
public issues if user want to post comment or create new issue.
2017-03-30 00:34:20 -04:00
Unknwon
9d06ebd01a markdown: improve filter of class attribute for code blocks
Only allow HighlightJS specific classes.

Reported by ChALkeR.
2017-03-29 19:52:53 -04:00
Kyle McCullough
09723ec0e5 migration.v17: handle mysql error (#4361) 2017-03-29 19:02:07 -04:00
Hassan Amouhzi
864761c2d0 js: use jQuery .find() (#4343) 2017-03-28 17:36:48 -04:00
Unknwon
abe7f7bc36 migration.v17: add nil error check 2017-03-28 16:38:16 -04:00
Unknwon
717bcc4ad8 migration/v17: skip if protect_branch_whitelist table not exist (#4355) 2017-03-28 11:20:58 -04:00
Unknwon
11ffdac3f8 org/dashboard: fix wrong repository count (#4351)
Should not include count for inaccessible repositories.
2017-03-28 11:16:29 -04:00
David Beitey
cf7d5d0c56 docker: fix heading in README.md (#4353)
The unicode non-breaking space character was breaking the heading format; this switches that character for an ASCII space.
2017-03-28 07:50:36 -04:00
59 changed files with 1175 additions and 931 deletions

5
.gitattributes vendored
View File

@@ -1,8 +1,7 @@
public/conf/gitignore/* linguist-vendored
public/conf/license/* linguist-vendored
conf/gitignore/* linguist-vendored
conf/license/* linguist-vendored
public/assets/* linguist-vendored
public/plugins/* linguist-vendored
public/plugins/* linguist-vendored
public/css/themes/* linguist-vendored
public/css/github.min.css linguist-vendored
public/css/semantic-2.2.7.min.css linguist-vendored

View File

@@ -50,7 +50,7 @@ The goal of this project is to make the easiest, fastest, and most painless way
- Gravatar and Federated avatar with custom source
- Mail service
- Administration panel
- Supports MySQL, PostgreSQL, SQLite3, MSSQL and [TiDB](https://github.com/pingcap/tidb) (experimental)
- Supports MySQL, PostgreSQL, SQLite3, MSSQL and [TiDB](https://github.com/pingcap/tidb) (via MySQL protocol)
- Multi-language support ([23 languages](https://crowdin.com/project/gogs))
## Hardware Requirements

View File

@@ -9,7 +9,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
## 项目概览
- 有关基本用法和变更日志,请通过 [使用手册](https://gogs.io/docs/intro/) 查看。
- 有关基本用法和变更日志,请通过 [使用手册](https://gogs.io/docs/intro) 查看。
- 想要先睹为快?直接去 [在线体验](https://try.gogs.io/gogs/gogs) 。
- 使用过程中遇到问题?尝试从 [故障排查](https://gogs.io/docs/intro/troubleshooting.html) 页面或 [用户论坛](https://discuss.gogs.io/) 获取帮助。
- 希望帮助多国语言界面的翻译吗?请立即访问 [详情页面](https://gogs.io/docs/features/i18n.html)
@@ -31,7 +31,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- 支持 Jupyter Notebook
- 支持邮件服务
- 支持后台管理面板
- 支持 MySQL、PostgreSQL、SQLite3、MSSQL 和 [TiDB](https://github.com/pingcap/tidb)实验性支持) 数据库
- 支持 MySQL、PostgreSQL、SQLite3、MSSQL 和 [TiDB](https://github.com/pingcap/tidb)通过 MySQL 协议)数据库
- 支持多语言本地化([23 种语言]([more](https://crowdin.com/project/gogs))
## 硬件要求

View File

@@ -230,7 +230,6 @@ func runWeb(ctx *cli.Context) error {
})
m.Group("/user", func() {
// r.Get("/feeds", binding.Bind(form.Feeds{}), user.Feeds)
m.Any("/activate", user.Activate)
m.Any("/activate_email", user.ActivateEmail)
m.Get("/email2user", user.Email2User)
@@ -460,7 +459,10 @@ func runWeb(ctx *cli.Context) error {
m.Group("/:username/:reponame", func() {
m.Get("/issues", repo.RetrieveLabels, repo.Issues)
m.Get("/issues/:index", repo.ViewIssue)
m.Get("/labels/", repo.RetrieveLabels, repo.Labels)
m.Get("/milestones", repo.Milestones)
}, ignSignIn, context.RepoAssignment(true))
m.Group("/:username/:reponame", func() {
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
// So they can apply their own enable/disable logic on routers.
m.Group("/issues", func() {
@@ -477,10 +479,7 @@ func runWeb(ctx *cli.Context) error {
m.Post("", repo.UpdateCommentContent)
m.Post("/delete", repo.DeleteComment)
})
m.Get("/labels/", repo.RetrieveLabels, repo.Labels)
m.Get("/milestones", repo.Milestones)
}, ignSignIn, context.RepoAssignment(true))
}, reqSignIn, context.RepoAssignment(true))
m.Group("/:username/:reponame", func() {
m.Group("/wiki", func() {
m.Get("/?:page", repo.Wiki)

View File

@@ -271,8 +271,8 @@ COOKIE_NAME = i_like_gogits
COOKIE_SECURE = false
; Enable set cookie, default is true
ENABLE_SET_COOKIE = true
; Session GC time interval, default is 86400
GC_INTERVAL_TIME = 86400
; Session GC time interval, default is 3600
GC_INTERVAL_TIME = 3600
; Session life time, default is 86400
SESSION_LIFE_TIME = 86400
; Cookie name for CSRF

View File

@@ -42,6 +42,7 @@ Ilya Makarov
Jamie Mansfield <dev AT jamierocks DOT uk>
Javier Ortiz Bultron <javier DOT ortiz DOT 78 AT gmail DOT com>
Jean THOMAS <contact AT tibounise DOT com>
John Behm <jxsl13 AT googlemail DOT com>
Jonas De Kegel <jonasgithub [AT] gmail [DOT] com>
Joubert RedRat <me+github AT redrat DOT com DOT br>
Juraj Bubniak <contact AT jbub DOT eu>

View File

@@ -82,7 +82,7 @@ app_url=URL aplikace
app_url_helper=Toto ovlivňuje URL klonů skrze HTTP/HTTPS a odkazů v e-mailech.
log_root_path=Adresář systémových záznamů
log_root_path_helper=Adresář, kam se budou zapisovat systémové záznamy.
enable_console_mode=Enable Console Mode
enable_console_mode=Povolit režim konzoly
enable_console_mode_popup=In addition to file mode, also print logs to console.
optional_title=Dodatečná nastavení
@@ -258,7 +258,7 @@ ssh_keys=Klíče SSH
social=Sociální účty
applications=Aplikace
orgs=Organizace
repos=Repositories
repos=Repositáře
delete=Smazat účet
uid=UID
@@ -347,8 +347,8 @@ orgs.none=Nejste členem žádné organizace.
orgs.leave_title=Opustit organizaci
orgs.leave_desc=Opuštěním organizace ztratíte přístup do všech repositářů a k týmům. Chcete pokračovat?
repos.leave=Leave
repos.leave_title=Leave repository
repos.leave=Opustit
repos.leave_title=Opustit repositář
repos.leave_desc=You will lose access to the repository after you left. Do you want to continue?
repos.leave_success=You have left repository '%s' successfully!
@@ -751,7 +751,7 @@ settings.webhook_deletion_success=Webový háček byl úspěšně smazán!
settings.webhook.test_delivery=Test doručitelnosti
settings.webhook.test_delivery_desc=Odeslat falešnou událost doručení odeslání pro test vašeho nastavení webových háčků
settings.webhook.test_delivery_success=Testovací webový háček byl přidán do fronty doručení. Bude to trvat několik sekund, než se ukáže v historii doručení.
settings.webhook.redelivery=Redelivery
settings.webhook.redelivery=Opětovné doručení
settings.webhook.redelivery_success=Hook task '%s' has been readded to delivery queue. It may take few seconds to update delivery status in history.
settings.webhook.request=Požadavek
settings.webhook.response=Odpověď

View File

@@ -82,8 +82,8 @@ app_url=Anwendungs-URL
app_url_helper=Dies hat Auswirkung auf die HTTP/HTTPS Klon-URLs und den Inhalt der E-Mails.
log_root_path=Logdateipfad
log_root_path_helper=Verzeichnis in das Logdateien geschrieben werden.
enable_console_mode=Enable Console Mode
enable_console_mode_popup=In addition to file mode, also print logs to console.
enable_console_mode=Konsolen-Modus einschalten
enable_console_mode_popup=Zusätzlich zum Datei-Modus, zeige Logs auch in der Konsole.
optional_title=Optionale Einstellungen
email_title=E-Mail-Service Einstellungen
@@ -347,10 +347,10 @@ orgs.none=Sie sind kein Mitglied einer Organisation.
orgs.leave_title=Organisation verlassen
orgs.leave_desc=Sie verlieren den Zugriff auf alle Repositories und Teams nach dem Verlassen der Organisation. Möchten Sie fortfahren?
repos.leave=Leave
repos.leave_title=Leave repository
repos.leave_desc=You will lose access to the repository after you left. Do you want to continue?
repos.leave_success=You have left repository '%s' successfully!
repos.leave=Verlassen
repos.leave_title=Repository verlassen
repos.leave_desc=Der Zugriff zum Repository wird verloren gehen, nachdem diese verlassen wird. Möchten Sie fortfahren?
repos.leave_success=Sie haben die Repository '%s' erfolgreich verlassen!
delete_account=Konto löschen
delete_prompt=Diese Aktion wird Ihr Konto dauerhaft löschen und kann <strong>NICHT</strong> rückgängig gemacht werden!
@@ -400,7 +400,7 @@ migrate_type_helper=Dieses Repository wird ein <span class="text blue">Mirror</s
migrate_repo=Repository migrieren
migrate.clone_address=Adresse kopieren
migrate.clone_address_desc=Dies kann eine HTTP/HTTPS/GIT-URL sein.
migrate.clone_address_desc_import_local=Sie dürfen auch eine Repository vom lokalen Serverpfad migrieren.
migrate.clone_address_desc_import_local=Sie dürfen auch ein Repository vom lokalen Serverpfad migrieren.
migrate.permission_denied=Ihnen fehlen die Rechte zum Importieren lokaler Repositories.
migrate.invalid_local_path=Der lokale Pfad ist ungültig, existiert nicht oder ist kein Ordner.
migrate.failed=Fehler bei Migration: %v
@@ -422,7 +422,7 @@ quick_guide=Kurzanleitung
clone_this_repo=Dieses Repository klonen
create_new_repo_command=Erstellen Sie ein neues Repository mittels der Kommandozeile
push_exist_repo=Bestehendes Repository von der Kommandozeile pushen
bare_message=This repository does not have any content yet.
bare_message=Diese Repository hat noch keinen Inhalt.
files=Dateien
branch=Branch
@@ -658,7 +658,7 @@ settings.collaboration.write=Schreibrechte
settings.collaboration.read=Leserechte
settings.collaboration.undefined=Nicht definiert
settings.branches=Branches
settings.branches_bare=You cannot manage branches for bare repository. Please push some content first.
settings.branches_bare=Branches leerer Repositories können nicht verwaltet werden. Bitte erst Datei(en) pushen.
settings.default_branch=Standard-Branch
settings.default_branch_desc=Der Standard-Branch gilt als Basis für Commits, Pull-Requests und Online-Bearbeitung.
settings.update=Aktualisieren
@@ -691,13 +691,13 @@ settings.change_reponame_prompt=Diese Änderung wirkt sich darauf aus, wie sich
settings.advanced_settings=Erweiterte Einstellungen
settings.wiki_desc=Wiki einschalten
settings.use_internal_wiki=Eingebautes Wiki verwenden
settings.allow_public_wiki_desc=Allow public access to wiki when repository is private
settings.allow_public_wiki_desc=Erlaube öffentlichen Zugang zum Wiki, auch wenn die Repository privat ist.
settings.use_external_wiki=Externes Wiki verwenden
settings.external_wiki_url=Externe Wiki URL
settings.external_wiki_url_desc=Besucher werden auf diese URL umgeleitet, wenn sie auf den Tab klicken.
settings.issues_desc=Issue-Tracker einschalten
settings.use_internal_issue_tracker=Eingebauten Issue-Tracker verwenden
settings.allow_public_issues_desc=Allow public access to issues when repository is private
settings.allow_public_issues_desc=Erlaube öffentlichen Zugriff auf Issues, auch wenn die Repository privat ist.
settings.use_external_issue_tracker=Externes Issue-System verwenden
settings.external_tracker_url=URL eines externen Issue Trackers
settings.external_tracker_url_desc=Besucher werden auf diese URL umgeleitet, wenn sie auf den Tab klicken.
@@ -751,8 +751,8 @@ settings.webhook_deletion_success=Webhook wurde erfolgreich entfernt!
settings.webhook.test_delivery=Senden testen
settings.webhook.test_delivery_desc=Sendet ein simuliertes Push-Ereignis, um die Webhook-Einstellungen zu testen
settings.webhook.test_delivery_success=Test-Webhook wurde zur Auslieferungswarteschlange hinzugefügt. Es kann einige Sekunden dauern, bevor es in der Auslieferungshistorie erscheint.
settings.webhook.redelivery=Redelivery
settings.webhook.redelivery_success=Hook task '%s' has been readded to delivery queue. It may take few seconds to update delivery status in history.
settings.webhook.redelivery=Erneuter Versand
settings.webhook.redelivery_success=Hook-Task '%s' wurde wieder zur Auslieferungswarteschlange hinzugefügt. Es kann einige Sekunden, bis sich der Auslieferungsstatus in der History aktualisiert hat.
settings.webhook.request=Anfrage
settings.webhook.response=Antwort
settings.webhook.headers=Kopfzeilen

View File

@@ -662,6 +662,7 @@ settings.branches_bare = You cannot manage branches for bare repository. Please
settings.default_branch = Default Branch
settings.default_branch_desc = The default branch is considered the "base" branch for code commits, pull requests and online editing.
settings.update = Update
settings.update_default_branch_unsupported = Change default branch is not supported by the Git version on server.
settings.update_default_branch_success = Default branch of this repository has been updated successfully!
settings.protected_branches = Protected Branches
settings.protected_branches_desc = Protect branches from force pushing, accidental deletion and whitelist code committers.

View File

@@ -75,15 +75,15 @@ domain_helper=Questo influisce sugli URL per il clonaggio via SSH.
ssh_port=Porta SSH
ssh_port_helper=Numero di porta utilizzato dal server SSH, lasciare vuoto per disabilitare l'integrazione SSH.
use_builtin_ssh_server=Usa il server SSH integrato
use_builtin_ssh_server_popup=Start builtin SSH server for Git operations to distinguish from system SSH daemon.
use_builtin_ssh_server_popup=Avvia il server SSH integrato per le operazioni Git per distinguerle dal demone SSH di sistema.
http_port=Porta HTTP
http_port_helper=Porta di ascolto dell'applicazione.
app_url=URL Applicazione
app_url_helper=Questo influisce sugli URL per il clonaggio via HTTP/HTTPS e da qualche parte nella posta elettronica.
log_root_path=Percorso dei log
log_root_path_helper=Directory in cui scrivere i file di log.
enable_console_mode=Enable Console Mode
enable_console_mode_popup=In addition to file mode, also print logs to console.
enable_console_mode=Abilita modalità Terminale
enable_console_mode_popup=In aggiunta alla modalità file, invia i log anche al terminale.
optional_title=Impostazioni Facoltative
email_title=Impostazioni E-mail

View File

@@ -658,7 +658,7 @@ settings.collaboration.write=Запис
settings.collaboration.read=Читання
settings.collaboration.undefined=Не визначено
settings.branches=Гілки
settings.branches_bare=You cannot manage branches for bare repository. Please push some content first.
settings.branches_bare=Ви не можете керувати гілками у bare-репозиторії. Спочатку додайте у нього якогось вмісту, будь ласка.
settings.default_branch=Гілка за замовчуванням
settings.default_branch_desc=Гілка за замовчанням вважається базовою для комітів, запросів злиття та онлайн редагувань.
settings.update=Оновлення

View File

@@ -1259,7 +1259,7 @@ delete_tag=删除了 <a href="%[1]s">%[3]s</a> 的标签 <code>%[2]s</code>
[tool]
ago=之前
from_now=之后
now=现在
now=刚刚
1s=1 秒%s
1m=1 分钟%s
1h=1 小时%s

View File

@@ -73,7 +73,7 @@ Most of settings are obvious and easy to understand, but there are some settings
Full documentation of application settings can be found [here](https://gogs.io/docs/advanced/configuration_cheat_sheet.html).
### Container Options
### Container Options
This container have some options available via environment variables, these options are opt-in features that can help the administration of this container:

View File

@@ -16,7 +16,7 @@ import (
"github.com/gogits/gogs/modules/setting"
)
const APP_VER = "0.10.31.0327 / 0.11 RC"
const APP_VER = "0.11.0.0403"
func init() {
setting.AppVer = APP_VER

View File

@@ -16,7 +16,7 @@ import (
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/models/errors"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
)
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
@@ -168,7 +168,7 @@ func (c *Comment) EventTag() string {
// mailParticipants sends new comment emails to repository watchers
// and mentioned people.
func (cmt *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
mentions := markdown.FindAllMentions(cmt.Content)
mentions := markup.FindAllMentions(cmt.Content)
if err = updateIssueMentions(e, cmt.IssueID, mentions); err != nil {
return fmt.Errorf("UpdateIssueMentions [%d]: %v", cmt.IssueID, err)
}

View File

@@ -11,7 +11,7 @@ import (
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/modules/mailer"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/setting"
)
@@ -162,7 +162,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
// MailParticipants sends new issue thread created emails to repository watchers
// and mentioned people.
func (issue *Issue) MailParticipants() (err error) {
mentions := markdown.FindAllMentions(issue.Content)
mentions := markup.FindAllMentions(issue.Content)
if err = updateIssueMentions(x, issue.ID, mentions); err != nil {
return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
}

View File

@@ -5,10 +5,18 @@
package migrations
import (
"fmt"
"github.com/go-xorm/xorm"
)
func removeInvalidProtectBranchWhitelist(x *xorm.Engine) error {
_, err := x.Exec("DELETE FROM protect_branch_whitelist WHERE protect_branch_id = 0")
exist, err := x.IsTableExist("protect_branch_whitelist")
if err != nil {
return fmt.Errorf("IsTableExist: %v", err)
} else if !exist {
return nil
}
_, err = x.Exec("DELETE FROM protect_branch_whitelist WHERE protect_branch_id = 0")
return err
}

View File

@@ -28,7 +28,7 @@ import (
"github.com/gogits/gogs/models/errors"
"github.com/gogits/gogs/modules/bindata"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/sync"
@@ -219,7 +219,7 @@ func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
case "external_tracker_style":
if len(repo.ExternalTrackerStyle) == 0 {
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_NUMERIC
}
case "created_unix":
repo.Created = time.Unix(repo.CreatedUnix, 0).Local()
@@ -356,10 +356,10 @@ func (repo *Repository) ComposeMetas() map[string]string {
"repo": repo.Name,
}
switch repo.ExternalTrackerStyle {
case markdown.ISSUE_NAME_STYLE_ALPHANUMERIC:
repo.ExternalMetas["style"] = markdown.ISSUE_NAME_STYLE_ALPHANUMERIC
case markup.ISSUE_NAME_STYLE_ALPHANUMERIC:
repo.ExternalMetas["style"] = markup.ISSUE_NAME_STYLE_ALPHANUMERIC
default:
repo.ExternalMetas["style"] = markdown.ISSUE_NAME_STYLE_NUMERIC
repo.ExternalMetas["style"] = markup.ISSUE_NAME_STYLE_NUMERIC
}
}

View File

@@ -1,11 +1,12 @@
package models_test
import (
. "github.com/gogits/gogs/models"
. "github.com/smartystreets/goconvey/convey"
"testing"
"github.com/gogits/gogs/modules/markdown"
. "github.com/smartystreets/goconvey/convey"
. "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/markup"
)
func TestRepo(t *testing.T) {
@@ -24,7 +25,7 @@ func TestRepo(t *testing.T) {
Convey("It should be nil even if other settings are present", func() {
repo.EnableExternalTracker = false
repo.ExternalTrackerFormat = "http://someurl.com/{user}/{repo}/{issue}"
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_NUMERIC
So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil))
})
})
@@ -33,17 +34,17 @@ func TestRepo(t *testing.T) {
repo.EnableExternalTracker = true
Convey("It should default to numeric issue style", func() {
metas := repo.ComposeMetas()
So(metas["style"], ShouldEqual, markdown.ISSUE_NAME_STYLE_NUMERIC)
So(metas["style"], ShouldEqual, markup.ISSUE_NAME_STYLE_NUMERIC)
})
Convey("It should pass through numeric issue style setting", func() {
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_NUMERIC
metas := repo.ComposeMetas()
So(metas["style"], ShouldEqual, markdown.ISSUE_NAME_STYLE_NUMERIC)
So(metas["style"], ShouldEqual, markup.ISSUE_NAME_STYLE_NUMERIC)
})
Convey("It should pass through alphanumeric issue style setting", func() {
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_ALPHANUMERIC
repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_ALPHANUMERIC
metas := repo.ComposeMetas()
So(metas["style"], ShouldEqual, markdown.ISSUE_NAME_STYLE_ALPHANUMERIC)
So(metas["style"], ShouldEqual, markup.ISSUE_NAME_STYLE_ALPHANUMERIC)
})
Convey("It should contain the user name", func() {
metas := repo.ComposeMetas()

View File

@@ -67,7 +67,7 @@ func SignedInID(ctx *macaron.Context, sess session.Store) int64 {
if id, ok := uid.(int64); ok {
if _, err := models.GetUserByID(id); err != nil {
if !errors.IsUserNotExist(err) {
log.Error(2, "GetUserById: %v", err)
log.Error(2, "GetUserByID: %v", err)
}
return 0
}

77
modules/base/file.go Normal file
View File

@@ -0,0 +1,77 @@
// Copyright 2017 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package base
import (
"fmt"
"math"
"net/http"
"strings"
)
// IsTextFile returns true if file content format is plain text or empty.
func IsTextFile(data []byte) bool {
if len(data) == 0 {
return true
}
return strings.Contains(http.DetectContentType(data), "text/")
}
func IsImageFile(data []byte) bool {
return strings.Contains(http.DetectContentType(data), "image/")
}
func IsPDFFile(data []byte) bool {
return strings.Contains(http.DetectContentType(data), "application/pdf")
}
func IsVideoFile(data []byte) bool {
return strings.Contains(http.DetectContentType(data), "video/")
}
const (
Byte = 1
KByte = Byte * 1024
MByte = KByte * 1024
GByte = MByte * 1024
TByte = GByte * 1024
PByte = TByte * 1024
EByte = PByte * 1024
)
var bytesSizeTable = map[string]uint64{
"b": Byte,
"kb": KByte,
"mb": MByte,
"gb": GByte,
"tb": TByte,
"pb": PByte,
"eb": EByte,
}
func logn(n, b float64) float64 {
return math.Log(n) / math.Log(b)
}
func humanateBytes(s uint64, base float64, sizes []string) string {
if s < 10 {
return fmt.Sprintf("%d B", s)
}
e := math.Floor(logn(float64(s), base))
suffix := sizes[int(e)]
val := float64(s) / math.Pow(base, math.Floor(e))
f := "%.0f"
if val < 10 {
f = "%.1f"
}
return fmt.Sprintf(f+" %s", val, suffix)
}
// FileSize calculates the file size and generate user-friendly string.
func FileSize(s int64) string {
sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
return humanateBytes(uint64(s), 1024, sizes)
}

View File

@@ -12,9 +12,7 @@ import (
"encoding/hex"
"fmt"
"html/template"
"math"
"math/big"
"net/http"
"strings"
"time"
"unicode"
@@ -346,51 +344,6 @@ func TimeSince(t time.Time, lang string) template.HTML {
return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, t.Format(setting.TimeFormat), timeSince(t, lang)))
}
const (
Byte = 1
KByte = Byte * 1024
MByte = KByte * 1024
GByte = MByte * 1024
TByte = GByte * 1024
PByte = TByte * 1024
EByte = PByte * 1024
)
var bytesSizeTable = map[string]uint64{
"b": Byte,
"kb": KByte,
"mb": MByte,
"gb": GByte,
"tb": TByte,
"pb": PByte,
"eb": EByte,
}
func logn(n, b float64) float64 {
return math.Log(n) / math.Log(b)
}
func humanateBytes(s uint64, base float64, sizes []string) string {
if s < 10 {
return fmt.Sprintf("%d B", s)
}
e := math.Floor(logn(float64(s), base))
suffix := sizes[int(e)]
val := float64(s) / math.Pow(base, math.Floor(e))
f := "%.0f"
if val < 10 {
f = "%.1f"
}
return fmt.Sprintf(f+" %s", val, suffix)
}
// FileSize calculates the file size and generate user-friendly string.
func FileSize(s int64) string {
sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
return humanateBytes(uint64(s), 1024, sizes)
}
// Subtract deals with subtraction of all types of number.
func Subtract(left interface{}, right interface{}) interface{} {
var rleft, rright int64
@@ -491,23 +444,3 @@ func Int64sToMap(ints []int64) map[int64]bool {
func IsLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}
// IsTextFile returns true if file content format is plain text or empty.
func IsTextFile(data []byte) bool {
if len(data) == 0 {
return true
}
return strings.Index(http.DetectContentType(data), "text/") != -1
}
func IsImageFile(data []byte) bool {
return strings.Index(http.DetectContentType(data), "image/") != -1
}
func IsPDFFile(data []byte) bool {
return strings.Index(http.DetectContentType(data), "application/pdf") != -1
}
func IsVideoFile(data []byte) bool {
return strings.Index(http.DetectContentType(data), "video/") != -1
}

File diff suppressed because one or more lines are too long

View File

@@ -79,12 +79,17 @@ func (ctx *Context) HasValue(name string) bool {
return ok
}
// HTML calls Context.HTML and converts template name to string.
// HTML responses template with given status.
func (ctx *Context) HTML(status int, name base.TplName) {
log.Trace("Template: %s", name)
ctx.Context.HTML(status, string(name))
}
// Success responses template with status 200.
func (c *Context) Success(name base.TplName) {
c.HTML(200, name)
}
// RenderWithErr used for page has form validation but need to prompt error to users.
func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, f interface{}) {
if f != nil {

View File

@@ -13,7 +13,7 @@ import (
"gopkg.in/macaron.v1"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/setting"
)
@@ -174,7 +174,7 @@ func composeTplData(subject, body, link string) map[string]interface{} {
func composeIssueMessage(issue Issue, repo Repository, doer User, tplName base.TplName, tos []string, info string) *Message {
subject := issue.MailSubject()
body := string(markdown.RenderSpecialLink([]byte(issue.Content()), repo.HTMLURL(), repo.ComposeMetas()))
body := string(markup.RenderSpecialLink([]byte(issue.Content()), repo.HTMLURL(), repo.ComposeMetas()))
data := composeTplData(subject, body, issue.HTMLURL())
data["Doer"] = doer
content, err := mailRender.HTMLString(string(tplName), data)

167
modules/markup/markdown.go Normal file
View File

@@ -0,0 +1,167 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package markup
import (
"bytes"
"fmt"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/russross/blackfriday"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/setting"
)
// IsMarkdownFile reports whether name looks like a Markdown file based on its extension.
func IsMarkdownFile(name string) bool {
extension := strings.ToLower(filepath.Ext(name))
for _, ext := range setting.Markdown.FileExtensions {
if strings.ToLower(ext) == extension {
return true
}
}
return false
}
// MarkdownRenderer is a extended version of underlying Markdown render object.
type MarkdownRenderer struct {
blackfriday.Renderer
urlPrefix string
}
var validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://|^mailto:`)
// isLink reports whether link fits valid format.
func isLink(link []byte) bool {
return validLinksPattern.Match(link)
}
// Link defines how formal links should be processed to produce corresponding HTML elements.
func (r *MarkdownRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
if len(link) > 0 && !isLink(link) {
if link[0] != '#' {
link = []byte(path.Join(r.urlPrefix, string(link)))
}
}
r.Renderer.Link(out, link, title, content)
}
// AutoLink defines how auto-detected links should be processed to produce corresponding HTML elements.
// Reference for kind: https://github.com/russross/blackfriday/blob/master/markdown.go#L69-L76
func (r *MarkdownRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
if kind != blackfriday.LINK_TYPE_NORMAL {
r.Renderer.AutoLink(out, link, kind)
return
}
// Since this method could only possibly serve one link at a time,
// we do not need to find all.
if bytes.HasPrefix(link, []byte(setting.AppUrl)) {
m := CommitPattern.Find(link)
if m != nil {
m = bytes.TrimSpace(m)
i := strings.Index(string(m), "commit/")
j := strings.Index(string(m), "#")
if j == -1 {
j = len(m)
}
out.WriteString(fmt.Sprintf(` <code><a href="%s">%s</a></code>`, m, base.ShortSha(string(m[i+7:j]))))
return
}
m = IssueFullPattern.Find(link)
if m != nil {
m = bytes.TrimSpace(m)
i := strings.Index(string(m), "issues/")
j := strings.Index(string(m), "#")
if j == -1 {
j = len(m)
}
index := string(m[i+7 : j])
fullRepoURL := setting.AppUrl + strings.TrimPrefix(r.urlPrefix, "/")
var link string
if strings.HasPrefix(string(m), fullRepoURL) {
// Use a short issue reference if the URL refers to this repository
link = fmt.Sprintf(`<a href="%s">#%s</a>`, m, index)
} else {
// Use a cross-repository issue reference if the URL refers to a different repository
repo := string(m[len(setting.AppUrl) : i-1])
link = fmt.Sprintf(`<a href="%s">%s#%s</a>`, m, repo, index)
}
out.WriteString(link)
return
}
}
r.Renderer.AutoLink(out, link, kind)
}
// ListItem defines how list items should be processed to produce corresponding HTML elements.
func (options *MarkdownRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
// Detect procedures to draw checkboxes.
switch {
case bytes.HasPrefix(text, []byte("[ ] ")):
text = append([]byte(`<input type="checkbox" disabled="" />`), text[3:]...)
case bytes.HasPrefix(text, []byte("[x] ")):
text = append([]byte(`<input type="checkbox" disabled="" checked="" />`), text[3:]...)
}
options.Renderer.ListItem(out, text, flags)
}
// RawMarkdown renders Markdown to HTML without handling special links.
func RawMarkdown(body []byte, urlPrefix string) []byte {
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_STYLE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
if setting.Smartypants.Enabled {
htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
if setting.Smartypants.Fractions {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
}
if setting.Smartypants.Dashes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_DASHES
}
if setting.Smartypants.LatexDashes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
}
if setting.Smartypants.AngledQuotes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES
}
}
renderer := &MarkdownRenderer{
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
urlPrefix: urlPrefix,
}
// set up the parser
extensions := 0
extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
extensions |= blackfriday.EXTENSION_TABLES
extensions |= blackfriday.EXTENSION_FENCED_CODE
extensions |= blackfriday.EXTENSION_AUTOLINK
extensions |= blackfriday.EXTENSION_STRIKETHROUGH
extensions |= blackfriday.EXTENSION_SPACE_HEADERS
extensions |= blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
if setting.Markdown.EnableHardLineBreak {
extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK
}
body = blackfriday.Markdown(body, renderer, extensions)
return body
}
// Markdown takes a string or []byte and renders to HTML in Markdown syntax with special links.
func Markdown(input interface{}, urlPrefix string, metas map[string]string) []byte {
return Render(MARKDOWN, input, urlPrefix, metas)
}

View File

@@ -0,0 +1,111 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package markup_test
import (
"bytes"
"strings"
"testing"
"github.com/russross/blackfriday"
. "github.com/smartystreets/goconvey/convey"
. "github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/setting"
)
func Test_IsMarkdownFile(t *testing.T) {
setting.Markdown.FileExtensions = strings.Split(".md,.markdown,.mdown,.mkd", ",")
Convey("Detect Markdown file extension", t, func() {
testCases := []struct {
ext string
match bool
}{
{".md", true},
{".markdown", true},
{".mdown", true},
{".mkd", true},
{".org", false},
{".rst", false},
{".asciidoc", false},
}
for _, tc := range testCases {
So(IsMarkdownFile(tc.ext), ShouldEqual, tc.match)
}
})
}
func Test_Markdown(t *testing.T) {
Convey("Rendering an issue URL", t, func() {
setting.AppUrl = "http://localhost:3000/"
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_STYLE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
renderer := &MarkdownRenderer{
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
}
buffer := new(bytes.Buffer)
Convey("To the internal issue tracker", func() {
Convey("It should render valid issue URLs", func() {
testCases := []string{
"http://localhost:3000/user/repo/issues/3333", "<a href=\"http://localhost:3000/user/repo/issues/3333\">#3333</a>",
}
for i := 0; i < len(testCases); i += 2 {
renderer.AutoLink(buffer, []byte(testCases[i]), blackfriday.LINK_TYPE_NORMAL)
line, _ := buffer.ReadString(0)
So(line, ShouldEqual, testCases[i+1])
}
})
Convey("It should render but not change non-issue URLs", func() {
testCases := []string{
"http://1111/2222/ssss-issues/3333?param=blah&blahh=333", "<a href=\"http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333\">http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333</a>",
"http://test.com/issues/33333", "<a href=\"http://test.com/issues/33333\">http://test.com/issues/33333</a>",
"http://test.com/issues/3", "<a href=\"http://test.com/issues/3\">http://test.com/issues/3</a>",
"http://issues/333", "<a href=\"http://issues/333\">http://issues/333</a>",
"https://issues/333", "<a href=\"https://issues/333\">https://issues/333</a>",
"http://tissues/0", "<a href=\"http://tissues/0\">http://tissues/0</a>",
}
for i := 0; i < len(testCases); i += 2 {
renderer.AutoLink(buffer, []byte(testCases[i]), blackfriday.LINK_TYPE_NORMAL)
line, _ := buffer.ReadString(0)
So(line, ShouldEqual, testCases[i+1])
}
})
})
})
Convey("Rendering a commit URL", t, func() {
setting.AppUrl = "http://localhost:3000/"
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_STYLE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
renderer := &MarkdownRenderer{
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
}
buffer := new(bytes.Buffer)
Convey("To the internal issue tracker", func() {
Convey("It should correctly convert URLs", func() {
testCases := []string{
"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae", " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">d8a994ef24</a></code>",
"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">d8a994ef24</a></code>",
"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", "<a href=\"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2</a>",
"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae", "<a href=\"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae</a>",
}
for i := 0; i < len(testCases); i += 2 {
renderer.AutoLink(buffer, []byte(testCases[i]), blackfriday.LINK_TYPE_NORMAL)
line, _ := buffer.ReadString(0)
So(line, ShouldEqual, testCases[i+1])
}
})
})
})
}

View File

@@ -1,79 +1,33 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2017 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package markdown
package markup
import (
"bytes"
"fmt"
"io"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/Unknwon/com"
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday"
"golang.org/x/net/html"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/setting"
)
// IsReadmeFile reports whether name looks like a README file based on its extension.
func IsReadmeFile(name string) bool {
return strings.HasPrefix(strings.ToLower(name), "readme")
}
const (
ISSUE_NAME_STYLE_NUMERIC = "numeric"
ISSUE_NAME_STYLE_ALPHANUMERIC = "alphanumeric"
)
var Sanitizer = bluemonday.UGCPolicy()
// BuildSanitizer initializes sanitizer with allowed attributes based on settings.
// This function should only be called once during entire application lifecycle.
func BuildSanitizer() {
// Normal markdown-stuff
Sanitizer.AllowAttrs("class").Matching(regexp.MustCompile(`[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*`)).OnElements("code")
// Checkboxes
Sanitizer.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
Sanitizer.AllowAttrs("checked", "disabled").OnElements("input")
// Custom URL-Schemes
Sanitizer.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
}
var validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://|^mailto:`)
// isLink reports whether link fits valid format.
func isLink(link []byte) bool {
return validLinksPattern.Match(link)
}
// IsMarkdownFile reports whether name looks like a Markdown file
// based on its extension.
func IsMarkdownFile(name string) bool {
extension := strings.ToLower(filepath.Ext(name))
for _, ext := range setting.Markdown.FileExtensions {
if strings.ToLower(ext) == extension {
return true
}
}
return false
}
// IsReadmeFile reports whether name looks like a README file
// based on its extension.
func IsReadmeFile(name string) bool {
name = strings.ToLower(name)
if len(name) < 6 {
return false
} else if len(name) == 6 {
return name == "readme"
}
return name[:7] == "readme."
}
var (
// MentionPattern matches string that mentions someone, e.g. @Unknwon
MentionPattern = regexp.MustCompile(`(\s|^|\W)@[0-9a-zA-Z-_\.]+`)
@@ -109,94 +63,6 @@ func FindAllMentions(content string) []string {
return mentions
}
// Renderer is a extended version of underlying render object.
type Renderer struct {
blackfriday.Renderer
urlPrefix string
}
// Link defines how formal links should be processed to produce corresponding HTML elements.
func (r *Renderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
if len(link) > 0 && !isLink(link) {
if link[0] != '#' {
link = []byte(path.Join(r.urlPrefix, string(link)))
}
}
r.Renderer.Link(out, link, title, content)
}
// AutoLink defines how auto-detected links should be processed to produce corresponding HTML elements.
// Reference for kind: https://github.com/russross/blackfriday/blob/master/markdown.go#L69-L76
func (r *Renderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
if kind != blackfriday.LINK_TYPE_NORMAL {
r.Renderer.AutoLink(out, link, kind)
return
}
// Since this method could only possibly serve one link at a time,
// we do not need to find all.
if bytes.HasPrefix(link, []byte(setting.AppUrl)) {
m := CommitPattern.Find(link)
if m != nil {
m = bytes.TrimSpace(m)
i := strings.Index(string(m), "commit/")
j := strings.Index(string(m), "#")
if j == -1 {
j = len(m)
}
out.WriteString(fmt.Sprintf(` <code><a href="%s">%s</a></code>`, m, base.ShortSha(string(m[i+7:j]))))
return
}
m = IssueFullPattern.Find(link)
if m != nil {
m = bytes.TrimSpace(m)
i := strings.Index(string(m), "issues/")
j := strings.Index(string(m), "#")
if j == -1 {
j = len(m)
}
index := string(m[i+7 : j])
fullRepoURL := setting.AppUrl + strings.TrimPrefix(r.urlPrefix, "/")
var link string
if strings.HasPrefix(string(m), fullRepoURL) {
// Use a short issue reference if the URL refers to this repository
link = fmt.Sprintf(`<a href="%s">#%s</a>`, m, index)
} else {
// Use a cross-repository issue reference if the URL refers to a different repository
repo := string(m[len(setting.AppUrl) : i-1])
link = fmt.Sprintf(`<a href="%s">%s#%s</a>`, m, repo, index)
}
out.WriteString(link)
return
}
}
r.Renderer.AutoLink(out, link, kind)
}
// ListItem defines how list items should be processed to produce corresponding HTML elements.
func (options *Renderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
// Detect procedures to draw checkboxes.
switch {
case bytes.HasPrefix(text, []byte("[ ] ")):
text = append([]byte(`<input type="checkbox" disabled="" />`), text[3:]...)
case bytes.HasPrefix(text, []byte("[x] ")):
text = append([]byte(`<input type="checkbox" disabled="" checked="" />`), text[3:]...)
}
options.Renderer.ListItem(out, text, flags)
}
// Note: this section is for purpose of increase performance and
// reduce memory allocation at runtime since they are constant literals.
var (
pound = []byte("#")
space = " "
spaceEncoded = "%20"
)
// cutoutVerbosePrefix cutouts URL prefix including sub-path to
// return a clean unified string of request URL path.
func cutoutVerbosePrefix(prefix string) string {
@@ -246,6 +112,10 @@ func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string
return rawBytes
}
// Note: this section is for purpose of increase performance and
// reduce memory allocation at runtime since they are constant literals.
var pound = []byte("#")
// RenderCrossReferenceIssueIndexPattern renders issue indexes from other repositories to corresponding links.
func RenderCrossReferenceIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
ms := CrossReferenceIssueNumericPattern.FindAll(rawBytes, -1)
@@ -289,51 +159,6 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]strin
return rawBytes
}
// RenderRaw renders Markdown to HTML without handling special links.
func RenderRaw(body []byte, urlPrefix string) []byte {
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_STYLE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
if setting.Smartypants.Enabled {
htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
if setting.Smartypants.Fractions {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
}
if setting.Smartypants.Dashes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_DASHES
}
if setting.Smartypants.LatexDashes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
}
if setting.Smartypants.AngledQuotes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES
}
}
renderer := &Renderer{
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
urlPrefix: urlPrefix,
}
// set up the parser
extensions := 0
extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
extensions |= blackfriday.EXTENSION_TABLES
extensions |= blackfriday.EXTENSION_FENCED_CODE
extensions |= blackfriday.EXTENSION_AUTOLINK
extensions |= blackfriday.EXTENSION_STRIKETHROUGH
extensions |= blackfriday.EXTENSION_SPACE_HEADERS
extensions |= blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
if setting.Markdown.EnableHardLineBreak {
extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK
}
body = blackfriday.Markdown(body, renderer, extensions)
return body
}
var (
leftAngleBracket = []byte("</")
rightAngleBracket = []byte(">")
@@ -343,8 +168,8 @@ var noEndTags = []string{"input", "br", "hr", "img"}
// wrapImgWithLink warps link to standalone <img> tags.
func wrapImgWithLink(urlPrefix string, buf *bytes.Buffer, token html.Token) {
var src, alt string
// Extract "src" and "alt" attributes
var src, alt string
for i := range token.Attr {
switch token.Attr[i].Key {
case "src":
@@ -399,9 +224,9 @@ func wrapImgWithLink(urlPrefix string, buf *bytes.Buffer, token html.Token) {
buf.WriteString(`</a>`)
}
// PostProcess treats different types of HTML differently,
// postProcessHTML treats different types of HTML differently,
// and only renders special links for plain text blocks.
func PostProcess(rawHTML []byte, urlPrefix string, metas map[string]string) []byte {
func postProcessHTML(rawHTML []byte, urlPrefix string, metas map[string]string) []byte {
startTags := make([]string, 0, 5)
buf := bytes.NewBuffer(nil)
tokenizer := html.NewTokenizer(bytes.NewReader(rawHTML))
@@ -475,16 +300,36 @@ OUTER_LOOP:
return rawHTML
}
// Render renders Markdown to HTML with special links.
func Render(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
urlPrefix = strings.Replace(urlPrefix, space, spaceEncoded, -1)
result := RenderRaw(rawBytes, urlPrefix)
result = PostProcess(result, urlPrefix, metas)
result = Sanitizer.SanitizeBytes(result)
return result
}
type Type string
// RenderString renders Markdown to HTML with special links and returns string type.
func RenderString(raw, urlPrefix string, metas map[string]string) string {
return string(Render([]byte(raw), urlPrefix, metas))
const (
UNRECOGNIZED Type = "unrecognized"
MARKDOWN Type = "markdown"
ORG_MODE Type = "orgmode"
)
// Render takes a string or []byte and renders to HTML in given type of syntax with special links.
func Render(typ Type, input interface{}, urlPrefix string, metas map[string]string) []byte {
var rawBytes []byte
switch v := input.(type) {
case []byte:
rawBytes = v
case string:
rawBytes = []byte(v)
default:
panic(fmt.Sprintf("unrecognized input content type: %T", input))
}
urlPrefix = strings.Replace(urlPrefix, " ", "%20", -1)
var rawHTML []byte
switch typ {
case MARKDOWN:
rawHTML = RawMarkdown(rawBytes, urlPrefix)
case ORG_MODE:
default:
return rawBytes // Do nothing if syntax type is not recognized
}
rawHTML = postProcessHTML(rawHTML, urlPrefix, metas)
return SanitizeBytes(rawHTML)
}

View File

@@ -1,17 +1,63 @@
package markdown_test
// Copyright 2017 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package markup_test
import (
. "github.com/gogits/gogs/modules/markdown"
. "github.com/smartystreets/goconvey/convey"
"strings"
"testing"
"bytes"
. "github.com/smartystreets/goconvey/convey"
. "github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/setting"
"github.com/russross/blackfriday"
)
func TestMarkdown(t *testing.T) {
Convey("Rendering an issue mention", t, func() {
func Test_IsReadmeFile(t *testing.T) {
Convey("Detect README file extension", t, func() {
testCases := []struct {
ext string
match bool
}{
{"readme", true},
{"README", true},
{"readme.md", true},
{"readme.markdown", true},
{"readme.mdown", true},
{"readme.mkd", true},
{"readme.org", true},
{"readme.rst", true},
{"readme.asciidoc", true},
{"readme_ZH", true},
}
for _, tc := range testCases {
So(IsReadmeFile(tc.ext), ShouldEqual, tc.match)
}
})
}
func Test_FindAllMentions(t *testing.T) {
Convey("Find all mention patterns", t, func() {
testCases := []struct {
content string
matches string
}{
{"@Unknwon, what do you think?", "Unknwon"},
{"@Unknwon what do you think?", "Unknwon"},
{"Hi @Unknwon, sounds good to me", "Unknwon"},
{"cc/ @Unknwon @User", "Unknwon,User"},
}
for _, tc := range testCases {
So(strings.Join(FindAllMentions(tc.content), ","), ShouldEqual, tc.matches)
}
})
}
func Test_RenderIssueIndexPattern(t *testing.T) {
Convey("Rendering an issue reference", t, func() {
var (
urlPrefix = "/prefix"
metas map[string]string = nil
@@ -235,74 +281,4 @@ func TestMarkdown(t *testing.T) {
})
})
})
Convey("Rendering an issue URL", t, func() {
setting.AppUrl = "http://localhost:3000/"
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_STYLE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
renderer := &Renderer{
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
}
buffer := new(bytes.Buffer)
Convey("To the internal issue tracker", func() {
Convey("It should render valid issue URLs", func() {
testCases := []string{
"http://localhost:3000/user/repo/issues/3333", "<a href=\"http://localhost:3000/user/repo/issues/3333\">#3333</a>",
}
for i := 0; i < len(testCases); i += 2 {
renderer.AutoLink(buffer, []byte(testCases[i]), blackfriday.LINK_TYPE_NORMAL)
line, _ := buffer.ReadString(0)
So(line, ShouldEqual, testCases[i+1])
}
})
Convey("It should render but not change non-issue URLs", func() {
testCases := []string{
"http://1111/2222/ssss-issues/3333?param=blah&blahh=333", "<a href=\"http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333\">http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333</a>",
"http://test.com/issues/33333", "<a href=\"http://test.com/issues/33333\">http://test.com/issues/33333</a>",
"http://test.com/issues/3", "<a href=\"http://test.com/issues/3\">http://test.com/issues/3</a>",
"http://issues/333", "<a href=\"http://issues/333\">http://issues/333</a>",
"https://issues/333", "<a href=\"https://issues/333\">https://issues/333</a>",
"http://tissues/0", "<a href=\"http://tissues/0\">http://tissues/0</a>",
}
for i := 0; i < len(testCases); i += 2 {
renderer.AutoLink(buffer, []byte(testCases[i]), blackfriday.LINK_TYPE_NORMAL)
line, _ := buffer.ReadString(0)
So(line, ShouldEqual, testCases[i+1])
}
})
})
})
Convey("Rendering a commit URL", t, func() {
setting.AppUrl = "http://localhost:3000/"
htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_STYLE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
renderer := &Renderer{
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
}
buffer := new(bytes.Buffer)
Convey("To the internal issue tracker", func() {
Convey("It should correctly convert URLs", func() {
testCases := []string{
"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae", " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">d8a994ef24</a></code>",
"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">d8a994ef24</a></code>",
"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", "<a href=\"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2</a>",
"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae", "<a href=\"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae</a>",
}
for i := 0; i < len(testCases); i += 2 {
renderer.AutoLink(buffer, []byte(testCases[i]), blackfriday.LINK_TYPE_NORMAL)
line, _ := buffer.ReadString(0)
So(line, ShouldEqual, testCases[i+1])
}
})
})
})
}

View File

@@ -0,0 +1,51 @@
// Copyright 2017 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package markup
import (
"regexp"
"sync"
"github.com/microcosm-cc/bluemonday"
"github.com/gogits/gogs/modules/setting"
)
// Sanitizer is a protection wrapper of *bluemonday.Policy which does not allow
// any modification to the underlying policies once it's been created.
type Sanitizer struct {
policy *bluemonday.Policy
init sync.Once
}
var sanitizer = &Sanitizer{}
// NewSanitizer initializes sanitizer with allowed attributes based on settings.
// Multiple calls to this function will only create one instance of Sanitizer during
// entire application lifecycle.
func NewSanitizer() {
sanitizer.init.Do(func() {
sanitizer.policy = bluemonday.UGCPolicy()
// We only want to allow HighlightJS specific classes for code blocks
sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^language-\w+$`)).OnElements("code")
// Checkboxes
sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
// Custom URL-Schemes
sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
})
}
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
func Sanitize(s string) string {
return sanitizer.policy.Sanitize(s)
}
// SanitizeBytes takes a []byte slice that contains a HTML fragment or document and applies policy whitelist.
func SanitizeBytes(b []byte) []byte {
return sanitizer.policy.SanitizeBytes(b)
}

View File

@@ -0,0 +1,38 @@
// Copyright 2017 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package markup_test
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
. "github.com/gogits/gogs/modules/markup"
)
func Test_Sanitizer(t *testing.T) {
NewSanitizer()
Convey("Sanitize HTML string and bytes", t, func() {
testCases := []string{
// Regular
`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, `<a href="http://www.google.com" rel="nofollow">Google</a>`,
// Code highlighting class
`<code class="random string"></code>`, `<code></code>`,
`<code class="language-random ui tab active menu attached animating sidebar following bar center"></code>`, `<code></code>`,
`<code class="language-go"></code>`, `<code class="language-go"></code>`,
// Input checkbox
`<input type="hidden">`, ``,
`<input type="checkbox">`, `<input type="checkbox">`,
`<input checked disabled autofocus>`, `<input checked="" disabled="">`,
}
for i := 0; i < len(testCases); i += 2 {
So(Sanitize(testCases[i]), ShouldEqual, testCases[i+1])
So(string(SanitizeBytes([]byte(testCases[i]))), ShouldEqual, testCases[i+1])
}
})
}

View File

@@ -773,7 +773,7 @@ func newSessionService() {
SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("i_like_gogits")
SessionConfig.CookiePath = AppSubUrl
SessionConfig.Secure = Cfg.Section("session").Key("COOKIE_SECURE").MustBool()
SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(86400)
SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(3600)
SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
CSRFCookieName = Cfg.Section("session").Key("CSRF_COOKIE_NAME").MustString("_csrf")

View File

@@ -23,7 +23,7 @@ import (
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/setting"
)
@@ -125,7 +125,7 @@ func Safe(raw string) template.HTML {
}
func Str2html(raw string) template.HTML {
return template.HTML(markdown.Sanitizer.Sanitize(raw))
return template.HTML(markup.Sanitize(raw))
}
func List(l *list.List) chan interface{} {
@@ -201,7 +201,7 @@ func ReplaceLeft(s, old, new string) string {
// RenderCommitMessage renders commit message with XSS-safe and special links.
func RenderCommitMessage(full bool, msg, urlPrefix string, metas map[string]string) template.HTML {
cleanMsg := template.HTMLEscapeString(msg)
fullMessage := string(markdown.RenderIssueIndexPattern([]byte(cleanMsg), urlPrefix, metas))
fullMessage := string(markup.RenderIssueIndexPattern([]byte(cleanMsg), urlPrefix, metas))
msgLines := strings.Split(strings.TrimSpace(fullMessage), "\n")
numLines := len(msgLines)
if numLines == 0 {

View File

@@ -1,6 +1,6 @@
{
"CodeKitInfo": "This is a CodeKit 2.x project configuration file. It is designed to sync project settings across multiple machines. MODIFYING THE CONTENTS OF THIS FILE IS A POOR LIFE DECISION. If you do so, you will likely cause CodeKit to crash. This file is not useful unless accompanied by the project that created it in CodeKit 2. This file is not backwards-compatible with CodeKit 1.x. For more information, see: http:\/\/incident57.com\/codekit",
"creatorBuild": "19127",
"creatorBuild": "19115",
"files": {
"\/css\/github.min.css": {
"fileType": 16,
@@ -66,7 +66,7 @@
"fileType": 32768,
"ignore": 0,
"ignoreWasSetByUser": 0,
"initialSize": 514087,
"initialSize": 4048,
"inputAbbreviatedPath": "\/img\/avatar_default.png",
"outputAbbreviatedPath": "\/img\/avatar_default.png",
"outputPathIsOutsideProject": 0,

View File

@@ -5,10 +5,20 @@
background-size: contain;
}
body {
font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
font-family: "PingFang SC", "Helvetica Neue", "Microsoft YaHei", SimSun, Arial, Helvetica, sans-serif !important;
background-color: #fff;
overflow-y: scroll;
}
h1,
h2,
h3,
h4,
h5,
.ui.header,
.ui.input input,
.ui.button {
font-family: "PingFang SC", 'Hiragino Sans GB', "Helvetica Neue", "Microsoft YaHei", SimSun, Arial, Helvetica, sans-serif !important;
}
img {
border-radius: 3px;
}
@@ -1442,7 +1452,8 @@ footer .ui.language .menu {
}
.repository.file.list #file-content .plain-text {
font-size: 14px;
padding: 10px 15px;
padding: 15px 15px 10px 15px;
font-family: Consolas;
}
.repository.file.list #file-content .code-view * {
font-size: 12px;

View File

@@ -341,7 +341,7 @@ function initRepository() {
if ($('.repository.view.issue').length > 0) {
// Edit issue title
var $issueTitle = $('#issue-title');
var $editInput = $('#edit-title-input input');
var $editInput = $('#edit-title-input').find('input');
var editTitleToggle = function () {
$issueTitle.toggle();
$('.not-in-edit').toggle();

View File

@@ -1,10 +1,16 @@
@footer-margin: 40px;
body {
font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
font-family: "PingFang SC", "Helvetica Neue", "Microsoft YaHei", SimSun, Arial, Helvetica, sans-serif !important;
background-color: #fff;
overflow-y: scroll;
}
h1, h2, h3, h4, h5,
.ui.header,
.ui.input input,
.ui.button {
font-family: "PingFang SC", 'Hiragino Sans GB', "Helvetica Neue", "Microsoft YaHei", SimSun, Arial, Helvetica, sans-serif !important;
}
img {
border-radius: 3px;
}

View File

@@ -403,7 +403,8 @@
.plain-text {
font-size: 14px;
padding: 10px 15px;
padding: 15px 15px 10px 15px;
font-family: Consolas;
}
.code-view {
* {

View File

@@ -8,7 +8,7 @@ import (
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
)
// https://github.com/gogits/go-gogs-client/wiki/Miscellaneous#render-an-arbitrary-markdown-document
@@ -25,9 +25,9 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
switch form.Mode {
case "gfm":
ctx.Write(markdown.Render([]byte(form.Text), form.Context, nil))
ctx.Write(markup.Markdown([]byte(form.Text), form.Context, nil))
default:
ctx.Write(markdown.RenderRaw([]byte(form.Text), ""))
ctx.Write(markup.RawMarkdown([]byte(form.Text), ""))
}
}
@@ -38,5 +38,5 @@ func MarkdownRaw(ctx *context.APIContext) {
ctx.Error(422, "", err)
return
}
ctx.Write(markdown.RenderRaw(body, ""))
ctx.Write(markup.RawMarkdown(body, ""))
}

View File

@@ -26,7 +26,7 @@ import (
"github.com/gogits/gogs/modules/cron"
"github.com/gogits/gogs/modules/form"
"github.com/gogits/gogs/modules/mailer"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/ssh"
"github.com/gogits/gogs/modules/template/highlight"
@@ -62,7 +62,7 @@ func GlobalInit() {
if setting.InstallLock {
highlight.NewContext()
markdown.BuildSanitizer()
markup.NewSanitizer()
if err := models.NewEngine(); err != nil {
log.Fatal(2, "Fail to initialize ORM engine: %v", err)
}

View File

@@ -22,7 +22,7 @@ import (
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/form"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/setting"
)
@@ -541,8 +541,7 @@ func viewIssue(ctx *context.Context, isPullList bool) {
ctx.Data["PageIsIssueList"] = true
}
issue.RenderedContent = string(markdown.Render([]byte(issue.Content), ctx.Repo.RepoLink,
ctx.Repo.Repository.ComposeMetas()))
issue.RenderedContent = string(markup.Markdown(issue.Content, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()))
repo := ctx.Repo.Repository
@@ -608,8 +607,7 @@ func viewIssue(ctx *context.Context, isPullList bool) {
participants[0] = issue.Poster
for _, comment = range issue.Comments {
if comment.Type == models.COMMENT_TYPE_COMMENT {
comment.RenderedContent = string(markdown.Render([]byte(comment.Content), ctx.Repo.RepoLink,
ctx.Repo.Repository.ComposeMetas()))
comment.RenderedContent = string(markup.Markdown(comment.Content, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()))
// Check tag.
tag, ok = marked[comment.PosterID]
@@ -727,8 +725,8 @@ func UpdateIssueContent(ctx *context.Context) {
return
}
ctx.JSON(200, map[string]interface{}{
"content": string(markdown.Render([]byte(issue.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
ctx.JSON(200, map[string]string{
"content": string(markup.Markdown(issue.Content, ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
})
}
@@ -938,8 +936,8 @@ func UpdateCommentContent(ctx *context.Context) {
return
}
ctx.JSON(200, map[string]interface{}{
"content": string(markdown.Render([]byte(comment.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
ctx.JSON(200, map[string]string{
"content": string(markup.Markdown(comment.Content, ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
})
}
@@ -1092,7 +1090,7 @@ func Milestones(ctx *context.Context) {
if m.NumOpenIssues+m.NumClosedIssues > 0 {
m.Completeness = m.NumClosedIssues * 100 / (m.NumOpenIssues + m.NumClosedIssues)
}
m.RenderedContent = string(markdown.Render([]byte(m.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()))
m.RenderedContent = string(markup.Markdown(m.Content, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()))
}
ctx.Data["Milestones"] = miles

View File

@@ -14,7 +14,7 @@ import (
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/form"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/setting"
)
@@ -83,7 +83,7 @@ func Releases(ctx *context.Context) {
return
}
r.Note = markdown.RenderString(r.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())
r.Note = string(markup.Markdown(r.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()))
results[i] = r
break
}
@@ -132,7 +132,7 @@ func Releases(ctx *context.Context) {
return
}
r.Note = markdown.RenderString(r.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())
r.Note = string(markup.Markdown(r.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()))
}
if len(drafts) > 0 {

View File

@@ -404,6 +404,10 @@ func UpdateDefaultBranch(ctx *context.Context) {
ctx.Handle(500, "SetDefaultBranch", err)
return
}
ctx.Flash.Warning(ctx.Tr("repo.settings.update_default_branch_unsupported"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings/branches")
return
}
}

View File

@@ -20,7 +20,7 @@ import (
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/template"
"github.com/gogits/gogs/modules/template/highlight"
@@ -55,7 +55,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
var readmeFile *git.Blob
for _, entry := range entries {
if entry.IsDir() || !markdown.IsReadmeFile(entry.Name()) {
if entry.IsDir() || !markup.IsReadmeFile(entry.Name()) {
continue
}
@@ -86,9 +86,9 @@ func renderDirectory(ctx *context.Context, treeLink string) {
d, _ := ioutil.ReadAll(dataRc)
buf = append(buf, d...)
switch {
case markdown.IsMarkdownFile(readmeFile.Name()):
case markup.IsMarkdownFile(readmeFile.Name()):
ctx.Data["IsMarkdown"] = true
buf = markdown.Render(buf, treeLink, ctx.Repo.Repository.ComposeMetas())
buf = markup.Markdown(buf, treeLink, ctx.Repo.Repository.ComposeMetas())
default:
buf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1)
}
@@ -153,14 +153,14 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
d, _ := ioutil.ReadAll(dataRc)
buf = append(buf, d...)
isMarkdown := markdown.IsMarkdownFile(blob.Name())
isMarkdown := markup.IsMarkdownFile(blob.Name())
ctx.Data["IsMarkdown"] = isMarkdown
ctx.Data["ReadmeExist"] = isMarkdown && markdown.IsReadmeFile(blob.Name())
ctx.Data["ReadmeExist"] = isMarkdown && markup.IsReadmeFile(blob.Name())
ctx.Data["IsIPythonNotebook"] = strings.HasSuffix(blob.Name(), ".ipynb")
if isMarkdown {
ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
ctx.Data["FileContent"] = string(markup.Markdown(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
} else {
// Building code view blocks with line number on server side.
var fileContent string

View File

@@ -15,7 +15,7 @@ import (
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/form"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/markup"
)
const (
@@ -107,7 +107,7 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, str
return nil, ""
}
if isViewPage {
ctx.Data["content"] = string(markdown.Render(data, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()))
ctx.Data["content"] = string(markup.Markdown(data, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()))
} else {
ctx.Data["content"] = string(data)
}

View File

@@ -154,9 +154,6 @@ func SignInPost(ctx *context.Context, f form.SignIn) {
func SignOut(ctx *context.Context) {
ctx.Session.Delete("uid")
ctx.Session.Delete("uname")
ctx.Session.Delete("socialId")
ctx.Session.Delete("socialName")
ctx.Session.Delete("socialEmail")
ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl)
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl)
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubUrl)

View File

@@ -124,8 +124,9 @@ func Dashboard(ctx *context.Context) {
var err error
var repos, mirrors []*models.Repository
var repoCount int64
if ctxUser.IsOrganization() {
repos, _, err = ctxUser.GetUserRepositories(ctx.User.ID, 1, setting.UI.User.RepoPagingNum)
repos, repoCount, err = ctxUser.GetUserRepositories(ctx.User.ID, 1, setting.UI.User.RepoPagingNum)
if err != nil {
ctx.Handle(500, "GetUserRepositories", err)
return
@@ -142,6 +143,7 @@ func Dashboard(ctx *context.Context) {
return
}
repos = ctxUser.Repos
repoCount = int64(ctxUser.NumRepos)
mirrors, err = ctxUser.GetMirrorRepositories()
if err != nil {
@@ -150,6 +152,7 @@ func Dashboard(ctx *context.Context) {
}
}
ctx.Data["Repos"] = repos
ctx.Data["RepoCount"] = repoCount
ctx.Data["MaxShowRepoNum"] = setting.UI.User.RepoPagingNum
if err := models.MirrorRepositoryList(mirrors).LoadAttributes(); err != nil {

View File

@@ -36,10 +36,16 @@ const (
SECURITY base.TplName = "user/security"
)
func Settings(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsProfile"] = true
ctx.HTML(200, SETTINGS_PROFILE)
func Settings(c *context.Context) {
c.Data["Title"] = c.Tr("settings")
c.Data["PageIsSettingsProfile"] = true
c.Data["origin_name"] = c.User.Name
c.Data["name"] = c.User.Name
c.Data["full_name"] = c.User.FullName
c.Data["email"] = c.User.Email
c.Data["website"] = c.User.Website
c.Data["location"] = c.User.Location
c.Success(SETTINGS_PROFILE)
}
func handleUsernameChange(ctx *context.Context, newName string) {
@@ -80,6 +86,7 @@ func handleUsernameChange(ctx *context.Context, newName string) {
func SettingsPost(ctx *context.Context, f form.UpdateProfile) {
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsProfile"] = true
ctx.Data["origin_name"] = ctx.User.Name
if ctx.HasError() {
ctx.HTML(200, SETTINGS_PROFILE)

View File

@@ -1 +1 @@
0.10.31.0327 / 0.11 RC
0.11.0.0403

View File

@@ -3,6 +3,11 @@
<p>{{.Flash.ErrorMsg | Str2html}}</p>
</div>
{{end}}
{{if .Flash.WarningMsg}}
<div class="ui warning message">
<p>{{.Flash.WarningMsg | Str2html}}</p>
</div>
{{end}}
{{if .Flash.SuccessMsg}}
<div class="ui positive message">
<p>{{.Flash.SuccessMsg | Str2html}}</p>

View File

@@ -36,14 +36,14 @@
</div>
<div class="ui five wide column">
<h4 class="ui top attached header">
<div class="ui top attached header">
<strong>{{.i18n.Tr "org.people"}}</strong>
{{if .IsOrganizationMember}}
<div class="ui right">
<a class="text grey" href="{{.OrgLink}}/members">{{.Org.NumMembers}} <span class="octicon octicon-chevron-right"></span></a>
</div>
{{end}}
</h4>
</div>
<div class="ui attached segment members">
{{$isMember := .IsOrganizationMember}}
{{range .Members}}

View File

@@ -1,7 +1,7 @@
<div id="file-content" class="{{TabSizeClass .Editorconfig .FileName}}">
<h4 class="ui top attached header" id="{{if .ReadmeExist}}repo-readme{{else}}repo-read-file{{end}}">
{{if .ReadmeExist}}
<i class="book icon ui left"></i>
<i class="octicon octicon-book"></i>
{{if .ReadmeInList}}
<strong>{{.FileName}}</strong>
{{else}}

View File

@@ -5,7 +5,7 @@
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{if .IsSocialLogin}}{{.i18n.Tr "social_sign_in" | Str2html}}{{else}}{{.i18n.Tr "sign_up"}}{{end}}
{{.i18n.Tr "sign_up"}}
</h3>
<div class="ui attached segment">
{{template "base/alert" .}}
@@ -45,7 +45,7 @@
</div>
<div class="inline field">
<label></label>
<a href="{{AppSubUrl}}/user/login">{{if .IsSocialLogin}}{{.i18n.Tr "auth.social_register_hepler_msg"}}{{else}}{{.i18n.Tr "auth.register_hepler_msg"}}{{end}}</a>
<a href="{{AppSubUrl}}/user/login">{{.i18n.Tr "auth.register_hepler_msg"}}</a>
</div>
{{end}}
</div>

View File

@@ -18,15 +18,15 @@
<a class="item" data-tab="mirrors">{{.i18n.Tr "mirror"}}</a>
</div>
<div class="ui tab active list" data-tab="repos">
<h4 class="ui top attached header">
{{.i18n.Tr "home.my_repos"}} <span class="ui grey label">{{.ContextUser.NumRepos}}</span>
<div class="ui top attached header">
{{.i18n.Tr "home.my_repos"}} <span class="ui grey label">{{.RepoCount}}</span>
<div class="ui right">
<a class="poping up" href="{{AppSubUrl}}/repo/create{{if .ContextUser.IsOrganization}}?org={{.ContextUser.ID}}{{end}}" data-content="{{.i18n.Tr "new_repo"}}" data-variation="tiny inverted" data-position="left center">
<i class="plus icon"></i>
<span class="sr-only">{{.i18n.Tr "new_repo"}}</span>
</a>
</div>
</h4>
</div>
<div class="ui attached table segment">
<ul class="repo-owner-name-list">
{{range .Repos}}
@@ -74,7 +74,7 @@
{{if not .ContextUser.IsOrganization}}
<div class="ui tab list" data-tab="orgs">
<h4 class="ui top attached header">
<div class="ui top attached header">
{{.i18n.Tr "home.my_orgs"}} <span class="ui grey label">{{.ContextUser.GetOrganizationCount}}</span>
<div class="ui right">
<a class="poping up" href="{{AppSubUrl}}/org/create" data-content="{{.i18n.Tr "new_org"}}" data-variation="tiny inverted" data-position="left center">
@@ -82,7 +82,7 @@
<span class="sr-only">{{.i18n.Tr "new_org"}}</span>
</a>
</div>
</h4>
</div>
<div class="ui attached table segment">
<ul class="repo-owner-name-list">
{{range .ContextUser.Orgs}}
@@ -102,7 +102,7 @@
{{end}}
<div class="ui tab list" data-tab="mirrors">
<h4 class="ui top attached header">
<div class="ui top attached header">
{{.i18n.Tr "home.my_mirrors"}} <span class="ui grey label">{{.MirrorCount}}</span>
<div class="ui right">
<a class="poping up" href="{{AppSubUrl}}/repo/migrate?mirror=1" data-content="{{.i18n.Tr "new_mirror"}}" data-variation="tiny inverted" data-position="left center">
@@ -110,7 +110,7 @@
<span class="sr-only">{{.i18n.Tr "new_mirror"}}</span>
</a>
</div>
</h4>
</div>
<div class="ui attached table segment">
<ul class="repo-owner-name-list">
{{range .Mirrors}}

View File

@@ -13,27 +13,27 @@
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_Name}}error{{end}}">
<label for="username">{{.i18n.Tr "username"}}<span class="text red hide" id="name-change-prompt"> {{.i18n.Tr "settings.change_username_prompt"}}</span></label>
<input id="username" name="name" value="{{.SignedUser.Name}}" data-name="{{.SignedUser.Name}}" autofocus required {{if not .SignedUser.IsLocal}}readonly{{end}}>
<label for="username">{{.i18n.Tr "username"}}<span class="text red {{if eq .name .origin_name}}hide{{end}}" id="name-change-prompt"> {{.i18n.Tr "settings.change_username_prompt"}}</span></label>
<input id="username" name="name" value="{{.name}}" data-name="{{.origin_name}}" autofocus required {{if not .SignedUser.IsLocal}}readonly{{end}}>
{{if not .SignedUser.IsLocal}}
<p class="help text blue">{{$.i18n.Tr "settings.password_username_disabled"}}</p>
{{end}}
</div>
<div class="field {{if .Err_FullName}}error{{end}}">
<label for="full_name">{{.i18n.Tr "settings.full_name"}}</label>
<input id="full_name" name="full_name" value="{{.SignedUser.FullName}}">
<input id="full_name" name="full_name" value="{{.full_name}}">
</div>
<div class="required field {{if .Err_Email}}error{{end}}">
<label for="email">{{.i18n.Tr "email"}}</label>
<input id="email" name="email" value="{{.SignedUser.Email}}" required>
<input id="email" name="email" value="{{.email}}" required>
</div>
<div class="field {{if .Err_Website}}error{{end}}">
<label for="website">{{.i18n.Tr "settings.website"}}</label>
<input id="website" name="website" type="url" value="{{.SignedUser.Website}}">
<input id="website" name="website" type="url" value="{{.website}}">
</div>
<div class="field">
<label for="location">{{.i18n.Tr "settings.location"}}</label>
<input id="location" name="location" value="{{.SignedUser.Location}}">
<input id="location" name="location" value="{{.location}}">
</div>
<div class="field">

View File

@@ -68,7 +68,7 @@ func (c *Captcha) CreateHtml() template.HTML {
panic(fmt.Errorf("fail to create captcha: %v", err))
}
return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">
<a class="captcha" href="javascript:">
<a class="captcha" href="javascript:" tabindex="-1">
<img onclick="this.src=('%s%s%s.png?reload='+(new Date()).getTime())" class="captcha-img" src="%s%s%s.png">
</a>`, c.FieldIdName, value, c.SubURL, c.URLPrefix, value, c.SubURL, c.URLPrefix, value))
}

View File

@@ -5,7 +5,7 @@ Package git-module is a Go module for Git access through shell commands.
## Limitations
- Go version must be at least **1.4**.
- Git version must be no less than **1.7.1**, and greater than or equal to **1.8.0** is recommended.
- Git version must be no less than **1.7.1**, and greater than or equal to **1.8.3** is recommended.
- For Windows users, try use as new a version as possible.
## License

View File

@@ -262,7 +262,7 @@ func GetRepoSize(repoPath string) (*CountObject, error) {
case strings.HasPrefix(line, _STAT_SIZE):
countObject.Size = com.StrTo(line[6:]).MustInt64() * 1024
case strings.HasPrefix(line, _STAT_IN_PACK):
countObject.InPack = com.StrTo(line[9:]).MustInt64() * 1024
countObject.InPack = com.StrTo(line[9:]).MustInt64()
case strings.HasPrefix(line, _STAT_PACKS):
countObject.Packs = com.StrTo(line[7:]).MustInt64()
case strings.HasPrefix(line, _STAT_SIZE_PACK):

12
vendor/vendor.json vendored
View File

@@ -75,10 +75,10 @@
"revisionTime": "2015-10-13T08:11:02Z"
},
{
"checksumSHA1": "ifjG+PNGY7+sNm3WoA3UntmUTa4=",
"checksumSHA1": "kWnClaQsy/2YLvx2SrSUGgSyhzk=",
"path": "github.com/go-macaron/captcha",
"revision": "8aa5919789ab301e865595eb4b1114d6b9847deb",
"revisionTime": "2015-11-23T22:51:53Z"
"revision": "a9d45b762e2537878323cd8a4147644378f09be1",
"revisionTime": "2017-03-28T22:26:02Z"
},
{
"checksumSHA1": "i52HJHpyI3avut3QAIrrD0h7gmY=",
@@ -159,10 +159,10 @@
"revisionTime": "2016-08-10T03:50:02Z"
},
{
"checksumSHA1": "0BVytmK2I0QObcFqVyVWWU544DA=",
"checksumSHA1": "WVDLV+YcA77fg1kgddSinycf3Do=",
"path": "github.com/gogits/git-module",
"revision": "e0ab95e3e61573e88bb808ae8052fd669324d80f",
"revisionTime": "2017-03-17T02:27:16Z"
"revision": "f4026b57acf35d799d5dec95066f3a10c572604e",
"revisionTime": "2017-04-03T19:07:43Z"
},
{
"checksumSHA1": "D2kVXl0QpIw6t3891Sl7IM9wL+w=",