diff --git a/build/generate-bindata.go b/build/generate-bindata.go index 477139d67bde..7941af60a162 100644 --- a/build/generate-bindata.go +++ b/build/generate-bindata.go @@ -58,11 +58,15 @@ func needsUpdate(dir string, filename string) (bool, []byte) { } func main() { - if len(os.Args) != 4 { + if len(os.Args) < 4 { log.Fatal("Insufficient number of arguments. Need: directory packageName filename") } dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3] + var useGlobalModTime bool + if len(os.Args) == 5 { + useGlobalModTime, _ = strconv.ParseBool(os.Args[4]) + } update, newHash := needsUpdate(dir, filename) @@ -74,10 +78,11 @@ func main() { fmt.Printf("generating bindata for %s\n", packageName) var fsTemplates http.FileSystem = http.Dir(dir) err := vfsgen.Generate(fsTemplates, vfsgen.Options{ - PackageName: packageName, - BuildTags: "bindata", - VariableName: "Assets", - Filename: filename, + PackageName: packageName, + BuildTags: "bindata", + VariableName: "Assets", + Filename: filename, + UseGlobalModTime: useGlobalModTime, }) if err != nil { log.Fatalf("%v\n", err) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index a58e52cb4163..f8236d4c935d 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -788,7 +788,7 @@ PATH = ;PULL_REQUEST_QUEUE_LENGTH = 1000 ;; ;; Preferred Licenses to place at the top of the List -;; The name here must match the filename in conf/license or custom/conf/license +;; The name here must match the filename in options/license or custom/options/license ;PREFERRED_LICENSES = Apache License 2.0,MIT License ;; ;; Disable the ability to interact with repositories using the HTTP protocol diff --git a/go.mod b/go.mod index 27e89fae3c17..96d05a22dc2d 100644 --- a/go.mod +++ b/go.mod @@ -141,3 +141,5 @@ require ( replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/golang-jwt/jwt v3.2.1+incompatible => github.com/golang-jwt/jwt v3.2.2+incompatible + +replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 diff --git a/go.sum b/go.sum index fd1105627e58..16a0fd3cc571 100644 --- a/go.sum +++ b/go.sum @@ -802,6 +802,8 @@ github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7 github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ= github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ= github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0= +github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI= +github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0/go.mod h1:JEfTc3+2DF9Z4PXhLLvXL42zexJyh8rIq3OzUj/0rAk= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -1044,8 +1046,6 @@ github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU= -github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY= github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= diff --git a/modules/charset/escape.go b/modules/charset/escape.go new file mode 100644 index 000000000000..abe813b465a9 --- /dev/null +++ b/modules/charset/escape.go @@ -0,0 +1,230 @@ +// Copyright 2021 The Gitea 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 charset + +import ( + "bytes" + "fmt" + "io" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/text/unicode/bidi" +) + +// EscapeStatus represents the findings of the unicode escaper +type EscapeStatus struct { + Escaped bool + HasError bool + HasBadRunes bool + HasControls bool + HasSpaces bool + HasMarks bool + HasBIDI bool + BadBIDI bool + HasRTLScript bool + HasLTRScript bool +} + +// Or combines two EscapeStatus structs into one representing the conjunction of the two +func (status EscapeStatus) Or(other EscapeStatus) EscapeStatus { + st := status + st.Escaped = st.Escaped || other.Escaped + st.HasError = st.HasError || other.HasError + st.HasBadRunes = st.HasBadRunes || other.HasBadRunes + st.HasControls = st.HasControls || other.HasControls + st.HasSpaces = st.HasSpaces || other.HasSpaces + st.HasMarks = st.HasMarks || other.HasMarks + st.HasBIDI = st.HasBIDI || other.HasBIDI + st.BadBIDI = st.BadBIDI || other.BadBIDI + st.HasRTLScript = st.HasRTLScript || other.HasRTLScript + st.HasLTRScript = st.HasLTRScript || other.HasLTRScript + return st +} + +// EscapeControlString escapes the unicode control sequences in a provided string and returns the findings as an EscapeStatus and the escaped string +func EscapeControlString(text string) (EscapeStatus, string) { + sb := &strings.Builder{} + escaped, _ := EscapeControlReader(strings.NewReader(text), sb) + return escaped, sb.String() +} + +// EscapeControlBytes escapes the unicode control sequences a provided []byte and returns the findings as an EscapeStatus and the escaped []byte +func EscapeControlBytes(text []byte) (EscapeStatus, []byte) { + buf := &bytes.Buffer{} + escaped, _ := EscapeControlReader(bytes.NewReader(text), buf) + return escaped, buf.Bytes() +} + +// EscapeControlReader escapes the unicode control sequences a provided Reader writing the escaped output to the output and returns the findings as an EscapeStatus and an error +func EscapeControlReader(text io.Reader, output io.Writer) (escaped EscapeStatus, err error) { + buf := make([]byte, 4096) + readStart := 0 + var n int + var writePos int + + lineHasBIDI := false + lineHasRTLScript := false + lineHasLTRScript := false + +readingloop: + for err == nil { + n, err = text.Read(buf[readStart:]) + bs := buf[:n+readStart] + i := 0 + + for i < len(bs) { + r, size := utf8.DecodeRune(bs[i:]) + // Now handle the codepoints + switch { + case r == utf8.RuneError: + if writePos < i { + if _, err = output.Write(bs[writePos:i]); err != nil { + escaped.HasError = true + return + } + writePos = i + } + // runes can be at most 4 bytes - so... + if len(bs)-i <= 3 { + // if not request more data + copy(buf, bs[i:]) + readStart = n - i + writePos = 0 + continue readingloop + } + // this is a real broken rune + escaped.HasBadRunes = true + escaped.Escaped = true + if err = writeBroken(output, bs[i:i+size]); err != nil { + escaped.HasError = true + return + } + writePos += size + case r == '\n': + if lineHasBIDI && !lineHasRTLScript && lineHasLTRScript { + escaped.BadBIDI = true + } + lineHasBIDI = false + lineHasRTLScript = false + lineHasLTRScript = false + + case r == '\r' || r == '\t' || r == ' ': + // These are acceptable control characters and space characters + case unicode.IsSpace(r): + escaped.HasSpaces = true + escaped.Escaped = true + if writePos < i { + if _, err = output.Write(bs[writePos:i]); err != nil { + escaped.HasError = true + return + } + } + if err = writeEscaped(output, r); err != nil { + escaped.HasError = true + return + } + writePos = i + size + case unicode.Is(unicode.Bidi_Control, r): + escaped.Escaped = true + escaped.HasBIDI = true + if writePos < i { + if _, err = output.Write(bs[writePos:i]); err != nil { + escaped.HasError = true + return + } + } + lineHasBIDI = true + if err = writeEscaped(output, r); err != nil { + escaped.HasError = true + return + } + writePos = i + size + case unicode.Is(unicode.C, r): + escaped.Escaped = true + escaped.HasControls = true + if writePos < i { + if _, err = output.Write(bs[writePos:i]); err != nil { + escaped.HasError = true + return + } + } + if err = writeEscaped(output, r); err != nil { + escaped.HasError = true + return + } + writePos = i + size + case unicode.Is(unicode.M, r): + escaped.Escaped = true + escaped.HasMarks = true + if writePos < i { + if _, err = output.Write(bs[writePos:i]); err != nil { + escaped.HasError = true + return + } + } + if err = writeEscaped(output, r); err != nil { + escaped.HasError = true + return + } + writePos = i + size + default: + p, _ := bidi.Lookup(bs[i : i+size]) + c := p.Class() + if c == bidi.R || c == bidi.AL { + lineHasRTLScript = true + escaped.HasRTLScript = true + } else if c == bidi.L { + lineHasLTRScript = true + escaped.HasLTRScript = true + } + } + i += size + } + if n > 0 { + // we read something... + // write everything unwritten + if writePos < i { + if _, err = output.Write(bs[writePos:i]); err != nil { + escaped.HasError = true + return + } + } + + // reset the starting positions for the next read + readStart = 0 + writePos = 0 + } + } + if readStart > 0 { + // this means that there is an incomplete or broken rune at 0-readStart and we read nothing on the last go round + escaped.Escaped = true + escaped.HasBadRunes = true + if err = writeBroken(output, buf[:readStart]); err != nil { + escaped.HasError = true + return + } + } + if err == io.EOF { + if lineHasBIDI && !lineHasRTLScript && lineHasLTRScript { + escaped.BadBIDI = true + } + err = nil + return + } + escaped.HasError = true + return +} + +func writeBroken(output io.Writer, bs []byte) (err error) { + _, err = fmt.Fprintf(output, `<%X>`, bs) + return +} + +func writeEscaped(output io.Writer, r rune) (err error) { + _, err = fmt.Fprintf(output, `%c`, r, r) + return +} diff --git a/modules/charset/escape_test.go b/modules/charset/escape_test.go new file mode 100644 index 000000000000..dec92b499299 --- /dev/null +++ b/modules/charset/escape_test.go @@ -0,0 +1,202 @@ +// Copyright 2021 The Gitea 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 charset + +import ( + "reflect" + "strings" + "testing" +) + +type escapeControlTest struct { + name string + text string + status EscapeStatus + result string +} + +var escapeControlTests = []escapeControlTest{ + { + name: "", + }, + { + name: "single line western", + text: "single line western", + result: "single line western", + status: EscapeStatus{HasLTRScript: true}, + }, + { + name: "multi line western", + text: "single line western\nmulti line western\n", + result: "single line western\nmulti line western\n", + status: EscapeStatus{HasLTRScript: true}, + }, + { + name: "multi line western non-breaking space", + text: "single line western\nmulti line western\n", + result: `single line western` + "\n" + `multi line western` + "\n", + status: EscapeStatus{Escaped: true, HasLTRScript: true, HasSpaces: true}, + }, + { + name: "mixed scripts: western + japanese", + text: "日属秘ぞしちゅ。Then some western.", + result: "日属秘ぞしちゅ。Then some western.", + status: EscapeStatus{HasLTRScript: true}, + }, + { + name: "japanese", + text: "日属秘ぞしちゅ。", + result: "日属秘ぞしちゅ。", + status: EscapeStatus{HasLTRScript: true}, + }, + { + name: "hebrew", + text: "עד תקופת יוון העתיקה היה העיסוק במתמטיקה תכליתי בלבד: היא שימשה כאוסף של נוסחאות לחישוב קרקע, אוכלוסין וכו'. פריצת הדרך של היוונים, פרט לתרומותיהם הגדולות לידע המתמטי, הייתה בלימוד המתמטיקה כשלעצמה, מתוקף ערכה הרוחני. יחסם של חלק מהיוונים הקדמונים למתמטיקה היה דתי - למשל, הכת שאסף סביבו פיתגורס האמינה כי המתמטיקה היא הבסיס לכל הדברים. היוונים נחשבים ליוצרי מושג ההוכחה המתמטית, וכן לראשונים שעסקו במתמטיקה לשם עצמה, כלומר כתחום מחקרי עיוני ומופשט ולא רק כעזר שימושי. עם זאת, לצדה", + result: "עד תקופת יוון העתיקה היה העיסוק במתמטיקה תכליתי בלבד: היא שימשה כאוסף של נוסחאות לחישוב קרקע, אוכלוסין וכו'. פריצת הדרך של היוונים, פרט לתרומותיהם הגדולות לידע המתמטי, הייתה בלימוד המתמטיקה כשלעצמה, מתוקף ערכה הרוחני. יחסם של חלק מהיוונים הקדמונים למתמטיקה היה דתי - למשל, הכת שאסף סביבו פיתגורס האמינה כי המתמטיקה היא הבסיס לכל הדברים. היוונים נחשבים ליוצרי מושג ההוכחה המתמטית, וכן לראשונים שעסקו במתמטיקה לשם עצמה, כלומר כתחום מחקרי עיוני ומופשט ולא רק כעזר שימושי. עם זאת, לצדה", + status: EscapeStatus{HasRTLScript: true}, + }, + { + name: "more hebrew", + text: `בתקופה מאוחרת יותר, השתמשו היוונים בשיטת סימון מתקדמת יותר, שבה הוצגו המספרים לפי 22 אותיות האלפבית היווני. לסימון המספרים בין 1 ל-9 נקבעו תשע האותיות הראשונות, בתוספת גרש ( ' ) בצד ימין של האות, למעלה; תשע האותיות הבאות ייצגו את העשרות מ-10 עד 90, והבאות את המאות. לסימון הספרות בין 1000 ל-900,000, השתמשו היוונים באותן אותיות, אך הוסיפו לאותיות את הגרש דווקא מצד שמאל של האותיות, למטה. ממיליון ומעלה, כנראה השתמשו היוונים בשני תגים במקום אחד. + + המתמטיקאי הבולט הראשון ביוון העתיקה, ויש האומרים בתולדות האנושות, הוא תאלס (624 לפנה"ס - 546 לפנה"ס בקירוב).[1] לא יהיה זה משולל יסוד להניח שהוא האדם הראשון שהוכיח משפט מתמטי, ולא רק גילה אותו. תאלס הוכיח שישרים מקבילים חותכים מצד אחד של שוקי זווית קטעים בעלי יחסים שווים (משפט תאלס הראשון), שהזווית המונחת על קוטר במעגל היא זווית ישרה (משפט תאלס השני), שהקוטר מחלק את המעגל לשני חלקים שווים, ושזוויות הבסיס במשולש שווה-שוקיים שוות זו לזו. מיוחסות לו גם שיטות למדידת גובהן של הפירמידות בעזרת מדידת צילן ולקביעת מיקומה של ספינה הנראית מן החוף. + + בשנים 582 לפנה"ס עד 496 לפנה"ס, בקירוב, חי מתמטיקאי חשוב במיוחד - פיתגורס. המקורות הראשוניים עליו מועטים, וההיסטוריונים מתקשים להפריד את העובדות משכבת המסתורין והאגדות שנקשרו בו. ידוע שסביבו התקבצה האסכולה הפיתגוראית מעין כת פסבדו-מתמטית שהאמינה ש"הכל מספר", או ליתר דיוק הכל ניתן לכימות, וייחסה למספרים משמעויות מיסטיות. ככל הנראה הפיתגוראים ידעו לבנות את הגופים האפלטוניים, הכירו את הממוצע האריתמטי, הממוצע הגאומטרי והממוצע ההרמוני והגיעו להישגים חשובים נוספים. ניתן לומר שהפיתגוראים גילו את היותו של השורש הריבועי של 2, שהוא גם האלכסון בריבוע שאורך צלעותיו 1, אי רציונלי, אך תגליתם הייתה למעשה רק שהקטעים "חסרי מידה משותפת", ומושג המספר האי רציונלי מאוחר יותר.[2] אזכור ראשון לקיומם של קטעים חסרי מידה משותפת מופיע בדיאלוג "תאיטיטוס" של אפלטון, אך רעיון זה היה מוכר עוד קודם לכן, במאה החמישית לפנה"ס להיפאסוס, בן האסכולה הפיתגוראית, ואולי לפיתגורס עצמו.[3]`, + result: `בתקופה מאוחרת יותר, השתמשו היוונים בשיטת סימון מתקדמת יותר, שבה הוצגו המספרים לפי 22 אותיות האלפבית היווני. לסימון המספרים בין 1 ל-9 נקבעו תשע האותיות הראשונות, בתוספת גרש ( ' ) בצד ימין של האות, למעלה; תשע האותיות הבאות ייצגו את העשרות מ-10 עד 90, והבאות את המאות. לסימון הספרות בין 1000 ל-900,000, השתמשו היוונים באותן אותיות, אך הוסיפו לאותיות את הגרש דווקא מצד שמאל של האותיות, למטה. ממיליון ומעלה, כנראה השתמשו היוונים בשני תגים במקום אחד. + + המתמטיקאי הבולט הראשון ביוון העתיקה, ויש האומרים בתולדות האנושות, הוא תאלס (624 לפנה"ס - 546 לפנה"ס בקירוב).[1] לא יהיה זה משולל יסוד להניח שהוא האדם הראשון שהוכיח משפט מתמטי, ולא רק גילה אותו. תאלס הוכיח שישרים מקבילים חותכים מצד אחד של שוקי זווית קטעים בעלי יחסים שווים (משפט תאלס הראשון), שהזווית המונחת על קוטר במעגל היא זווית ישרה (משפט תאלס השני), שהקוטר מחלק את המעגל לשני חלקים שווים, ושזוויות הבסיס במשולש שווה-שוקיים שוות זו לזו. מיוחסות לו גם שיטות למדידת גובהן של הפירמידות בעזרת מדידת צילן ולקביעת מיקומה של ספינה הנראית מן החוף. + + בשנים 582 לפנה"ס עד 496 לפנה"ס, בקירוב, חי מתמטיקאי חשוב במיוחד - פיתגורס. המקורות הראשוניים עליו מועטים, וההיסטוריונים מתקשים להפריד את העובדות משכבת המסתורין והאגדות שנקשרו בו. ידוע שסביבו התקבצה האסכולה הפיתגוראית מעין כת פסבדו-מתמטית שהאמינה ש"הכל מספר", או ליתר דיוק הכל ניתן לכימות, וייחסה למספרים משמעויות מיסטיות. ככל הנראה הפיתגוראים ידעו לבנות את הגופים האפלטוניים, הכירו את הממוצע האריתמטי, הממוצע הגאומטרי והממוצע ההרמוני והגיעו להישגים חשובים נוספים. ניתן לומר שהפיתגוראים גילו את היותו של השורש הריבועי של 2, שהוא גם האלכסון בריבוע שאורך צלעותיו 1, אי רציונלי, אך תגליתם הייתה למעשה רק שהקטעים "חסרי מידה משותפת", ומושג המספר האי רציונלי מאוחר יותר.[2] אזכור ראשון לקיומם של קטעים חסרי מידה משותפת מופיע בדיאלוג "תאיטיטוס" של אפלטון, אך רעיון זה היה מוכר עוד קודם לכן, במאה החמישית לפנה"ס להיפאסוס, בן האסכולה הפיתגוראית, ואולי לפיתגורס עצמו.[3]`, + status: EscapeStatus{HasRTLScript: true}, + }, + { + name: "Mixed RTL+LTR", + text: `Many computer programs fail to display bidirectional text correctly. +For example, the Hebrew name Sarah (שרה) is spelled: sin (ש) (which appears rightmost), +then resh (ר), and finally heh (ה) (which should appear leftmost).`, + result: `Many computer programs fail to display bidirectional text correctly. +For example, the Hebrew name Sarah (שרה) is spelled: sin (ש) (which appears rightmost), +then resh (ר), and finally heh (ה) (which should appear leftmost).`, + status: EscapeStatus{ + HasRTLScript: true, + HasLTRScript: true, + }, + }, + { + name: "Mixed RTL+LTR+BIDI", + text: `Many computer programs fail to display bidirectional text correctly. + For example, the Hebrew name Sarah ` + "\u2067" + `שרה` + "\u2066\n" + + `sin (ש) (which appears rightmost), then resh (ר), and finally heh (ה) (which should appear leftmost).`, + result: `Many computer programs fail to display bidirectional text correctly. + For example, the Hebrew name Sarah ` + "\u2067" + `שרה` + "\u2066" + `` + "\n" + + `sin (ש) (which appears rightmost), then resh (ר), and finally heh (ה) (which should appear leftmost).`, + status: EscapeStatus{ + Escaped: true, + HasBIDI: true, + HasRTLScript: true, + HasLTRScript: true, + }, + }, + { + name: "Accented characters", + text: string([]byte{0xc3, 0xa1, 0xc3, 0xa9, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba}), + result: string([]byte{0xc3, 0xa1, 0xc3, 0xa9, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba}), + status: EscapeStatus{HasLTRScript: true}, + }, + { + name: "Program", + text: "string([]byte{0xc3, 0xa1, 0xc3, 0xa9, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba})", + result: "string([]byte{0xc3, 0xa1, 0xc3, 0xa9, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba})", + status: EscapeStatus{HasLTRScript: true}, + }, + { + name: "CVE testcase", + text: "if access_level != \"user\u202E \u2066// Check if admin\u2069 \u2066\" {", + result: `if access_level != "user` + "\u202e" + ` ` + "\u2066" + `// Check if admin` + "\u2069" + ` ` + "\u2066" + `" {`, + status: EscapeStatus{Escaped: true, HasBIDI: true, BadBIDI: true, HasLTRScript: true}, + }, + { + name: "Mixed testcase with fail", + text: `Many computer programs fail to display bidirectional text correctly. + For example, the Hebrew name Sarah ` + "\u2067" + `שרה` + "\u2066\n" + + `sin (ש) (which appears rightmost), then resh (ר), and finally heh (ה) (which should appear leftmost).` + + "\nif access_level != \"user\u202E \u2066// Check if admin\u2069 \u2066\" {\n", + result: `Many computer programs fail to display bidirectional text correctly. + For example, the Hebrew name Sarah ` + "\u2067" + `שרה` + "\u2066" + `` + "\n" + + `sin (ש) (which appears rightmost), then resh (ר), and finally heh (ה) (which should appear leftmost).` + + "\n" + `if access_level != "user` + "\u202e" + ` ` + "\u2066" + `// Check if admin` + "\u2069" + ` ` + "\u2066" + `" {` + "\n", + status: EscapeStatus{Escaped: true, HasBIDI: true, BadBIDI: true, HasLTRScript: true, HasRTLScript: true}, + }, +} + +func TestEscapeControlString(t *testing.T) { + for _, tt := range escapeControlTests { + t.Run(tt.name, func(t *testing.T) { + status, result := EscapeControlString(tt.text) + if !reflect.DeepEqual(status, tt.status) { + t.Errorf("EscapeControlString() status = %v, wanted= %v", status, tt.status) + } + if result != tt.result { + t.Errorf("EscapeControlString()\nresult= %v,\nwanted= %v", result, tt.result) + } + }) + } +} + +func TestEscapeControlBytes(t *testing.T) { + for _, tt := range escapeControlTests { + t.Run(tt.name, func(t *testing.T) { + status, result := EscapeControlBytes([]byte(tt.text)) + if !reflect.DeepEqual(status, tt.status) { + t.Errorf("EscapeControlBytes() status = %v, wanted= %v", status, tt.status) + } + if string(result) != tt.result { + t.Errorf("EscapeControlBytes()\nresult= %v,\nwanted= %v", result, tt.result) + } + }) + } +} + +func TestEscapeControlReader(t *testing.T) { + // lets add some control characters to the tests + tests := make([]escapeControlTest, 0, len(escapeControlTests)*3) + copy(tests, escapeControlTests) + for _, test := range escapeControlTests { + test.name += " (+Control)" + test.text = "\u001E" + test.text + test.result = `` + "\u001e" + `` + test.result + test.status.Escaped = true + test.status.HasControls = true + tests = append(tests, test) + } + + for _, test := range escapeControlTests { + test.name += " (+Mark)" + test.text = "\u0300" + test.text + test.result = `` + "\u0300" + `` + test.result + test.status.Escaped = true + test.status.HasMarks = true + tests = append(tests, test) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + input := strings.NewReader(tt.text) + output := &strings.Builder{} + status, err := EscapeControlReader(input, output) + result := output.String() + if err != nil { + t.Errorf("EscapeControlReader(): err = %v", err) + } + + if !reflect.DeepEqual(status, tt.status) { + t.Errorf("EscapeControlReader() status = %v, wanted= %v", status, tt.status) + } + if result != tt.result { + t.Errorf("EscapeControlReader()\nresult= %v,\nwanted= %v", result, tt.result) + } + }) + } +} diff --git a/modules/public/public_bindata.go b/modules/public/public_bindata.go index eb10d96426bf..c5485b7cc0e8 100644 --- a/modules/public/public_bindata.go +++ b/modules/public/public_bindata.go @@ -7,4 +7,4 @@ package public -//go:generate go run -mod=vendor ../../build/generate-bindata.go ../../public public bindata.go +//go:generate go run -mod=vendor ../../build/generate-bindata.go ../../public public bindata.go true diff --git a/modules/public/static.go b/modules/public/static.go index 32ba0fe258aa..2e35329a0719 100644 --- a/modules/public/static.go +++ b/modules/public/static.go @@ -18,8 +18,14 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/timeutil" ) +// GlobalModTime provide a gloabl mod time for embedded asset files +func GlobalModTime(filename string) time.Time { + return timeutil.GetExecutableModTime() +} + func fileSystem(dir string) http.FileSystem { return Assets } diff --git a/modules/templates/static.go b/modules/templates/static.go index fdd68c1e6a06..c2295b2bd8e3 100644 --- a/modules/templates/static.go +++ b/modules/templates/static.go @@ -15,9 +15,11 @@ import ( "path/filepath" "strings" texttmpl "text/template" + "time" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" ) @@ -26,6 +28,11 @@ var ( bodyTemplates = template.New("") ) +// GlobalModTime provide a gloabl mod time for embedded asset files +func GlobalModTime(filename string) time.Time { + return timeutil.GetExecutableModTime() +} + // GetAsset get a special asset, only for chi func GetAsset(name string) ([]byte, error) { bs, err := os.ReadFile(filepath.Join(setting.CustomPath, name)) diff --git a/modules/templates/templates_bindata.go b/modules/templates/templates_bindata.go index 887f9eeba25b..95f71d0042ae 100644 --- a/modules/templates/templates_bindata.go +++ b/modules/templates/templates_bindata.go @@ -7,4 +7,4 @@ package templates -//go:generate go run -mod=vendor ../../build/generate-bindata.go ../../templates templates bindata.go +//go:generate go run -mod=vendor ../../build/generate-bindata.go ../../templates templates bindata.go true diff --git a/modules/timeutil/executable.go b/modules/timeutil/executable.go new file mode 100644 index 000000000000..b0a3280753e8 --- /dev/null +++ b/modules/timeutil/executable.go @@ -0,0 +1,49 @@ +// Copyright 2022 The Gitea 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 timeutil + +import ( + "os" + "path/filepath" + "sync" + "time" + + "code.gitea.io/gitea/modules/log" +) + +var executablModTime = time.Now() +var executablModTimeOnce sync.Once + +// GetExecutableModTime get executable file modified time of current process. +func GetExecutableModTime() time.Time { + executablModTimeOnce.Do(func() { + exePath, err := os.Executable() + if err != nil { + log.Error("os.Executable: %v", err) + return + } + + exePath, err = filepath.Abs(exePath) + if err != nil { + log.Error("filepath.Abs: %v", err) + return + } + + exePath, err = filepath.EvalSymlinks(exePath) + if err != nil { + log.Error("filepath.EvalSymlinks: %v", err) + return + } + + st, err := os.Stat(exePath) + if err != nil { + log.Error("os.Stat: %v", err) + return + } + + executablModTime = st.ModTime() + }) + return executablModTime +} diff --git a/options/locale/locale_bg-BG.ini b/options/locale/locale_bg-BG.ini index 11e60ee8ccff..48bcb896ce8d 100644 --- a/options/locale/locale_bg-BG.ini +++ b/options/locale/locale_bg-BG.ini @@ -627,7 +627,6 @@ commits.newer=По-нови commits.signed_by=Подписан от commits.gpg_key_id=GPG ключ ID -ext_issues=Външни задачи ext_issues.desc=Свързване на външна система за следене на задачи. @@ -815,7 +814,6 @@ signing.wont_sign.nokey=Няма наличен ключ за подписван signing.wont_sign.never=Ревизиите никога не се подписват signing.wont_sign.always=Ревизиите винаги се подписват -ext_wiki=Външно Уики ext_wiki.desc=Връзка към външното уики. wiki=Уики @@ -1013,8 +1011,6 @@ members.invite_now=Покани teams.join=Присъедини се teams.leave=Напусни -teams.read_access=Достъп за четене -teams.write_access=Достъп за запис teams.no_desc=Този екип няма описание teams.settings=Настройки teams.members=Участници в екипа diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index ee9b8c8ec15e..392579f0d2f7 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1004,7 +1004,6 @@ commits.signed_by_untrusted_user=Podepsáno nedůvěryhodným uživatelem commits.signed_by_untrusted_user_unmatched=Podepsáno nedůvěryhodným uživatelem, který nesouhlasí s přispěvatelem commits.gpg_key_id=ID GPG klíče -ext_issues=Ext. úkoly ext_issues.desc=Odkaz na externí systém úkolů. projects=Projekty @@ -1462,7 +1461,6 @@ signing.wont_sign.commitssigned=Sloučení nebude podepsáno, protože všechny signing.wont_sign.approved=Sloučení nebude podepsáno, protože požadavek na natažení není schválen signing.wont_sign.not_signed_in=Nejste přihlášeni -ext_wiki=Ext. Wiki ext_wiki.desc=Odkaz do externí Wiki. wiki=Wiki @@ -1701,7 +1699,6 @@ settings.webhook_deletion_desc=Odstranění webového háčku smaže jeho nastav settings.webhook_deletion_success=Webový háček byl smazán. settings.webhook.test_delivery=Test doručitelnosti settings.webhook.test_delivery_desc=Vyzkoušet tento webový háček pomocí falešné události. -settings.webhook.test_delivery_success=Falešná událost byla přidána do fronty doručení. Může to trvat několik sekund, než se zobrazí v historii doručení. settings.webhook.request=Požadavek settings.webhook.response=Odpověď settings.webhook.headers=Hlavičky @@ -2108,9 +2105,7 @@ teams.join=Připojit teams.leave=Opustit teams.can_create_org_repo=Vytvořit repozitáře teams.can_create_org_repo_helper=Členové mohou vytvářet nové repozitáře v organizaci. Tvůrce získá přístup správce do nového repozitáře. -teams.read_access=Právo čtení teams.read_access_helper=Členové mohou zobrazit a klonovat repozitáře týmu. -teams.write_access=Právo zápisu teams.write_access_helper=Členové mohou číst a nahrávat do repozitářů týmu. teams.admin_access=Přístup správce teams.admin_access_helper=Členové mohou natahovat i nahrávat do repozitářů týmu a mohou přidávat spolupracovníky. diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 0f71a2545f7d..f841b91355df 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1060,7 +1060,6 @@ commits.signed_by_untrusted_user=Signiert von nicht vertrauenswürdigen Benutzer commits.signed_by_untrusted_user_unmatched=Signiert von nicht vertrauenswürdigen Benutzern, der nicht mit dem Committer übereinstimmt commits.gpg_key_id=GPG-Schlüssel-ID -ext_issues=Externe Issues ext_issues.desc=Link zu externem Issuetracker. projects=Projekte @@ -1531,7 +1530,6 @@ signing.wont_sign.commitssigned=Der Merge Commit wird nicht signiert werden, da signing.wont_sign.approved=Der Merge Commit wird nicht signiert werden, da der Pull Request nicht genehmigt wurde signing.wont_sign.not_signed_in=Du bist nicht eingeloggt -ext_wiki=Externes Wiki ext_wiki.desc=Verweis auf externes Wiki. wiki=Wiki @@ -1776,7 +1774,6 @@ settings.webhook_deletion_desc=Das Entfernen eines Webhooks löscht seine Einste settings.webhook_deletion_success=Webhook wurde entfernt. settings.webhook.test_delivery=Senden testen settings.webhook.test_delivery_desc=Teste diesen Webhook mit einem Fake-Event. -settings.webhook.test_delivery_success=Ein Fake-Event wurde zur Auslieferungswarteschlange hinzugefügt. Es kann einige Sekunden dauern, bevor es im Zustellungsverlauf erscheint. settings.webhook.request=Anfrage settings.webhook.response=Antwort settings.webhook.headers=Kopfzeilen @@ -2202,9 +2199,7 @@ teams.leave=Verlassen teams.leave.detail=%s verlassen? teams.can_create_org_repo=Repositories erstellen teams.can_create_org_repo_helper=Mitglieder können neue Repositories in der Organisation erstellen. Der Ersteller erhält Administrator-Zugriff auf das neue Repository. -teams.read_access=Lesezugriff teams.read_access_helper=Mitglieder können Teamrepositories ansehen und klonen. -teams.write_access=Schreibzugriff teams.write_access_helper=Mitglieder können Teamrepositories ansehen und auf sie pushen. teams.admin_access=Administratorzugang teams.admin_access_helper=Mitglieder können auf Team-Repositorys pushen, von ihnen pullen und Mitarbeiter hinzufügen. diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 4e4bb21cc219..442adbc7dc1d 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -1099,7 +1099,6 @@ commits.signed_by_untrusted_user_unmatched=Υπογράφηκε από ένα μ commits.gpg_key_id=ID Κλειδιού GPG commits.ssh_key_fingerprint=Αποτύπωμα Κλειδιού SSH -ext_issues=Εξωτ. Ζητήματα ext_issues.desc=Σύνδεση σε εξωτερικό εφαρμογή ζητημάτων. projects=Έργα @@ -1579,7 +1578,6 @@ signing.wont_sign.commitssigned=Η συγχώνευση δεν θα υπογρα signing.wont_sign.approved=Η συγχώνευση δεν θα υπογραφεί καθώς το PR δεν εγκρίνεται signing.wont_sign.not_signed_in=Δεν είστε συνδεδεμένοι -ext_wiki=Έξωτ. Wiki ext_wiki.desc=Σύνδεση σε ένα εξωτερικό wiki. wiki=Wiki @@ -1831,7 +1829,6 @@ settings.webhook_deletion_desc=Η αφαίρεση ενός webhook διαγρά settings.webhook_deletion_success=Το webhook έχει αφαιρεθεί. settings.webhook.test_delivery=Δοκιμή Παράδοσης settings.webhook.test_delivery_desc=Δοκιμάστε αυτό το webhook με ένα ψεύτικο συμβάν. -settings.webhook.test_delivery_success=Ένα ψεύτικο συμβάν έχει προστεθεί στην ουρά παράδοσης. Μπορεί να χρειαστούν λίγα δευτερόλεπτα πριν εμφανιστεί στο ιστορικό παραδόσεων. settings.webhook.request=Αίτημα settings.webhook.response=Απάντηση settings.webhook.headers=Κεφαλίδες @@ -2261,9 +2258,7 @@ teams.leave=Αποχώρηση teams.leave.detail=Αποχώρηση από %s; teams.can_create_org_repo=Δημιουργία αποθετηρίων teams.can_create_org_repo_helper=Τα μέλη μπορούν να δημιουργήσουν νέα αποθετήρια στον οργανισμό. Ο δημιουργός θα αποκτήσει πρόσβαση διαχειριστή στο νέο αποθετήριο. -teams.read_access=Πρόσβαση Ανάγνωσης teams.read_access_helper=Τα μέλη μπορούν να δουν και να κλωνοποιήσουν τα αποθετήρια της ομάδας. -teams.write_access=Πρόσβαση Εγγραφής teams.write_access_helper=Τα μέλη μπορούν να δουν και να κλωνοποιήσουν τα αποθετήρια της ομάδας. teams.admin_access=Πρόσβαση Διαχειριστή teams.admin_access_helper=Τα μέλη μπορούν να κάνουν push και pull στα αποθετήρια της ομάδας όπως και να προσθέσουν συνεργάτες σε αυτά. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 3d60df5d682d..eacd74e1a006 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1005,6 +1005,16 @@ file_view_rendered = View Rendered file_view_raw = View Raw file_permalink = Permalink file_too_large = The file is too large to be shown. +bidi_bad_header = `This file contains unexpected Bidirectional Unicode characters!` +bidi_bad_description = `This file contains unexpected Bidirectional Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.` +bidi_bad_description_escaped = `This file contains unexpected Bidirectional Unicode characters. Hidden unicode characters are escaped below. Use the Unescape button to show how they render.` +unicode_header = `This file contains hidden Unicode characters!` +unicode_description = `This file contains hidden Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.` +unicode_description_escaped = `This file contains hidden Unicode characters. Hidden unicode characters are escaped below. Use the Unescape button to show how they render.` +line_unicode = `This line has hidden unicode characters` + +escape_control_characters = Escape +unescape_control_characters = Unescape file_copy_permalink = Copy Permalink video_not_supported_in_browser = Your browser does not support the HTML5 'video' tag. audio_not_supported_in_browser = Your browser does not support the HTML5 'audio' tag. @@ -2101,6 +2111,7 @@ diff.protected = Protected diff.image.side_by_side = Side by Side diff.image.swipe = Swipe diff.image.overlay = Overlay +diff.has_escaped = This line has hidden Unicode characters releases.desc = Track project versions and downloads. release.releases = Releases diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 0d2608e761fd..0c497a4a6e21 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -1088,7 +1088,6 @@ commits.signed_by_untrusted_user_unmatched=Firmado por un usuario no fiable que commits.gpg_key_id=ID de clave GPG commits.ssh_key_fingerprint=Huella clave SSH -ext_issues=Incidencias externas ext_issues.desc=Enlace a un gestor de incidencias externo. projects=Proyectos @@ -1565,7 +1564,6 @@ signing.wont_sign.commitssigned=Esta fusión no se firmará ya que todos sus com signing.wont_sign.approved=Esta fusión no se firmará ya que el PR no está aprobado signing.wont_sign.not_signed_in=No has iniciado sesión -ext_wiki=Wiki externa ext_wiki.desc=Enlace a una wiki externa. wiki=Wiki @@ -1817,7 +1815,6 @@ settings.webhook_deletion_desc=Eliminar un webhook borra sus ajustes e historial settings.webhook_deletion_success=El webhook ha sido eliminado. settings.webhook.test_delivery=Test de entrega settings.webhook.test_delivery_desc=Prueba este webhook con un evento falso. -settings.webhook.test_delivery_success=Se ha añadido un evento falso a la cola. Puede tardar unos segundos antes de que se muestre en el historial de entrega. settings.webhook.request=Petición settings.webhook.response=Respuesta settings.webhook.headers=Encabezado @@ -2246,9 +2243,7 @@ teams.leave=Abandonar teams.leave.detail=¿Irse de %s? teams.can_create_org_repo=Crear repositorios teams.can_create_org_repo_helper=Los miembros pueden crear nuevos repositorios en la organización. El creador obtendrá acceso al administrador del nuevo repositorio. -teams.read_access=Acceso de Lectura teams.read_access_helper=Los miembros pueden ver y clonar los repositorios del equipo. -teams.write_access=Acceso de Escritura teams.write_access_helper=Los miembros pueden leer y hacer push a los repositorios del equipo. teams.admin_access=Acceso administrativo teams.admin_access_helper=Los miembros pueden hacer pull y push a los repositorios del equipo y añadir colaboradores a ellos. diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index b2ca58a6dad0..dab01afdfd96 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -845,7 +845,6 @@ commits.signed_by_untrusted_user=امضا شده توسط یک کاربر غیر commits.signed_by_untrusted_user_unmatched=امضا شده توسط یک کاربر غیرقابل اعتماد که با اعمال کننده تغییرات مطابقت ندارد commits.gpg_key_id=شناسه کلید GPG -ext_issues=مسائل اضافی ext_issues.desc=پیوند به ردیاب خارجی برای موضوع. projects=پروژه‌ها @@ -1191,7 +1190,6 @@ signing.wont_sign.headsigned=زمانیکه آخرین تغییرات (head) ا signing.wont_sign.commitssigned=زمانیکه تمامی تغییرات مرتبط امضا نشده باشند، ادغام نیز امضا نخواهد شد signing.wont_sign.approved=تا زمانیکه درخواست دریافت تایید نشده است ادغام امضا نخواهد شد -ext_wiki=دانشنامه خارجی ext_wiki.desc=پیوند به یک دانشنامه خارجی. wiki=دانشنامه @@ -1397,7 +1395,6 @@ settings.webhook_deletion_desc=حذف هوک تحت وب موجب حذف تنظ settings.webhook_deletion_success=هوک تحت وب حذف شد. settings.webhook.test_delivery=امتحان‌کردن تحویل settings.webhook.test_delivery_desc=Webhook را با رویداد جعلی امتحان کنید. -settings.webhook.test_delivery_success=یک رویداد آزمایشی به صف تحویل افزوده شد. ممکن است چند ثانیه ای طول بکشد تا اینکه در لیست تاریخچه تحویل ها قرار گیرد. settings.webhook.request=درخواست settings.webhook.response=پاسخ settings.webhook.headers=سربرگ‎ها @@ -1712,9 +1709,7 @@ teams.join=پیوستن teams.leave=ترک‌کردن teams.can_create_org_repo=ایجاد مخزن teams.can_create_org_repo_helper=اعضا می توانند مخازن جدیدی را در سازمان ایجاد کنند. خالق دسترسی سرپرست به مخزن جدید را دریافت می کند. -teams.read_access=دسترسی خواندن teams.read_access_helper=اعضا می‌توانند مخازن مربوط به تیم را مشاهده و از آن همسان تهیه کنند. -teams.write_access=دسترسی نوشتن teams.write_access_helper=اعضا می‌توانند مخازن مربوط به تیم را بخوانند و کامیت ها در آن درج کنند. teams.admin_access=دسترسی مدیریت teams.admin_access_helper=اعضا می‌توانند واکشی یا درج بر روی مخازن تیم را انجام دهند و می‌توانند به آنها همکار اضافه کنند. diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 2ece74d6b3e3..01ef8727c151 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -1047,9 +1047,7 @@ members.invite_now=Kutsu nyt teams.join=Liity teams.leave=Poistu -teams.read_access=Lukuoikeus teams.read_access_helper=Tiimin jäsenet voivat katsella ja kloonata tiimin varastoja. -teams.write_access=Kirjoitusoikeus teams.write_access_helper=Tiimin jäsenet voivat lukea ja työntää tiimin varastoja/varastoihin. teams.admin_access=Ylläpito-oikeus teams.admin_access_helper=Tiimin jäsenet voivat työntää (push) ja vetää (pull) tiimin varastoista/varastoihin ja lisätä yhteistyökumppaneita. diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index d762088bdeb5..cd25c89d48e5 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -984,7 +984,6 @@ commits.signed_by_untrusted_user=Signé par un utilisateur non approuvé commits.signed_by_untrusted_user_unmatched=Signé par un utilisateur non fiable qui ne correspond pas au validateur commits.gpg_key_id=ID de la clé GPG -ext_issues=Gestionnaire de tickets externe ext_issues.desc=Lien vers un gestionnaire de tickets externe. projects=Projets @@ -1431,7 +1430,6 @@ signing.wont_sign.commitssigned=La fusion ne sera pas signée car toutes les ré signing.wont_sign.approved=La fusion ne sera pas signée car la PR n'a pas approuvée signing.wont_sign.not_signed_in=Vous n'êtes pas authentifié -ext_wiki=Wiki externe ext_wiki.desc=Lier un wiki externe. wiki=Wiki @@ -1668,7 +1666,6 @@ settings.webhook_deletion_desc=Supprimer un webhook supprime ses paramètres et settings.webhook_deletion_success=Le webhook a été supprimé. settings.webhook.test_delivery=Tester la version settings.webhook.test_delivery_desc=Testez ce webhook avec un faux événement. -settings.webhook.test_delivery_success=Un faux événement a été ajouté à la file d'attente. L'affichage dans l'historique peut prendre quelques secondes. settings.webhook.request=Requête settings.webhook.response=Réponse settings.webhook.headers=Entêtes @@ -2045,9 +2042,7 @@ teams.join=Rejoindre teams.leave=Quitter teams.can_create_org_repo=Créer des dépôts teams.can_create_org_repo_helper=Les membres peuvent créer de nouveaux dépôts dans l'organisation. Le créateur obtiendra l'accès administrateur au nouveau dépôt. -teams.read_access=Accès en lecture teams.read_access_helper=Les membres peuvent voir et cloner les dépôts de l'équipe. -teams.write_access=Accès en écriture teams.write_access_helper=Les membres peuvent voir et pousser dans les dépôts de l'équipe. teams.admin_access=Accès Administrateur teams.admin_access_helper=Les membres peuvent tirer et pousser des modifications vers les dépôts de l'équipe, et y ajouter des collaborateurs. diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 505c13ea4a6d..da954db1e6da 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -781,7 +781,6 @@ commits.newer=Újabb commits.signed_by=Aláírta commits.gpg_key_id=GPG kulcs azonosító -ext_issues=Külső hibajegyek ext_issues.desc=Külső hibakövető csatlakoztatás. @@ -1025,7 +1024,6 @@ milestones.filter_sort.most_issues=Legtöbb hibajegy milestones.filter_sort.least_issues=Legkevesebb hibajegy -ext_wiki=Külső Wiki ext_wiki.desc=Külső wiki csatolása. wiki=Wiki @@ -1391,9 +1389,7 @@ teams.join=Csatlakozás teams.leave=Távozás teams.can_create_org_repo=Tárolók létrehozása teams.can_create_org_repo_helper=A tagok létrehozhatnak új tárolókat a szervezetben. A létrehozó adminisztrátor hozzáférést fog kapni az új tárolóhoz. -teams.read_access=Olvasási jogosultság teams.read_access_helper=A tagok megtekinthetik és klónozhatják a csapat tárolóit. -teams.write_access=Írási jogosultság teams.admin_access=Adminisztrátori hozzáférés teams.no_desc=Ennek a csoportnak nincs leírása teams.settings=Beállítások diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index c5c0cf5df672..bcc3af80ce39 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -1075,8 +1075,6 @@ members.invite_now=Mengundang Sekarang teams.join=Ikut teams.leave=Meninggalkan -teams.read_access=Akses Baca -teams.write_access=Akses Menulis teams.no_desc=Tim ini tidak memiliki keterangan teams.settings=Pengaturan teams.members=Anggota tim diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 9a7d8c6c6fe9..fef69b2680e4 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -912,7 +912,6 @@ commits.signed_by_untrusted_user=Firmato da un utente non attendibile commits.signed_by_untrusted_user_unmatched=Firmato da un utente non attendibile che non corrisponde al committer commits.gpg_key_id=ID Chiave GPG -ext_issues=Issue esterne ext_issues.desc=Collegamento al puntatore di una issue esterna. projects=Progetti @@ -1316,7 +1315,6 @@ signing.wont_sign.commitssigned=Questo merge non sarà firmato poiché i commit signing.wont_sign.approved=Il merge non sarà firmato poiché il PR non è approvato signing.wont_sign.not_signed_in=Non hai effettuato l'accesso -ext_wiki=Wiki esterna ext_wiki.desc=Collegamento a una wiki esterna. wiki=Wiki @@ -1530,7 +1528,6 @@ settings.webhook_deletion_desc=Rimuovere un webhook rimuove le sue impostazioni settings.webhook_deletion_success=Il webhook è stato rimosso. settings.webhook.test_delivery=Test di consegna settings.webhook.test_delivery_desc=Prova questo webhook con un evento falso. -settings.webhook.test_delivery_success=Un evento falso è stato aggiunto alla coda di consegna. Potrebbe richiedere qualche secondo prima che appaia nella cronologia di consegna. settings.webhook.request=Richiesta settings.webhook.response=Risposta settings.webhook.headers=Intestazioni @@ -1881,9 +1878,7 @@ teams.join=Iscriviti teams.leave=Abbandona teams.can_create_org_repo=Crea repository teams.can_create_org_repo_helper=I membri possono creare nuovi repository nell'organizzazione. Il creatore otterrà l'accesso di amministratore alla nuova repository. -teams.read_access=Accesso di Lettura teams.read_access_helper=I membri possono visualizzare e clonare i repository del team. -teams.write_access=Accesso di Scrittura teams.write_access_helper=I membri possono leggere e pushare sui repository del team. teams.admin_access=Accesso amministratore teams.admin_access_helper=I membri possono pullare e pushare sulle repository del team e anche aggiungere collaboratori. diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 66bcf276e18a..ccfaefe56816 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -1099,7 +1099,6 @@ commits.signed_by_untrusted_user_unmatched=コミッターと一致しない信 commits.gpg_key_id=GPGキーID commits.ssh_key_fingerprint=SSH鍵のフィンガープリント -ext_issues=外部イシュー ext_issues.desc=外部のイシュートラッカーへのリンク。 projects=プロジェクト @@ -1579,7 +1578,6 @@ signing.wont_sign.commitssigned=関連するコミットに署名されていな signing.wont_sign.approved=PRが未承認のため、マージは署名されません signing.wont_sign.not_signed_in=あなたはサインインしていません -ext_wiki=外部Wiki ext_wiki.desc=外部Wikiへのリンク。 wiki=Wiki @@ -1831,7 +1829,6 @@ settings.webhook_deletion_desc=Webhook設定と配信履歴が削除されます settings.webhook_deletion_success=Webhookを削除しました。 settings.webhook.test_delivery=テスト配信 settings.webhook.test_delivery_desc=ダミーのイベントでこのWebhookをテストします。 -settings.webhook.test_delivery_success=ダミーイベントを配信キューに追加しました。 配信履歴に表示されるまで数秒かかります。 settings.webhook.request=リクエスト settings.webhook.response=レスポンス settings.webhook.headers=ヘッダー @@ -2261,9 +2258,7 @@ teams.leave=脱退 teams.leave.detail=%s から脱退しますか? teams.can_create_org_repo=リポジトリを作成 teams.can_create_org_repo_helper=メンバーは組織のリポジトリを新たに作成できます。作成者には新しいリポジトリの管理者権限が与えられます。 -teams.read_access=読み取りアクセス権 teams.read_access_helper=メンバーはチームリポジトリの閲覧とクローンが可能。 -teams.write_access=書き込みアクセス権 teams.write_access_helper=メンバーはチームリポジトリの読み取りとプッシュが可能。 teams.admin_access=管理者アクセス権 teams.admin_access_helper=メンバーは、チームリポジトリへのプル、プッシュ、共同作業者の追加が可能。 diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index 45d6dfdfb46b..6a5850bb34ac 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -695,7 +695,6 @@ commits.newer=최신 commits.signed_by=로그인 계정 commits.gpg_key_id=GPG 키 ID -ext_issues=외부 버그 ext_issues.desc=외부 이슈 트래커 연결. @@ -913,7 +912,6 @@ milestones.filter_sort.most_issues=이슈 많은 순 milestones.filter_sort.least_issues=이슈 적은 순 -ext_wiki=외부 위키 ext_wiki.desc=외부 위키에 연결하기. wiki=위키 @@ -1245,8 +1243,6 @@ members.invite_now=지금 초대하기 teams.join=가입 teams.leave=탈퇴 -teams.read_access=읽기 접근 -teams.write_access=쓰기 접근 teams.admin_access=관리자 액세스 teams.no_desc=이 팀은 설명이 없습니다. teams.settings=설정 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 5e26da7d9b9f..98bab33ebae7 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1030,7 +1030,6 @@ commits.signed_by_untrusted_user=Parakstījis neuzticams lietotājs commits.signed_by_untrusted_user_unmatched=Parakstījis neuzticams lietotājs, kas neatbilst izmaiņu autoram commits.gpg_key_id=GPG atslēgas ID -ext_issues=Ārējās problēmas ext_issues.desc=Saite uz ārējo problēmu sekotāju. projects=Projekti @@ -1480,7 +1479,6 @@ signing.wont_sign.commitssigned=Sapludināšanas revīzija netiks parakstīta, j signing.wont_sign.approved=Sapludināsanas revīzija netiks parakstīta, jo izmaiņu pieprasījums nav apstiprināts signing.wont_sign.not_signed_in=Jūs neesat autorizējies -ext_wiki=Ārējā vikivietne ext_wiki.desc=Ārējā vikivietne norāda uz ārējo vikivietnes adresi. wiki=Vikivietne @@ -1725,7 +1723,6 @@ settings.webhook_deletion_desc=Noņemot tīmekļa āķi, tiks dzēsti visi tā i settings.webhook_deletion_success=Tīmekļa āķis tika noņemts. settings.webhook.test_delivery=Testa piegāde settings.webhook.test_delivery_desc=Veikt viltus push-notikuma piegādi, lai notestētu Jūsu tīmekļa āķa iestatījumus. -settings.webhook.test_delivery_success=Pārbaudes tīmekļa āķis tika veiksmīgi pievienots piegādes rindai. Var paiet vairākas sekundes līdz tas parādās piegādes vēsturē. settings.webhook.request=Pieprasījums settings.webhook.response=Atbilde settings.webhook.headers=Galvenes @@ -2135,9 +2132,7 @@ teams.join=Pievienoties teams.leave=Atstāt teams.can_create_org_repo=Veidot jaunus repozitorijus teams.can_create_org_repo_helper=Komandas biedri varēs veidot jaunus repozitorijus šajā organizācijā. Izveidotājam tiks piešķirtas administratora tiesības uz jauno repozitoriju. -teams.read_access=Lasīšanas piekļuve teams.read_access_helper=Komanda varēs skatīties un klonēt šīs organizācijas repozitorijus. -teams.write_access=Rakstīšanas piekļuve teams.write_access_helper=Šī komanda varēs lasīt un nosūtīt izmaiņas uz tās repozitorijiem. teams.admin_access=Administratora piekļuve teams.admin_access_helper=Šī komanda varēs nosūtīt un saņemt izmaiņas no tās repozitorijiem, kā arī pievienot tiem citus līdzstrādniekus. diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 04efb7a5e6d1..5f16b168b0a6 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -932,7 +932,6 @@ commits.signed_by_untrusted_user=Ondertekend door niet-vertrouwde gebruiker commits.signed_by_untrusted_user_unmatched=Ondertekend door niet-vertrouwde gebruiker die niet overeenkomt met de committer commits.gpg_key_id=GPG sleutel-ID -ext_issues=Ext. issues ext_issues.desc=Koppelen aan een externe kwestie-tracker. projects=Projecten @@ -1345,7 +1344,6 @@ signing.wont_sign.commitssigned=De samenvoeging wordt niet ondertekend omdat all signing.wont_sign.approved=De samenvoeging wordt niet ondertekend omdat de PR niet is goedgekeurd signing.wont_sign.not_signed_in=U bent niet ingelogd -ext_wiki=Ext. wiki ext_wiki.desc=Koppelen aan een externe wiki. wiki=Wiki @@ -1567,7 +1565,6 @@ settings.webhook_deletion_desc=Verwijderen van een webhook verwijdert de instell settings.webhook_deletion_success=Webhook is verwijderd. settings.webhook.test_delivery=Test-bezorging settings.webhook.test_delivery_desc=Test deze webhook met een nep-gebeurtenis. -settings.webhook.test_delivery_success=Een nep gebeurtenis is toegevoegd aan de wachtrij. Het kan enkele seconden duren voordat het in de geschiedenis van afleveringen wordt weergegeven. settings.webhook.request=Verzoek settings.webhook.response=Antwoord settings.webhook.headers=Headers @@ -1935,9 +1932,7 @@ teams.join=Lid worden teams.leave=Vertlaat teams.can_create_org_repo=Maak repositories teams.can_create_org_repo_helper=Leden kunnen nieuwe repositories aanmaken in de organisatie. De maker krijgt beheerder toegang tot de nieuwe repository. -teams.read_access=Leestoegang teams.read_access_helper=Leden kunnen teamrepositories bekijken en klonen. -teams.write_access=Schrijf toegang teams.write_access_helper=Leden kunnen lezen en pushen naar teamrepositories. teams.admin_access=Beheerder toegang teams.admin_access_helper=Leden kunnen van en naar repositories pullen, pushen, en er medewerkers aan toevoegen. diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 97124968d791..99a000fcdbb4 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -843,7 +843,6 @@ commits.signed_by_untrusted_user=Podpisane przez niezaufanego użytkownika commits.signed_by_untrusted_user_unmatched=Podpisane przez niezaufanego użytkownika, który nie pasuje do autora commita commits.gpg_key_id=ID klucza GPG -ext_issues=Zgłoszenia zewn. ext_issues.desc=Link do zewnętrznego systemu śledzenia zgłoszeń. projects=Projekty @@ -1230,7 +1229,6 @@ signing.wont_sign.commitssigned=Scalenie nie będzie podpisane, ponieważ wszyst signing.wont_sign.approved=Scalenie nie będzie podpisane, ponieważ PR nie został zatwierdzony signing.wont_sign.not_signed_in=Nie jesteś zalogowany -ext_wiki=Zewn. wiki ext_wiki.desc=Link do zewnętrznego wiki. wiki=Wiki @@ -1437,7 +1435,6 @@ settings.webhook_deletion_desc=Usunięcie Webhooka wykasuje jego ustawienia i hi settings.webhook_deletion_success=Webhook został usunięty. settings.webhook.test_delivery=Testuj dostawę settings.webhook.test_delivery_desc=Sprawdź tego Webhooka przy pomocy testowego zdarzenia. -settings.webhook.test_delivery_success=Testowe zdarzenie zostało dodane do kolejki dostaw. Może zająć kilka sekund, zanim pojawi się w historii dostaw. settings.webhook.request=Żądanie settings.webhook.response=Odpowiedź settings.webhook.headers=Nagłówki @@ -1800,9 +1797,7 @@ teams.join=Dołącz teams.leave=Opuść teams.can_create_org_repo=Tworzenie repozytoriów teams.can_create_org_repo_helper=Członkowie mogą tworzyć nowe repozytoria w organizacji. Twórca otrzyma uprawnienia administracyjne do nowego repozytorium. -teams.read_access=Dostęp do odczytu teams.read_access_helper=Członkowie mogą wyświetlać i klonować repozytoria zespołów. -teams.write_access=Dostęp do zapisu teams.write_access_helper=Członkowie mają uprawnienia odczytu i wypychania do repozytoriów zespołu. teams.admin_access=Dostęp administratora teams.admin_access_helper=Członkowie mogą ściągać i wypychać zmiany do repozytoriów zespołu oraz dodawać do niego współpracowników. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index da16b8a6990f..78828b50fa78 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1007,7 +1007,6 @@ commits.signed_by_untrusted_user=Assinado por usuário não confiável commits.signed_by_untrusted_user_unmatched=Assinado por usuário não confiável que não corresponde ao autor da submissão commits.gpg_key_id=ID da chave GPG -ext_issues=Ext. Issues ext_issues.desc=Link para o issue tracker externo. projects=Projetos @@ -1425,7 +1424,6 @@ signing.wont_sign.headsigned=O merge não será assinado porque o commit princip signing.wont_sign.commitssigned=O merge não será assinado pois todos os commits associados não foram assinados signing.wont_sign.approved=O merge não será assinado pois o PR não foi aprovado -ext_wiki=Ext. Wiki ext_wiki.desc=Link para uma wiki externa. wiki=Wiki @@ -1623,7 +1621,6 @@ settings.webhook_deletion_desc=A exclusão de um webhook exclui suas configuraç settings.webhook_deletion_success=O webhook foi removido. settings.webhook.test_delivery=Entrega de teste settings.webhook.test_delivery_desc=Teste este webhook com um falso evento. -settings.webhook.test_delivery_success=Um falso webhook foi adicionado a fila de envio. Pode levar alguns segundos até ele apareça no histórico de envio. settings.webhook.request=Solicitação settings.webhook.response=Resposta settings.webhook.headers=Cabeçalhos @@ -1964,9 +1961,7 @@ teams.leave=Deixar teams.leave.detail=Sair de %s? teams.can_create_org_repo=Criar repositórios teams.can_create_org_repo_helper=Membros podem criar novos repositórios na organização. O criador terá acesso administrativo ao novo repositório. -teams.read_access=Acesso de leitura teams.read_access_helper=Os membros podem ver e clonar os repositórios da equipe. -teams.write_access=Acesso de escrita teams.write_access_helper=Os membros podem ler e realizar push para os repositórios da equipe. teams.admin_access=Acesso de administrador teams.admin_access_helper=Os membros podem realizar pull e push em repositórios da equipe e adicionar colaboradores a eles. diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 62a908182109..8db03e54c235 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1098,7 +1098,7 @@ commits.signed_by_untrusted_user_unmatched=Assinado por um utilizador não fiáv commits.gpg_key_id=ID da chave GPG commits.ssh_key_fingerprint=Identificação digital da chave SSH -ext_issues=Questões ext. +ext_issues=Acesso a questões externas ext_issues.desc=Ligação para um rastreador de questões externo. projects=Projectos @@ -1576,7 +1576,7 @@ signing.wont_sign.commitssigned=A integração não irá ser assinada, uma vez q signing.wont_sign.approved=A integração não irá ser assinada, uma vez que o pedido de integração não foi assinado signing.wont_sign.not_signed_in=Não tem a sessão iniciada -ext_wiki=Wiki Ext. +ext_wiki=Acesso a wiki externo ext_wiki.desc=Ligação para um wiki externo. wiki=Wiki @@ -1828,12 +1828,13 @@ settings.webhook_deletion_desc=Remover um automatismo web elimina as configuraç settings.webhook_deletion_success=O automatismo web foi removido. settings.webhook.test_delivery=Entrega de teste settings.webhook.test_delivery_desc=Testar este automatismo web com um evento falso. -settings.webhook.test_delivery_success=Foi adicionado um evento fictício à fila de entrega. Pode demorar alguns segundos a aparecer no histórico de entregas. settings.webhook.request=Pedido settings.webhook.response=Resposta settings.webhook.headers=Cabeçalhos settings.webhook.payload=Conteúdo settings.webhook.body=Corpo +settings.webhook.replay.description=Voltar a executar este automatismo web. +settings.webhook.delivery.success=Foi adicionado um evento à fila de entrega. Pode demorar alguns segundos a aparecer no histórico de entregas. settings.githooks_desc=Os Automatismos do Git são executados pelo próprio Git. Pode editar os ficheiros de automatismo abaixo para configurar operações personalizadas. settings.githook_edit_desc=Se o automatismo estiver desligado, será apresentado um conteúdo de teste. Deixar o conteúdo em branco irá desabilitar este automatismo. settings.githook_name=Nome do automatismo @@ -2257,9 +2258,13 @@ teams.leave=Sair teams.leave.detail=Sair de %s? teams.can_create_org_repo=Criar repositórios teams.can_create_org_repo_helper=Os membros podem criar novos repositórios na organização. O criador terá acesso de administrador ao novo repositório. -teams.read_access=Acesso de leitura +teams.none_access=Sem acesso +teams.none_access_helper=Os membros não podem ver nem fazer qualquer outra operação nesta unidade. +teams.general_access=Acesso geral +teams.general_access_helper=As permissões dos membros serão decididas pela tabela de permissões abaixo. +teams.read_access=Ler teams.read_access_helper=Os membros podem ver e clonar os repositórios da equipa. -teams.write_access=Acesso de escrita +teams.write_access=Escrever teams.write_access_helper=Os membros podem ler e enviar para os repositórios da equipa. teams.admin_access=Acesso de administrador teams.admin_access_helper=Os membros podem puxar de, e enviar para os repositórios da equipa e adicionar colaboradores a esses repositórios. @@ -2888,6 +2893,7 @@ error.probable_bad_signature=AVISO! Embora exista uma chave com este ID na base error.probable_bad_default_signature=AVISO! Embora a chave padrão tenha este ID, ela não valida este cometimento! Este cometimento é SUSPEITO. [units] +unit=Unidade error.no_unit_allowed_repo=Não tem permissão para aceder a nenhuma parte deste repositório. error.unit_not_allowed=Não tem permissão para aceder a esta parte do repositório. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 91dbc517d69a..7f62462a949b 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1061,7 +1061,6 @@ commits.signed_by_untrusted_user=Подписано ненадежным пол commits.signed_by_untrusted_user_unmatched=Подписан ненадежным пользователем, который не соответствует коммиту commits.gpg_key_id=Идентификатор GPG ключа -ext_issues=Внешние задачи ext_issues.desc=Ссылка на внешнюю систему отслеживания ошибок. projects=Проекты @@ -1533,7 +1532,6 @@ signing.wont_sign.commitssigned=Слияние не будет подписан signing.wont_sign.approved=Слияние не будет подписано, так как PR не одобрен signing.wont_sign.not_signed_in=Вы не авторизовались -ext_wiki=Внешняя вики ext_wiki.desc=Ссылка на внешнюю вики. wiki=Вики @@ -1778,7 +1776,6 @@ settings.webhook_deletion_desc=Удаление этого веб-хука пр settings.webhook_deletion_success=Вебхук был удалён. settings.webhook.test_delivery=Проверить доставку settings.webhook.test_delivery_desc=Отправить тестовое событие для тестирования настройки веб-хука. -settings.webhook.test_delivery_success=Тест веб-хука был добавлен в очередь доставки. Это может занять несколько секунд, прежде чем он отобразится в истории доставки. settings.webhook.request=Запрос settings.webhook.response=Ответ settings.webhook.headers=Заголовки @@ -2207,9 +2204,7 @@ teams.leave=Выйти teams.leave.detail=Покинуть %s? teams.can_create_org_repo=Создать репозитории teams.can_create_org_repo_helper=Участники могут создавать новые репозитории в организации. Создатель получит администраторский доступ к новому репозиторию. -teams.read_access=Доступ на чтение teams.read_access_helper=Участники могут просматривать и клонировать командные репозитории. -teams.write_access=Доступ на запись teams.write_access_helper=Участники могут читать и выполнять push в командные репозитории. teams.admin_access=Доступ администратора teams.admin_access_helper=Участники могут выполнять pull, push в командные репозитории и добавлять соавторов в команду. diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 7d77e88687b8..c236330e2be7 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -673,8 +673,6 @@ teams.leave=හැරයන්න teams.leave.detail=%s හැරයනවාද? teams.can_create_org_repo=කෝෂ්ඨය සාදන්න teams.can_create_org_repo_helper=සාමාජිකයින්ට සංවිධානයෙහි නව කෝෂ්ඨ සෑදීමට හැකිය. නිර්මාතෘ ට නව කෝෂ්ඨයෙහි පරිපාලක ප්‍රවේශය ලැබෙනු ඇත. -teams.read_access=කියවීමේ ප්‍රවේශය -teams.write_access=ලිවීමේ ප්‍රවේශය teams.admin_access=පරිපාලක ප්‍රවේශය teams.no_desc=මෙම කණ්ඩායමට සවිස්තරයක් නැත teams.settings=සැකසුම් diff --git a/options/locale/locale_sr-SP.ini b/options/locale/locale_sr-SP.ini index b2fc4427ad93..a83ab8ec5d38 100644 --- a/options/locale/locale_sr-SP.ini +++ b/options/locale/locale_sr-SP.ini @@ -498,8 +498,6 @@ members.invite_now=Позовите сада teams.join=Придружи се teams.leave=Изаћи -teams.read_access=Приступ за читање -teams.write_access=Приступ за писање teams.no_desc=Овај тим нема описа teams.settings=Подешавања teams.members=Чланови тима diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 3439ff208310..3aaff2b8e0f7 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -872,7 +872,6 @@ commits.signed_by_untrusted_user=Signerad av opålitlig användare commits.signed_by_untrusted_user_unmatched=Signerad av opålitlig användare som inte matchar den som committat commits.gpg_key_id=GPG-nyckel ID -ext_issues=Externa ärenden ext_issues.desc=Länk till externt ärendehanteringssystem. projects=Projekt @@ -1224,7 +1223,6 @@ signing.wont_sign.always=Commits signeras alltid signing.wont_sign.pubkey=Comitten kommer inte signeras på grund av att du inte har någon publik nyckel kopplad till kontot signing.wont_sign.twofa=Du måste ha tvåfaktorsautentisering aktiverad för att få commits signerade -ext_wiki=Extern Wiki ext_wiki.desc=Länk till extern wiki. wiki=Wiki @@ -1410,7 +1408,6 @@ settings.webhook_deletion_desc=Borttagning utav en webhook tar även bort dess i settings.webhook_deletion_success=Webhooken har blivit borttagen. settings.webhook.test_delivery=Testa Leverans settings.webhook.test_delivery_desc=Testa webhooken genom ett testevent. -settings.webhook.test_delivery_success=Ett testevent har lagts till i leveranskön. Det kan ta några sekunder innan det dyker upp i leveranshistoriken. settings.webhook.request=Begäran settings.webhook.response=Svar settings.webhook.headers=Huvuden @@ -1719,9 +1716,7 @@ teams.join=Gå med teams.leave=Gå ur teams.can_create_org_repo=Skapa utvecklingskataloger teams.can_create_org_repo_helper=Medlemmar kan skapa nya utvecklingskataloger i organisationen. Skaparen får administratörsåtkomst till den nya katalogen. -teams.read_access=Läsåtkomst teams.read_access_helper=Medlemmar kan se och klona teamets utvecklingskataloger. -teams.write_access=Skrivåtkomst teams.write_access_helper=Medlemmar kan läsa och pusha till teamets utvecklingskataloger. teams.admin_access=Administratörsåtkomst teams.admin_access_helper=Medlemmar kan pulla och pusha till teamets utvecklingskataloger och lägga till medarbetare till dessa. diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index b63727fc3a0c..18de292d1046 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -1044,7 +1044,6 @@ commits.signed_by_untrusted_user=Güvenilmeyen kullanıcı tarafından imzaland commits.signed_by_untrusted_user_unmatched=İşleyici ile eşleşmeyen güvenilmeyen kullanıcı tarafından imzalanmış commits.gpg_key_id=GPG Anahtar Kimliği -ext_issues=Dışsal Konular ext_issues.desc=Dışsal konu takip sistemine bağla. projects=Projeler @@ -1505,7 +1504,6 @@ signing.wont_sign.commitssigned=İlişkili tüm işlemeler imzalanmadığı içi signing.wont_sign.approved=Değişiklik İsteği onaylanmadığı için birleştirme imzalanmayacak signing.wont_sign.not_signed_in=Oturum açmadınız -ext_wiki=Harici Wiki ext_wiki.desc=Harici bir wiki'ye bağlantı. wiki=Wiki @@ -1750,7 +1748,6 @@ settings.webhook_deletion_desc=Bir web isteğini kaldırmak, ayarlarını ve tes settings.webhook_deletion_success=Web isteği silindi. settings.webhook.test_delivery=Test Dağıtımı settings.webhook.test_delivery_desc=Bu web isteğini sahte bir olayla test edin. -settings.webhook.test_delivery_success=Teslim sırasına sahte bir olay eklendi. Teslim geçmişinde görünmesi birkaç saniye sürebilir. settings.webhook.request=İstekler settings.webhook.response=Cevaplar settings.webhook.headers=Başlıklar @@ -2168,9 +2165,7 @@ teams.leave=Ayrıl teams.leave.detail=%s bırakılsın mı? teams.can_create_org_repo=Depoları oluştur teams.can_create_org_repo_helper=Üyeler organizasyonda yeni depolar oluşturabilirler. Oluşturan yeni depoya yönetici erişimi sağlayacak. -teams.read_access=Okuma Erişimi teams.read_access_helper=Üyeler, takım depolarını görüntüleyebilir ve klonlayabilir. -teams.write_access=Yazma Erişimi teams.write_access_helper=Üyeler takım depolarını okuyabilir ve itme yapabilir. teams.admin_access=Yönetici Erişimi teams.admin_access_helper=Üyeler takım depolarını çekip itebilir ve katkıcı ekleyebilir. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 11b23055de43..dbda3d525f1a 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1012,7 +1012,6 @@ commits.signed_by_untrusted_user=Підписаний недовіреним к commits.signed_by_untrusted_user_unmatched=Підписаний недовіреним користувачем, який не відповідає комітеру commits.gpg_key_id=Ідентифікатор GPG ключа -ext_issues=Зов. Проблеми ext_issues.desc=Посилання на зовнішню систему відстеження проблем. projects=Проєкти @@ -1462,7 +1461,6 @@ signing.wont_sign.commitssigned=Злиття не буде підписано, signing.wont_sign.approved=Злиття не буде підписано, оскільки PR не затверджено signing.wont_sign.not_signed_in=Ви не ввійшли -ext_wiki=Зов. Вікі ext_wiki.desc=Посилання на зовнішню вікі. wiki=Вікі @@ -1706,7 +1704,6 @@ settings.webhook_deletion_desc=Видалення цього веб-хука п settings.webhook_deletion_success=Webhook видалено. settings.webhook.test_delivery=Перевірити доставку settings.webhook.test_delivery_desc=Перевірте цей веб-хук з підробленою подією. -settings.webhook.test_delivery_success=Тест веб-хука був доданий в чергу доставки. Це може зайняти кілька секунд, перш ніж він відобразиться в історії доставки. settings.webhook.request=Запит settings.webhook.response=Відповідь settings.webhook.headers=Заголовки @@ -2116,9 +2113,7 @@ teams.join=Приєднатися teams.leave=Покинути teams.can_create_org_repo=Створити репозиторії teams.can_create_org_repo_helper=Учасники можуть створювати нові репозиторії в організації. Автор отримає доступ адміністратора до нового репозиторію. -teams.read_access=Доступ для читання teams.read_access_helper=Учасники можуть переглядати та клонувати репозиторії команд. -teams.write_access=Доступ на запис teams.write_access_helper=Учасники можуть читати і виконувати push в репозиторії команд. teams.admin_access=Доступ адміністратора teams.admin_access_helper=Учасники можуть виконувати pull, push в репозиторії команд і додавати співавторів в команду. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index a35263d48c80..73e9070139ac 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1099,7 +1099,6 @@ commits.signed_by_untrusted_user_unmatched=由与提交者不匹配的未授信 commits.gpg_key_id=GPG 密钥 ID commits.ssh_key_fingerprint=SSH 密钥指纹 -ext_issues=外部工单 ext_issues.desc=链接到外部工单跟踪系统。 projects=项目 @@ -1579,7 +1578,6 @@ signing.wont_sign.commitssigned=合并将不会被签名,因为所有相关的 signing.wont_sign.approved=合并将不会被签名,因为合并请求未被批准 signing.wont_sign.not_signed_in=您还没有登录。 -ext_wiki=外部百科 ext_wiki.desc=链接到外部 wiki。 wiki=百科 @@ -1831,7 +1829,6 @@ settings.webhook_deletion_desc=删除 web钩子 将删除其设置和历史记 settings.webhook_deletion_success=Web 钩子删除成功! settings.webhook.test_delivery=测试推送 settings.webhook.test_delivery_desc=用假事件测试这个 web钩子。 -settings.webhook.test_delivery_success=测试推送已经加入到队列,请耐心等待数秒再刷新推送记录。 settings.webhook.request=请求内容 settings.webhook.response=响应内容 settings.webhook.headers=头信息 @@ -2261,9 +2258,7 @@ teams.leave=离开团队 teams.leave.detail=离开 %s? teams.can_create_org_repo=创建仓库 teams.can_create_org_repo_helper=成员可以在组织中创建仓库。创建者将自动获得创建的仓库的管理员权限。 -teams.read_access=读取权限 teams.read_access_helper=成员可以查看和克隆团队仓库。 -teams.write_access=写入权限 teams.write_access_helper=成员可以查看和推送提交到团队仓库。 teams.admin_access=管理员权限 teams.admin_access_helper=成员可以拉取和推送到团队仓库同时可以添加协作者。 diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini index b6ce1b28e123..28cb75a8b5c4 100644 --- a/options/locale/locale_zh-HK.ini +++ b/options/locale/locale_zh-HK.ini @@ -594,8 +594,6 @@ members.invite_now=立即邀請 teams.join=加入團隊 teams.leave=離開團隊 -teams.read_access=讀取權限 -teams.write_access=寫入權限 teams.no_desc=該團隊暫無描述 teams.settings=團隊設定 teams.members=團隊成員 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 8f49ab4e75b6..2329060dcf28 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -1099,7 +1099,7 @@ commits.signed_by_untrusted_user_unmatched=由不受信任且與提交者不相 commits.gpg_key_id=GPG 金鑰 ID commits.ssh_key_fingerprint=SSH 金鑰指紋 -ext_issues=外部問題 +ext_issues=存取外部問題 ext_issues.desc=連結到外部問題追蹤器。 projects=專案 @@ -1579,7 +1579,7 @@ signing.wont_sign.commitssigned=將不會簽署此合併,因為所有關聯的 signing.wont_sign.approved=合併請求未被核可,所以不會簽署此合併。 signing.wont_sign.not_signed_in=你還沒有登入 -ext_wiki=外部 Wiki +ext_wiki=存取外部 Wiki ext_wiki.desc=連結外部 Wiki。 wiki=Wiki @@ -1830,13 +1830,14 @@ settings.webhook_deletion=移除 Webhook settings.webhook_deletion_desc=移除 Webhook 將刪除它的設定及傳送記錄,是否繼續? settings.webhook_deletion_success=Webhook 已移除。 settings.webhook.test_delivery=傳送測試資料 -settings.webhook.test_delivery_desc=使用假事件測試這個 Webhook -settings.webhook.test_delivery_success=已將假事件加入到傳送佇列,可能需要等待幾分鐘才會出現於傳送紀錄。 +settings.webhook.test_delivery_desc=使用假事件測試此 Webhook。 settings.webhook.request=請求 settings.webhook.response=回應 settings.webhook.headers=標頭 settings.webhook.payload=內容 settings.webhook.body=本體 +settings.webhook.replay.description=再次執行此 Webhook。 +settings.webhook.delivery.success=已將事件加入到傳送佇列,可能需要等待幾分鐘才會出現於傳送紀錄。 settings.githooks_desc=Git Hook 是 Git 本身提供的功能。您可以在下方編輯 hook 檔案以設定自訂作業。 settings.githook_edit_desc=如果 Hook 未啟動,則會顯示範例文件中的內容。如果想要刪除某個 Hook,則送出空白內容即可。 settings.githook_name=Hook 名稱 @@ -2261,9 +2262,13 @@ teams.leave=離開 teams.leave.detail=確定要離開 %s 嗎? teams.can_create_org_repo=建立儲存庫 teams.can_create_org_repo_helper=成員可以在組織中新增儲存庫。建立者將自動取得新儲存庫的管理員權限。 -teams.read_access=讀取權限 +teams.none_access=沒有權限 +teams.none_access_helper=成員無法檢視此單元或對其執行其他動作。 +teams.general_access=一般權限 +teams.general_access_helper=成員權限將由下列權限表決定。 +teams.read_access=讀取 teams.read_access_helper=成員可以查看和 Clone 團隊儲存庫。 -teams.write_access=寫入權限 +teams.write_access=寫入 teams.write_access_helper=成員可以查看和推送到團隊儲存庫。 teams.admin_access=管理員權限 teams.admin_access_helper=成員可以拉取、推送和新增協作者到團隊儲存庫中。 @@ -2892,6 +2897,7 @@ error.probable_bad_signature=警告!雖然資料庫中有此 ID 的金鑰, error.probable_bad_default_signature=警告!雖然預設金鑰擁有此 ID,但此提交未通過它的驗證!此提交是有疑慮的。 [units] +unit=單元 error.no_unit_allowed_repo=您未被允許存取此儲存庫的任何區域。 error.unit_not_allowed=您未被允許訪問此儲存庫區域 diff --git a/package-lock.json b/package-lock.json index d32846cf6dc0..4dfd3753c81f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "less": "4.1.2", "less-loader": "10.2.0", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "8.13.4", + "mermaid": "8.13.8", "mini-css-extract-plugin": "2.4.5", "monaco-editor": "0.30.1", "monaco-editor-webpack-plugin": "6.0.0", @@ -3751,9 +3751,9 @@ } }, "node_modules/dompurify": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.3.tgz", - "integrity": "sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg==" + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.4.tgz", + "integrity": "sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ==" }, "node_modules/domutils": { "version": "2.8.0", @@ -7476,15 +7476,15 @@ } }, "node_modules/mermaid": { - "version": "8.13.4", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.13.4.tgz", - "integrity": "sha512-zdWtsXabVy1PEAE25Jkm4zbTDlQe8rqNlTMq2B3j+D+NxDskJEY5OsgalarvNLsw+b5xFa1a8D1xcm/PijrDow==", + "version": "8.13.8", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.13.8.tgz", + "integrity": "sha512-Z5v31rvo8P7BPTiGicdJl9BbzyUe9s5sXILK8sM1g7ijkagpfFjPtXZVsq5P1WlN8m/fUp2PPNXVF9SqeTM91w==", "dependencies": { "@braintree/sanitize-url": "^3.1.0", "d3": "^7.0.0", "dagre": "^0.8.5", "dagre-d3": "^0.6.4", - "dompurify": "2.3.3", + "dompurify": "2.3.4", "graphlib": "^2.1.8", "khroma": "^1.4.1", "moment-mini": "^2.24.0", @@ -13443,9 +13443,9 @@ } }, "dompurify": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.3.tgz", - "integrity": "sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg==" + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.4.tgz", + "integrity": "sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ==" }, "domutils": { "version": "2.8.0", @@ -16202,15 +16202,15 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, "mermaid": { - "version": "8.13.4", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.13.4.tgz", - "integrity": "sha512-zdWtsXabVy1PEAE25Jkm4zbTDlQe8rqNlTMq2B3j+D+NxDskJEY5OsgalarvNLsw+b5xFa1a8D1xcm/PijrDow==", + "version": "8.13.8", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.13.8.tgz", + "integrity": "sha512-Z5v31rvo8P7BPTiGicdJl9BbzyUe9s5sXILK8sM1g7ijkagpfFjPtXZVsq5P1WlN8m/fUp2PPNXVF9SqeTM91w==", "requires": { "@braintree/sanitize-url": "^3.1.0", "d3": "^7.0.0", "dagre": "^0.8.5", "dagre-d3": "^0.6.4", - "dompurify": "2.3.3", + "dompurify": "2.3.4", "graphlib": "^2.1.8", "khroma": "^1.4.1", "moment-mini": "^2.24.0", diff --git a/package.json b/package.json index f292fc0706cb..d2ca602a2f94 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "less": "4.1.2", "less-loader": "10.2.0", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "8.13.4", + "mermaid": "8.13.8", "mini-css-extract-plugin": "2.4.5", "monaco-editor": "0.30.1", "monaco-editor-webpack-plugin": "6.0.0", diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index 75246c3acb62..bff6a039e886 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -14,6 +14,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/highlight" @@ -39,6 +40,7 @@ type blameRow struct { CommitMessage string CommitSince gotemplate.HTML Code gotemplate.HTML + EscapeStatus charset.EscapeStatus } // RefBlame render blame page @@ -233,6 +235,7 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m } var lines = make([]string, 0) rows := make([]*blameRow, 0) + escapeStatus := charset.EscapeStatus{} var i = 0 var commitCnt = 0 @@ -277,11 +280,14 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m fileName := fmt.Sprintf("%v", ctx.Data["FileName"]) line = highlight.Code(fileName, language, line) + br.EscapeStatus, line = charset.EscapeControlString(line) br.Code = gotemplate.HTML(line) rows = append(rows, br) + escapeStatus = escapeStatus.Or(br.EscapeStatus) } } + ctx.Data["EscapeStatus"] = escapeStatus ctx.Data["BlameRows"] = rows ctx.Data["CommitCnt"] = commitCnt } diff --git a/routers/web/repo/lfs.go b/routers/web/repo/lfs.go index 6cc05430dde0..8943641381f6 100644 --- a/routers/web/repo/lfs.go +++ b/routers/web/repo/lfs.go @@ -300,10 +300,11 @@ func LFSFileGet(ctx *context.Context) { rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc)) // Building code view blocks with line number on server side. - fileContent, _ := io.ReadAll(rd) + escapedContent := &bytes.Buffer{} + ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, escapedContent) var output bytes.Buffer - lines := strings.Split(string(fileContent), "\n") + lines := strings.Split(escapedContent.String(), "\n") //Remove blank line at the end of file if len(lines) > 0 && lines[len(lines)-1] == "" { lines = lines[:len(lines)-1] diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 384681caf6d1..e8c02b64b87c 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -339,21 +339,24 @@ func renderDirectory(ctx *context.Context, treeLink string) { }, rd, &result) if err != nil { log.Error("Render failed: %v then fallback", err) - bs, _ := io.ReadAll(rd) + buf := &bytes.Buffer{} + ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf) ctx.Data["FileContent"] = strings.ReplaceAll( - gotemplate.HTMLEscapeString(string(bs)), "\n", `
`, + gotemplate.HTMLEscapeString(buf.String()), "\n", `
`, ) } else { - ctx.Data["FileContent"] = result.String() + ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String()) } } else { ctx.Data["IsRenderedHTML"] = true - buf, err = io.ReadAll(rd) + buf := &bytes.Buffer{} + ctx.Data["EscapeStatus"], err = charset.EscapeControlReader(rd, buf) if err != nil { - log.Error("ReadAll failed: %v", err) + log.Error("Read failed: %v", err) } + ctx.Data["FileContent"] = strings.ReplaceAll( - gotemplate.HTMLEscapeString(string(buf)), "\n", `
`, + gotemplate.HTMLEscapeString(buf.String()), "\n", `
`, ) } } @@ -502,12 +505,15 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.ServerError("Render", err) return } - ctx.Data["FileContent"] = result.String() + ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String()) } else if readmeExist { - buf, _ := io.ReadAll(rd) + buf := &bytes.Buffer{} ctx.Data["IsRenderedHTML"] = true + + ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf) + ctx.Data["FileContent"] = strings.ReplaceAll( - gotemplate.HTMLEscapeString(string(buf)), "\n", `
`, + gotemplate.HTMLEscapeString(buf.String()), "\n", `
`, ) } else { buf, _ := io.ReadAll(rd) @@ -540,7 +546,15 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st language = "" } } - ctx.Data["FileContent"] = highlight.File(lineNums, blob.Name(), language, buf) + fileContent := highlight.File(lineNums, blob.Name(), language, buf) + status, _ := charset.EscapeControlReader(bytes.NewReader(buf), io.Discard) + ctx.Data["EscapeStatus"] = status + statuses := make([]charset.EscapeStatus, len(fileContent)) + for i, line := range fileContent { + statuses[i], fileContent[i] = charset.EscapeControlString(line) + } + ctx.Data["FileContent"] = fileContent + ctx.Data["LineEscapeStatus"] = statuses } if !isLFSFile { if ctx.Repo.CanEnableEditor() { @@ -588,7 +602,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.ServerError("Render", err) return } - ctx.Data["FileContent"] = result.String() + + ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String()) } } diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index d449800b8441..d8666c7a2945 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" @@ -232,7 +233,8 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { ctx.ServerError("Render", err) return nil, nil } - ctx.Data["content"] = buf.String() + + ctx.Data["EscapeStatus"], ctx.Data["content"] = charset.EscapeControlString(buf.String()) buf.Reset() if err := markdown.Render(rctx, bytes.NewReader(sidebarContent), &buf); err != nil { @@ -243,7 +245,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { return nil, nil } ctx.Data["sidebarPresent"] = sidebarContent != nil - ctx.Data["sidebarContent"] = buf.String() + ctx.Data["sidebarEscapeStatus"], ctx.Data["sidebarContent"] = charset.EscapeControlString(buf.String()) buf.Reset() if err := markdown.Render(rctx, bytes.NewReader(footerContent), &buf); err != nil { @@ -254,7 +256,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { return nil, nil } ctx.Data["footerPresent"] = footerContent != nil - ctx.Data["footerContent"] = buf.String() + ctx.Data["footerEscapeStatus"], ctx.Data["footerContent"] = charset.EscapeControlString(buf.String()) // get commit count - wiki revisions commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename) diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 166660b87e18..292c270b7e3e 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -169,11 +169,11 @@ func getDiffLineSectionInfo(treePath, line string, lastLeftIdx, lastRightIdx int } // escape a line's content or return
needed for copy/paste purposes -func getLineContent(content string) string { +func getLineContent(content string) DiffInline { if len(content) > 0 { - return html.EscapeString(content) + return DiffInlineWithUnicodeEscape(template.HTML(html.EscapeString(content))) } - return "
" + return DiffInline{Content: "
"} } // DiffSection represents a section of a DiffFile. @@ -411,7 +411,7 @@ func fixupBrokenSpans(diffs []diffmatchpatch.Diff) []diffmatchpatch.Diff { return fixedup } -func diffToHTML(fileName string, diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML { +func diffToHTML(fileName string, diffs []diffmatchpatch.Diff, lineType DiffLineType) DiffInline { buf := bytes.NewBuffer(nil) match := "" @@ -483,7 +483,7 @@ func diffToHTML(fileName string, diffs []diffmatchpatch.Diff, lineType DiffLineT buf.Write(codeTagSuffix) } } - return template.HTML(buf.Bytes()) + return DiffInlineWithUnicodeEscape(template.HTML(buf.String())) } // GetLine gets a specific line by type (add or del) and file line number @@ -535,10 +535,28 @@ func init() { diffMatchPatch.DiffEditCost = 100 } +// DiffInline is a struct that has a content and escape status +type DiffInline struct { + EscapeStatus charset.EscapeStatus + Content template.HTML +} + +// DiffInlineWithUnicodeEscape makes a DiffInline with hidden unicode characters escaped +func DiffInlineWithUnicodeEscape(s template.HTML) DiffInline { + status, content := charset.EscapeControlString(string(s)) + return DiffInline{EscapeStatus: status, Content: template.HTML(content)} +} + +// DiffInlineWithHighlightCode makes a DiffInline with code highlight and hidden unicode characters escaped +func DiffInlineWithHighlightCode(fileName, language, code string) DiffInline { + status, content := charset.EscapeControlString(highlight.Code(fileName, language, code)) + return DiffInline{EscapeStatus: status, Content: template.HTML(content)} +} + // GetComputedInlineDiffFor computes inline diff for the given line. -func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML { +func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) DiffInline { if setting.Git.DisableDiffHighlight { - return template.HTML(getLineContent(diffLine.Content[1:])) + return getLineContent(diffLine.Content[1:]) } var ( @@ -555,26 +573,26 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) tem // try to find equivalent diff line. ignore, otherwise switch diffLine.Type { case DiffLineSection: - return template.HTML(getLineContent(diffLine.Content[1:])) + return getLineContent(diffLine.Content[1:]) case DiffLineAdd: compareDiffLine = diffSection.GetLine(DiffLineDel, diffLine.RightIdx) if compareDiffLine == nil { - return template.HTML(highlight.Code(diffSection.FileName, language, diffLine.Content[1:])) + return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:]) } diff1 = compareDiffLine.Content diff2 = diffLine.Content case DiffLineDel: compareDiffLine = diffSection.GetLine(DiffLineAdd, diffLine.LeftIdx) if compareDiffLine == nil { - return template.HTML(highlight.Code(diffSection.FileName, language, diffLine.Content[1:])) + return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:]) } diff1 = diffLine.Content diff2 = compareDiffLine.Content default: if strings.IndexByte(" +-", diffLine.Content[0]) > -1 { - return template.HTML(highlight.Code(diffSection.FileName, language, diffLine.Content[1:])) + return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:]) } - return template.HTML(highlight.Code(diffSection.FileName, language, diffLine.Content)) + return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content) } diffRecord := diffMatchPatch.DiffMain(highlight.Code(diffSection.FileName, language, diff1[1:]), highlight.Code(diffSection.FileName, language, diff2[1:]), true) diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index 21afdb4cac44..b64ac092aada 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -38,14 +38,14 @@ func TestDiffToHTML(t *testing.T) { {Type: dmp.DiffInsert, Text: "bar"}, {Type: dmp.DiffDelete, Text: " baz"}, {Type: dmp.DiffEqual, Text: " biz"}, - }, DiffLineAdd)) + }, DiffLineAdd).Content) assertEqual(t, "foo bar biz", diffToHTML("", []dmp.Diff{ {Type: dmp.DiffEqual, Text: "foo "}, {Type: dmp.DiffDelete, Text: "bar"}, {Type: dmp.DiffInsert, Text: " baz"}, {Type: dmp.DiffEqual, Text: " biz"}, - }, DiffLineDel)) + }, DiffLineDel).Content) assertEqual(t, "if !nohl && (lexer != nil || r.GuessLanguage) {", diffToHTML("", []dmp.Diff{ {Type: dmp.DiffEqual, Text: "if !nohl && lexer != nil"}, {Type: dmp.DiffInsert, Text: " || r.GuessLanguage)"}, {Type: dmp.DiffEqual, Text: " {"}, - }, DiffLineAdd)) + }, DiffLineAdd).Content) assertEqual(t, "tagURL := fmt.Sprintf("## [%s](%s/%s/%s/%s?q=&type=all&state=closed&milestone=%d) - %s", ge.Milestone\", ge.BaseURL, ge.Owner, ge.Repo, from, milestoneID, time.Now().Format("2006-01-02"))", diffToHTML("", []dmp.Diff{ {Type: dmp.DiffEqual, Text: "tagURL := , milestoneID, time.Now().Format("2006-01-02")"}, {Type: dmp.DiffInsert, Text: "ge.Milestone, from, milestoneID"}, {Type: dmp.DiffEqual, Text: ")"}, - }, DiffLineDel)) + }, DiffLineDel).Content) assertEqual(t, "r.WrapperRenderer(w, language, true, attrs, false)", diffToHTML("", []dmp.Diff{ {Type: dmp.DiffEqual, Text: "r.WrapperRenderer(w, "}, @@ -71,14 +71,14 @@ func TestDiffToHTML(t *testing.T) { {Type: dmp.DiffEqual, Text: "c"}, {Type: dmp.DiffDelete, Text: "lass=\"p\">, true, attrs"}, {Type: dmp.DiffEqual, Text: ", false)"}, - }, DiffLineDel)) + }, DiffLineDel).Content) assertEqual(t, "language, true, attrs, false)", diffToHTML("", []dmp.Diff{ {Type: dmp.DiffInsert, Text: "language, true, attrs"}, {Type: dmp.DiffEqual, Text: ", false)"}, - }, DiffLineAdd)) + }, DiffLineAdd).Content) assertEqual(t, "print("// ", sys.argv)", diffToHTML("", []dmp.Diff{ {Type: dmp.DiffEqual, Text: "print"}, @@ -87,14 +87,14 @@ func TestDiffToHTML(t *testing.T) { {Type: dmp.DiffInsert, Text: "class=\"p\">("}, {Type: dmp.DiffEqual, Text: ""// ", sys.argv"}, {Type: dmp.DiffInsert, Text: ")"}, - }, DiffLineAdd)) + }, DiffLineAdd).Content) assertEqual(t, "sh 'useradd -u $(stat -c "%u" .gitignore) jenkins'", diffToHTML("", []dmp.Diff{ {Type: dmp.DiffEqual, Text: "sh "}, {Type: dmp.DiffDelete, Text: "4;useradd -u 111 jenkins""}, {Type: dmp.DiffInsert, Text: "9;useradd -u $(stat -c "%u" .gitignore) jenkins'"}, {Type: dmp.DiffEqual, Text: ";"}, - }, DiffLineAdd)) + }, DiffLineAdd).Content) assertEqual(t, " <h4 class="release-list-title df ac">", diffToHTML("", []dmp.Diff{ {Type: dmp.DiffEqual, Text: " <h"}, @@ -102,7 +102,7 @@ func TestDiffToHTML(t *testing.T) { {Type: dmp.DiffEqual, Text: "3"}, {Type: dmp.DiffInsert, Text: "4;release-list-title df ac""}, {Type: dmp.DiffEqual, Text: ">"}, - }, DiffLineAdd)) + }, DiffLineAdd).Content) } func TestParsePatch_skipTo(t *testing.T) { @@ -718,7 +718,7 @@ func TestDiffToHTML_14231(t *testing.T) { expected := ` run(db)` output := diffToHTML("main.v", diffRecord, DiffLineAdd) - assertEqual(t, expected, output) + assertEqual(t, expected, output.Content) } func TestNoCrashes(t *testing.T) { diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl index cdd31c0eba7a..3dc3522275b1 100644 --- a/templates/repo/blame.tmpl +++ b/templates/repo/blame.tmpl @@ -16,11 +16,13 @@ {{end}} {{.i18n.Tr "repo.normal_view"}} {{.i18n.Tr "repo.file_history"}} + {{.i18n.Tr "repo.unescape_control_characters"}} +
-
+
{{range $row := .BlameRows}} @@ -52,6 +54,9 @@ + {{if $.EscapeStatus.Escaped}} + + {{end}} diff --git a/templates/repo/diff/blob_excerpt.tmpl b/templates/repo/diff/blob_excerpt.tmpl index 792c539ac592..e529ed3bcd07 100644 --- a/templates/repo/diff/blob_excerpt.tmpl +++ b/templates/repo/diff/blob_excerpt.tmpl @@ -19,14 +19,26 @@ {{end}} - + {{else}} - + - + {{end}} {{end}} diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index 3ab7a11bbd1f..f115a5f49941 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -94,6 +94,10 @@ {{if $file.IsProtected}} {{$.i18n.Tr "repo.diff.protected"}} {{end}} + {{if not (or $file.IsIncomplete $file.IsBin $file.IsSubmodule)}} + {{$.i18n.Tr "repo.unescape_control_characters"}} + + {{end}} {{if and (not $file.IsSubmodule) (not $.PageIsWiki)}} {{if $file.IsDeleted}} {{$.i18n.Tr "repo.diff.view_file"}} @@ -104,7 +108,7 @@
-
+
{{if or $file.IsIncomplete $file.IsBin}}
{{if $file.IsIncomplete}} diff --git a/templates/repo/diff/section_split.tmpl b/templates/repo/diff/section_split.tmpl index 81223642db79..754f7cec10e0 100644 --- a/templates/repo/diff/section_split.tmpl +++ b/templates/repo/diff/section_split.tmpl @@ -21,23 +21,75 @@ {{svg "octicon-fold"}} {{end}} - -
+ {{$inlineDiff := $section.GetComputedInlineDiffFor $line}} + + {{else if and (eq .GetType 3) $hasmatch}}{{/* DEL */}} {{$match := index $section.Lines $line.Match}} + {{- $leftDiff := ""}}{{if $line.LeftIdx}}{{$leftDiff = $section.GetComputedInlineDiffFor $line}}{{end}} + {{- $rightDiff := ""}}{{if $match.RightIdx}}{{$rightDiff = $section.GetComputedInlineDiffFor $match}}{{end}} + - + + - + {{else}} + {{$inlineDiff := $section.GetComputedInlineDiffFor $line}} + - + + - + {{end}} {{if and (eq .GetType 3) $hasmatch}} @@ -45,6 +97,7 @@ {{if or (gt (len $line.Comments) 0) (gt (len $match.Comments) 0)}} + + + + {{end}} + {{$inlineDiff := $section.GetComputedInlineDiffFor $line -}} + {{if eq .GetType 4}} - + {{else}} - + {{end}} {{if gt (len $line.Comments) 0}} - + diff --git a/templates/repo/editor/diff_preview.tmpl b/templates/repo/editor/diff_preview.tmpl index 0ed330c57bd3..e6956648ca8b 100644 --- a/templates/repo/editor/diff_preview.tmpl +++ b/templates/repo/editor/diff_preview.tmpl @@ -1,6 +1,6 @@
-
+
{{if $row.EscapeStatus.Escaped}}{{end}} {{$row.Code}} {{$.section.GetComputedInlineDiffFor $line}}{{$inlineDiff := $.section.GetComputedInlineDiffFor $line}}{{$inlineDiff}}{{$inlineDiff.Content}} {{if $line.LeftIdx}}{{end}}{{if $line.LeftIdx}}{{$.section.GetComputedInlineDiffFor $line}}{{end}}{{/* + */}}{{if $line.LeftIdx}}{{/* + */}}{{$inlineDiff := $.section.GetComputedInlineDiffFor $line}}{{$inlineDiff.Content}}{{/* + */}}{{else}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}} {{if $line.RightIdx}}{{end}}{{if $line.RightIdx}}{{$.section.GetComputedInlineDiffFor $line}}{{end}}{{/* + */}}{{if $line.RightIdx}}{{/* + */}}{{$inlineDiff := $.section.GetComputedInlineDiffFor $line}}{{$inlineDiff.Content}}{{/* + */}}{{else}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}}
{{$section.GetComputedInlineDiffFor $line}}{{if $inlineDiff.EscapeStatus.Escaped}}{{end}}{{$inlineDiff.Content}}{{if $line.LeftIdx}}{{if $leftDiff.EscapeStatus.Escaped}}{{end}}{{end}} {{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{svg "octicon-plus"}}{{end}}{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}{{/* + */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{/* + */}}{{/* + */}}{{svg "octicon-plus"}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}}{{if $line.LeftIdx}}{{/* + */}}{{$leftDiff.Content}}{{/* + */}}{{else}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}} {{if $match.RightIdx}}{{if $rightDiff.EscapeStatus.Escaped}}{{end}}{{end}} {{if $match.RightIdx}}{{end}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{svg "octicon-plus"}}{{end}}{{if $match.RightIdx}}{{$section.GetComputedInlineDiffFor $match}}{{end}}{{/* + */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{/* + */}}{{/* + */}}{{svg "octicon-plus"}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}}{{if $match.RightIdx}}{{/* + */}}{{$rightDiff.Content}}{{/* + */}}{{else}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}}{{if $line.LeftIdx}}{{if $inlineDiff.EscapeStatus.Escaped}}{{end}}{{end}} {{if $line.LeftIdx}}{{end}}{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 2))}}{{svg "octicon-plus"}}{{end}}{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}{{/* + */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 2))}}{{/* + */}}{{/* + */}}{{svg "octicon-plus"}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}}{{if $line.LeftIdx}}{{/* + */}}{{$inlineDiff.Content}}{{/* + */}}{{else}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}} {{if $line.RightIdx}}{{if $inlineDiff.EscapeStatus.Escaped}}{{end}}{{end}} {{if $line.RightIdx}}{{end}}{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 3))}}{{svg "octicon-plus"}}{{end}}{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}{{/* + */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 3))}}{{/* + */}}{{/* + */}}{{svg "octicon-plus"}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}}{{if $line.RightIdx}}{{/* + */}}{{$inlineDiff.Content}}{{/* + */}}{{else}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}}
{{if gt (len $line.Comments) 0}} @@ -59,6 +112,7 @@ {{end}} {{if eq $line.GetCommentSide "proposed"}} @@ -75,6 +129,7 @@ {{else if gt (len $line.Comments) 0}}
{{if gt (len $line.Comments) 0}} @@ -84,6 +139,7 @@ {{end}} {{if eq $line.GetCommentSide "proposed"}} diff --git a/templates/repo/diff/section_unified.tmpl b/templates/repo/diff/section_unified.tmpl index 74634a760f50..93f9af52b474 100644 --- a/templates/repo/diff/section_unified.tmpl +++ b/templates/repo/diff/section_unified.tmpl @@ -25,16 +25,29 @@ {{if $inlineDiff.EscapeStatus.Escaped}}{{end}} {{$section.GetComputedInlineDiffFor $line}}{{/* + */}}{{$inlineDiff.Content}}{{/* + */}} + {{$line.Content}} + {{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{svg "octicon-plus"}}{{end}}{{$section.GetComputedInlineDiffFor $line}}{{/* + */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{/* + */}}{{/* + */}}{{svg "octicon-plus"}}{{/* + */}}{{/* + */}}{{end}}{{/* + */}}{{$inlineDiff.Content}}{{/* + */}}
{{template "repo/diff/conversation" mergeinto $.root "comments" $line.Comments}}
{{template "repo/diff/section_unified" dict "file" .File "root" $}} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 026e1de0fd1a..3242a5b3e563 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -512,7 +512,7 @@ {{$file := (index $diff.Files 0)}}
-
+
{{template "repo/diff/section_unified" dict "file" $file "root" $}} diff --git a/templates/repo/settings/lfs_file.tmpl b/templates/repo/settings/lfs_file.tmpl index f6510f17db31..a4d6b21f1c9e 100644 --- a/templates/repo/settings/lfs_file.tmpl +++ b/templates/repo/settings/lfs_file.tmpl @@ -8,10 +8,15 @@

{{.i18n.Tr "repo.settings.lfs"}} / {{.LFSFile.Oid}}

+ {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
{{if .IsMarkup}} {{if .FileContent}}{{.FileContent | Safe}}{{end}} diff --git a/templates/repo/unicode_escape_prompt.tmpl b/templates/repo/unicode_escape_prompt.tmpl new file mode 100644 index 000000000000..d45df012e159 --- /dev/null +++ b/templates/repo/unicode_escape_prompt.tmpl @@ -0,0 +1,17 @@ +{{if .EscapeStatus.BadBIDI}} +
+ {{svg "octicon-x" 16 "close inside"}} +
+ {{$.root.i18n.Tr "repo.bidi_bad_header"}} +
+

{{$.root.i18n.Tr "repo.bidi_bad_description" | Str2html}}

+
+{{else if .EscapeStatus.Escaped}} +
+ {{svg "octicon-x" 16 "close inside"}} +
+ {{$.root.i18n.Tr "repo.unicode_header"}} +
+

{{$.root.i18n.Tr "repo.unicode_description" | Str2html}}

+
+{{end}} diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 6bd54cc8e53a..d5308c154b4e 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -30,7 +30,6 @@
{{end}}
- {{if not .ReadmeInList}}
{{if .HasSourceRenderedToggle}}
@@ -38,33 +37,42 @@ {{svg "octicon-file" 15}}
{{end}} -
- {{.i18n.Tr "repo.file_raw"}} - {{if not .IsViewCommit}} - {{.i18n.Tr "repo.file_permalink"}} - {{end}} - {{if .IsRepresentableAsText}} - {{.i18n.Tr "repo.blame"}} - {{end}} - {{.i18n.Tr "repo.file_history"}} -
- {{svg "octicon-download"}} - {{if .Repository.CanEnableEditor}} - {{if .CanEditFile}} - {{svg "octicon-pencil"}} - {{else}} - {{svg "octicon-pencil"}} - {{end}} - {{if .CanDeleteFile}} - {{svg "octicon-trash"}} - {{else}} - {{svg "octicon-trash"}} + {{if not .ReadmeInList}} +
+ {{.i18n.Tr "repo.file_raw"}} + {{if not .IsViewCommit}} + {{.i18n.Tr "repo.file_permalink"}} + {{end}} + {{if .IsRepresentableAsText}} + {{.i18n.Tr "repo.blame"}} + {{end}} + {{.i18n.Tr "repo.file_history"}} + {{if .EscapeStatus.Escaped}} + + {{.i18n.Tr "repo.escape_control_characters"}} + {{end}} +
+ {{svg "octicon-download"}} + {{if .Repository.CanEnableEditor}} + {{if .CanEditFile}} + {{svg "octicon-pencil"}} + {{else}} + {{svg "octicon-pencil"}} + {{end}} + {{if .CanDeleteFile}} + {{svg "octicon-trash"}} + {{else}} + {{svg "octicon-trash"}} + {{end}} {{end}} + {{else if .EscapeStatus.Escaped}} + + {{.i18n.Tr "repo.escape_control_characters"}} {{end}}
- {{end}}
+ {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
{{if .IsMarkup}} {{if .FileContent}}{{.FileContent | Safe}}{{end}} @@ -104,6 +112,9 @@ {{$line := Add $idx 1}}
+ {{if $.EscapeStatus.Escaped}} + + {{end}} {{end}} diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index b71c950e1747..db0ce148785f 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -45,6 +45,10 @@
+ {{if .EscapeStatus.Escaped}} + + {{.i18n.Tr "repo.escape_control_characters"}} + {{end}} {{if and .CanWriteWiki (not .Repository.IsMirror)}} {{end}}
-
- {{.content | Str2html}} +
+ {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}} + {{.content | Safe}}
{{if .sidebarPresent}}
-
+
{{end}}
{{if .footerPresent}} -
- {{if and .CanWriteWiki (not .Repository.IsMirror)}} - {{svg "octicon-pencil"}} - {{end}} - {{.footerContent | Str2html}} + {{end}}
diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl index 3e1ef95d9826..b006ca5186bf 100644 --- a/templates/user/dashboard/milestones.tmpl +++ b/templates/user/dashboard/milestones.tmpl @@ -117,7 +117,7 @@
{{end}} {{if .Content}} -
+
{{.RenderedContent|Str2html}}
{{end}} diff --git a/vendor/github.com/shurcooL/vfsgen/generator.go b/vendor/github.com/shurcooL/vfsgen/generator.go index c0067c5d3625..dfe6c24a5eda 100644 --- a/vendor/github.com/shurcooL/vfsgen/generator.go +++ b/vendor/github.com/shurcooL/vfsgen/generator.go @@ -30,7 +30,9 @@ func Generate(input http.FileSystem, opt Options) error { return err } - var toc toc + var toc = toc{ + UseGlobalModTime: opt.UseGlobalModTime, + } err = findAndWriteFiles(buf, input, &toc) if err != nil { return err @@ -56,6 +58,8 @@ type toc struct { HasCompressedFile bool // There's at least one compressedFile. HasFile bool // There's at least one uncompressed file. + UseGlobalModTime bool // copy from opt + } // fileInfo is a definition of a file. @@ -64,14 +68,16 @@ type fileInfo struct { Name string ModTime time.Time UncompressedSize int64 + UseGlobalModTime bool } // dirInfo is a definition of a directory. type dirInfo struct { - Path string - Name string - ModTime time.Time - Entries []string + Path string + Name string + ModTime time.Time + Entries []string + UseGlobalModTime bool } // findAndWriteFiles recursively finds all the file paths in the given directory tree. @@ -91,6 +97,7 @@ func findAndWriteFiles(buf *bytes.Buffer, fs http.FileSystem, toc *toc) error { Name: pathpkg.Base(path), ModTime: fi.ModTime().UTC(), UncompressedSize: fi.Size(), + UseGlobalModTime: toc.UseGlobalModTime, } marker := buf.Len() @@ -125,10 +132,11 @@ func findAndWriteFiles(buf *bytes.Buffer, fs http.FileSystem, toc *toc) error { } dir := &dirInfo{ - Path: path, - Name: pathpkg.Base(path), - ModTime: fi.ModTime().UTC(), - Entries: entries, + Path: path, + Name: pathpkg.Base(path), + ModTime: fi.ModTime().UTC(), + Entries: entries, + UseGlobalModTime: toc.UseGlobalModTime, } toc.dirs = append(toc.dirs, dir) @@ -242,7 +250,9 @@ var {{.VariableName}} = func() http.FileSystem { {{define "CompressedFileInfo-Before"}} {{quote .Path}}: &vfsgen۰CompressedFileInfo{ name: {{quote .Name}}, + {{if not .UseGlobalModTime}} modTime: {{template "Time" .ModTime}}, + {{end}} uncompressedSize: {{.UncompressedSize}}, {{/* This blank line separating compressedContent is neccessary to prevent potential gofmt issues. See issue #19. */}} compressedContent: []byte("{{end}}{{define "CompressedFileInfo-After"}}"), @@ -253,7 +263,9 @@ var {{.VariableName}} = func() http.FileSystem { {{define "FileInfo-Before"}} {{quote .Path}}: &vfsgen۰FileInfo{ name: {{quote .Name}}, + {{if not .UseGlobalModTime}} modTime: {{template "Time" .ModTime}}, + {{end}} content: []byte("{{end}}{{define "FileInfo-After"}}"), }, {{end}} @@ -262,7 +274,9 @@ var {{.VariableName}} = func() http.FileSystem { {{define "DirInfo"}} {{quote .Path}}: &vfsgen۰DirInfo{ name: {{quote .Name}}, + {{if not .UseGlobalModTime}} modTime: {{template "Time" .ModTime}}, + {{end}} }, {{end}} @@ -335,7 +349,7 @@ func (f *vfsgen۰CompressedFileInfo) GzipBytes() []byte { func (f *vfsgen۰CompressedFileInfo) Name() string { return f.name } func (f *vfsgen۰CompressedFileInfo) Size() int64 { return f.uncompressedSize } func (f *vfsgen۰CompressedFileInfo) Mode() os.FileMode { return 0444 } -func (f *vfsgen۰CompressedFileInfo) ModTime() time.Time { return f.modTime } +func (f *vfsgen۰CompressedFileInfo) ModTime() time.Time { return {{if .UseGlobalModTime}}GlobalModTime(f.name){{else}}f.modTime{{end}} } func (f *vfsgen۰CompressedFileInfo) IsDir() bool { return false } func (f *vfsgen۰CompressedFileInfo) Sys() interface{} { return nil } @@ -407,7 +421,7 @@ func (f *vfsgen۰FileInfo) NotWorthGzipCompressing() {} func (f *vfsgen۰FileInfo) Name() string { return f.name } func (f *vfsgen۰FileInfo) Size() int64 { return int64(len(f.content)) } func (f *vfsgen۰FileInfo) Mode() os.FileMode { return 0444 } -func (f *vfsgen۰FileInfo) ModTime() time.Time { return f.modTime } +func (f *vfsgen۰FileInfo) ModTime() time.Time { return {{if .UseGlobalModTime}}GlobalModTime(f.name){{else}}f.modTime{{end}} } func (f *vfsgen۰FileInfo) IsDir() bool { return false } func (f *vfsgen۰FileInfo) Sys() interface{} { return nil } @@ -440,7 +454,7 @@ func (d *vfsgen۰DirInfo) Stat() (os.FileInfo, error) { return d, nil } func (d *vfsgen۰DirInfo) Name() string { return d.name } func (d *vfsgen۰DirInfo) Size() int64 { return 0 } func (d *vfsgen۰DirInfo) Mode() os.FileMode { return 0755 | os.ModeDir } -func (d *vfsgen۰DirInfo) ModTime() time.Time { return d.modTime } +func (d *vfsgen۰DirInfo) ModTime() time.Time { return {{if .UseGlobalModTime}}GlobalModTime(d.name){{else}}d.modTime{{end}} } func (d *vfsgen۰DirInfo) IsDir() bool { return true } func (d *vfsgen۰DirInfo) Sys() interface{} { return nil } diff --git a/vendor/github.com/shurcooL/vfsgen/options.go b/vendor/github.com/shurcooL/vfsgen/options.go index d10d348e7091..40f43a697a5d 100644 --- a/vendor/github.com/shurcooL/vfsgen/options.go +++ b/vendor/github.com/shurcooL/vfsgen/options.go @@ -26,6 +26,10 @@ type Options struct { // VariableComment is the comment of the http.FileSystem variable in the generated code. // If left empty, it defaults to "{{.VariableName}} statically implements the virtual filesystem provided to vfsgen.". VariableComment string + + // UseGlobalModTime indicates that not retrieve files' modified time if it's true. Once this + // is true, you have to define a function GlobalModTime(filename string) time.Time in the same package of generated files + UseGlobalModTime bool } // fillMissing sets default values for mandatory options that are left empty. diff --git a/vendor/modules.txt b/vendor/modules.txt index 1609ad964ce5..c525a3e98727 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -727,7 +727,7 @@ github.com/sergi/go-diff/diffmatchpatch # github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 ## explicit github.com/shurcooL/httpfs/vfsutil -# github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 +# github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 ## explicit github.com/shurcooL/vfsgen # github.com/sirupsen/logrus v1.8.1 @@ -1061,3 +1061,4 @@ xorm.io/xorm/schemas xorm.io/xorm/tags # github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 # github.com/golang-jwt/jwt v3.2.1+incompatible => github.com/golang-jwt/jwt v3.2.2+incompatible +# github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 92c9fb81550f..bf9d21ac49b1 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -297,8 +297,20 @@ export function initGlobalButtons() { }); $('.hide-panel.button').on('click', function (event) { - $($(this).data('panel')).hide(); + // a `.hide-panel.button` can hide a panel, by `data-panel="selector"` or `data-panel-closest="selector"` event.preventDefault(); + let sel = $(this).attr('data-panel'); + if (sel) { + $(sel).hide(); + return; + } + sel = $(this).attr('data-panel-closest'); + if (sel) { + $(this).closest(sel).hide(); + return; + } + // should never happen, otherwise there is a bug in code + alert('Nothing to hide'); }); $('.show-modal.button').on('click', function () { diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index fccec8ccac95..c364beada9a4 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -10,6 +10,7 @@ import { initRepoIssueWipToggle, initRepoPullRequestMerge, initRepoPullRequestUpdate, updateIssuesMeta, } from './repo-issue.js'; +import {initUnicodeEscapeButton} from './repo-unicode-escape.js'; import {svg} from '../svg.js'; import {htmlEscape} from 'escape-goat'; import {initRepoBranchTagDropdown} from '../components/RepoBranchTagDropdown.js'; @@ -533,6 +534,8 @@ export function initRepository() { easyMDE.codemirror.refresh(); }); } + + initUnicodeEscapeButton(); } function initRepoIssueCommentEdit() { diff --git a/web_src/js/features/repo-unicode-escape.js b/web_src/js/features/repo-unicode-escape.js new file mode 100644 index 000000000000..5791c23155aa --- /dev/null +++ b/web_src/js/features/repo-unicode-escape.js @@ -0,0 +1,28 @@ +export function initUnicodeEscapeButton() { + $(document).on('click', 'a.escape-button', (e) => { + e.preventDefault(); + $(e.target).parents('.file-content, .non-diff-file-content').find('.file-code, .file-view').addClass('unicode-escaped'); + $(e.target).hide(); + $(e.target).siblings('a.unescape-button').show(); + }); + $(document).on('click', 'a.unescape-button', (e) => { + e.preventDefault(); + $(e.target).parents('.file-content, .non-diff-file-content').find('.file-code, .file-view').removeClass('unicode-escaped'); + $(e.target).hide(); + $(e.target).siblings('a.escape-button').show(); + }); + $(document).on('click', 'a.toggle-escape-button', (e) => { + e.preventDefault(); + const fileContent = $(e.target).parents('.file-content, .non-diff-file-content'); + const fileView = fileContent.find('.file-code, .file-view'); + if (fileView.hasClass('unicode-escaped')) { + fileView.removeClass('unicode-escaped'); + fileContent.find('a.unescape-button').hide(); + fileContent.find('a.escape-button').show(); + } else { + fileView.addClass('unicode-escaped'); + fileContent.find('a.unescape-button').show(); + fileContent.find('a.escape-button').hide(); + } + }); +} diff --git a/web_src/less/_base.less b/web_src/less/_base.less index 741efeadca72..c19030cccf4c 100644 --- a/web_src/less/_base.less +++ b/web_src/less/_base.less @@ -668,6 +668,12 @@ a.ui.card:hover, color: var(--color-text-dark); } +.ui.error.message .header, +.ui.warning.message .header { + color: inherit; + filter: saturate(2); +} + .dont-break-out { overflow-wrap: break-word; word-wrap: break-word; @@ -1569,6 +1575,10 @@ a.ui.label:hover { } } +.lines-escape { + width: 0; +} + .lines-code { background-color: var(--color-code-bg); padding-left: 5px; diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index 7320f3e30251..4894a0a2c92b 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -76,6 +76,24 @@ } } + .unicode-escaped .escaped-code-point { + &[data-escaped]::before { + visibility: visible; + content: attr(data-escaped); + font-family: var(--fonts-monospace); + color: var(--color-red); + } + + .char { + display: none; + } + } + + .broken-code-point { + font-family: var(--fonts-monospace); + color: blue; + } + .metas { .menu { overflow-x: auto; @@ -3020,6 +3038,26 @@ td.blob-excerpt { padding-left: 8px; } +.ui.message.unicode-escape-prompt { + margin-bottom: 0; + border-radius: 0; + display: flex; + flex-direction: column; +} + +.wiki-content-sidebar .ui.message.unicode-escape-prompt, +.wiki-content-footer .ui.message.unicode-escape-prompt { + p { + display: none; + } +} + +/* fomantic's last-child selector does not work with hidden last child */ +.ui.buttons .unescape-button { + border-top-right-radius: .28571429rem; + border-bottom-right-radius: .28571429rem; +} + .webhook-info { padding: 7px 12px; margin: 10px 0; @@ -3110,6 +3148,7 @@ td.blob-excerpt { .code-diff-unified .del-code, .code-diff-unified .del-code td, .code-diff-split .del-code .lines-num-old, +.code-diff-split .del-code .lines-escape-old, .code-diff-split .del-code .lines-type-marker-old, .code-diff-split .del-code .lines-code-old { background: var(--color-diff-removed-row-bg); @@ -3120,9 +3159,11 @@ td.blob-excerpt { .code-diff-unified .add-code td, .code-diff-split .add-code .lines-num-new, .code-diff-split .add-code .lines-type-marker-new, +.code-diff-split .add-code .lines-escape-new, .code-diff-split .add-code .lines-code-new, .code-diff-split .del-code .add-code.lines-num-new, .code-diff-split .del-code .add-code.lines-type-marker-new, +.code-diff-split .del-code .add-code.lines-escape-new, .code-diff-split .del-code .add-code.lines-code-new { background: var(--color-diff-added-row-bg); border-color: var(--color-diff-added-row-border); @@ -3131,7 +3172,9 @@ td.blob-excerpt { .code-diff-split .del-code .lines-num-new, .code-diff-split .del-code .lines-type-marker-new, .code-diff-split .del-code .lines-code-new, +.code-diff-split .del-code .lines-escape-new, .code-diff-split .add-code .lines-num-old, +.code-diff-split .add-code .lines-escape-old, .code-diff-split .add-code .lines-type-marker-old, .code-diff-split .add-code .lines-code-old { background: var(--color-diff-inactive); diff --git a/web_src/less/_review.less b/web_src/less/_review.less index 12bd6a608a8b..1070ad7ddedc 100644 --- a/web_src/less/_review.less +++ b/web_src/less/_review.less @@ -16,6 +16,17 @@ } } +.lines-escape a.toggle-escape-button::before { + visibility: visible; + content: '⚠️'; + font-family: var(--fonts-emoji); + color: var(--color-red); +} + +.repository .diff-file-box .code-diff td.lines-escape { + padding-left: 0 !important; +} + .diff-file-box .lines-code:hover .ui.button.add-code-comment { opacity: 1; }
{{if (index $.LineEscapeStatus $idx).Escaped}}{{end}}{{$code | Safe}}