Merge branch 'master' into refactor_issues-subscription

This commit is contained in:
6543 2019-11-06 05:44:00 +01:00 committed by GitHub
commit 90c36e37d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 5437 additions and 512 deletions

View File

@ -1,4 +1,4 @@
[简体中文](https://github.com/go-gitea/gitea/blob/master/README_ZH.md)
[简体中文](README_ZH.md)
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
@ -105,9 +105,8 @@ for the full license text.
## Screenshots
Looking for an overview of the interface? Check it out!
| | | |
|![Dashboard](https://dl.gitea.io/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.io/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.io/screenshots/global_issues.png)|
|:---:|:---:|:---:|
|![Dashboard](https://image.ibb.co/dms6DG/1.png)|![Repository](https://image.ibb.co/m6MSLw/2.png)|![Commits History](https://image.ibb.co/cjrSLw/3.png)|
|![Branches](https://image.ibb.co/e6vbDG/4.png)|![Issues](https://image.ibb.co/bJTJSb/5.png)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)|
|![Releases](https://image.ibb.co/cUzgfw/7.png)|![Activity](https://image.ibb.co/eZgGDG/8.png)|![Wiki](https://image.ibb.co/dYV9YG/9.png)|
|![Diff](https://image.ibb.co/ewA9YG/10.png)|![Organization](https://image.ibb.co/ceOwDG/11.png)|![Profile](https://image.ibb.co/c44Q7b/12.png)|
|![Branches](https://dl.gitea.io/screenshots/branches.png)|![Web Editor](https://dl.gitea.io/screenshots/web_editor.png)|![Activity](https://dl.gitea.io/screenshots/activity.png)|
|![New Migration](https://dl.gitea.io/screenshots/migration.png)|![Migrating](https://dl.gitea.io/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)
![Pull Request Dark](https://dl.gitea.io/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.io/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.io/screenshots/diff_dark.png)|

View File

@ -1,14 +1,15 @@
[English](https://github.com/go-gitea/gitea/blob/master/README.md)
[English](README.md)
# Gitea - Git with a cup of tea
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
[![Build Status](https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg)](https://drone.gitea.io/go-gitea/gitea)
[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/gitea)
[![Join the Discord chat at https://discord.gg/NsatcWJ](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/NsatcWJ)
[![](https://images.microbadger.com/badges/image/gitea/gitea.svg)](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
[![codecov](https://codecov.io/gh/go-gitea/gitea/branch/master/graph/badge.svg)](https://codecov.io/gh/go-gitea/gitea)
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea)
[![GoDoc](https://godoc.org/code.gitea.io/gitea?status.svg)](https://godoc.org/code.gitea.io/gitea)
[![GitHub release](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest)
[![Help Contribute to Open Source](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea)
[![Become a backer/sponsor of gitea](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
@ -45,9 +46,8 @@ Fork -> Patch -> Push -> Pull Request
## 截图
| | | |
|![Dashboard](https://dl.gitea.io/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.io/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.io/screenshots/global_issues.png)|
|:---:|:---:|:---:|
|![Dashboard](https://image.ibb.co/dms6DG/1.png)|![Repository](https://image.ibb.co/m6MSLw/2.png)|![Commits History](https://image.ibb.co/cjrSLw/3.png)|
|![Branches](https://image.ibb.co/e6vbDG/4.png)|![Issues](https://image.ibb.co/bJTJSb/5.png)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)|
|![Releases](https://image.ibb.co/cUzgfw/7.png)|![Activity](https://image.ibb.co/eZgGDG/8.png)|![Wiki](https://image.ibb.co/dYV9YG/9.png)|
|![Diff](https://image.ibb.co/ewA9YG/10.png)|![Organization](https://image.ibb.co/ceOwDG/11.png)|![Profile](https://image.ibb.co/c44Q7b/12.png)|
|![Branches](https://dl.gitea.io/screenshots/branches.png)|![Web Editor](https://dl.gitea.io/screenshots/web_editor.png)|![Activity](https://dl.gitea.io/screenshots/activity.png)|
|![New Migration](https://dl.gitea.io/screenshots/migration.png)|![Migrating](https://dl.gitea.io/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)
![Pull Request Dark](https://dl.gitea.io/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.io/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.io/screenshots/diff_dark.png)|

View File

@ -30,7 +30,8 @@ After=network.target
## PartOf=gitea.service
##
## [Socket]
## ListenStream=
## Service=gitea.service
## ListenStream=<some_port>
## NoDelay=true
##
## [Install]
@ -53,7 +54,7 @@ WorkingDirectory=/var/lib/gitea/
# If using unix socket: Tells Systemd to create /run/gitea folder to home gitea.sock
# Manual cration would vanish after reboot.
#RuntimeDirectory=gitea
ExecStart=/usr/local/bin/gitea web -c /etc/gitea/app.ini
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
# If you want to bind Gitea to a port below 1024, uncomment

View File

@ -18,7 +18,7 @@ params:
description: Git with a cup of tea
author: The Gitea Authors
website: https://docs.gitea.io
version: 1.9.3
version: 1.9.5
menu:
page:

View File

@ -42,6 +42,7 @@ Also see [Support Options]({{< relref "doc/help/seek-help.en-us.md" >}})
* [SSH Common Errors](#ssh-common-errors)
* [Missing releases after migration repository with tags](#missing-releases-after-migrating-repository-with-tags)
* [LFS Issues](#lfs-issues)
* [How can I create users before starting Gitea](#how-can-i-create-users-before-starting-gitea)
## Difference between 1.x and 1.x.x downloads
@ -272,3 +273,6 @@ Check the value of `LFS_HTTP_AUTH_EXPIRY` in your `app.ini` file.
By default, your LFS token will expire after 20 minutes. If you have a slow connection or a large file (or both), it may not finish uploading within the time limit.
You may want to set this value to `60m` or `120m`.
## How can I create users before starting Gitea
Gitea provides a sub-command `gitea migrate` to initialize the database, after which you can use the [admin CLI commands]({{< relref "doc/usage/command-line.en-us.md" >}}) to add users like normal.

View File

@ -281,3 +281,10 @@ provided key. You should also set the value
NB: opensshd requires the gitea program to be owned by root and not
writable by group or others. The program must be specified by an absolute
path.
#### migrate
Migrates the database. This command can be used to run other commands before starting the server for the first time.
This command is idempotent.
#### convert
Converts an existing MySQL database from utf8 to utf8mb4.

6
go.mod
View File

@ -69,12 +69,10 @@ require (
github.com/mattn/go-sqlite3 v1.11.0
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75
github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae // indirect
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5
github.com/niklasfasching/go-org v0.1.7
github.com/niklasfasching/go-org v0.1.8
github.com/oliamb/cutter v0.2.2
github.com/philhofer/fwd v1.0.0 // indirect
github.com/pkg/errors v0.8.1
@ -103,7 +101,7 @@ require (
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 // indirect
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b
golang.org/x/text v0.3.2

4
go.sum
View File

@ -427,6 +427,8 @@ github.com/niklasfasching/go-org v0.1.6 h1:F521WcqRNl8OJumlgAnekZgERaTA2HpfOYYfV
github.com/niklasfasching/go-org v0.1.6/go.mod h1:AsLD6X7djzRIz4/RFZu8vwRL0VGjUvGZCCH1Nz0VdrU=
github.com/niklasfasching/go-org v0.1.7 h1:t3V+3XnS/7BhKv/7SlMUa8FvAiq577/a1T3D7mLIRXE=
github.com/niklasfasching/go-org v0.1.7/go.mod h1:AsLD6X7djzRIz4/RFZu8vwRL0VGjUvGZCCH1Nz0VdrU=
github.com/niklasfasching/go-org v0.1.8 h1:Kjvs6lP+LIILHhc9zIJ4Gu90a/pVY483if2Qmu8v4Fg=
github.com/niklasfasching/go-org v0.1.8/go.mod h1:AsLD6X7djzRIz4/RFZu8vwRL0VGjUvGZCCH1Nz0VdrU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oliamb/cutter v0.2.2 h1:Lfwkya0HHNU1YLnGv2hTkzHfasrSMkgv4Dn+5rmlk3k=
github.com/oliamb/cutter v0.2.2/go.mod h1:4BenG2/4GuRBDbVm/OPahDVqbrOemzpPiG5mi1iryBU=
@ -658,6 +660,8 @@ golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsi
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss=
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9 h1:DPz9iiH3YoKiKhX/ijjoZvT0VFwK2c6CWYWQ7Zyr8TU=
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=

View File

@ -1293,8 +1293,12 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
log.Warn("Malformed Labels argument: %s", opts.Labels)
} else {
for i, labelID := range labelIDs {
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
if labelID > 0 {
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
} else {
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label WHERE label_id = ?)", -labelID)
}
}
}
}

View File

@ -107,7 +107,7 @@ func parseKeyString(content string) (string, error) {
var keyType, keyContent, keyComment string
if content[:len(ssh2keyStart)] == ssh2keyStart {
if strings.HasPrefix(content, ssh2keyStart) {
// Parse SSH2 file format.
// Transform all legal line endings to a single "\n".

View File

@ -131,6 +131,19 @@ AAAAC3NzaC1lZDI1NTE5AAAAICV0MGX/W9IvLA4FXpIuUcdDcbj5KX4syHgsTy7soVgf
_, err := CheckPublicKeyString(test.content)
assert.NoError(t, err)
}
for _, invalidKeys := range []struct {
content string
}{
{"test"},
{"---- NOT A REAL KEY ----"},
{"bad\nkey"},
{"\t\t:)\t\r\n"},
{"\r\ntest \r\ngitea\r\n\r\n"},
} {
_, err := CheckPublicKeyString(invalidKeys.content)
assert.Error(t, err)
}
}
func Test_calcFingerprint(t *testing.T) {

View File

@ -118,33 +118,6 @@ func (w *Webhook) AfterLoad() {
}
}
// GetSlackHook returns slack metadata
func (w *Webhook) GetSlackHook() *SlackMeta {
s := &SlackMeta{}
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("webhook.GetSlackHook(%d): %v", w.ID, err)
}
return s
}
// GetDiscordHook returns discord metadata
func (w *Webhook) GetDiscordHook() *DiscordMeta {
s := &DiscordMeta{}
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("webhook.GetDiscordHook(%d): %v", w.ID, err)
}
return s
}
// GetTelegramHook returns telegram metadata
func (w *Webhook) GetTelegramHook() *TelegramMeta {
s := &TelegramMeta{}
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("webhook.GetTelegramHook(%d): %v", w.ID, err)
}
return s
}
// History returns history of webhook by given conditions.
func (w *Webhook) History(page int) ([]*HookTask, error) {
return HookTasks(w.ID, page)

View File

@ -24,18 +24,6 @@ func TestIsValidHookContentType(t *testing.T) {
assert.False(t, IsValidHookContentType("invalid"))
}
func TestWebhook_GetSlackHook(t *testing.T) {
w := &Webhook{
Meta: `{"channel": "foo", "username": "username", "color": "blue"}`,
}
slackHook := w.GetSlackHook()
assert.Equal(t, *slackHook, SlackMeta{
Channel: "foo",
Username: "username",
Color: "blue",
})
}
func TestWebhook_History(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
webhook := AssertExistsAndLoadBean(t, &Webhook{ID: 1}).(*Webhook)

View File

@ -8,7 +8,6 @@ import (
"bytes"
"fmt"
"html"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
@ -91,7 +90,7 @@ func (r *Renderer) WriteRegularLink(l org.RegularLink) {
description := string(link)
if l.Description != nil {
description = r.nodesAsString(l.Description...)
description = r.WriteNodesAsString(l.Description...)
}
switch l.Kind() {
case "image":
@ -102,21 +101,3 @@ func (r *Renderer) WriteRegularLink(l org.RegularLink) {
r.WriteString(fmt.Sprintf(`<a href="%s" title="%s">%s</a>`, link, description, description))
}
}
func (r *Renderer) emptyClone() *Renderer {
wcopy := *(r.HTMLWriter)
wcopy.Builder = strings.Builder{}
rcopy := *r
rcopy.HTMLWriter = &wcopy
wcopy.ExtendingWriter = &rcopy
return &rcopy
}
func (r *Renderer) nodesAsString(nodes ...org.Node) string {
tmp := r.emptyClone()
org.WriteNodes(tmp, nodes...)
return tmp.String()
}

View File

@ -252,27 +252,30 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
}
// download attachment
resp, err := http.Get(asset.URL)
err = func() error {
resp, err := http.Get(asset.URL)
if err != nil {
return err
}
defer resp.Body.Close()
localPath := attach.LocalPath()
if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
return fmt.Errorf("MkdirAll: %v", err)
}
fw, err := os.Create(localPath)
if err != nil {
return fmt.Errorf("Create: %v", err)
}
defer fw.Close()
_, err = io.Copy(fw, resp.Body)
return err
}()
if err != nil {
return err
}
defer resp.Body.Close()
localPath := attach.LocalPath()
if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
return fmt.Errorf("MkdirAll: %v", err)
}
fw, err := os.Create(localPath)
if err != nil {
return fmt.Errorf("Create: %v", err)
}
defer fw.Close()
if _, err := io.Copy(fw, resp.Body); err != nil {
return err
}
rel.Attachments = append(rel.Attachments, &attach)
}
@ -468,21 +471,24 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR
}
// download patch file
resp, err := http.Get(pr.PatchURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
pullDir := filepath.Join(g.repo.RepoPath(), "pulls")
if err = os.MkdirAll(pullDir, os.ModePerm); err != nil {
return nil, err
}
f, err := os.Create(filepath.Join(pullDir, fmt.Sprintf("%d.patch", pr.Number)))
if err != nil {
return nil, err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
err := func() error {
resp, err := http.Get(pr.PatchURL)
if err != nil {
return err
}
defer resp.Body.Close()
pullDir := filepath.Join(g.repo.RepoPath(), "pulls")
if err = os.MkdirAll(pullDir, os.ModePerm); err != nil {
return err
}
f, err := os.Create(filepath.Join(pullDir, fmt.Sprintf("%d.patch", pr.Number)))
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}()
if err != nil {
return nil, err
}
@ -496,8 +502,8 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR
if err != nil {
return nil, err
}
defer p.Close()
_, err = p.WriteString(pr.Head.SHA)
p.Close()
if err != nil {
return nil, err
}
@ -531,8 +537,8 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR
if err != nil {
return nil, err
}
defer b.Close()
_, err = b.WriteString(pr.Head.SHA)
b.Close()
if err != nil {
return nil, err
}

View File

@ -0,0 +1,77 @@
// Copyright 2019 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 action
import (
"fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification/base"
)
type actionNotifier struct {
base.NullNotifier
}
var (
_ base.Notifier = &actionNotifier{}
)
// NewNotifier create a new webhookNotifier notifier
func NewNotifier() base.Notifier {
return &actionNotifier{}
}
func (a *actionNotifier) NotifyNewIssue(issue *models.Issue) {
if err := issue.LoadPoster(); err != nil {
log.Error("issue.LoadPoster: %v", err)
return
}
if err := issue.LoadRepo(); err != nil {
log.Error("issue.LoadRepo: %v", err)
return
}
repo := issue.Repo
if err := models.NotifyWatchers(&models.Action{
ActUserID: issue.Poster.ID,
ActUser: issue.Poster,
OpType: models.ActionCreateIssue,
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
if err := pull.LoadIssue(); err != nil {
log.Error("pull.LoadIssue: %v", err)
return
}
if err := pull.Issue.LoadRepo(); err != nil {
log.Error("pull.Issue.LoadRepo: %v", err)
return
}
if err := pull.Issue.LoadPoster(); err != nil {
log.Error("pull.Issue.LoadPoster: %v", err)
return
}
if err := models.NotifyWatchers(&models.Action{
ActUserID: pull.Issue.Poster.ID,
ActUser: pull.Issue.Poster,
OpType: models.ActionCreatePullRequest,
Content: fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title),
RepoID: pull.Issue.Repo.ID,
Repo: pull.Issue.Repo,
IsPrivate: pull.Issue.Repo.IsPrivate,
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}

View File

@ -30,6 +30,7 @@ type Notifier interface {
NotifyNewPullRequest(*models.PullRequest)
NotifyMergePullRequest(*models.PullRequest, *models.User, *git.Repository)
NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest)
NotifyPullRequestReview(*models.PullRequest, *models.Review, *models.Comment)
NotifyCreateIssueComment(*models.User, *models.Repository,
@ -40,4 +41,6 @@ type Notifier interface {
NotifyNewRelease(rel *models.Release)
NotifyUpdateRelease(doer *models.User, rel *models.Release)
NotifyDeleteRelease(doer *models.User, rel *models.Release)
NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits)
}

View File

@ -46,6 +46,10 @@ func (*NullNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models.R
func (*NullNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User, baseRepo *git.Repository) {
}
// NotifyPullRequestSynchronized places a place holder function
func (*NullNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest) {
}
// NotifyUpdateComment places a place holder function
func (*NullNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) {
}
@ -106,3 +110,7 @@ func (*NullNotifier) NotifyCreateRepository(doer *models.User, u *models.User, r
// NotifyMigrateRepository places a place holder function
func (*NullNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
}
// NotifyPushCommits notifies commits pushed to notifiers
func (*NullNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
}

View File

@ -7,6 +7,7 @@ package notification
import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/notification/action"
"code.gitea.io/gitea/modules/notification/base"
"code.gitea.io/gitea/modules/notification/indexer"
"code.gitea.io/gitea/modules/notification/mail"
@ -33,6 +34,7 @@ func NewContext() {
}
RegisterNotifier(indexer.NewNotifier())
RegisterNotifier(webhook.NewNotifier())
RegisterNotifier(action.NewNotifier())
}
// NotifyCreateIssueComment notifies issue comment related message to notifiers
@ -71,6 +73,13 @@ func NotifyNewPullRequest(pr *models.PullRequest) {
}
}
// NotifyPullRequestSynchronized notifies Synchronized pull request
func NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest) {
for _, notifier := range notifiers {
notifier.NotifyPullRequestSynchronized(doer, pr)
}
}
// NotifyPullRequestReview notifies new pull request review
func NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) {
for _, notifier := range notifiers {
@ -183,3 +192,10 @@ func NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Rep
notifier.NotifyMigrateRepository(doer, u, repo)
}
}
// NotifyPushCommits notifies commits pushed to notifiers
func NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
for _, notifier := range notifiers {
notifier.NotifyPushCommits(pusher, repo, refName, oldCommitID, newCommitID, commits)
}
}

View File

@ -8,7 +8,9 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification/base"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/webhook"
webhook_module "code.gitea.io/gitea/modules/webhook"
)
@ -250,6 +252,15 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *models.User, issue *mode
}
func (m *webhookNotifier) NotifyNewIssue(issue *models.Issue) {
if err := issue.LoadRepo(); err != nil {
log.Error("issue.LoadRepo: %v", err)
return
}
if err := issue.LoadPoster(); err != nil {
log.Error("issue.LoadPoster: %v", err)
return
}
mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
if err := webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{
Action: api.HookIssueOpened,
@ -262,6 +273,32 @@ func (m *webhookNotifier) NotifyNewIssue(issue *models.Issue) {
}
}
func (m *webhookNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
if err := pull.LoadIssue(); err != nil {
log.Error("pull.LoadIssue: %v", err)
return
}
if err := pull.Issue.LoadRepo(); err != nil {
log.Error("pull.Issue.LoadRepo: %v", err)
return
}
if err := pull.Issue.LoadPoster(); err != nil {
log.Error("pull.Issue.LoadPoster: %v", err)
return
}
mode, _ := models.AccessLevel(pull.Issue.Poster, pull.Issue.Repo)
if err := webhook.PrepareWebhooks(pull.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueOpened,
Index: pull.Issue.Index,
PullRequest: pull.APIFormat(),
Repository: pull.Issue.Repo.APIFormat(mode),
Sender: pull.Issue.Poster.APIFormat(),
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
}
func (m *webhookNotifier) NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string) {
mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
var err error
@ -461,3 +498,87 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m
log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
}
}
func (m *webhookNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
apiPusher := pusher.APIFormat()
apiCommits, err := commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
if err != nil {
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
return
}
if err := webhook_module.PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{
Ref: refName,
Before: oldCommitID,
After: newCommitID,
CompareURL: setting.AppURL + commits.CompareURL,
Commits: apiCommits,
Repo: repo.APIFormat(models.AccessModeOwner),
Pusher: apiPusher,
Sender: apiPusher,
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
}
func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) {
var reviewHookType models.HookEventType
switch review.Type {
case models.ReviewTypeApprove:
reviewHookType = models.HookEventPullRequestApproved
case models.ReviewTypeComment:
reviewHookType = models.HookEventPullRequestComment
case models.ReviewTypeReject:
reviewHookType = models.HookEventPullRequestRejected
default:
// unsupported review webhook type here
log.Error("Unsupported review webhook type")
return
}
if err := pr.LoadIssue(); err != nil {
log.Error("pr.LoadIssue: %v", err)
return
}
mode, err := models.AccessLevel(review.Issue.Poster, review.Issue.Repo)
if err != nil {
log.Error("models.AccessLevel: %v", err)
return
}
if err := webhook.PrepareWebhooks(review.Issue.Repo, reviewHookType, &api.PullRequestPayload{
Action: api.HookIssueSynchronized,
Index: review.Issue.Index,
PullRequest: pr.APIFormat(),
Repository: review.Issue.Repo.APIFormat(mode),
Sender: review.Reviewer.APIFormat(),
Review: &api.ReviewPayload{
Type: string(reviewHookType),
Content: review.Content,
},
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
}
func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest) {
if err := pr.LoadIssue(); err != nil {
log.Error("pr.LoadIssue: %v", err)
return
}
if err := pr.Issue.LoadAttributes(); err != nil {
log.Error("LoadAttributes: %v", err)
return
}
if err := webhook.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueSynchronized,
Index: pr.Issue.Index,
PullRequest: pr.Issue.PullRequest.APIFormat(),
Repository: pr.Issue.Repo.APIFormat(models.AccessModeNone),
Sender: doer.APIFormat(),
}); err != nil {
log.Error("PrepareWebhooks [pull_id: %v]: %v", pr.ID, err)
}
}

View File

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/webhook"
@ -190,22 +191,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
}
if isHookEventPush {
commits, err := opts.Commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
if err != nil {
return err
}
if err = webhook.PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{
Ref: opts.RefFullName,
Before: opts.OldCommitID,
After: opts.NewCommitID,
CompareURL: setting.AppURL + opts.Commits.CompareURL,
Commits: commits,
Repo: apiRepo,
Pusher: apiPusher,
Sender: apiPusher,
}); err != nil {
return fmt.Errorf("PrepareWebhooks: %v", err)
}
notification.NotifyPushCommits(pusher, repo, opts.RefFullName, opts.OldCommitID, opts.NewCommitID, opts.Commits)
}
return nil

View File

@ -99,7 +99,8 @@ type EditIssueOption struct {
Milestone *int64 `json:"milestone"`
State *string `json:"state"`
// swagger:strfmt date-time
Deadline *time.Time `json:"due_date"`
Deadline *time.Time `json:"due_date"`
RemoveDeadline *bool `json:"unset_due_date"`
}
// EditDeadlineOption options for creating a deadline

View File

@ -88,5 +88,6 @@ type EditPullRequestOption struct {
Labels []int64 `json:"labels"`
State *string `json:"state"`
// swagger:strfmt date-time
Deadline *time.Time `json:"due_date"`
Deadline *time.Time `json:"due_date"`
RemoveDeadline *bool `json:"unset_due_date"`
}

View File

@ -2,13 +2,14 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package webhook
import (
"encoding/json"
"fmt"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
@ -184,7 +185,7 @@ func getDingtalkIssuesPayload(p *api.IssuePayload) (*DingtalkPayload, error) {
func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) (*DingtalkPayload, error) {
title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
var content string
switch p.Action {
case api.HookIssueCommentCreated:
@ -286,7 +287,7 @@ func getDingtalkPullRequestPayload(p *api.PullRequestPayload) (*DingtalkPayload,
}, nil
}
func getDingtalkPullRequestApprovalPayload(p *api.PullRequestPayload, event HookEventType) (*DingtalkPayload, error) {
func getDingtalkPullRequestApprovalPayload(p *api.PullRequestPayload, event models.HookEventType) (*DingtalkPayload, error) {
var text, title string
switch p.Action {
case api.HookIssueSynchronized:
@ -392,29 +393,29 @@ func getDingtalkReleasePayload(p *api.ReleasePayload) (*DingtalkPayload, error)
}
// GetDingtalkPayload converts a ding talk webhook into a DingtalkPayload
func GetDingtalkPayload(p api.Payloader, event HookEventType, meta string) (*DingtalkPayload, error) {
func GetDingtalkPayload(p api.Payloader, event models.HookEventType, meta string) (*DingtalkPayload, error) {
s := new(DingtalkPayload)
switch event {
case HookEventCreate:
case models.HookEventCreate:
return getDingtalkCreatePayload(p.(*api.CreatePayload))
case HookEventDelete:
case models.HookEventDelete:
return getDingtalkDeletePayload(p.(*api.DeletePayload))
case HookEventFork:
case models.HookEventFork:
return getDingtalkForkPayload(p.(*api.ForkPayload))
case HookEventIssues:
case models.HookEventIssues:
return getDingtalkIssuesPayload(p.(*api.IssuePayload))
case HookEventIssueComment:
case models.HookEventIssueComment:
return getDingtalkIssueCommentPayload(p.(*api.IssueCommentPayload))
case HookEventPush:
case models.HookEventPush:
return getDingtalkPushPayload(p.(*api.PushPayload))
case HookEventPullRequest:
case models.HookEventPullRequest:
return getDingtalkPullRequestPayload(p.(*api.PullRequestPayload))
case HookEventPullRequestApproved, HookEventPullRequestRejected, HookEventPullRequestComment:
case models.HookEventPullRequestApproved, models.HookEventPullRequestRejected, models.HookEventPullRequestComment:
return getDingtalkPullRequestApprovalPayload(p.(*api.PullRequestPayload), event)
case HookEventRepository:
case models.HookEventRepository:
return getDingtalkRepositoryPayload(p.(*api.RepositoryPayload))
case HookEventRelease:
case models.HookEventRelease:
return getDingtalkReleasePayload(p.(*api.ReleasePayload))
}

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package webhook
import (
"encoding/json"
@ -11,7 +11,9 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
)
@ -63,6 +65,15 @@ type (
}
)
// GetDiscordHook returns discord metadata
func GetDiscordHook(w *models.Webhook) *DiscordMeta {
s := &DiscordMeta{}
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("webhook.GetDiscordHook(%d): %v", w.ID, err)
}
return s
}
func color(clr string) int {
if clr != "" {
clr = strings.TrimLeft(clr, "#")
@ -288,7 +299,7 @@ func getDiscordIssuesPayload(p *api.IssuePayload, meta *DiscordMeta) (*DiscordPa
func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, discord *DiscordMeta) (*DiscordPayload, error) {
title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
content := ""
var color int
switch p.Action {
@ -421,7 +432,7 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta)
}, nil
}
func getDiscordPullRequestApprovalPayload(p *api.PullRequestPayload, meta *DiscordMeta, event HookEventType) (*DiscordPayload, error) {
func getDiscordPullRequestApprovalPayload(p *api.PullRequestPayload, meta *DiscordMeta, event models.HookEventType) (*DiscordPayload, error) {
var text, title string
var color int
switch p.Action {
@ -435,11 +446,11 @@ func getDiscordPullRequestApprovalPayload(p *api.PullRequestPayload, meta *Disco
text = p.Review.Content
switch event {
case HookEventPullRequestApproved:
case models.HookEventPullRequestApproved:
color = greenColor
case HookEventPullRequestRejected:
case models.HookEventPullRequestRejected:
color = redColor
case HookEventPullRequestComment:
case models.HookEventPullRequestComment:
color = greyColor
default:
color = yellowColor
@ -534,7 +545,7 @@ func getDiscordReleasePayload(p *api.ReleasePayload, meta *DiscordMeta) (*Discor
}
// GetDiscordPayload converts a discord webhook into a DiscordPayload
func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*DiscordPayload, error) {
func GetDiscordPayload(p api.Payloader, event models.HookEventType, meta string) (*DiscordPayload, error) {
s := new(DiscordPayload)
discord := &DiscordMeta{}
@ -543,40 +554,40 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*Disc
}
switch event {
case HookEventCreate:
case models.HookEventCreate:
return getDiscordCreatePayload(p.(*api.CreatePayload), discord)
case HookEventDelete:
case models.HookEventDelete:
return getDiscordDeletePayload(p.(*api.DeletePayload), discord)
case HookEventFork:
case models.HookEventFork:
return getDiscordForkPayload(p.(*api.ForkPayload), discord)
case HookEventIssues:
case models.HookEventIssues:
return getDiscordIssuesPayload(p.(*api.IssuePayload), discord)
case HookEventIssueComment:
case models.HookEventIssueComment:
return getDiscordIssueCommentPayload(p.(*api.IssueCommentPayload), discord)
case HookEventPush:
case models.HookEventPush:
return getDiscordPushPayload(p.(*api.PushPayload), discord)
case HookEventPullRequest:
case models.HookEventPullRequest:
return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord)
case HookEventPullRequestRejected, HookEventPullRequestApproved, HookEventPullRequestComment:
case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment:
return getDiscordPullRequestApprovalPayload(p.(*api.PullRequestPayload), discord, event)
case HookEventRepository:
case models.HookEventRepository:
return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord)
case HookEventRelease:
case models.HookEventRelease:
return getDiscordReleasePayload(p.(*api.ReleasePayload), discord)
}
return s, nil
}
func parseHookPullRequestEventType(event HookEventType) (string, error) {
func parseHookPullRequestEventType(event models.HookEventType) (string, error) {
switch event {
case HookEventPullRequestApproved:
case models.HookEventPullRequestApproved:
return "approved", nil
case HookEventPullRequestRejected:
case models.HookEventPullRequestRejected:
return "rejected", nil
case HookEventPullRequestComment:
case models.HookEventPullRequestComment:
return "comment", nil
default:

View File

@ -2,13 +2,14 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package webhook
import (
"encoding/json"
"fmt"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
)
@ -357,7 +358,7 @@ func getMSTeamsIssuesPayload(p *api.IssuePayload) (*MSTeamsPayload, error) {
func getMSTeamsIssueCommentPayload(p *api.IssueCommentPayload) (*MSTeamsPayload, error) {
title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
content := ""
var color int
switch p.Action {
@ -530,7 +531,7 @@ func getMSTeamsPullRequestPayload(p *api.PullRequestPayload) (*MSTeamsPayload, e
}, nil
}
func getMSTeamsPullRequestApprovalPayload(p *api.PullRequestPayload, event HookEventType) (*MSTeamsPayload, error) {
func getMSTeamsPullRequestApprovalPayload(p *api.PullRequestPayload, event models.HookEventType) (*MSTeamsPayload, error) {
var text, title string
var color int
switch p.Action {
@ -544,11 +545,11 @@ func getMSTeamsPullRequestApprovalPayload(p *api.PullRequestPayload, event HookE
text = p.Review.Content
switch event {
case HookEventPullRequestApproved:
case models.HookEventPullRequestApproved:
color = greenColor
case HookEventPullRequestRejected:
case models.HookEventPullRequestRejected:
color = redColor
case HookEventPullRequestComment:
case models.HookEventPullRequestComment:
color = greyColor
default:
color = yellowColor
@ -699,29 +700,29 @@ func getMSTeamsReleasePayload(p *api.ReleasePayload) (*MSTeamsPayload, error) {
}
// GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload
func GetMSTeamsPayload(p api.Payloader, event HookEventType, meta string) (*MSTeamsPayload, error) {
func GetMSTeamsPayload(p api.Payloader, event models.HookEventType, meta string) (*MSTeamsPayload, error) {
s := new(MSTeamsPayload)
switch event {
case HookEventCreate:
case models.HookEventCreate:
return getMSTeamsCreatePayload(p.(*api.CreatePayload))
case HookEventDelete:
case models.HookEventDelete:
return getMSTeamsDeletePayload(p.(*api.DeletePayload))
case HookEventFork:
case models.HookEventFork:
return getMSTeamsForkPayload(p.(*api.ForkPayload))
case HookEventIssues:
case models.HookEventIssues:
return getMSTeamsIssuesPayload(p.(*api.IssuePayload))
case HookEventIssueComment:
case models.HookEventIssueComment:
return getMSTeamsIssueCommentPayload(p.(*api.IssueCommentPayload))
case HookEventPush:
case models.HookEventPush:
return getMSTeamsPushPayload(p.(*api.PushPayload))
case HookEventPullRequest:
case models.HookEventPullRequest:
return getMSTeamsPullRequestPayload(p.(*api.PullRequestPayload))
case HookEventPullRequestRejected, HookEventPullRequestApproved, HookEventPullRequestComment:
case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment:
return getMSTeamsPullRequestApprovalPayload(p.(*api.PullRequestPayload), event)
case HookEventRepository:
case models.HookEventRepository:
return getMSTeamsRepositoryPayload(p.(*api.RepositoryPayload))
case HookEventRelease:
case models.HookEventRelease:
return getMSTeamsReleasePayload(p.(*api.ReleasePayload))
}

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package webhook
import (
"encoding/json"
@ -10,7 +10,9 @@ import (
"fmt"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
)
@ -23,6 +25,15 @@ type SlackMeta struct {
Color string `json:"color"`
}
// GetSlackHook returns slack metadata
func GetSlackHook(w *models.Webhook) *SlackMeta {
s := &SlackMeta{}
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("webhook.GetSlackHook(%d): %v", w.ID, err)
}
return s
}
// SlackPayload contains the information about the slack channel
type SlackPayload struct {
Channel string `json:"channel"`
@ -181,7 +192,7 @@ func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)),
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID)),
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
var text, title, attachmentText string
switch p.Action {
@ -335,7 +346,7 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
}, nil
}
func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event HookEventType) (*SlackPayload, error) {
func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event models.HookEventType) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
@ -388,7 +399,7 @@ func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*Sla
}
// GetSlackPayload converts a slack webhook into a SlackPayload
func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) {
func GetSlackPayload(p api.Payloader, event models.HookEventType, meta string) (*SlackPayload, error) {
s := new(SlackPayload)
slack := &SlackMeta{}
@ -397,25 +408,25 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackP
}
switch event {
case HookEventCreate:
case models.HookEventCreate:
return getSlackCreatePayload(p.(*api.CreatePayload), slack)
case HookEventDelete:
case models.HookEventDelete:
return getSlackDeletePayload(p.(*api.DeletePayload), slack)
case HookEventFork:
case models.HookEventFork:
return getSlackForkPayload(p.(*api.ForkPayload), slack)
case HookEventIssues:
case models.HookEventIssues:
return getSlackIssuesPayload(p.(*api.IssuePayload), slack)
case HookEventIssueComment:
case models.HookEventIssueComment:
return getSlackIssueCommentPayload(p.(*api.IssueCommentPayload), slack)
case HookEventPush:
case models.HookEventPush:
return getSlackPushPayload(p.(*api.PushPayload), slack)
case HookEventPullRequest:
case models.HookEventPullRequest:
return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
case HookEventPullRequestRejected, HookEventPullRequestApproved, HookEventPullRequestComment:
case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment:
return getSlackPullRequestApprovalPayload(p.(*api.PullRequestPayload), slack, event)
case HookEventRepository:
case models.HookEventRepository:
return getSlackRepositoryPayload(p.(*api.RepositoryPayload), slack)
case HookEventRelease:
case models.HookEventRelease:
return getSlackReleasePayload(p.(*api.ReleasePayload), slack)
}

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
package webhook
import (
"encoding/json"
@ -10,7 +10,9 @@ import (
"html"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
api "code.gitea.io/gitea/modules/structs"
)
@ -30,6 +32,15 @@ type (
}
)
// GetTelegramHook returns telegram metadata
func GetTelegramHook(w *models.Webhook) *TelegramMeta {
s := &TelegramMeta{}
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("webhook.GetTelegramHook(%d): %v", w.ID, err)
}
return s
}
// SetSecret sets the telegram secret
func (p *TelegramPayload) SetSecret(_ string) {}
@ -169,7 +180,7 @@ func getTelegramIssuesPayload(p *api.IssuePayload) (*TelegramPayload, error) {
}
func getTelegramIssueCommentPayload(p *api.IssueCommentPayload) (*TelegramPayload, error) {
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
title := fmt.Sprintf(`<a href="%s">#%d %s</a>`, url, p.Issue.Index, html.EscapeString(p.Issue.Title))
var text string
switch p.Action {
@ -214,7 +225,7 @@ func getTelegramPullRequestPayload(p *api.PullRequestPayload) (*TelegramPayload,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueAssigned:
list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID})
list, err := models.MakeAssigneeList(&models.Issue{ID: p.PullRequest.ID})
if err != nil {
return &TelegramPayload{}, err
}
@ -297,27 +308,27 @@ func getTelegramReleasePayload(p *api.ReleasePayload) (*TelegramPayload, error)
}
// GetTelegramPayload converts a telegram webhook into a TelegramPayload
func GetTelegramPayload(p api.Payloader, event HookEventType, meta string) (*TelegramPayload, error) {
func GetTelegramPayload(p api.Payloader, event models.HookEventType, meta string) (*TelegramPayload, error) {
s := new(TelegramPayload)
switch event {
case HookEventCreate:
case models.HookEventCreate:
return getTelegramCreatePayload(p.(*api.CreatePayload))
case HookEventDelete:
case models.HookEventDelete:
return getTelegramDeletePayload(p.(*api.DeletePayload))
case HookEventFork:
case models.HookEventFork:
return getTelegramForkPayload(p.(*api.ForkPayload))
case HookEventIssues:
case models.HookEventIssues:
return getTelegramIssuesPayload(p.(*api.IssuePayload))
case HookEventIssueComment:
case models.HookEventIssueComment:
return getTelegramIssueCommentPayload(p.(*api.IssueCommentPayload))
case HookEventPush:
case models.HookEventPush:
return getTelegramPushPayload(p.(*api.PushPayload))
case HookEventPullRequest:
case models.HookEventPullRequest:
return getTelegramPullRequestPayload(p.(*api.PullRequestPayload))
case HookEventRepository:
case models.HookEventRepository:
return getTelegramRepositoryPayload(p.(*api.RepositoryPayload))
case HookEventRelease:
case models.HookEventRelease:
return getTelegramReleasePayload(p.(*api.ReleasePayload))
}

View File

@ -90,27 +90,27 @@ func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.Hoo
// Use separate objects so modifications won't be made on payload on non-Gogs/Gitea type hooks.
switch w.HookTaskType {
case models.SLACK:
payloader, err = models.GetSlackPayload(p, event, w.Meta)
payloader, err = GetSlackPayload(p, event, w.Meta)
if err != nil {
return fmt.Errorf("GetSlackPayload: %v", err)
}
case models.DISCORD:
payloader, err = models.GetDiscordPayload(p, event, w.Meta)
payloader, err = GetDiscordPayload(p, event, w.Meta)
if err != nil {
return fmt.Errorf("GetDiscordPayload: %v", err)
}
case models.DINGTALK:
payloader, err = models.GetDingtalkPayload(p, event, w.Meta)
payloader, err = GetDingtalkPayload(p, event, w.Meta)
if err != nil {
return fmt.Errorf("GetDingtalkPayload: %v", err)
}
case models.TELEGRAM:
payloader, err = models.GetTelegramPayload(p, event, w.Meta)
payloader, err = GetTelegramPayload(p, event, w.Meta)
if err != nil {
return fmt.Errorf("GetTelegramPayload: %v", err)
}
case models.MSTEAMS:
payloader, err = models.GetMSTeamsPayload(p, event, w.Meta)
payloader, err = GetMSTeamsPayload(p, event, w.Meta)
if err != nil {
return fmt.Errorf("GetMSTeamsPayload: %v", err)
}

View File

@ -12,6 +12,18 @@ import (
"github.com/stretchr/testify/assert"
)
func TestWebhook_GetSlackHook(t *testing.T) {
w := &models.Webhook{
Meta: `{"channel": "foo", "username": "username", "color": "blue"}`,
}
slackHook := GetSlackHook(w)
assert.Equal(t, *slackHook, SlackMeta{
Channel: "foo",
Username: "username",
Color: "blue",
})
}
func TestPrepareWebhooks(t *testing.T) {
assert.NoError(t, models.PrepareTestDatabase())

View File

@ -1090,6 +1090,9 @@ activity.period.daily = 1 day
activity.period.halfweekly = 3 days
activity.period.weekly = 1 week
activity.period.monthly = 1 month
activity.period.quarterly = 3 months
activity.period.semiyearly = 6 months
activity.period.yearly = 1 year
activity.overview = Overview
activity.active_prs_count_1 = <strong>%d</strong> Active Pull Request
activity.active_prs_count_n = <strong>%d</strong> Active Pull Requests

View File

@ -741,6 +741,7 @@ editor.no_changes_to_show=表示する変更箇所はありません。
editor.fail_to_update_file=ファイル '%s' を作成または変更できませんでした: %v
editor.add_subdir=ディレクトリを追加…
editor.unable_to_upload_files='%s' へファイルをアップロードすることができませんでした: %v
editor.upload_file_is_locked=ファイル '%[1]s' は %[2]s がロックしています。
editor.upload_files_to_dir='%s' にファイルをアップロード
editor.cannot_commit_to_protected_branch=保護されたブランチ '%s' にコミットすることはできません。
@ -1088,6 +1089,9 @@ activity.period.daily=1日
activity.period.halfweekly=3日
activity.period.weekly=1週間
activity.period.monthly=1ヶ月
activity.period.quarterly=3ヶ月
activity.period.semiyearly=6ヶ月
activity.period.yearly=1年
activity.overview=概要
activity.active_prs_count_1=<strong>%d</strong>件のアクティブなプルリクエスト
activity.active_prs_count_n=<strong>%d</strong>件のアクティブなプルリクエスト
@ -1379,8 +1383,20 @@ settings.unarchive.success=リポジトリのアーカイブを解除しまし
settings.unarchive.error=リポジトリのアーカイブ解除でエラーが発生しました。 詳細はログを確認してください。
settings.update_avatar_success=リポジトリのアバターを更新しました。
settings.lfs=LFS
settings.lfs_filelist=このリポジトリに含まれているLFSファイル
settings.lfs_no_lfs_files=このリポジトリにLFSファイルはありません
settings.lfs_findcommits=コミットを検索
settings.lfs_lfs_file_no_commits=このLFSファイルに関するコミットはありません
settings.lfs_delete=LFSファイル(OID %s)の削除
settings.lfs_delete_warning=LFSファイルを削除すると、チェックアウトのときに 'object does not exist' エラーが発生するかもしれません。 よろしいですか?
settings.lfs_findpointerfiles=ポインタファイルを検索
settings.lfs_pointers.found=%d件のblobポインタ - 登録済 %d件、未登録 %d件 (実体ファイルなし %d件)
settings.lfs_pointers.sha=Blob SHA
settings.lfs_pointers.oid=OID
settings.lfs_pointers.inRepo=Repo内
settings.lfs_pointers.exists=実体ファイルあり
settings.lfs_pointers.accessible=ユーザーがアクセス可
settings.lfs_pointers.associateAccessible=アクセス可能な%d件のOIDを登録
diff.browse_source=ソースを参照
diff.parent=

View File

@ -1089,6 +1089,9 @@ activity.period.daily=1 dia
activity.period.halfweekly=3 dias
activity.period.weekly=1 semana
activity.period.monthly=1 mês
activity.period.quarterly=3 meses
activity.period.semiyearly=6 meses
activity.period.yearly=1 ano
activity.overview=Visão geral
activity.active_prs_count_1=<strong>%d</strong> Pull request ativo
activity.active_prs_count_n=<strong>%d</strong> Pull requests ativos

View File

@ -701,6 +701,7 @@ editor.no_changes_to_show=Gösterilecek değişiklik yok.
editor.fail_to_update_file=Şu hata ile '%s' dosyasını güncelleme/oluşturma başarısız oldu: %v
editor.add_subdir=Bir dizin ekle…
editor.unable_to_upload_files=Şu hata ile dosyalar '%s' 'a yüklenemedi: %v
editor.upload_file_is_locked='%s' dosyası %s tarafından kilitlendi.
editor.upload_files_to_dir=Dosyaları '%s' 'a yükle
editor.cannot_commit_to_protected_branch=Korunan '%s' dalına işleme yapılamıyor.
@ -1046,17 +1047,20 @@ activity.period.daily=1 gün
activity.period.halfweekly=3 gün
activity.period.weekly=1 hafta
activity.period.monthly=1 ay
activity.period.quarterly=3 ay
activity.period.semiyearly=6 ay
activity.period.yearly=1 yıl
activity.overview=Genel Bakış
activity.active_prs_count_1=<strong>%d</strong> Aktif Çekme İsteği
activity.active_prs_count_n=<strong>%d</strong> Aktif Çekme İsteği
activity.merged_prs_count_1=Birleştirilmiş Çekme İsteği
activity.merged_prs_count_n=Birleştirilmiş Çekme İstekleri
activity.merged_prs_count_n=Birleştirilmiş Çekme İsteği
activity.opened_prs_count_1=Önerilen Çekme İsteği
activity.opened_prs_count_n=Önerilen Çekme İstekleri
activity.opened_prs_count_n=Önerilen Çekme İsteği
activity.title.user_1=%d kullanıcı
activity.title.user_n=%d kullanıcı
activity.title.prs_1=%d Çekme isteği
activity.title.prs_n=%d Çekme istekleri
activity.title.prs_n=%d Çekme isteği
activity.title.prs_merged_by=%s tarafından %s birleştirildi
activity.title.prs_opened_by=%s tarafından %s önerildi
activity.merged_prs_label=Birleştirilen
@ -1064,20 +1068,21 @@ activity.opened_prs_label=Önerilen
activity.active_issues_count_1=<strong>%d</strong> Aktif Konu
activity.active_issues_count_n=<strong>%d</strong> Aktif Konu
activity.closed_issues_count_1=Kapalı Konu
activity.closed_issues_count_n=Kapalı Konular
activity.closed_issues_count_n=Kapalı Konu
activity.title.issues_1=%d Konu
activity.title.issues_n=%d Konu
activity.title.issues_closed_by=%s %s tarafından kapatıldı
activity.title.issues_created_by=%s %s tarafından oluşturuldu
activity.closed_issue_label=Kapalı
activity.new_issues_count_1=Yeni Konu
activity.new_issues_count_n=Yeni Konular
activity.new_issues_count_n=Yeni Konu
activity.new_issue_label=ıldı
activity.title.unresolved_conv_1=%d Çözümlenmemiş Konuşma
activity.title.unresolved_conv_n=%d Çözümlenmemiş Konuşma
activity.unresolved_conv_desc=Son zamanlarda değişen bu konu ve değişiklik istekleri henüz çözümlenmedi.
activity.unresolved_conv_label=ık
activity.title.releases_1=%d Serbest bırak
activity.title.releases_n=%d Serbest bırakmalar
activity.title.releases_1=%d Sürüm
activity.title.releases_n=%d Sürüm
activity.title.releases_published_by=%s tarafından %s yayınlandı
activity.published_release_label=Yayınlandı
activity.no_git_activity=Bu dönemde herhangi bir işleme yapılmamıştır.
@ -1095,6 +1100,7 @@ activity.git_stats_file_1=%d dosya
activity.git_stats_file_n=%d dosya
activity.git_stats_files_changed_1=değişti
activity.git_stats_files_changed_n=değişti
activity.git_stats_additions=ve oldu
activity.git_stats_addition_1=%d ekleme
activity.git_stats_addition_n=%d ekleme
activity.git_stats_and_deletions=ve

View File

@ -79,12 +79,11 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
}
u := &models.User{
Name: form.UserName,
Email: form.Email,
Passwd: form.Password,
IsActive: true,
LoginType: models.LoginPlain,
MustChangePassword: form.MustChangePassword,
Name: form.UserName,
Email: form.Email,
Passwd: form.Password,
IsActive: true,
LoginType: models.LoginPlain,
}
if len(form.LoginType) > 0 {
@ -95,9 +94,12 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
u.LoginName = form.LoginName
}
}
if !password.IsComplexEnough(form.Password) {
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserNew, &form)
return
if u.LoginType == models.LoginPlain {
if !password.IsComplexEnough(form.Password) {
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserNew, &form)
return
}
u.MustChangePassword = form.MustChangePassword
}
if err := models.CreateUser(u); err != nil {
switch {

View File

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/structs"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/webhook"
"github.com/unknwon/com"
)
@ -166,7 +167,7 @@ func ToHook(repoLink string, w *models.Webhook) *api.Hook {
"content_type": w.ContentType.Name(),
}
if w.HookTaskType == models.SLACK {
s := w.GetSlackHook()
s := webhook.GetSlackHook(w)
config["channel"] = s.Channel
config["username"] = s.Username
config["icon_url"] = s.IconURL

View File

@ -458,9 +458,16 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
issue.Content = *form.Body
}
// Update the deadline
if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) {
deadlineUnix := timeutil.TimeStamp(form.Deadline.Unix())
// Update or remove the deadline, only if set and allowed
if (form.Deadline != nil || form.RemoveDeadline != nil) && ctx.Repo.CanWrite(models.UnitTypeIssues) {
var deadlineUnix timeutil.TimeStamp
if (form.RemoveDeadline == nil || !*form.RemoveDeadline) && !form.Deadline.IsZero() {
deadline := time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
23, 59, 59, 0, form.Deadline.Location())
deadlineUnix = timeutil.TimeStamp(deadline.Unix())
}
if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
ctx.Error(500, "UpdateIssueDeadline", err)
return

View File

@ -8,6 +8,7 @@ import (
"fmt"
"net/http"
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
@ -326,7 +327,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
// swagger:operation PATCH /repos/{owner}/{repo}/pulls/{index} repository repoEditPullRequest
// ---
// summary: Update a pull request
// summary: Update a pull request. If using deadline only the date will be taken into account, and time of day ignored.
// consumes:
// - application/json
// produces:
@ -385,9 +386,15 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
issue.Content = form.Body
}
// Update Deadline
if form.Deadline != nil {
deadlineUnix := timeutil.TimeStamp(form.Deadline.Unix())
// Update or remove deadline if set
if form.Deadline != nil || form.RemoveDeadline != nil {
var deadlineUnix timeutil.TimeStamp
if (form.RemoveDeadline == nil || !*form.RemoveDeadline) && !form.Deadline.IsZero() {
deadline := time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
23, 59, 59, 0, form.Deadline.Location())
deadlineUnix = timeutil.TimeStamp(deadline.Unix())
}
if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
ctx.Error(500, "UpdateIssueDeadline", err)
return

View File

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/routers/api/v1/convert"
"code.gitea.io/gitea/routers/utils"
@ -129,7 +130,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
return nil, false
}
meta, err := json.Marshal(&models.SlackMeta{
meta, err := json.Marshal(&webhook.SlackMeta{
Channel: strings.TrimSpace(channel),
Username: form.Config["username"],
IconURL: form.Config["icon_url"],
@ -203,7 +204,7 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webho
if w.HookTaskType == models.SLACK {
if channel, ok := form.Config["channel"]; ok {
meta, err := json.Marshal(&models.SlackMeta{
meta, err := json.Marshal(&webhook.SlackMeta{
Channel: channel,
Username: form.Config["username"],
IconURL: form.Config["icon_url"],

View File

@ -35,6 +35,12 @@ func Activity(ctx *context.Context) {
timeFrom = timeUntil.Add(-time.Hour * 168)
case "monthly":
timeFrom = timeUntil.AddDate(0, -1, 0)
case "quarterly":
timeFrom = timeUntil.AddDate(0, -3, 0)
case "semiyearly":
timeFrom = timeUntil.AddDate(0, -6, 0)
case "yearly":
timeFrom = timeUntil.AddDate(-1, 0, 0)
default:
ctx.Data["Period"] = "weekly"
timeFrom = timeUntil.Add(-time.Hour * 168)
@ -70,6 +76,12 @@ func ActivityAuthors(ctx *context.Context) {
timeFrom = timeUntil.Add(-time.Hour * 168)
case "monthly":
timeFrom = timeUntil.AddDate(0, -1, 0)
case "quarterly":
timeFrom = timeUntil.AddDate(0, -3, 0)
case "semiyearly":
timeFrom = timeUntil.AddDate(0, -6, 0)
case "yearly":
timeFrom = timeUntil.AddDate(-1, 0, 0)
default:
timeFrom = timeUntil.Add(-time.Hour * 168)
}

View File

@ -268,7 +268,7 @@ func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) {
return
}
meta, err := json.Marshal(&models.DiscordMeta{
meta, err := json.Marshal(&webhook.DiscordMeta{
Username: form.Username,
IconURL: form.IconURL,
})
@ -357,7 +357,7 @@ func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) {
return
}
meta, err := json.Marshal(&models.TelegramMeta{
meta, err := json.Marshal(&webhook.TelegramMeta{
BotToken: form.BotToken,
ChatID: form.ChatID,
})
@ -452,7 +452,7 @@ func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
return
}
meta, err := json.Marshal(&models.SlackMeta{
meta, err := json.Marshal(&webhook.SlackMeta{
Channel: strings.TrimSpace(form.Channel),
Username: form.Username,
IconURL: form.IconURL,
@ -515,11 +515,11 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
ctx.Data["HookType"] = w.HookTaskType.Name()
switch w.HookTaskType {
case models.SLACK:
ctx.Data["SlackHook"] = w.GetSlackHook()
ctx.Data["SlackHook"] = webhook.GetSlackHook(w)
case models.DISCORD:
ctx.Data["DiscordHook"] = w.GetDiscordHook()
ctx.Data["DiscordHook"] = webhook.GetDiscordHook(w)
case models.TELEGRAM:
ctx.Data["TelegramHook"] = w.GetTelegramHook()
ctx.Data["TelegramHook"] = webhook.GetTelegramHook(w)
}
ctx.Data["History"], err = w.History(1)
@ -646,7 +646,7 @@ func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
return
}
meta, err := json.Marshal(&models.SlackMeta{
meta, err := json.Marshal(&webhook.SlackMeta{
Channel: strings.TrimSpace(form.Channel),
Username: form.Username,
IconURL: form.IconURL,
@ -690,7 +690,7 @@ func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) {
return
}
meta, err := json.Marshal(&models.DiscordMeta{
meta, err := json.Marshal(&webhook.DiscordMeta{
Username: form.Username,
IconURL: form.IconURL,
})
@ -763,7 +763,7 @@ func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm)
ctx.HTML(200, orCtx.NewTemplate)
return
}
meta, err := json.Marshal(&models.TelegramMeta{
meta, err := json.Marshal(&webhook.TelegramMeta{
BotToken: form.BotToken,
ChatID: form.ChatID,
})

View File

@ -5,10 +5,7 @@
package issue
import (
"fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
)
@ -24,18 +21,6 @@ func NewIssue(repo *models.Repository, issue *models.Issue, labelIDs []int64, uu
}
}
if err := models.NotifyWatchers(&models.Action{
ActUserID: issue.Poster.ID,
ActUser: issue.Poster,
OpType: models.ActionCreateIssue,
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
notification.NotifyNewIssue(issue)
return nil

View File

@ -9,9 +9,8 @@ import (
"fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/webhook"
)
func syncAction(opType models.ActionType, repo *models.Repository, refName string, data []byte) error {
@ -45,25 +44,9 @@ func SyncPushAction(repo *models.Repository, opts SyncPushActionOptions) error {
opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
}
apiCommits, err := opts.Commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
if err != nil {
return err
}
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
apiPusher := repo.MustOwner().APIFormat()
if err := webhook.PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{
Ref: opts.RefName,
Before: opts.OldCommitID,
After: opts.NewCommitID,
CompareURL: setting.AppURL + opts.Commits.CompareURL,
Commits: apiCommits,
Repo: repo.APIFormat(models.AccessModeOwner),
Pusher: apiPusher,
Sender: apiPusher,
}); err != nil {
return fmt.Errorf("PrepareWebhooks: %v", err)
}
notification.NotifyPushCommits(repo.MustOwner(), repo, opts.RefName, opts.OldCommitID, opts.NewCommitID, opts.Commits)
data, err := json.Marshal(opts.Commits)
if err != nil {

View File

@ -20,10 +20,9 @@ import (
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/webhook"
"github.com/mcuadros/go-version"
)
@ -360,16 +359,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
return nil
}
mode, _ := models.AccessLevel(doer, pr.Issue.Repo)
if err = webhook.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueClosed,
Index: pr.Index,
PullRequest: pr.APIFormat(),
Repository: pr.Issue.Repo.APIFormat(mode),
Sender: doer.APIFormat(),
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
notification.NotifyIssueChangeStatus(doer, pr.Issue, true)
return nil
}

View File

@ -10,8 +10,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/modules/notification"
issue_service "code.gitea.io/gitea/services/issue"
)
@ -27,30 +26,10 @@ func NewPullRequest(repo *models.Repository, pull *models.Issue, labelIDs []int6
}
}
if err := models.NotifyWatchers(&models.Action{
ActUserID: pull.Poster.ID,
ActUser: pull.Poster,
OpType: models.ActionCreatePullRequest,
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Title),
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
pr.Issue = pull
pull.PullRequest = pr
mode, _ := models.AccessLevel(pull.Poster, repo)
if err := webhook.PrepareWebhooks(repo, models.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueOpened,
Index: pull.Index,
PullRequest: pr.APIFormat(),
Repository: repo.APIFormat(mode),
Sender: pull.Poster.APIFormat(),
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
notification.NotifyNewPullRequest(pr)
return nil
}
@ -109,23 +88,9 @@ func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSy
if err == nil {
for _, pr := range prs {
pr.Issue.PullRequest = pr
if err = pr.Issue.LoadAttributes(); err != nil {
log.Error("LoadAttributes: %v", err)
continue
}
if err = webhook.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueSynchronized,
Index: pr.Issue.Index,
PullRequest: pr.Issue.PullRequest.APIFormat(),
Repository: pr.Issue.Repo.APIFormat(models.AccessModeNone),
Sender: doer.APIFormat(),
}); err != nil {
log.Error("PrepareWebhooks [pull_id: %v]: %v", pr.ID, err)
continue
}
notification.NotifyPullRequestSynchronized(doer, pr)
}
}
}
addHeadRepoTasks(prs)

View File

@ -7,8 +7,7 @@ package pull
import (
"code.gitea.io/gitea/models"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/modules/notification"
)
// CreateReview creates a new review based on opts
@ -18,7 +17,9 @@ func CreateReview(opts models.CreateReviewOptions) (*models.Review, error) {
return nil, err
}
return review, reviewHook(review)
notification.NotifyPullRequestReview(review.Issue.PullRequest, review, nil)
return review, nil
}
// UpdateReview updates a review
@ -28,43 +29,7 @@ func UpdateReview(review *models.Review) error {
return err
}
return reviewHook(review)
}
func reviewHook(review *models.Review) error {
var reviewHookType models.HookEventType
switch review.Type {
case models.ReviewTypeApprove:
reviewHookType = models.HookEventPullRequestApproved
case models.ReviewTypeComment:
reviewHookType = models.HookEventPullRequestComment
case models.ReviewTypeReject:
reviewHookType = models.HookEventPullRequestRejected
default:
// unsupported review webhook type here
return nil
}
pr := review.Issue.PullRequest
if err := pr.LoadIssue(); err != nil {
return err
}
mode, err := models.AccessLevel(review.Issue.Poster, review.Issue.Repo)
if err != nil {
return err
}
return webhook.PrepareWebhooks(review.Issue.Repo, reviewHookType, &api.PullRequestPayload{
Action: api.HookIssueSynchronized,
Index: review.Issue.Index,
PullRequest: pr.APIFormat(),
Repository: review.Issue.Repo.APIFormat(mode),
Sender: review.Reviewer.APIFormat(),
Review: &api.ReviewPayload{
Type: string(reviewHookType),
Content: review.Content,
},
})
notification.NotifyPullRequestReview(review.Issue.PullRequest, review, nil)
return nil
}

View File

@ -42,7 +42,7 @@
<input id="password" name="password" type="password" value="{{.password}}" {{if eq .login_type "0-0"}}required{{end}}>
</div>
<div class="inline field">
<div class="inline field local{{if ne .login_type "0-0"}} hide{{end}}">
<div class="ui checkbox">
<label><strong>{{.i18n.Tr "auth.allow_password_change" }}</strong></label>
<input name="must_change_password" type="checkbox" checked>

View File

@ -17,6 +17,9 @@
<a class="{{if eq .Period "halfweekly"}}active {{end}}item" href="{{$.RepoLink}}/activity/halfweekly">{{.i18n.Tr "repo.activity.period.halfweekly"}}</a>
<a class="{{if eq .Period "weekly"}}active {{end}}item" href="{{$.RepoLink}}/activity/weekly">{{.i18n.Tr "repo.activity.period.weekly"}}</a>
<a class="{{if eq .Period "monthly"}}active {{end}}item" href="{{$.RepoLink}}/activity/monthly">{{.i18n.Tr "repo.activity.period.monthly"}}</a>
<a class="{{if eq .Period "quarterly"}}active {{end}}item" href="{{$.RepoLink}}/activity/quarterly">{{.i18n.Tr "repo.activity.period.quarterly"}}</a>
<a class="{{if eq .Period "semiyearly"}}active {{end}}item" href="{{$.RepoLink}}/activity/semiyearly">{{.i18n.Tr "repo.activity.period.semiyearly"}}</a>
<a class="{{if eq .Period "yearly"}}active {{end}}item" href="{{$.RepoLink}}/activity/yearly">{{.i18n.Tr "repo.activity.period.yearly"}}</a>
</div>
</div>
</div>

View File

@ -13,56 +13,55 @@
{{range $r}}
<tr>
<td class="author">
{{$userName := .Author.Name}}
{{$userName := .Author.Name}}
{{if .User}}
{{if .User.FullName}}
{{$userName = .User.FullName}}
{{$userName = .User.FullName}}
{{end}}
<img class="ui avatar image" src="{{.User.RelAvatarLink}}" alt=""/>&nbsp;&nbsp;<a href="{{AppSubUrl}}/{{.User.Name}}">{{$userName}}</a>
<img class="ui avatar image" src="{{.User.RelAvatarLink}}" alt=""/>&nbsp;&nbsp;<a href="{{AppSubUrl}}/{{.User.Name}}">{{$userName}}</a>
{{else}}
<img class="ui avatar image" src="{{AvatarLink .Author.Email}}" alt=""/>&nbsp;&nbsp;{{$userName}}
{{end}}
</td>
<td class="sha">
{{$class := "ui sha label"}}
{{if .Signature}}
{{$class = (printf "%s%s" $class " isSigned")}}
{{if .Verification.Verified}}
{{$class = (printf "%s%s" $class " isVerified")}}
{{else if .Verification.Warning}}
{{$class = (printf "%s%s" $class " isWarning")}}
{{end}}
{{end}}
{{if $.Reponame}}
<a href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}" rel="nofollow" class="{{$class}}">
{{else}}
<span class="{{$class}}">
{{end}}
{{$class := "ui sha label"}}
{{if .Signature}}
{{$class = (printf "%s%s" $class " isSigned")}}
{{if .Verification.Verified}}
{{$class = (printf "%s%s" $class " isVerified")}}
{{else if .Verification.Warning}}
{{$class = (printf "%s%s" $class " isWarning")}}
{{end}}
{{end}}
{{if $.Reponame}}
<a href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}" rel="nofollow" class="{{$class}}">
{{else}}
<span class="{{$class}}">
{{end}}
{{ShortSha .ID.String}}
{{if .Signature}}
<div class="ui detail icon button">
{{if .Verification.Verified}}
<i title="{{.Verification.Reason}}" class="lock green icon"></i>
{{if ne .Verification.SigningUser.ID 0}}
<i title="{{.Verification.Reason}}" class="lock green icon"></i>
{{else}}
<i title="{{.Verification.Reason}}" class="icons">
<i class="green lock icon"></i>
<i class="tiny inverted cog icon centerlock"></i>
</i>
{{end}}
<i title="{{.Verification.Reason}}" class="lock green icon"></i>
{{else}}
<i title="{{.Verification.Reason}}" class="icons">
<i class="green lock icon"></i>
<i class="tiny inverted cog icon centerlock"></i>
</i>
{{end}}
{{else if .Verification.Warning}}
<i title="{{$.i18n.Tr .Verification.Reason}}" class="red unlock icon"></i>
{{else}}
<i title="{{$.i18n.Tr .Verification.Reason}}" class="red unlock icon"></i>
{{else}}
<i title="{{$.i18n.Tr .Verification.Reason}}" class="unlock icon"></i>
{{end}}
</div>
{{end}}
{{if $.Reponame}}
</a>
{{else}}
</span>
{{end}}
{{if $.Reponame}}
</a>
{{else}}
</span>
{{end}}
</td>
<td class="message">
<span class="message-wrapper">
@ -73,7 +72,7 @@
<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button>
{{end}}
{{if eq (CommitType .) "SignCommitWithStatuses"}}
{{template "repo/commit_status" .Status}}
{{template "repo/commit_status" .Status}}
{{end}}
{{if IsMultilineCommitMessage .Message}}
<pre class="commit-body" style="display: none;">{{RenderCommitBody .Message $.RepoLink $.Repository.ComposeMetas}}</pre>

View File

@ -3,7 +3,12 @@
<i class="dropdown icon"></i>
<div class="menu">
<a class="item tiny basic toggle button" data-target="#diff-files">{{.i18n.Tr "repo.diff.show_diff_stats"}}</a>
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.patch" download="{{.Issue.Index}}.patch">{{.i18n.Tr "repo.diff.download_patch"}}</a>
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.diff" download="{{.Issue.Index}}.diff">{{.i18n.Tr "repo.diff.download_diff"}}</a>
{{if .Issue.Index}}
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.patch" download="{{.Issue.Index}}.patch">{{.i18n.Tr "repo.diff.download_patch"}}</a>
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.diff" download="{{.Issue.Index}}.diff">{{.i18n.Tr "repo.diff.download_diff"}}</a>
{{else if .Commit.ID.String}}
<a class="item" href="{{$.RepoLink}}/commit/{{.Commit.ID.String}}.patch" download="{{ShortSha .Commit.ID.String}}.patch">{{.i18n.Tr "repo.diff.download_patch"}}</a>
<a class="item" href="{{$.RepoLink}}/commit/{{.Commit.ID.String}}.diff" download="{{ShortSha .Commit.ID.String}}.diff">{{.i18n.Tr "repo.diff.download_diff"}}</a>
{{end}}
</div>
</div>

View File

@ -132,7 +132,7 @@
{{range $.branch_status_check_contexts}}
<tr><td>
<span class="ui checkbox">
<input class="enable-whitelist" name="status_check_contexts" value="{{.}}" type="checkbox" {{if $.is_context_require}}{{if call $.is_context_required .}}checked{{end}}{{end}}>
<input class="enable-whitelist" name="status_check_contexts" value="{{.}}" type="checkbox" {{if $.is_context_required}}{{if call $.is_context_required .}}checked{{end}}{{end}}>
</span>
{{.}}
{{if $.is_context_required}}{{if call $.is_context_required .}}<div class="ui label right">Required</div>{{end}}{{end}}

View File

@ -4715,7 +4715,7 @@
"tags": [
"repository"
],
"summary": "Update a pull request",
"summary": "Update a pull request. If using deadline only the date will be taken into account, and time of day ignored.",
"operationId": "repoEditPullRequest",
"parameters": [
{
@ -8532,6 +8532,10 @@
"title": {
"type": "string",
"x-go-name": "Title"
},
"unset_due_date": {
"type": "boolean",
"x-go-name": "RemoveDeadline"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
@ -8660,6 +8664,10 @@
"title": {
"type": "string",
"x-go-name": "Title"
},
"unset_due_date": {
"type": "boolean",
"x-go-name": "RemoveDeadline"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"

View File

@ -80,5 +80,5 @@ func trimIndentUpTo(max int) func(string) string {
}
}
func (n Example) String() string { return orgWriter.nodesAsString(n) }
func (n Block) String() string { return orgWriter.nodesAsString(n) }
func (n Example) String() string { return orgWriter.WriteNodesAsString(n) }
func (n Block) String() string { return orgWriter.WriteNodesAsString(n) }

View File

@ -90,7 +90,7 @@ func New() *Configuration {
}
// String returns the pretty printed Org mode string for the given nodes (see OrgWriter).
func String(nodes []Node) string { return orgWriter.nodesAsString(nodes...) }
func String(nodes []Node) string { return orgWriter.WriteNodesAsString(nodes...) }
// Write is called after with an instance of the Writer interface to export a parsed Document into another format.
func (d *Document) Write(w Writer) (out string, err error) {

View File

@ -93,5 +93,5 @@ func (d *PropertyDrawer) Get(key string) (string, bool) {
return "", false
}
func (n Drawer) String() string { return orgWriter.nodesAsString(n) }
func (n PropertyDrawer) String() string { return orgWriter.nodesAsString(n) }
func (n Drawer) String() string { return orgWriter.WriteNodesAsString(n) }
func (n PropertyDrawer) String() string { return orgWriter.WriteNodesAsString(n) }

View File

@ -32,4 +32,4 @@ func (d *Document) parseFootnoteDefinition(i int, parentStop stopFn) (int, Node)
return consumed, definition
}
func (n FootnoteDefinition) String() string { return orgWriter.nodesAsString(n) }
func (n FootnoteDefinition) String() string { return orgWriter.WriteNodesAsString(n) }

View File

@ -98,4 +98,4 @@ func (parent *Section) add(current *Section) {
}
}
func (n Headline) String() string { return orgWriter.nodesAsString(n) }
func (n Headline) String() string { return orgWriter.WriteNodesAsString(n) }

View File

@ -69,16 +69,13 @@ func NewHTMLWriter() *HTMLWriter {
}
}
func (w *HTMLWriter) emptyClone() *HTMLWriter {
wcopy := *w
wcopy.Builder = strings.Builder{}
return &wcopy
}
func (w *HTMLWriter) nodesAsString(nodes ...Node) string {
tmp := w.emptyClone()
WriteNodes(tmp, nodes...)
return tmp.String()
func (w *HTMLWriter) WriteNodesAsString(nodes ...Node) string {
original := w.Builder
w.Builder = strings.Builder{}
WriteNodes(w, nodes...)
out := w.String()
w.Builder = original
return out
}
func (w *HTMLWriter) WriterWithExtensions() Writer {
@ -104,12 +101,14 @@ func (w *HTMLWriter) WritePropertyDrawer(PropertyDrawer) {}
func (w *HTMLWriter) WriteBlock(b Block) {
content := ""
if isRawTextBlock(b.Name) {
exportWriter := w.emptyClone()
exportWriter.htmlEscape = false
WriteNodes(exportWriter, b.Children...)
content = strings.TrimRightFunc(exportWriter.String(), unicode.IsSpace)
builder, htmlEscape := w.Builder, w.htmlEscape
w.Builder, w.htmlEscape = strings.Builder{}, false
WriteNodes(w, b.Children...)
out := w.String()
w.Builder, w.htmlEscape = builder, htmlEscape
content = strings.TrimRightFunc(out, unicode.IsSpace)
} else {
content = w.nodesAsString(b.Children...)
content = w.WriteNodesAsString(b.Children...)
}
switch name := b.Name; {
case name == "SRC":
@ -194,7 +193,7 @@ func (w *HTMLWriter) writeSection(section *Section) {
// NOTE: To satisfy hugo ExtractTOC() check we cannot use `<li>\n` here. Doesn't really matter, just a note.
w.WriteString("<li>")
h := section.Headline
title := cleanHeadlineTitleForHTMLAnchorRegexp.ReplaceAllString(w.nodesAsString(h.Title...), "")
title := cleanHeadlineTitleForHTMLAnchorRegexp.ReplaceAllString(w.WriteNodesAsString(h.Title...), "")
w.WriteString(fmt.Sprintf("<a href=\"#%s\">%s</a>\n", h.ID(), title))
if len(section.Children) != 0 {
w.WriteString("<ul>\n")
@ -306,7 +305,7 @@ func (w *HTMLWriter) WriteRegularLink(l RegularLink) {
}
description := url
if l.Description != nil {
description = w.nodesAsString(l.Description...)
description = w.WriteNodesAsString(l.Description...)
}
switch l.Kind() {
case "image":
@ -384,10 +383,10 @@ func (w *HTMLWriter) WriteHorizontalRule(h HorizontalRule) {
}
func (w *HTMLWriter) WriteNodeWithMeta(n NodeWithMeta) {
out := w.nodesAsString(n.Node)
out := w.WriteNodesAsString(n.Node)
if p, ok := n.Node.(Paragraph); ok {
if len(p.Children) == 1 && isImageOrVideoLink(p.Children[0]) {
out = w.nodesAsString(p.Children[0])
out = w.WriteNodesAsString(p.Children[0])
}
}
for _, attributes := range n.Meta.HTMLAttributes {
@ -399,7 +398,7 @@ func (w *HTMLWriter) WriteNodeWithMeta(n NodeWithMeta) {
if i != 0 {
caption += " "
}
caption += w.nodesAsString(ns...)
caption += w.WriteNodesAsString(ns...)
}
out = fmt.Sprintf("<figure>\n%s<figcaption>\n%s\n</figcaption>\n</figure>\n", out, caption)
}

View File

@ -147,8 +147,8 @@ func (d *Document) parseExplicitLineBreakOrLatexFragment(input string, start int
switch {
case start+2 >= len(input):
case input[start+1] == '\\' && start != 0 && input[start-1] != '\n':
for i := start + 2; unicode.IsSpace(rune(input[i])); i++ {
if i >= len(input) || input[i] == '\n' {
for i := start + 2; i <= len(input)-1 && unicode.IsSpace(rune(input[i])); i++ {
if input[i] == '\n' {
return i + 1 - start, ExplicitLineBreak{}
}
}
@ -346,12 +346,12 @@ func (l RegularLink) Kind() string {
return "regular"
}
func (n Text) String() string { return orgWriter.nodesAsString(n) }
func (n LineBreak) String() string { return orgWriter.nodesAsString(n) }
func (n ExplicitLineBreak) String() string { return orgWriter.nodesAsString(n) }
func (n StatisticToken) String() string { return orgWriter.nodesAsString(n) }
func (n Emphasis) String() string { return orgWriter.nodesAsString(n) }
func (n LatexFragment) String() string { return orgWriter.nodesAsString(n) }
func (n FootnoteLink) String() string { return orgWriter.nodesAsString(n) }
func (n RegularLink) String() string { return orgWriter.nodesAsString(n) }
func (n Timestamp) String() string { return orgWriter.nodesAsString(n) }
func (n Text) String() string { return orgWriter.WriteNodesAsString(n) }
func (n LineBreak) String() string { return orgWriter.WriteNodesAsString(n) }
func (n ExplicitLineBreak) String() string { return orgWriter.WriteNodesAsString(n) }
func (n StatisticToken) String() string { return orgWriter.WriteNodesAsString(n) }
func (n Emphasis) String() string { return orgWriter.WriteNodesAsString(n) }
func (n LatexFragment) String() string { return orgWriter.WriteNodesAsString(n) }
func (n FootnoteLink) String() string { return orgWriter.WriteNodesAsString(n) }
func (n RegularLink) String() string { return orgWriter.WriteNodesAsString(n) }
func (n Timestamp) String() string { return orgWriter.WriteNodesAsString(n) }

View File

@ -177,8 +177,8 @@ func (d *Document) loadSetupFile(k Keyword) (int, Node) {
return 1, k
}
func (n Comment) String() string { return orgWriter.nodesAsString(n) }
func (n Keyword) String() string { return orgWriter.nodesAsString(n) }
func (n NodeWithMeta) String() string { return orgWriter.nodesAsString(n) }
func (n NodeWithName) String() string { return orgWriter.nodesAsString(n) }
func (n Include) String() string { return orgWriter.nodesAsString(n) }
func (n Comment) String() string { return orgWriter.WriteNodesAsString(n) }
func (n Keyword) String() string { return orgWriter.WriteNodesAsString(n) }
func (n NodeWithMeta) String() string { return orgWriter.WriteNodesAsString(n) }
func (n NodeWithName) String() string { return orgWriter.WriteNodesAsString(n) }
func (n Include) String() string { return orgWriter.WriteNodesAsString(n) }

View File

@ -109,6 +109,6 @@ func (d *Document) parseListItem(l List, i int, parentStop stopFn) (int, Node) {
return i - start, ListItem{bullet, status, nodes}
}
func (n List) String() string { return orgWriter.nodesAsString(n) }
func (n ListItem) String() string { return orgWriter.nodesAsString(n) }
func (n DescriptiveListItem) String() string { return orgWriter.nodesAsString(n) }
func (n List) String() string { return orgWriter.WriteNodesAsString(n) }
func (n ListItem) String() string { return orgWriter.WriteNodesAsString(n) }
func (n DescriptiveListItem) String() string { return orgWriter.WriteNodesAsString(n) }

View File

@ -43,39 +43,33 @@ func (w *OrgWriter) WriterWithExtensions() Writer {
func (w *OrgWriter) Before(d *Document) {}
func (w *OrgWriter) After(d *Document) {}
func (w *OrgWriter) emptyClone() *OrgWriter {
wcopy := *w
wcopy.Builder = strings.Builder{}
return &wcopy
}
func (w *OrgWriter) nodesAsString(nodes ...Node) string {
tmp := w.emptyClone()
WriteNodes(tmp, nodes...)
return tmp.String()
func (w *OrgWriter) WriteNodesAsString(nodes ...Node) string {
builder := w.Builder
w.Builder = strings.Builder{}
WriteNodes(w, nodes...)
out := w.String()
w.Builder = builder
return out
}
func (w *OrgWriter) WriteHeadline(h Headline) {
tmp := w.emptyClone()
tmp.WriteString(strings.Repeat("*", h.Lvl))
start := w.Len()
w.WriteString(strings.Repeat("*", h.Lvl))
if h.Status != "" {
tmp.WriteString(" " + h.Status)
w.WriteString(" " + h.Status)
}
if h.Priority != "" {
tmp.WriteString(" [#" + h.Priority + "]")
w.WriteString(" [#" + h.Priority + "]")
}
tmp.WriteString(" ")
WriteNodes(tmp, h.Title...)
hString := tmp.String()
w.WriteString(" ")
WriteNodes(w, h.Title...)
if len(h.Tags) != 0 {
tString := ":" + strings.Join(h.Tags, ":") + ":"
if n := w.TagsColumn - len(tString) - len(hString); n > 0 {
w.WriteString(hString + strings.Repeat(" ", n) + tString)
if n := w.TagsColumn - len(tString) - (w.Len() - start); n > 0 {
w.WriteString(strings.Repeat(" ", n) + tString)
} else {
w.WriteString(hString + " " + tString)
w.WriteString(" " + tString)
}
} else {
w.WriteString(hString)
}
w.WriteString("\n")
if len(h.Children) != 0 {
@ -123,7 +117,7 @@ func (w *OrgWriter) WritePropertyDrawer(d PropertyDrawer) {
func (w *OrgWriter) WriteFootnoteDefinition(f FootnoteDefinition) {
w.WriteString(fmt.Sprintf("[fn:%s]", f.Name))
content := w.nodesAsString(f.Children...)
content := w.WriteNodesAsString(f.Children...)
if content != "" && !unicode.IsSpace(rune(content[0])) {
w.WriteString(" ")
}
@ -131,7 +125,7 @@ func (w *OrgWriter) WriteFootnoteDefinition(f FootnoteDefinition) {
}
func (w *OrgWriter) WriteParagraph(p Paragraph) {
content := w.nodesAsString(p.Children...)
content := w.WriteNodesAsString(p.Children...)
if len(content) > 0 && content[0] != '\n' {
w.WriteString(w.indent)
}
@ -141,7 +135,7 @@ func (w *OrgWriter) WriteParagraph(p Paragraph) {
func (w *OrgWriter) WriteExample(e Example) {
for _, n := range e.Children {
w.WriteString(w.indent + ":")
if content := w.nodesAsString(n); content != "" {
if content := w.WriteNodesAsString(n); content != "" {
w.WriteString(" " + content)
}
w.WriteString("\n")
@ -185,10 +179,11 @@ func (w *OrgWriter) WriteComment(c Comment) {
func (w *OrgWriter) WriteList(l List) { WriteNodes(w, l.Items...) }
func (w *OrgWriter) WriteListItem(li ListItem) {
liWriter := w.emptyClone()
liWriter.indent = w.indent + strings.Repeat(" ", len(li.Bullet)+1)
WriteNodes(liWriter, li.Children...)
content := strings.TrimPrefix(liWriter.String(), liWriter.indent)
originalBuilder, originalIndent := w.Builder, w.indent
w.Builder, w.indent = strings.Builder{}, w.indent+strings.Repeat(" ", len(li.Bullet)+1)
WriteNodes(w, li.Children...)
content := strings.TrimPrefix(w.String(), w.indent)
w.Builder, w.indent = originalBuilder, originalIndent
w.WriteString(w.indent + li.Bullet)
if li.Status != "" {
w.WriteString(fmt.Sprintf(" [%s]", li.Status))
@ -207,14 +202,15 @@ func (w *OrgWriter) WriteDescriptiveListItem(di DescriptiveListItem) {
}
indent := w.indent + strings.Repeat(" ", len(di.Bullet)+1)
if len(di.Term) != 0 {
term := w.nodesAsString(di.Term...)
term := w.WriteNodesAsString(di.Term...)
w.WriteString(" " + term + " ::")
indent = indent + strings.Repeat(" ", len(term)+4)
}
diWriter := w.emptyClone()
diWriter.indent = indent
WriteNodes(diWriter, di.Details...)
details := strings.TrimPrefix(diWriter.String(), diWriter.indent)
originalBuilder, originalIndent := w.Builder, w.indent
w.Builder, w.indent = strings.Builder{}, indent
WriteNodes(w, di.Details...)
details := strings.TrimPrefix(w.String(), w.indent)
w.Builder, w.indent = originalBuilder, originalIndent
if len(details) > 0 && details[0] == '\n' {
w.WriteString(details)
} else {
@ -239,7 +235,7 @@ func (w *OrgWriter) WriteTable(t Table) {
w.WriteString(`|`)
for _, column := range row.Columns {
w.WriteString(` `)
content := w.nodesAsString(column.Children...)
content := w.WriteNodesAsString(column.Children...)
if content == "" {
content = " "
}
@ -326,9 +322,6 @@ func (w *OrgWriter) WriteRegularLink(l RegularLink) {
} else if l.Description == nil {
w.WriteString(fmt.Sprintf("[[%s]]", l.URL))
} else {
descriptionWriter := w.emptyClone()
WriteNodes(descriptionWriter, l.Description...)
description := descriptionWriter.String()
w.WriteString(fmt.Sprintf("[[%s][%s]]", l.URL, description))
w.WriteString(fmt.Sprintf("[[%s][%s]]", l.URL, w.WriteNodesAsString(l.Description...)))
}
}

View File

@ -42,5 +42,5 @@ func (d *Document) parseHorizontalRule(i int, parentStop stopFn) (int, Node) {
return 1, HorizontalRule{}
}
func (n Paragraph) String() string { return orgWriter.nodesAsString(n) }
func (n HorizontalRule) String() string { return orgWriter.nodesAsString(n) }
func (n Paragraph) String() string { return orgWriter.WriteNodesAsString(n) }
func (n HorizontalRule) String() string { return orgWriter.WriteNodesAsString(n) }

View File

@ -127,4 +127,4 @@ func isSpecialRow(rawColumns []string) bool {
return isAlignRow
}
func (n Table) String() string { return orgWriter.nodesAsString(n) }
func (n Table) String() string { return orgWriter.WriteNodesAsString(n) }

View File

@ -9,6 +9,7 @@ type Writer interface {
String() string // String is called at the very end to retrieve the final output.
WriterWithExtensions() Writer
WriteNodesAsString(...Node) string
WriteKeyword(Keyword)
WriteInclude(Include)

View File

@ -1,6 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
// +build go1.13
// +build go1.13,!go1.14
package idna

4733
vendor/golang.org/x/net/idna/tables12.00.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

4
vendor/modules.txt vendored
View File

@ -330,7 +330,7 @@ github.com/mschoch/smat
github.com/msteinert/pam
# github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5
github.com/nfnt/resize
# github.com/niklasfasching/go-org v0.1.7
# github.com/niklasfasching/go-org v0.1.8
github.com/niklasfasching/go-org/org
# github.com/oliamb/cutter v0.2.2
github.com/oliamb/cutter
@ -464,7 +464,7 @@ golang.org/x/crypto/scrypt
golang.org/x/crypto/ssh
golang.org/x/crypto/ssh/agent
golang.org/x/crypto/ssh/knownhosts
# golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271
# golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9
golang.org/x/net/context
golang.org/x/net/context/ctxhttp
golang.org/x/net/html