Merge branch 'master' into refactor_issues-subscription

This commit is contained in:
6543 2019-11-15 11:06:59 +01:00 committed by GitHub
commit cd6f2bef54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 8159 additions and 4700 deletions

View File

@ -24,7 +24,7 @@ indent_size = 2
[*.js]
indent_style = space
indent_size = 4
indent_size = 2
[Makefile]
indent_style = tab

View File

@ -1,27 +1,51 @@
root: true
extends:
- eslint-config-airbnb-base
- eslint:recommended
parserOptions:
ecmaVersion: 2015
ecmaVersion: 2020
env:
browser: true
jquery: true
es6: true
jquery: true
node: true
globals:
Clipboard: false
CodeMirror: false
emojify: false
SimpleMDE: false
Vue: false
Dropzone: false
u2fApi: false
emojify: false
hljs: false
SimpleMDE: false
u2fApi: false
Vue: false
rules:
no-unused-vars: [error, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, ignoreRestSiblings: true}]
prefer-const: [2, {destructuring: all}]
camelcase: [0]
comma-dangle: [2, only-multiline]
consistent-return: [0]
default-case: [0]
func-names: [0]
max-len: [0]
newline-per-chained-call: [0]
arrow-body-style: [0]
no-alert: [0]
no-continue: [0]
no-mixed-operators: [0]
no-multi-assign: [0]
no-new: [0]
no-param-reassign: [0]
no-plusplus: [0]
no-restricted-syntax: [0]
no-shadow: [0]
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, ignoreRestSiblings: true}]
no-use-before-define: [0]
no-var: [2]
one-var-declaration-per-line: [0]
one-var: [0]
prefer-const: [2, {destructuring: all}]
prefer-destructuring: [0]
radix: [2, as-needed]

View File

@ -4,11 +4,44 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.10.0-RC2](https://github.com/go-gitea/gitea/releases/tag/v1.10.0-rc2) - 2019-10-30
## [1.10.0](https://github.com/go-gitea/gitea/releases/tag/v1.10.0) - 2019-11-13
* BREAKING
* Fix deadline on update issue or PR via API (#8698)
* Hide some user information via API if user doesn't have enough permission (#8655) (#8657)
* Remove legacy handling of drone token (#8191)
* Change repo search to use exact match for topic search. (#7941)
* Add pagination for admin api get orgs and fix only list public orgs bug (#7742)
* Implement the ability to change the ssh port to match what is in the gitea config (#7286)
* SECURITY
* Fix issue with user.fullname (#8903)
* Ignore mentions for users with no access (#8395)
* Be more strict with git arguments (#7715)
* Extract the username and password from the mirror url (#7651)
* reserve .well-known username (#7637)
* FEATURE
* Org/Members: display 2FA members states + optimize sql requests (#7621)
* SetDefaultBranch on pushing to empty repository (#7610)
* Adds side-by-side diff for images (#6784)
* API method to list all commits of a repository (#6408)
* Password Complexity Checks (#6230)
* Add option to initialize repository with labels (#6061)
* Add additional password hash algorithms (#6023)
* BUGFIXES
* Allow to merge if file path contains " or \ (#8629) (#8771)
* On windows set core.longpaths true (#8776) (#8786)
* Fix 500 when edit hook (#8782) (#8789)
* Fix Checkbox at RepoSettings Protected Branch (#8799) (#8801)
* Fix SSH2 conditional in key parsing code (#8806) (#8810)
* Fix commit expand button to not go to commit link (#8745) (#8825)
* Fix new user form for non-local users (#8826) (#8828)
* Fix to close opened io resources as soon as not needed (#8839) (#8846)
* Fix edit content button on migrated issue content (#8877) (#8884)
* Fix require external registration password (#8885) (#8890)
* Fix password complexity check on registration (#8887) (#8888)
* Update Github Migration Tests (#8896) (#8938) (#8945)
* Enable punctuations ending mentions (#8889) (#8894)
* Add Close() method to gogitRepository (#8901) (#8956)
* Hotfix for review actions and notifications (#8965)
* Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) (#8618)
* Fix milestone close timestamp (#8728) (#8730)
* Fix 500 when getting user as unauthenticated user (#8653) (#8663)
@ -29,22 +62,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Fix password complexity regex for special characters (#8524)
* Prevent .code-view from overriding font on icon fonts (#8614) (#8627)
* Allow more than 255 characters for tokens in external_login_user table (#8554)
## [1.10.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.10.0-rc1) - 2019-10-14
* BREAKING
* Remove legacy handling of drone token (#8191)
* Change repo search to use exact match for topic search. (#7941)
* Add pagination for admin api get orgs and fix only list public orgs bug (#7742)
* Implement the ability to change the ssh port to match what is in the gitea config (#7286)
* FEATURE
* Org/Members: display 2FA members states + optimize sql requests (#7621)
* SetDefaultBranch on pushing to empty repository (#7610)
* Adds side-by-side diff for images (#6784)
* API method to list all commits of a repository (#6408)
* Password Complexity Checks (#6230)
* Add option to initialize repository with labels (#6061)
* Add additional password hash algorithms (#6023)
* BUGFIXES
* Fix errors in create org UI regarding team access permission (#8506)
* Fix bug on FindExternalUsersByProvider (#8504)
* Create .ssh dir as necessary (#8486)
@ -244,10 +261,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Support setting cookie domain (#6288)
* Move migrating repository from frontend to backend (#6200)
* Delete releases attachments if release is deleted (#6068)
* SECURITY
* Ignore mentions for users with no access (#8395)
* Be more strict with git arguments (#7715)
* reserve .well-known username (#7637)
* TRANSLATION
* Latvian translation for home page (#8468)
* Add home template italian translation (#8352)
@ -276,7 +289,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Fix global search result CSS, misc CSS tweaks (#7789)
* Tweak label border CSS (#7739)
* Fix create menu item widths (#7708)
* Extract the username and password from the mirror url (#7651)
* [Branch View] Delete duplicate protection symbol (#7624)
* [Branch View] Delete Table Header (#7622)
* [Branch View] icons to buttons (#7602)
@ -289,6 +301,14 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* wiki - editor - add buttons 'inline code', 'empty checkbox', 'checked checkbox' (#7243)
* Fix Statuses API only shows first 10 statuses: Add paging and extend API GetCommitStatuses (#7141)
## [1.9.6](https://github.com/go-gitea/gitea/releases/tag/v1.9.6) - 2019-11-13
* BUGFIXES
* Allow to merge if file path contains " or \ (#8629) (#8772)
* Fix 500 when edit hook (#8782) (#8790)
* Fix issue with user.fullname (#8904)
* Update Github Migration Test (#8897) (#8946)
* Add Close() method to gogitRepository (#8901) (#8958)
## [1.9.5](https://github.com/go-gitea/gitea/releases/tag/v1.9.5) - 2019-10-30
* BREAKING
* Hide some user information via API if user doesn't have enough permission (#8655) (#8658)

View File

@ -434,7 +434,8 @@ npm-update: npm-check
.PHONY: js
js: npm
npx eslint public/js
npx eslint web_src/js webpack.config.js
npx webpack
.PHONY: css
css: npm

View File

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
)
@ -55,7 +56,13 @@ var (
func runHookPreReceive(c *cli.Context) error {
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
return nil
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
fail(`Rejecting changes as Gitea environment not set.
If you are pushing over SSH you must push with a key managed by
Gitea or set your environment appropriately.`, "")
} else {
return nil
}
}
setup("hooks/pre-receive.log")
@ -115,7 +122,13 @@ func runHookPreReceive(c *cli.Context) error {
func runHookUpdate(c *cli.Context) error {
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
return nil
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
fail(`Rejecting changes as Gitea environment not set.
If you are pushing over SSH you must push with a key managed by
Gitea or set your environment appropriately.`, "")
} else {
return nil
}
}
setup("hooks/update.log")
@ -125,7 +138,13 @@ func runHookUpdate(c *cli.Context) error {
func runHookPostReceive(c *cli.Context) error {
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
return nil
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
fail(`Rejecting changes as Gitea environment not set.
If you are pushing over SSH you must push with a key managed by
Gitea or set your environment appropriately.`, "")
} else {
return nil
}
}
setup("hooks/post-receive.log")

View File

@ -190,7 +190,7 @@ PROTOCOL = http
DOMAIN = localhost
ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
; when STATIC_URL_PREFIX is empty it will follow APP_URL
STATIC_URL_PREFIX =
STATIC_URL_PREFIX =
; The address to listen on. Either a IPv4/IPv6 address or the path to a unix socket.
HTTP_ADDR = 0.0.0.0
HTTP_PORT = 3000
@ -383,6 +383,8 @@ MIN_PASSWORD_LENGTH = 6
IMPORT_LOCAL_PATHS = false
; Set to true to prevent all users (including admin) from creating custom git hooks
DISABLE_GIT_HOOKS = false
; Set to false to allow pushes to gitea repositories despite having an incomplete environment - NOT RECOMMENDED
ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET = true
;Comma separated list of character classes required to pass minimum complexity.
;If left empty or no valid values are specified, the default values ("lower,upper,digit,spec") will be used.
;Use "off" to disable checking.
@ -515,9 +517,9 @@ SKIP_TLS_VERIFY = false
; Number of history information in each page
PAGING_NUM = 10
; Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
PROXY_URL =
PROXY_URL =
; Comma separated list of host names requiring proxy. Glob patterns (*) are accepted; use ** to match all hosts.
PROXY_HOSTS =
PROXY_HOSTS =
[mailer]
ENABLED = false

1
docs/.gitignore vendored
View File

@ -1,3 +1,4 @@
public/
templates/swagger/v1_json.tmpl
themes/
resources/

176
docs/assets/js/search.js Normal file
View File

@ -0,0 +1,176 @@
function ready(fn) {
if (document.readyState != 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(doSearch);
const summaryInclude = 60;
const fuseOptions = {
shouldSort: true,
includeMatches: true,
matchAllTokens: true,
threshold: 0.0, // for parsing diacritics
tokenize: true,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [{
name: "title",
weight: 0.8
},
{
name: "contents",
weight: 0.5
},
{
name: "tags",
weight: 0.3
},
{
name: "categories",
weight: 0.3
}
]
};
function param(name) {
return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
}
let searchQuery = param("s");
function doSearch() {
if (searchQuery) {
document.getElementById("search-query").value = searchQuery;
executeSearch(searchQuery);
} else {
const para = document.createElement("P");
para.innerText = "Please enter a word or phrase above";
document.getElementById("search-results").appendChild(para);
}
}
function getJSON(url, fn) {
const request = new XMLHttpRequest();
request.open('GET', url, true);
request.onload = function () {
if (request.status >= 200 && request.status < 400) {
const data = JSON.parse(request.responseText);
fn(data);
} else {
console.log("Target reached on " + url + " with error " + request.status);
}
};
request.onerror = function () {
console.log("Connection error " + request.status);
};
request.send();
}
function executeSearch(searchQuery) {
getJSON("/" + document.LANG + "/index.json", function (data) {
const pages = data;
const fuse = new Fuse(pages, fuseOptions);
const result = fuse.search(searchQuery);
console.log({
"matches": result
});
document.getElementById("search-results").innerHTML = "";
if (result.length > 0) {
populateResults(result);
} else {
const para = document.createElement("P");
para.innerText = "No matches found";
document.getElementById("search-results").appendChild(para);
}
});
}
function populateResults(result) {
result.forEach(function (value, key) {
const content = value.item.contents;
let snippet = "";
const snippetHighlights = [];
if (fuseOptions.tokenize) {
snippetHighlights.push(searchQuery);
value.matches.forEach(function (mvalue) {
if (mvalue.key === "tags" || mvalue.key === "categories") {
snippetHighlights.push(mvalue.value);
} else if (mvalue.key === "contents") {
const ind = content.toLowerCase().indexOf(searchQuery.toLowerCase());
const start = ind - summaryInclude > 0 ? ind - summaryInclude : 0;
const end = ind + searchQuery.length + summaryInclude < content.length ? ind + searchQuery.length + summaryInclude : content.length;
snippet += content.substring(start, end);
if (ind > -1) {
snippetHighlights.push(content.substring(ind, ind + searchQuery.length))
} else {
snippetHighlights.push(mvalue.value.substring(mvalue.indices[0][0], mvalue.indices[0][1] - mvalue.indices[0][0] + 1));
}
}
});
}
if (snippet.length < 1) {
snippet += content.substring(0, summaryInclude * 2);
}
//pull template from hugo templarte definition
const templateDefinition = document.getElementById("search-result-template").innerHTML;
//replace values
const output = render(templateDefinition, {
key: key,
title: value.item.title,
link: value.item.permalink,
tags: value.item.tags,
categories: value.item.categories,
snippet: snippet
});
document.getElementById("search-results").appendChild(htmlToElement(output));
snippetHighlights.forEach(function (snipvalue) {
new Mark(document.getElementById("summary-" + key)).mark(snipvalue);
});
});
}
function render(templateString, data) {
let conditionalMatches, copy;
const conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g;
//since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop
copy = templateString;
while ((conditionalMatches = conditionalPattern.exec(templateString)) !== null) {
if (data[conditionalMatches[1]]) {
//valid key, remove conditionals, leave content.
copy = copy.replace(conditionalMatches[0], conditionalMatches[2]);
} else {
//not valid, remove entire section
copy = copy.replace(conditionalMatches[0], '');
}
}
templateString = copy;
//now any conditionals removed we can do simple substitution
let key, find, re;
for (key in data) {
find = '\\$\\{\\s*' + key + '\\s*\\}';
re = new RegExp(find, 'g');
templateString = templateString.replace(re, data[key]);
}
return templateString;
}
/**
* By Mark Amery: https://stackoverflow.com/a/35385518
* @param {String} HTML representing a single element
* @return {Element}
*/
function htmlToElement(html) {
const template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstChild;
}

View File

@ -18,7 +18,13 @@ params:
description: Git with a cup of tea
author: The Gitea Authors
website: https://docs.gitea.io
version: 1.9.5
version: 1.10.0
outputs:
home:
- HTML
- RSS
- JSON
menu:
page:

View File

@ -244,6 +244,7 @@ relation to port exhaustion.
authentication provided email.
- `DISABLE_GIT_HOOKS`: **false**: Set to `true` to prevent all users (including admin) from creating custom
git hooks.
- `ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET`: **true**: Set to `false` to allow local users to push to gitea-repositories without setting up the Gitea environment. This is not recommended and if you want local users to push to gitea repositories you should set the environment appropriately.
- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.
- `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary.
- `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
@ -306,6 +307,7 @@ relation to port exhaustion.
- `AUTO_WATCH_ON_CHANGES`: **false**: Enable this to make users watch a repository after their first commit to it
- `DEFAULT_ORG_VISIBILITY`: **public**: Set default visibility mode for organisations, either "public", "limited" or "private".
- `DEFAULT_ORG_MEMBER_VISIBLE`: **false** True will make the membership of the users visible when added to the organisation.
- `ALLOW_ONLY_EXTERNAL_REGISTRATION`: **false** Set to true to force registration only using third-party services.
## Webhook (`webhook`)

View File

@ -2,12 +2,12 @@
date: "2017-01-20T15:00:00+08:00"
title: "Help"
slug: "help"
weight: 50
weight: 5
toc: false
draft: false
menu:
sidebar:
name: "Help"
weight: 50
weight: 5
identifier: "help"
---

View File

@ -0,0 +1,13 @@
---
date: "2017-01-20T15:00:00+08:00"
title: "Aide"
slug: "help"
weight: 5
toc: false
draft: false
menu:
sidebar:
name: "Aide"
weight: 5
identifier: "help"
---

View File

@ -2,12 +2,12 @@
date: "2017-01-20T15:00:00+08:00"
title: "帮助"
slug: "help"
weight: 50
weight: 5
toc: false
draft: false
menu:
sidebar:
name: "帮助"
weight: 50
weight: 5
identifier: "help"
---

View File

@ -0,0 +1,13 @@
---
date: "2017-01-20T15:00:00+08:00"
title: "救命"
slug: "help"
weight: 5
toc: false
draft: false
menu:
sidebar:
name: "救命"
weight: 5
identifier: "help"
---

View File

@ -0,0 +1,25 @@
---
date: "2019-11-12T16:00:00+02:00"
title: "Search"
slug: "search"
weight: 4
toc: true
draft: false
menu:
sidebar:
parent: "help"
name: "Search"
weight: 4
identifier: "search"
sitemap:
priority : 0.1
layout: "search"
---
This file exists solely to respond to /search URL with the related `search` layout template.
No content shown here is rendered, all content is based in the template layouts/doc/search.html
Setting a very low sitemap priority will tell search engines this is not important content.

View File

@ -0,0 +1,25 @@
---
date: "2019-11-12T16:00:00+02:00"
title: "Chercher"
slug: "search"
weight: 4
toc: true
draft: false
menu:
sidebar:
parent: "help"
name: "Chercher"
weight: 4
identifier: "search"
sitemap:
priority : 0.1
layout: "search"
---
This file exists solely to respond to /search URL with the related `search` layout template.
No content shown here is rendered, all content is based in the template layouts/doc/search.html
Setting a very low sitemap priority will tell search engines this is not important content.

View File

@ -0,0 +1,25 @@
---
date: "2019-11-12T16:00:00+02:00"
title: "搜索"
slug: "search"
weight: 4
toc: true
draft: false
menu:
sidebar:
parent: "help"
name: "搜索"
weight: 4
identifier: "search"
sitemap:
priority : 0.1
layout: "search"
---
This file exists solely to respond to /search URL with the related `search` layout template.
No content shown here is rendered, all content is based in the template layouts/doc/search.html
Setting a very low sitemap priority will tell search engines this is not important content.

View File

@ -0,0 +1,25 @@
---
date: "2019-11-12T16:00:00+02:00"
title: "搜索"
slug: "search"
weight: 4
toc: true
draft: false
menu:
sidebar:
parent: "help"
name: "搜索"
weight: 4
identifier: "search"
sitemap:
priority : 0.1
layout: "search"
---
This file exists solely to respond to /search URL with the related `search` layout template.
No content shown here is rendered, all content is based in the template layouts/doc/search.html
Setting a very low sitemap priority will tell search engines this is not important content.

View File

@ -40,7 +40,7 @@ To register Gitea as a Windows service, open a command prompt (cmd) as an Admini
then run the following command:
```
sc create gitea start= auto binPath= ""C:\gitea\gitea.exe" web --config "C:\gitea\custom\conf\app.ini""
sc create gitea start= auto binPath= "\"C:\gitea\gitea.exe\" web --config \"C:\gitea\custom\conf\app.ini\""
```
Do not forget to replace `C:\gitea` with the correct Gitea directory.

View File

@ -18,7 +18,7 @@ menu:
Pour activer le service Windows Gitea, ouvrez une `cmd` en tant qu'Administrateur puis utilisez la commande suivante :
```
sc create gitea start= auto binPath= ""C:\gitea\gitea.exe" web --config "C:\gitea\custom\conf\app.ini""
sc create gitea start= auto binPath= "\"C:\gitea\gitea.exe\" web --config \"C:\gitea\custom\conf\app.ini\""
```
N'oubliez pas de remplacer `C:\gitea` par le chemin que vous avez utilisez pour votre installation.

View File

@ -18,7 +18,7 @@ menu:
要注册为Windows服务首先以Administrator身份运行 `cmd`,然后执行以下命令:
```
sc create gitea start= auto binPath= ""C:\gitea\gitea.exe" web --config "C:\gitea\custom\conf\app.ini""
sc create gitea start= auto binPath= "\"C:\gitea\gitea.exe\" web --config \"C:\gitea\custom\conf\app.ini\""
```
别忘了将 `C:\gitea` 替换成你的 Gitea 安装目录。

View File

@ -18,7 +18,7 @@ menu:
要註冊為 Windows 服務,首先要以管理者身份執行 `cmd`,跳出命令列視窗後執行底下指令:
```
sc create gitea start= auto binPath= ""C:\gitea\gitea.exe" web --config "C:\gitea\custom\conf\app.ini""
sc create gitea start= auto binPath= "\"C:\gitea\gitea.exe\" web --config \"C:\gitea\custom\conf\app.ini\""
```
別忘記將 `C:\gitea` 取代為您的 Gitea 安裝路徑。

View File

@ -51,6 +51,8 @@ services:
- gitea
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
@ -80,6 +82,8 @@ services:
- gitea
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- - "3000:3000"
- - "222:22"
@ -115,6 +119,8 @@ services:
- gitea
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
@ -163,6 +169,8 @@ services:
- gitea
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
@ -209,6 +217,8 @@ services:
volumes:
- - ./gitea:/data
+ - gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
@ -306,6 +316,8 @@ UID/GID as the container values `USER_UID`/`USER_GID`. You should also create th
- gitea
volumes:
- /var/lib/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "127.0.0.1:2222:22"

View File

@ -0,0 +1,5 @@
{{- $.Scratch.Add "index" slice -}}
{{- range .Site.RegularPages -}}
{{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink) -}}
{{- end -}}
{{- $.Scratch.Get "index" | jsonify -}}

View File

@ -0,0 +1,44 @@
{{ partial "header.html" . }}
{{ partial "navbar.html" . }}
<section class="section">
<div class="container is-centered page">
<div class="columns">
<div class="column is-one-quarter">
{{ partial "menu" . }}
</div>
<div class="column">
<div class=" content">
<section class="resume-section p-3 p-lg-5 d-flex flex-column">
<div class="my-auto" >
<form action="{{ "search" | absLangURL }}">
<label>Search:
<input id="search-query" name="s"/>
</label>
</form>
<br/>
<div id="search-results"></div>
</div>
</section>
<!-- this template is sucked in by search.js and appended to the search-results div above. So editing here will adjust style -->
<script id="search-result-template" type="text/x-js-template">
<div id="summary-${key}">
<h4><a href="${link}">${title}</a></h4>
<p>${snippet}</p>
${ isset tags }<p>Tags: ${tags}</p>${ end }
${ isset categories }<p>Categories: ${categories}</p>${ end }
<hr/>
</div>
</script>
</div>
</div>
</div>
</div>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.4.5/fuse.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"></script>
<script>document.LANG = "{{ .Language.Lang }}";</script>
{{ $script := resources.Get "js/search.js" | minify | fingerprint -}}
<script src="{{ $script.Permalink }}" {{ printf "integrity=%q" $script.Data.Integrity | safeHTMLAttr }}></script>
{{ partial "footer.html" . }}

View File

@ -1,5 +1,5 @@
/*
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' https://fonts.googleapis.com https://cdnjs.cloudflare.com; font-src 'self' data: https://cdnjs.cloudflare.com https://fonts.gstatic.com
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; style-src 'self' https://fonts.googleapis.com https://cdnjs.cloudflare.com; font-src 'self' data: https://cdnjs.cloudflare.com https://fonts.gstatic.com
X-Frame-Options: DENY
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff

1
go.mod
View File

@ -80,7 +80,6 @@ require (
github.com/prometheus/client_golang v1.1.0
github.com/prometheus/procfs v0.0.4 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
github.com/russross/blackfriday v2.0.0+incompatible // indirect
github.com/russross/blackfriday/v2 v2.0.1
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
github.com/satori/go.uuid v1.2.0

45
go.sum
View File

@ -1,12 +1,10 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3 h1:0sMegbmn/8uTwpNkB0q9cLEpZ2W5a6kl+wtBQgPWBJQ=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.0 h1:bALuGBSgE+BD4rxsopAYlqjcwqcQtye6pWG4bC3N/k0=
cloud.google.com/go v0.45.0/go.mod h1:452BcPOeI9AZfbvDw0Tbo7D32wA+WX9WME8AZwMEDZU=
@ -67,7 +65,6 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@ -167,7 +164,6 @@ github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70t
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
github.com/go-openapi/analysis v0.19.4 h1:1TjOzrWkj+9BrjnM1yPAICbaoC0FyfD49oVkTBrSSa0=
github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
github.com/go-openapi/analysis v0.19.5 h1:8b2ZgKfKIUTVQpTb77MoRDIMEIwvDVw40o3aOXdfYzI=
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
@ -179,39 +175,33 @@ github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNP
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.2 h1:rf5ArTHmIJxyV5Oiks+Su0mUens1+AjpkPoWr5xFRcI=
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
github.com/go-openapi/loads v0.19.3 h1:jwIoahqCmaA5OBoc/B+1+Mu2L0Gr8xYQnbeyQEo/7b0=
github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
github.com/go-openapi/runtime v0.19.4 h1:csnOgcgAiuGoM/Po7PEpKDoNulCcF3FGbSnbHfxgjMI=
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
github.com/go-openapi/runtime v0.19.5 h1:h4Zk7oTfB3ZYM2oMNliQvL+3BrDstTIX8lqP7yaYCuI=
github.com/go-openapi/runtime v0.19.5/go.mod h1:WIH6IYPXOrtgTClTV8xzdrD20jBlrK25D0aQbdSlqp8=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
github.com/go-openapi/strfmt v0.19.2 h1:clPGfBnJohokno0e+d7hs6Yocrzjlgz6EsQSDncCRnE=
github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
github.com/go-openapi/strfmt v0.19.3 h1:eRfyY5SkaNJCAwmmMcADjY31ow9+N7MCLW7oRkbsINA=
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
@ -221,7 +211,6 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.19.2 h1:ky5l57HjyVRrsJfd2+Ro5Z9PjGuKbsmftwyMtk8H7js=
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
github.com/go-openapi/validate v0.19.3 h1:PAH/2DylwWcIU1s0Y7k3yNmeAgWOcKrNE2Q7Ww/kCg4=
github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
@ -255,18 +244,15 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -288,7 +274,6 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@ -302,7 +287,6 @@ github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.1.1 h1:YMDmfaK68mUixINzY/XjscuJ47uXFWSSHzFbBQM0PrE=
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
@ -385,7 +369,6 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
@ -423,21 +406,15 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niklasfasching/go-org v0.1.6 h1:F521WcqRNl8OJumlgAnekZgERaTA2HpfOYYfVEKOeI8=
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=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@ -466,7 +443,6 @@ github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -491,8 +467,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qq
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
@ -503,8 +477,6 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b h1:4kg1wyftSKxLtnPAvcRWakIPpokB9w780/KwrNLnfPA=
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc h1:3wIrJvFb3Pf6B/2mDBnN1G5IfUVev4X5apadQlWOczE=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
@ -517,7 +489,6 @@ github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
@ -548,11 +519,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/goleveldb v0.0.0-20190203031304-2f17a3356c66 h1:AwmkkZT+TucFotNCL+aNJ/0KCMsRtlXN9fs8uoOMSRk=
github.com/syndtr/goleveldb v0.0.0-20190203031304-2f17a3356c66/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
@ -594,7 +563,6 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.0 h1:aeOqSrhl9eDRAap/3T5pCfMBEBxZ0vuXBP+RMtp2KX8=
go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1 h1:Sq1fR+0c58RME5EoqKdjkiQAmPjmfHlZOoRI6fTUOcs=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
@ -614,7 +582,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -651,20 +618,13 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsiImhB2jgVBGc9L0lPGc=
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=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -694,7 +654,6 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b h1:3S2h5FadpNr0zUUCVZjlKIEYF+KaX/OBplTGo89CYHI=
@ -738,9 +697,7 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo=
google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
@ -764,7 +721,6 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8KqxhFwKci30UxHy3HXPTyQ=
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -776,7 +732,6 @@ gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkp
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.48.0 h1:URjZc+8ugRY5mL5uUeQH/a63JcHwdX9xZaWvmNWD7z8=
gopkg.in/ini.v1 v1.48.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -79,8 +79,10 @@ func allowLFSFilters() []string {
return globalArgs
}
func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
prepareTestEnv(t, 1)
func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL), prepare ...bool) {
if len(prepare) == 0 || prepare[0] {
prepareTestEnv(t, 1)
}
s := http.Server{
Handler: mac,
}

View File

@ -23,14 +23,7 @@ import (
)
func TestGPGGit(t *testing.T) {
onGiteaRun(t, testGPGGit)
}
func testGPGGit(t *testing.T, u *url.URL) {
username := "user2"
baseAPITestContext := NewAPITestContext(t, username, "repo1")
u.Path = baseAPITestContext.GitPath()
// OK Set a new GPG home
tmpDir, err := ioutil.TempDir("", "temp-gpg")
@ -65,130 +58,218 @@ func testGPGGit(t *testing.T, u *url.URL) {
setting.Repository.Signing.SigningEmail = "gitea@fake.local"
user := models.AssertExistsAndLoadBean(t, &models.User{Name: username}).(*models.User)
t.Run("Unsigned-Initial", func(t *testing.T) {
PrintCurrentTest(t)
setting.Repository.Signing.InitialCommit = []string{"never"}
testCtx := NewAPITestContext(t, username, "initial-unsigned")
t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
assert.Empty(t, branch.Commit.Verification.Signature)
}))
setting.Repository.Signing.CRUDActions = []string{"never"}
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
setting.Repository.Signing.InitialCommit = []string{"never"}
setting.Repository.Signing.CRUDActions = []string{"never"}
baseAPITestContext := NewAPITestContext(t, username, "repo1")
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("Unsigned-Initial", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-unsigned")
t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
assert.Empty(t, branch.Commit.Verification.Signature)
}))
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "never", "never2", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
}))
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "never", "never2", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
}))
})
}, false)
setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-unsigned")
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
}))
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
t, testCtx, user, "parentsigned", "parentsigned2", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
}))
})
}, false)
setting.Repository.Signing.CRUDActions = []string{"never"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("Unsigned-Initial-CRUD-Never", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-unsigned")
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
}))
})
}, false)
setting.Repository.Signing.CRUDActions = []string{"always"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("Unsigned-Initial-CRUD-Always", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-unsigned")
t.Run("CreateCRUDFile-Always", crudActionCreateFile(
t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
t.Run("CreateCRUDFile-ParentSigned-always", crudActionCreateFile(
t, testCtx, user, "parentsigned", "parentsigned-always", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
})
}, false)
setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-unsigned")
t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile(
t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
})
}, false)
setting.Repository.Signing.InitialCommit = []string{"always"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("AlwaysSign-Initial", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-always")
t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.True(t, branch.Commit.Verification.Verified)
assert.Equal(t, "gitea@fake.local", branch.Commit.Verification.Signer.Email)
}))
setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
})
}, false)
setting.Repository.Signing.CRUDActions = []string{"never"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-always")
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
}))
})
}, false)
setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("AlwaysSign-Initial-CRUD-ParentSigned-On-Always", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-always")
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
})
}, false)
setting.Repository.Signing.CRUDActions = []string{"always"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("AlwaysSign-Initial-CRUD-Always", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-always")
t.Run("CreateCRUDFile-Always", crudActionCreateFile(
t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
})
}, false)
var pr api.PullRequest
setting.Repository.Signing.Merges = []string{"commitssigned"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("UnsignedMerging", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-unsigned")
var err error
t.Run("CreatePullRequest", func(t *testing.T) {
pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t)
assert.NoError(t, err)
})
t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
assert.Empty(t, branch.Commit.Verification.Signature)
}))
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
t, testCtx, user, "parentsigned", "parentsigned2", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
})
}, false)
setting.Repository.Signing.Merges = []string{"basesigned"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("BaseSignedMerging", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-unsigned")
var err error
t.Run("CreatePullRequest", func(t *testing.T) {
pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t)
assert.NoError(t, err)
})
t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
assert.Empty(t, branch.Commit.Verification.Signature)
}))
setting.Repository.Signing.CRUDActions = []string{"never"}
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
}))
setting.Repository.Signing.CRUDActions = []string{"always"}
t.Run("CreateCRUDFile-Always", crudActionCreateFile(
t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
t.Run("CreateCRUDFile-ParentSigned-always", crudActionCreateFile(
t, testCtx, user, "parentsigned", "parentsigned-always", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile(
t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
})
t.Run("AlwaysSign-Initial", func(t *testing.T) {
PrintCurrentTest(t)
setting.Repository.Signing.InitialCommit = []string{"always"}
testCtx := NewAPITestContext(t, username, "initial-always")
t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.True(t, branch.Commit.Verification.Verified)
assert.Equal(t, "gitea@fake.local", branch.Commit.Verification.Signer.Email)
}))
setting.Repository.Signing.CRUDActions = []string{"never"}
t.Run("CreateCRUDFile-Never", crudActionCreateFile(
t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
assert.False(t, response.Verification.Verified)
}))
setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
setting.Repository.Signing.CRUDActions = []string{"always"}
t.Run("CreateCRUDFile-Always", crudActionCreateFile(
t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
assert.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
})
}, false)
setting.Repository.Signing.Merges = []string{"commitssigned"}
onGiteaRun(t, func(t *testing.T, u *url.URL) {
u.Path = baseAPITestContext.GitPath()
t.Run("CommitsSignedMerging", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-unsigned")
var err error
t.Run("CreatePullRequest", func(t *testing.T) {
pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t)
assert.NoError(t, err)
})
t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.True(t, branch.Commit.Verification.Verified)
}))
})
t.Run("UnsignedMerging", func(t *testing.T) {
PrintCurrentTest(t)
testCtx := NewAPITestContext(t, username, "initial-unsigned")
var pr api.PullRequest
var err error
t.Run("CreatePullRequest", func(t *testing.T) {
pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t)
assert.NoError(t, err)
})
setting.Repository.Signing.Merges = []string{"commitssigned"}
t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
assert.Empty(t, branch.Commit.Verification.Signature)
}))
setting.Repository.Signing.Merges = []string{"basesigned"}
t.Run("CreatePullRequest", func(t *testing.T) {
pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t)
assert.NoError(t, err)
})
t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.False(t, branch.Commit.Verification.Verified)
assert.Empty(t, branch.Commit.Verification.Signature)
}))
setting.Repository.Signing.Merges = []string{"commitssigned"}
t.Run("CreatePullRequest", func(t *testing.T) {
pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t)
assert.NoError(t, err)
})
t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
assert.NotNil(t, branch.Commit)
assert.NotNil(t, branch.Commit.Verification)
assert.True(t, branch.Commit.Verification.Verified)
}))
})
}, false)
}
func crudActionCreateFile(t *testing.T, ctx APITestContext, user *models.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {

View File

@ -30,26 +30,28 @@ type ActionType int
// Possible action types.
const (
ActionCreateRepo ActionType = iota + 1 // 1
ActionRenameRepo // 2
ActionStarRepo // 3
ActionWatchRepo // 4
ActionCommitRepo // 5
ActionCreateIssue // 6
ActionCreatePullRequest // 7
ActionTransferRepo // 8
ActionPushTag // 9
ActionCommentIssue // 10
ActionMergePullRequest // 11
ActionCloseIssue // 12
ActionReopenIssue // 13
ActionClosePullRequest // 14
ActionReopenPullRequest // 15
ActionDeleteTag // 16
ActionDeleteBranch // 17
ActionMirrorSyncPush // 18
ActionMirrorSyncCreate // 19
ActionMirrorSyncDelete // 20
ActionCreateRepo ActionType = iota + 1 // 1
ActionRenameRepo // 2
ActionStarRepo // 3
ActionWatchRepo // 4
ActionCommitRepo // 5
ActionCreateIssue // 6
ActionCreatePullRequest // 7
ActionTransferRepo // 8
ActionPushTag // 9
ActionCommentIssue // 10
ActionMergePullRequest // 11
ActionCloseIssue // 12
ActionReopenIssue // 13
ActionClosePullRequest // 14
ActionReopenPullRequest // 15
ActionDeleteTag // 16
ActionDeleteBranch // 17
ActionMirrorSyncPush // 18
ActionMirrorSyncCreate // 19
ActionMirrorSyncDelete // 20
ActionApprovePullRequest // 21
ActionRejectPullRequest // 22
)
// Action represents user operation type and other information to
@ -520,52 +522,6 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, bra
return nil
}
func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) {
if err = notifyWatchers(e, &Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: ActionTransferRepo,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
Content: path.Join(oldOwner.Name, repo.Name),
}); err != nil {
return fmt.Errorf("notifyWatchers: %v", err)
}
// Remove watch for organization.
if oldOwner.IsOrganization() {
if err = watchRepo(e, oldOwner.ID, repo.ID, false); err != nil {
return fmt.Errorf("watchRepo [false]: %v", err)
}
}
return nil
}
// TransferRepoAction adds new action for transferring repository,
// the Owner field of repository is assumed to be new owner.
func TransferRepoAction(doer, oldOwner *User, repo *Repository) error {
return transferRepoAction(x, doer, oldOwner, repo)
}
func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue) error {
return notifyWatchers(e, &Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: ActionMergePullRequest,
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
})
}
// MergePullRequestAction adds new action for merging pull request.
func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error {
return mergePullRequestAction(x, actUser, repo, pull)
}
// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
RequestedUser *User

View File

@ -332,54 +332,6 @@ func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
CheckConsistencyFor(t, &Action{})
}
func TestTransferRepoAction(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1, OwnerID: user2.ID}).(*Repository)
repo.OwnerID = user4.ID
repo.Owner = user4
actionBean := &Action{
OpType: ActionTransferRepo,
ActUserID: user2.ID,
ActUser: user2,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
}
AssertNotExistsBean(t, actionBean)
assert.NoError(t, TransferRepoAction(user2, user2, repo))
AssertExistsAndLoadBean(t, actionBean)
_, err := x.ID(repo.ID).Cols("owner_id").Update(repo)
assert.NoError(t, err)
CheckConsistencyFor(t, &Action{})
}
func TestMergePullRequestAction(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1, OwnerID: user.ID}).(*Repository)
repo.Owner = user
issue := AssertExistsAndLoadBean(t, &Issue{ID: 3, RepoID: repo.ID}).(*Issue)
actionBean := &Action{
OpType: ActionMergePullRequest,
ActUserID: user.ID,
ActUser: user,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
}
AssertNotExistsBean(t, actionBean)
assert.NoError(t, MergePullRequestAction(user, repo, issue))
AssertExistsAndLoadBean(t, actionBean)
CheckConsistencyFor(t, &Action{})
}
func TestGetFeeds(t *testing.T) {
// test with an individual user
assert.NoError(t, PrepareTestDatabase())

View File

@ -539,8 +539,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
return nil, err
}
if err = sendCreateCommentAction(e, opts, comment); err != nil {
return nil, err
if !opts.NoAction {
if err = sendCreateCommentAction(e, opts, comment); err != nil {
return nil, err
}
}
if err = comment.addCrossReferences(e, opts.Doer); err != nil {
@ -816,6 +818,7 @@ type CreateCommentOptions struct {
RefCommentID int64
RefAction references.XRefAction
RefIsPull bool
NoAction bool
}
// CreateComment creates comment of issue or commit.

View File

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// 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.
@ -403,6 +404,19 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource, autoR
var isAttributeSSHPublicKeySet = len(strings.TrimSpace(source.LDAP().AttributeSSHPublicKey)) > 0
// Update User admin flag if exist
if isExist, err := IsUserExist(0, sr.Username); err != nil {
return nil, err
} else if isExist &&
!user.ProhibitLogin && len(source.LDAP().AdminFilter) > 0 && user.IsAdmin != sr.IsAdmin {
// Change existing admin flag only if AdminFilter option is set
user.IsAdmin = sr.IsAdmin
err = UpdateUserCols(user, "is_admin")
if err != nil {
return nil, err
}
}
if !autoRegister {
if isAttributeSSHPublicKeySet && synchronizeLdapSSHPublicKeys(user, source, sr.SSHPublicKey) {
return user, RewriteAllPublicKeys()

View File

@ -1792,8 +1792,13 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
if err = watchRepo(sess, doer.ID, repo.ID, true); err != nil {
return fmt.Errorf("watchRepo: %v", err)
} else if err = transferRepoAction(sess, doer, owner, repo); err != nil {
return fmt.Errorf("transferRepoAction: %v", err)
}
// Remove watch for organization.
if owner.IsOrganization() {
if err = watchRepo(sess, owner.ID, repo.ID, false); err != nil {
return fmt.Errorf("watchRepo [false]: %v", err)
}
}
// Rename remote repository to new path and delete local copy.
@ -1824,23 +1829,21 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
}
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error) {
oldRepoName = strings.ToLower(oldRepoName)
func ChangeRepositoryName(doer *User, repo *Repository, newRepoName string) (err error) {
newRepoName = strings.ToLower(newRepoName)
if err = IsUsableRepoName(newRepoName); err != nil {
return err
}
has, err := IsRepositoryExist(u, newRepoName)
if err := repo.GetOwner(); err != nil {
return err
}
has, err := IsRepositoryExist(repo.Owner, newRepoName)
if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
return ErrRepoAlreadyExist{u.Name, newRepoName}
}
repo, err := GetRepositoryByName(u.ID, oldRepoName)
if err != nil {
return fmt.Errorf("GetRepositoryByName: %v", err)
return ErrRepoAlreadyExist{repo.Owner.Name, newRepoName}
}
// Change repository directory name. We must lock the local copy of the
@ -1849,14 +1852,14 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
newRepoPath := RepoPath(u.Name, newRepoName)
newRepoPath := RepoPath(repo.Owner.Name, newRepoName)
if err = os.Rename(repo.RepoPath(), newRepoPath); err != nil {
return fmt.Errorf("rename repository directory: %v", err)
}
wikiPath := repo.WikiPath()
if com.IsExist(wikiPath) {
if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil {
if err = os.Rename(wikiPath, WikiPath(repo.Owner.Name, newRepoName)); err != nil {
return fmt.Errorf("rename repository wiki: %v", err)
}
}
@ -1868,7 +1871,7 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
}
// If there was previously a redirect at this location, remove it.
if err = deleteRepoRedirect(sess, u.ID, newRepoName); err != nil {
if err = deleteRepoRedirect(sess, repo.OwnerID, newRepoName); err != nil {
return fmt.Errorf("delete repo redirect: %v", err)
}

View File

@ -15,7 +15,6 @@ import (
"code.gitea.io/gitea/modules/markup"
"github.com/stretchr/testify/assert"
"github.com/unknwon/com"
)
func TestRepo(t *testing.T) {
@ -142,29 +141,6 @@ func TestRepoAPIURL(t *testing.T) {
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL())
}
func TestTransferOwnership(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
repo.Owner = AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User)
assert.NoError(t, TransferOwnership(doer, "user2", repo))
transferredRepo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
assert.EqualValues(t, 2, transferredRepo.OwnerID)
assert.False(t, com.IsExist(RepoPath("user3", "repo3")))
assert.True(t, com.IsExist(RepoPath("user2", "repo3")))
AssertExistsAndLoadBean(t, &Action{
OpType: ActionTransferRepo,
ActUserID: 2,
RepoID: 3,
Content: "user3/repo3",
})
CheckConsistencyFor(t, &Repository{}, &User{}, &Team{})
}
func TestUploadAvatar(t *testing.T) {
// Generate image

View File

@ -216,6 +216,21 @@ func NotifyWatchers(act *Action) error {
return notifyWatchers(x, act)
}
// NotifyWatchersActions creates batch of actions for every watcher.
func NotifyWatchersActions(acts []*Action) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
for _, act := range acts {
if err := notifyWatchers(sess, act); err != nil {
return err
}
}
return sess.Commit()
}
func watchIfAuto(e Engine, userID, repoID int64, isWrite bool) error {
if !isWrite || !setting.Service.AutoWatchOnChanges {
return nil

View File

@ -5,14 +5,12 @@
package models
import (
"fmt"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
"xorm.io/core"
"xorm.io/xorm"
)
// ReviewType defines the sort of feedback a review gives
@ -86,6 +84,11 @@ func (r *Review) loadReviewer(e Engine) (err error) {
return
}
// LoadReviewer loads reviewer
func (r *Review) LoadReviewer() error {
return r.loadReviewer(x)
}
func (r *Review) loadAttributes(e Engine) (err error) {
if err = r.loadReviewer(e); err != nil {
return
@ -101,54 +104,6 @@ func (r *Review) LoadAttributes() error {
return r.loadAttributes(x)
}
// Publish will send notifications / actions to participants for all code comments; parts are concurrent
func (r *Review) Publish() error {
return r.publish(x)
}
func (r *Review) publish(e *xorm.Engine) error {
if r.Type == ReviewTypePending || r.Type == ReviewTypeUnknown {
return fmt.Errorf("review cannot be published if type is pending or unknown")
}
if r.Issue == nil {
if err := r.loadIssue(e); err != nil {
return err
}
}
if err := r.Issue.loadRepo(e); err != nil {
return err
}
if len(r.CodeComments) == 0 {
if err := r.loadCodeComments(e); err != nil {
return err
}
}
for _, lines := range r.CodeComments {
for _, comments := range lines {
for _, comment := range comments {
go func(en *xorm.Engine, review *Review, comm *Comment) {
sess := en.NewSession()
defer sess.Close()
opts := &CreateCommentOptions{
Doer: comm.Poster,
Issue: review.Issue,
Repo: review.Issue.Repo,
Type: comm.Type,
Content: comm.Content,
}
if err := updateCommentInfos(sess, opts, comm); err != nil {
log.Warn("updateCommentInfos: %v", err)
}
if err := sendCreateCommentAction(sess, opts, comm); err != nil {
log.Warn("sendCreateCommentAction: %v", err)
}
}(e, r, comment)
}
}
}
return nil
}
func getReviewByID(e Engine, id int64) (*Review, error) {
review := new(Review)
if has, err := e.ID(id).Get(review); err != nil {
@ -271,12 +226,79 @@ func GetCurrentReview(reviewer *User, issue *Issue) (*Review, error) {
return getCurrentReview(x, reviewer, issue)
}
// UpdateReview will update all cols of the given review in db
func UpdateReview(r *Review) error {
if _, err := x.ID(r.ID).AllCols().Update(r); err != nil {
return err
// ContentEmptyErr represents an content empty error
type ContentEmptyErr struct {
}
func (ContentEmptyErr) Error() string {
return "Review content is empty"
}
// IsContentEmptyErr returns true if err is a ContentEmptyErr
func IsContentEmptyErr(err error) bool {
_, ok := err.(ContentEmptyErr)
return ok
}
// SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
func SubmitReview(doer *User, issue *Issue, reviewType ReviewType, content string) (*Review, *Comment, error) {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return nil, nil, err
}
return nil
review, err := getCurrentReview(sess, doer, issue)
if err != nil {
if !IsErrReviewNotExist(err) {
return nil, nil, err
}
if reviewType != ReviewTypeApprove && len(strings.TrimSpace(content)) == 0 {
return nil, nil, ContentEmptyErr{}
}
// No current review. Create a new one!
review, err = createReview(sess, CreateReviewOptions{
Type: reviewType,
Issue: issue,
Reviewer: doer,
Content: content,
})
if err != nil {
return nil, nil, err
}
} else {
if err := review.loadCodeComments(sess); err != nil {
return nil, nil, err
}
if reviewType != ReviewTypeApprove && len(review.CodeComments) == 0 && len(strings.TrimSpace(content)) == 0 {
return nil, nil, ContentEmptyErr{}
}
review.Issue = issue
review.Content = content
review.Type = reviewType
if _, err := sess.ID(review.ID).Cols("content, type").Update(review); err != nil {
return nil, nil, err
}
}
comm, err := createComment(sess, &CreateCommentOptions{
Type: CommentTypeReview,
Doer: doer,
Content: review.Content,
Issue: issue,
Repo: issue.Repo,
ReviewID: review.ID,
NoAction: true,
})
if err != nil || comm == nil {
return nil, nil, err
}
comm.Review = review
return review, comm, sess.Commit()
}
// PullReviewersWithType represents the type used to display a review overview

View File

@ -98,14 +98,6 @@ func TestCreateReview(t *testing.T) {
AssertExistsAndLoadBean(t, &Review{Content: "New Review"})
}
func TestUpdateReview(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
review := AssertExistsAndLoadBean(t, &Review{ID: 1}).(*Review)
review.Content = "Updated Review"
assert.NoError(t, UpdateReview(review))
AssertExistsAndLoadBean(t, &Review{ID: 1, Content: "Updated Review"})
}
func TestGetReviewersByPullID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())

View File

@ -6,6 +6,7 @@
package git
import (
"bytes"
"encoding/base64"
"io"
"io/ioutil"
@ -50,6 +51,28 @@ func (b *Blob) GetBlobContent() (string, error) {
return string(buf), nil
}
// GetBlobLineCount gets line count of lob as raw text
func (b *Blob) GetBlobLineCount() (int, error) {
reader, err := b.DataAsync()
if err != nil {
return 0, err
}
defer reader.Close()
buf := make([]byte, 32*1024)
count := 0
lineSep := []byte{'\n'}
for {
c, err := reader.Read(buf)
count += bytes.Count(buf[:c], lineSep)
switch {
case err == io.EOF:
return count, nil
case err != nil:
return count, err
}
}
}
// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string
func (b *Blob) GetBlobContentBase64() (string, error) {
dataRc, err := b.DataAsync()

View File

@ -6,6 +6,8 @@ package action
import (
"fmt"
"path"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
@ -76,7 +78,9 @@ func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
}
}
func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string) {
func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
log.Trace("action.ChangeRepositoryName: %s/%s", doer.Name, repo.Name)
if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID,
ActUser: doer,
@ -84,11 +88,23 @@ func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
Content: oldName,
Content: oldRepoName,
}); err != nil {
log.Error("notify watchers: %v", err)
} else {
log.Trace("action.renameRepoAction: %s/%s", doer.Name, repo.Name)
log.Error("NotifyWatchers: %v", err)
}
}
func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: models.ActionTransferRepo,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
Content: path.Join(oldOwnerName, repo.Name),
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
@ -117,3 +133,61 @@ func (a *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *
log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
}
}
func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) {
if err := review.LoadReviewer(); err != nil {
log.Error("LoadReviewer '%d/%d': %v", review.ID, review.ReviewerID, err)
return
}
if err := review.LoadCodeComments(); err != nil {
log.Error("LoadCodeComments '%d/%d': %v", review.Reviewer.ID, review.ID, err)
return
}
var actions = make([]*models.Action, 0, 10)
for _, lines := range review.CodeComments {
for _, comments := range lines {
for _, comm := range comments {
actions = append(actions, &models.Action{
ActUserID: review.Reviewer.ID,
ActUser: review.Reviewer,
Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
OpType: models.ActionCommentIssue,
RepoID: review.Issue.RepoID,
Repo: review.Issue.Repo,
IsPrivate: review.Issue.Repo.IsPrivate,
Comment: comm,
CommentID: comm.ID,
})
}
}
}
if review.Type != models.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
action := &models.Action{
ActUserID: review.Reviewer.ID,
ActUser: review.Reviewer,
Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
RepoID: review.Issue.RepoID,
Repo: review.Issue.Repo,
IsPrivate: review.Issue.Repo.IsPrivate,
Comment: comment,
CommentID: comment.ID,
}
switch review.Type {
case models.ReviewTypeApprove:
action.OpType = models.ActionApprovePullRequest
case models.ReviewTypeReject:
action.OpType = models.ActionRejectPullRequest
default:
action.OpType = models.ActionCommentIssue
}
actions = append(actions, action)
}
if err := models.NotifyWatchersActions(actions); err != nil {
log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
}
}

View File

@ -17,7 +17,8 @@ type Notifier interface {
NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository)
NotifyDeleteRepository(doer *models.User, repo *models.Repository)
NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository)
NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string)
NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string)
NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string)
NotifyNewIssue(*models.Issue)
NotifyIssueChangeStatus(*models.User, *models.Issue, bool)

View File

@ -58,18 +58,6 @@ func (*NullNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, o
func (*NullNotifier) NotifyDeleteComment(doer *models.User, c *models.Comment) {
}
// NotifyDeleteRepository places a place holder function
func (*NullNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
}
// NotifyForkRepository places a place holder function
func (*NullNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
}
// NotifyRenameRepository places a place holder function
func (*NullNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string) {
}
// NotifyNewRelease places a place holder function
func (*NullNotifier) NotifyNewRelease(rel *models.Release) {
}
@ -111,6 +99,14 @@ func (*NullNotifier) NotifyIssueChangeLabels(doer *models.User, issue *models.Is
func (*NullNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
}
// NotifyDeleteRepository places a place holder function
func (*NullNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
}
// NotifyForkRepository places a place holder function
func (*NullNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
}
// NotifyMigrateRepository places a place holder function
func (*NullNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
}
@ -126,3 +122,11 @@ func (*NullNotifier) NotifyCreateRef(doer *models.User, repo *models.Repository,
// NotifyDeleteRef notifies branch or tag deleteion to notifiers
func (*NullNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
}
// NotifyRenameRepository places a place holder function
func (*NullNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
}
// NotifyTransferRepository places a place holder function
func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
}

View File

@ -101,27 +101,6 @@ func NotifyDeleteComment(doer *models.User, c *models.Comment) {
}
}
// NotifyDeleteRepository notifies delete repository to notifiers
func NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
for _, notifier := range notifiers {
notifier.NotifyDeleteRepository(doer, repo)
}
}
// NotifyForkRepository notifies fork repository to notifiers
func NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
for _, notifier := range notifiers {
notifier.NotifyForkRepository(doer, oldRepo, repo)
}
}
// NotifyRenameRepository notifies repository renamed
func NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string) {
for _, notifier := range notifiers {
notifier.NotifyRenameRepository(doer, repo, oldName)
}
}
// NotifyNewRelease notifies new release to notifiers
func NotifyNewRelease(rel *models.Release) {
for _, notifier := range notifiers {
@ -200,6 +179,34 @@ func NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Rep
}
}
// NotifyTransferRepository notifies create repository to notifiers
func NotifyTransferRepository(doer *models.User, repo *models.Repository, newOwnerName string) {
for _, notifier := range notifiers {
notifier.NotifyTransferRepository(doer, repo, newOwnerName)
}
}
// NotifyDeleteRepository notifies delete repository to notifiers
func NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
for _, notifier := range notifiers {
notifier.NotifyDeleteRepository(doer, repo)
}
}
// NotifyForkRepository notifies fork repository to notifiers
func NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
for _, notifier := range notifiers {
notifier.NotifyForkRepository(doer, oldRepo, repo)
}
}
// NotifyRenameRepository notifies repository renamed
func NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string) {
for _, notifier := range notifiers {
notifier.NotifyRenameRepository(doer, repo, oldName)
}
}
// 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 {

View File

@ -55,6 +55,15 @@ func TestGetDiffPreview(t *testing.T) {
Type: 4,
Content: "@@ -1,3 +1,4 @@",
Comments: nil,
SectionInfo: &gitdiff.DiffLineSectionInfo{
Path: "README.md",
LastLeftIdx: 0,
LastRightIdx: 0,
LeftIdx: 1,
RightIdx: 1,
LeftHunkSize: 3,
RightHunkSize: 4,
},
},
{
LeftIdx: 1,

View File

@ -140,18 +140,19 @@ var (
}
// Security settings
InstallLock bool
SecretKey string
LogInRememberDays int
CookieUserName string
CookieRememberName string
ReverseProxyAuthUser string
ReverseProxyAuthEmail string
MinPasswordLength int
ImportLocalPaths bool
DisableGitHooks bool
PasswordComplexity []string
PasswordHashAlgo string
InstallLock bool
SecretKey string
LogInRememberDays int
CookieUserName string
CookieRememberName string
ReverseProxyAuthUser string
ReverseProxyAuthEmail string
MinPasswordLength int
ImportLocalPaths bool
DisableGitHooks bool
OnlyAllowPushIfGiteaEnvironmentSet bool
PasswordComplexity []string
PasswordHashAlgo string
// UI settings
UI = struct {
@ -778,6 +779,7 @@ func NewContext() {
MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(false)
OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true)
PasswordHashAlgo = sec.Key("PASSWORD_HASH_ALGO").MustString("pbkdf2")
CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true)

View File

@ -555,6 +555,10 @@ func ActionIcon(opType models.ActionType) string {
return "issue-reopened"
case models.ActionMirrorSyncPush, models.ActionMirrorSyncCreate, models.ActionMirrorSyncDelete:
return "repo-clone"
case models.ActionApprovePullRequest:
return "eye"
case models.ActionRejectPullRequest:
return "x"
default:
return "invalid type"
}

View File

@ -582,7 +582,7 @@ email_notifications.submit = Set Email Preference
owner = Owner
repo_name = Repository Name
repo_name_helper = Good repository names use short, memorable and unique keywords.
repo_size = Repository Size
repo_size = Repository Size
template = Template
template_select = Select a template.
template_helper = Make repository a template
@ -2000,6 +2000,8 @@ compare_commits_general = Compare commits
mirror_sync_push = synced commits to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a> from mirror
mirror_sync_create = synced new reference <a href="%s/src/%s">%[2]s</a> to <a href="%[1]s">%[3]s</a> from mirror
mirror_sync_delete = synced and deleted reference <code>%[2]s</code> at <a href="%[1]s">%[3]s</a> from mirror
approve_pull_request = `approved <a href="%s/pulls/%s">%s#%[2]s</a>`
reject_pull_request = `suggested changes for <a href="%s/pulls/%s">%s#%[2]s</a>`
[tool]
ago = %s ago

View File

@ -581,6 +581,7 @@ email_notifications.submit=Atualizar preferências de e-mail
owner=Proprietário
repo_name=Nome do repositório
repo_name_helper=Um bom nome de repositório é composto por palavras curtas, memorizáveis e únicas.
repo_size=Tamanho do repositório
template=Modelo
template_select=Selecione um modelo.
template_helper=Tornar repositório um modelo
@ -1711,6 +1712,7 @@ users.auth_login_name=Nome de acesso da autenticação
users.password_helper=Deixe a senha em branco para mantê-la inalterada.
users.update_profile_success=A conta de usuário foi atualizada.
users.edit_account=Editar a conta de usuário
users.max_repo_creation=Número máximo de repositórios
users.max_repo_creation_desc=(Use -1 para usar o limite padrão global.)
users.is_activated=Conta de usuário está ativada
users.prohibit_login=Desabilitar acesso

View File

@ -68,6 +68,10 @@ pull_requests=合并请求
issues=工单管理
cancel=取消
add=添加
add_all=添加所有
remove=移除
remove_all=移除所有
write=撰写
preview=预览
@ -577,6 +581,11 @@ email_notifications.submit=邮件通知设置
owner=拥有者
repo_name=仓库名称
repo_name_helper=好的存储库名称使用简短、深刻和独特的关键字。
repo_size=仓库大小
template=模板
template_select=选择模板
template_helper=设置仓库为模板仓库
template_description=模板仓库让用户通过拷贝目录结构,文件和可选设置来生成仓库。
visibility=可见性
visibility_description=只有组织所有人或拥有权利的组织成员才能看到。
visibility_helper=将仓库设为私有
@ -586,6 +595,9 @@ clone_helper=不知道如何克隆?查看<a target="_blank" rel="noopener nore
fork_repo=派生仓库
fork_from=派生自
fork_visibility_helper=无法更改派生仓库的可见性。
use_template=使用此模板
generate_repo=生成仓库
generate_from=生成自
repo_desc=仓库描述
repo_lang=仓库语言
repo_gitignore_helper=选择 .gitignore 模板。
@ -613,6 +625,11 @@ forks=派生仓库
pick_reaction=选择你的表情
reactions_more=再加载 %d
template.items=模板选项
template.git_content=Git数据(默认分支)
template.topics=主题
template.one_item=必须至少选择一个模板项
template.invalid=必须选择一个模板仓库
archive.title=此仓库已存档。您可以查看文件和克隆,但不能推送或创建工单/合并请求。
archive.issue.nocomment=此仓库已存档,您不能在此工单添加评论。
@ -648,6 +665,7 @@ migrate.migrating_failed=从 <b>%s</b> 迁移失败。
mirror_from=镜像自地址
forked_from=派生自
generated_from=生成自
fork_from_self=无法派生已经拥有的仓库!
fork_guest_user=登录并 派生 这个仓库。
copy_link=复制链接
@ -742,6 +760,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=文件%s被 %s 锁定。
editor.upload_files_to_dir=上传文件至 '%s'
editor.cannot_commit_to_protected_branch=不可以提交到受保护的分支 '%s'。
@ -1021,6 +1040,10 @@ pulls.rebase_merge_pull_request=变基并合并
pulls.rebase_merge_commit_pull_request=变基合并 (--no-ff)
pulls.squash_merge_pull_request=压缩提交并合并
pulls.invalid_merge_option=你可以在此合并请求中使用合并选项。
pulls.merge_conflict=合并失败:合并时发生冲突:%[1]s<br>[2]<br> 提示:尝试不同的合并策略
pulls.rebase_conflict=合并失败Rebase合并时发生冲突%[1]s<br>[2]<br> 提示:尝试不同的合并策略
pulls.unrelated_histories=合并失败:两个分支没有共同历史。提示:尝试不同的策略
pulls.merge_out_of_date=合并失败:在生成合并时,主分支已更新。提示:再试一次。
pulls.open_unmerged_pull_exists=`您不能执行重新打开操作, 因为已经存在相同的合并请求 (#%d)。`
pulls.status_checking=一些检测仍在等待运行
pulls.status_checks_success=所有检测均成功
@ -1089,6 +1112,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> 合并请求
@ -1511,6 +1537,7 @@ team_name=团队名称
team_desc=团队描述
team_name_helper=团队名字应该简单明了。
team_desc_helper=描述团队的目的或作用。
team_access_desc=仓库权限
team_permission_desc=权限
team_unit_desc=允许访问仓库单元
@ -1578,10 +1605,21 @@ teams.write_permission_desc=该团队拥有对所属仓库的 <strong>读取</st
teams.admin_permission_desc=该团队拥有一定的 <strong>管理</strong> 权限,团队成员可以读取、克隆、推送以及添加其它仓库协作者。
teams.repositories=团队仓库
teams.search_repo_placeholder=搜索仓库...
teams.remove_all_repos_title=移除所有团队仓库
teams.remove_all_repos_desc=这将从团队中移除所有仓库。
teams.add_all_repos_title=添加所有仓库
teams.add_all_repos_desc=这将把组织的所有仓库添加到团队。
teams.add_nonexistent_repo=您尝试添加到团队的仓库不存在,请先创建仓库!
teams.add_duplicate_users=用户已经是团队成员。
teams.repos.none=此团队无法访问任何仓库。
teams.members.none=团队中没有成员。
teams.specific_repositories=指定仓库
teams.specific_repositories_helper=团队成员将只能访问添加到团队的仓库。 选择此项 <strong>将不会</strong> 自动删除已经添加的仓库。
teams.all_repositories=所有仓库
teams.all_repositories_helper=团队可以访问所有仓库。选择此选项将 <strong>添加所有现有的</strong> 仓库到指定团队。
teams.all_repositories_read_permission_desc=此团队授予<strong>读取</strong><strong>所有仓库</strong>的访问权限: 成员可以查看和克隆仓库。
teams.all_repositories_write_permission_desc=此团队授予<strong>修改</strong><strong>所有仓库</strong>的访问权限: 成员可以查看和推送至仓库。
teams.all_repositories_admin_permission_desc=该团队拥有 <strong>管理</strong> <strong>所有仓库</strong>的权限:团队成员可以读取、克隆、推送以及添加其它仓库协作者。
[admin]
dashboard=管理面板
@ -1674,6 +1712,7 @@ users.auth_login_name=认证登录名称
users.password_helper=保持密码为空将不更改密码。
users.update_profile_success=该帐户已被更新。
users.edit_account=编辑帐号
users.max_repo_creation=最大仓库数
users.max_repo_creation_desc=(设置为 -1 表示使用全局默认值)
users.is_activated=该用户已被激活
users.prohibit_login=禁用登录

3443
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,14 +5,23 @@
"node": ">=8"
},
"devDependencies": {
"autoprefixer": "9.6.1",
"eslint": "6.3.0",
"@babel/core": "7.7.2",
"@babel/preset-env": "7.7.1",
"autoprefixer": "9.7.1",
"babel-loader": "8.0.6",
"core-js": "3.4.1",
"eslint": "6.6.0",
"eslint-config-airbnb-base": "14.0.0",
"eslint-plugin-import": "2.18.2",
"less": "3.10.3",
"less-plugin-clean-css": "1.5.1",
"postcss-cli": "6.1.3",
"stylelint": "10.1.0",
"stylelint-config-standard": "18.3.0",
"updates": "8.5.3"
"stylelint": "11.1.1",
"stylelint-config-standard": "19.0.0",
"terser-webpack-plugin": "2.2.1",
"updates": "9.0.1",
"webpack": "4.41.2",
"webpack-cli": "3.3.10"
},
"browserslist": [
"> 1%",

View File

@ -73,7 +73,7 @@ a{cursor:pointer}
.right.stackable.menu{margin-left:auto;display:flex;align-items:inherit;flex-direction:inherit}
.ui.left{float:left}
.ui.right{float:right}
.ui.button,.ui.menu .item{-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto}
.ui.button,.ui.menu .item{-webkit-user-select:auto;-ms-user-select:auto;user-select:auto}
.ui.container.fluid.padded{padding:0 10px 0 10px}
.ui.form .ui.button{font-weight:400}
.ui.floating.label{z-index:10}
@ -233,14 +233,14 @@ i.icons .icon:first-child{margin-right:0}
i.icon.centerlock{top:1.5em}
.ui.label>.detail .icons{margin-right:.25em}
.ui.label>.detail .icons .icon{margin-right:0}
.lines-num{vertical-align:top;text-align:right!important;color:#999;background:#f5f5f5;width:1%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.lines-num{vertical-align:top;text-align:right!important;color:#999;background:#f5f5f5;width:1%;-webkit-user-select:none;-ms-user-select:none;user-select:none}
.lines-num span:before{content:attr(data-line-number);line-height:20px!important;padding:0 10px;cursor:pointer;display:block}
.lines-code,.lines-num{padding:0!important}
.lines-code .hljs,.lines-code ol,.lines-code pre,.lines-num .hljs,.lines-num ol,.lines-num pre{background-color:#fff;margin:0;padding:0!important}
.lines-code .hljs li,.lines-code ol li,.lines-code pre li,.lines-num .hljs li,.lines-num ol li,.lines-num pre li{display:block;width:100%}
.lines-code .hljs li:before,.lines-code ol li:before,.lines-code pre li:before,.lines-num .hljs li:before,.lines-num ol li:before,.lines-num pre li:before{content:' '}
.lines-commit{vertical-align:top;color:#999;padding:0!important;background:#f5f5f5;width:1%;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}
.lines-commit .blame-info{width:350px;max-width:350px;display:block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;padding:0 0 0 10px}
.lines-commit{vertical-align:top;color:#999;padding:0!important;background:#f5f5f5;width:1%;-ms-user-select:none;-webkit-user-select:none;user-select:none}
.lines-commit .blame-info{width:350px;max-width:350px;display:block;-webkit-user-select:none;-ms-user-select:none;user-select:none;padding:0 0 0 10px}
.lines-commit .blame-info .blame-data{display:flex;font-family:-apple-system,BlinkMacSystemFont,system-ui,'Segoe UI',Roboto,Helvetica,Arial}
.lines-commit .blame-info .blame-data .blame-message{flex-grow:2;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;line-height:20px}
.lines-commit .blame-info .blame-data .blame-avatar,.lines-commit .blame-info .blame-data .blame-time{flex-shrink:0}
@ -333,7 +333,7 @@ i.icon.centerlock{top:1.5em}
.repository.wiki.revisions .ui.container>.ui.stackable.grid>.header{margin-top:0}
.repository.wiki.revisions .ui.container>.ui.stackable.grid>.header .sub.header{padding-left:52px;word-break:break-word}
.file-revisions-btn{display:block;float:left;margin-bottom:2px!important;padding:11px!important;margin-right:10px!important}
.file-revisions-btn i{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.file-revisions-btn i{-webkit-touch-callout:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}
.home .logo{max-width:220px}
@media only screen and (max-width:767px){.home .hero h1{font-size:3.5em}
.home .hero h2{font-size:2em}
@ -687,7 +687,7 @@ i.icon.centerlock{top:1.5em}
.repository .diff-box .header .file{flex:1;color:#888;word-break:break-all}
.repository .diff-box .header .button{margin:-5px 0 -5px 12px;padding:8px 10px;flex:0 0 auto}
.repository .diff-file-box .header{background-color:#f7f7f7}
.repository .diff-file-box .file-body.file-code .lines-num{text-align:right;color:#a6a6a6;background:#fafafa;width:1%;min-width:50px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:top}
.repository .diff-file-box .file-body.file-code .lines-num{text-align:right;color:#a6a6a6;background:#fafafa;width:1%;min-width:50px;-webkit-user-select:none;-ms-user-select:none;user-select:none;vertical-align:top}
.repository .diff-file-box .file-body.file-code .lines-num span.fold{display:block;text-align:center}
.repository .diff-file-box .file-body.file-code .lines-num-old{border-right:1px solid #ddd}
.repository .diff-file-box .code-diff{font-size:12px}
@ -698,7 +698,7 @@ i.icon.centerlock{top:1.5em}
.repository .diff-file-box .code-diff tbody tr .removed-code{background-color:#f99}
.repository .diff-file-box .code-diff tbody tr .added-code{background-color:#9f9}
.repository .diff-file-box .code-diff tbody tr [data-line-num]::before{content:attr(data-line-num);text-align:right}
.repository .diff-file-box .code-diff tbody tr .lines-type-marker{width:10px;min-width:10px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.repository .diff-file-box .code-diff tbody tr .lines-type-marker{width:10px;min-width:10px;-webkit-user-select:none;-ms-user-select:none;user-select:none}
.repository .diff-file-box .code-diff tbody tr [data-type-marker]::before{content:attr(data-type-marker);text-align:right;display:inline-block}
.repository .diff-file-box .code-diff-unified tbody tr.del-code td{background-color:#ffe0e0!important;border-color:#f1c0c0!important}
.repository .diff-file-box .code-diff-unified tbody tr.add-code td{background-color:#d6fcd6!important;border-color:#c1e9c1!important}
@ -898,6 +898,7 @@ tbody.commit-list{vertical-align:baseline}
.repo-buttons .disabled-repo-button a.button:hover{background:0 0!important;color:rgba(0,0,0,.6)!important;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset!important}
.repo-buttons .ui.labeled.button>.label{border-left:0!important;margin:0!important}
.tag-code,.tag-code td{background-color:#f0f0f0!important;border-color:#d3cfcf!important;padding-top:8px;padding-bottom:8px}
td.blob-excerpt{background-color:#fafafa}
.issue-keyword{border-bottom:1px dotted #959da5;display:inline-block}
.file-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px!important}
.file-info{display:flex;align-items:center}
@ -1068,4 +1069,8 @@ tbody.commit-list{vertical-align:baseline}
.comment-code-cloud .footer:after{clear:both;content:"";display:block}
.comment-code-cloud button.comment-form-reply{margin:.5em .5em .5em 4.5em}
.comment-code-cloud form.comment-form-reply{margin:0 0 0 4em}
.file-comment{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.87)}
.file-comment{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.87)}
.ui.fold-code{margin-right:1em;padding-left:5px;cursor:pointer;width:22px;font-size:12px}
.ui.fold-code:hover{color:#428bca}
.ui.blob-excerpt{display:block;line-height:20px;font-size:16px;cursor:pointer}
.ui.blob-excerpt:hover{color:#428bca}

View File

@ -14,7 +14,7 @@ body{background:#383c4a;color:#9e9e9e}
*{scrollbar-width:thin;scrollbar-color:#87ab63 rgba(255,255,255,.1)}
::-webkit-scrollbar{-webkit-appearance:none!important;width:10px!important;height:10px!important}
::-webkit-scrollbar-track{border-radius:0!important;background:rgba(255,255,255,.1)!important}
::-webkit-scrollbar-thumb{cursor:pointer!important;border-radius:5px!important;transition:color .2s ease!important;background:#87ab63!important}
::-webkit-scrollbar-thumb{cursor:pointer!important;border-radius:5px!important;-webkit-transition:color .2s ease!important;transition:color .2s ease!important;background:#87ab63!important}
::-webkit-scrollbar-thumb:window-inactive{background:#87ab63!important}
::-webkit-scrollbar-thumb:hover{background:#87ab63!important}
a{color:#87ab63}

View File

@ -1,15 +0,0 @@
/* globals gitGraph */
$(document).ready(function () {
const graphList = [];
if (!document.getElementById('graph-canvas')) {
return;
}
$("#graph-raw-list li span.node-relation").each(function () {
graphList.push($(this).text());
})
gitGraph(document.getElementById('graph-canvas'), graphList);
})

File diff suppressed because one or more lines are too long

1
public/js/index.js.map Normal file

File diff suppressed because one or more lines are too long

View File

@ -23,7 +23,7 @@ Version: 745f604212e2abfe2f0a59169ea530857b46625c
File(s): /vendor/plugins/vue/vue.min.js
Version: 2.1.10
File(s): /vendor/plugins/emojify/emojify.min.js
File(s): /vendor/plugins/emojify/emojify.custom.js
Version: 1.1.0
File(s): /vendor/plugins/cssrelpreload/

View File

@ -38,12 +38,7 @@
<tr>
<td><a href="../js/index.js">index.js</a></td>
<td><a href="https://github.com/go-gitea/gitea/blob/master/LICENSE">Expat</a></td>
<td><a href="https://github.com/go-gitea/gitea/tree/master/public/js">index.js</a></td>
</tr>
<tr>
<td><a href="../js/draw.js">draw.js</a></td>
<td><a href="https://github.com/go-gitea/gitea/blob/master/LICENSE">Expat</a></td>
<td><a href="https://github.com/go-gitea/gitea/tree/master/public/js">draw.js</a></td>
<td><a href="https://github.com/go-gitea/gitea/tree/master/web_src/js">*.js</a></td>
</tr>
<tr>
<td><a href="./plugins/clipboard/clipboard.min.js">clipboard.min.js</a></td>
@ -61,7 +56,7 @@
<td><a href="https://github.com/vuejs/vue/archive/v2.6.6.tar.gz">vue.js-v2.6.6.tar.gz</a></td>
</tr>
<tr>
<td><a href="./plugins/emojify/emojify.min.js">emojify.min.js</a></td>
<td><a href="./plugins/emojify/emojify.custom.js">emojify.custom.js</a></td>
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td>
<td><a href="https://github.com/Ranks/emojify.js/archive/1.1.0.tar.gz">emojify-1.1.0.tar.gz</a></td>
</tr>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -6,6 +6,7 @@
package admin
import (
"encoding/json"
"fmt"
"net/url"
"os"
@ -25,6 +26,7 @@ import (
"code.gitea.io/gitea/services/mailer"
"gitea.com/macaron/macaron"
"gitea.com/macaron/session"
"github.com/unknwon/com"
)
@ -207,7 +209,7 @@ func SendTestMail(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/admin/config")
}
func shadownPasswordKV(cfgItem, splitter string) string {
func shadowPasswordKV(cfgItem, splitter string) string {
fields := strings.Split(cfgItem, splitter)
for i := 0; i < len(fields); i++ {
if strings.HasPrefix(fields[i], "password=") {
@ -218,10 +220,10 @@ func shadownPasswordKV(cfgItem, splitter string) string {
return strings.Join(fields, splitter)
}
func shadownURL(provider, cfgItem string) string {
func shadowURL(provider, cfgItem string) string {
u, err := url.Parse(cfgItem)
if err != nil {
log.Error("shodowPassword %v failed: %v", provider, err)
log.Error("Shadowing Password for %v failed: %v", provider, err)
return cfgItem
}
if u.User != nil {
@ -239,7 +241,7 @@ func shadownURL(provider, cfgItem string) string {
func shadowPassword(provider, cfgItem string) string {
switch provider {
case "redis":
return shadownPasswordKV(cfgItem, ",")
return shadowPasswordKV(cfgItem, ",")
case "mysql":
//root:@tcp(localhost:3306)/macaron?charset=utf8
atIdx := strings.Index(cfgItem, "@")
@ -253,15 +255,21 @@ func shadowPassword(provider, cfgItem string) string {
case "postgres":
// user=jiahuachen dbname=macaron port=5432 sslmode=disable
if !strings.HasPrefix(cfgItem, "postgres://") {
return shadownPasswordKV(cfgItem, " ")
return shadowPasswordKV(cfgItem, " ")
}
fallthrough
case "couchbase":
return shadowURL(provider, cfgItem)
// postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full
// Notice: use shadwonURL
// Notice: use shadowURL
case "VirtualSession":
var realSession session.Options
if err := json.Unmarshal([]byte(cfgItem), &realSession); err == nil {
return shadowPassword(realSession.Provider, realSession.ProviderConfig)
}
}
// "couchbase"
return shadownURL(provider, cfgItem)
return cfgItem
}
// Config show admin config page

View File

@ -631,15 +631,13 @@ func Edit(ctx *context.APIContext, opts api.EditRepoOption) {
func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) error {
owner := ctx.Repo.Owner
repo := ctx.Repo.Repository
oldRepoName := repo.Name
newRepoName := repo.Name
if opts.Name != nil {
newRepoName = *opts.Name
}
// Check if repository name has been changed and not just a case change
if repo.LowerName != strings.ToLower(newRepoName) {
if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil {
if err := repo_service.ChangeRepositoryName(ctx.User, repo, newRepoName); err != nil {
switch {
case models.IsErrRepoAlreadyExist(err):
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err)
@ -653,14 +651,6 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
return err
}
err := models.NewRepoRedirect(ctx.Repo.Owner.ID, repo.ID, repo.Name, newRepoName)
if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "NewRepoRedirect", err)
return err
}
notification.NotifyRenameRepository(ctx.User, repo, oldRepoName)
log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
}
// Update the name in the repo object for the response

View File

@ -245,6 +245,7 @@ func Diff(ctx *context.Context) {
}
ctx.Data["CommitID"] = commitID
ctx.Data["AfterCommitID"] = commitID
ctx.Data["Username"] = userName
ctx.Data["Reponame"] = repoName

View File

@ -5,21 +5,26 @@
package repo
import (
"bufio"
"fmt"
"html"
"path"
"path/filepath"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/gitdiff"
)
const (
tplCompare base.TplName = "repo/diff/compare"
tplCompare base.TplName = "repo/diff/compare"
tplBlobExcerpt base.TplName = "repo/diff/blob_excerpt"
)
// setPathsCompareContext sets context data for source and raw paths
@ -434,3 +439,109 @@ func CompareDiff(ctx *context.Context) {
ctx.HTML(200, tplCompare)
}
// ExcerptBlob render blob excerpt contents
func ExcerptBlob(ctx *context.Context) {
commitID := ctx.Params("sha")
lastLeft := ctx.QueryInt("last_left")
lastRight := ctx.QueryInt("last_right")
idxLeft := ctx.QueryInt("left")
idxRight := ctx.QueryInt("right")
leftHunkSize := ctx.QueryInt("left_hunk_size")
rightHunkSize := ctx.QueryInt("right_hunk_size")
anchor := ctx.Query("anchor")
direction := ctx.Query("direction")
filePath := ctx.Query("path")
gitRepo := ctx.Repo.GitRepo
chunkSize := gitdiff.BlobExceprtChunkSize
commit, err := gitRepo.GetCommit(commitID)
if err != nil {
ctx.Error(500, "GetCommit")
return
}
section := &gitdiff.DiffSection{
Name: filePath,
}
if direction == "up" && (idxLeft-lastLeft) > chunkSize {
idxLeft -= chunkSize
idxRight -= chunkSize
leftHunkSize += chunkSize
rightHunkSize += chunkSize
section.Lines, err = getExcerptLines(commit, filePath, idxLeft-1, idxRight-1, chunkSize)
} else if direction == "down" && (idxLeft-lastLeft) > chunkSize {
section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, chunkSize)
lastLeft += chunkSize
lastRight += chunkSize
} else {
section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, idxRight-lastRight-1)
leftHunkSize = 0
rightHunkSize = 0
idxLeft = lastLeft
idxRight = lastRight
}
if err != nil {
ctx.Error(500, "getExcerptLines")
return
}
if idxRight > lastRight {
lineText := " "
if rightHunkSize > 0 || leftHunkSize > 0 {
lineText = fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", idxLeft, leftHunkSize, idxRight, rightHunkSize)
}
lineText = html.EscapeString(lineText)
lineSection := &gitdiff.DiffLine{
Type: gitdiff.DiffLineSection,
Content: lineText,
SectionInfo: &gitdiff.DiffLineSectionInfo{
Path: filePath,
LastLeftIdx: lastLeft,
LastRightIdx: lastRight,
LeftIdx: idxLeft,
RightIdx: idxRight,
LeftHunkSize: leftHunkSize,
RightHunkSize: rightHunkSize,
}}
if direction == "up" {
section.Lines = append([]*gitdiff.DiffLine{lineSection}, section.Lines...)
} else if direction == "down" {
section.Lines = append(section.Lines, lineSection)
}
}
ctx.Data["section"] = section
ctx.Data["fileName"] = filePath
ctx.Data["highlightClass"] = highlight.FileNameToHighlightClass(filepath.Base(filePath))
ctx.Data["AfterCommitID"] = commitID
ctx.Data["Anchor"] = anchor
ctx.HTML(200, tplBlobExcerpt)
}
func getExcerptLines(commit *git.Commit, filePath string, idxLeft int, idxRight int, chunkSize int) ([]*gitdiff.DiffLine, error) {
blob, err := commit.Tree.GetBlobByPath(filePath)
if err != nil {
return nil, err
}
reader, err := blob.DataAsync()
if err != nil {
return nil, err
}
defer reader.Close()
scanner := bufio.NewScanner(reader)
var diffLines []*gitdiff.DiffLine
for line := 0; line < idxRight+chunkSize; line++ {
if ok := scanner.Scan(); !ok {
break
}
if line < idxRight {
continue
}
lineText := scanner.Text()
diffLine := &gitdiff.DiffLine{
LeftIdx: idxLeft + (line - idxRight) + 1,
RightIdx: line + 1,
Type: gitdiff.DiffLinePlain,
Content: " " + lineText,
}
diffLines = append(diffLines, diffLine)
}
return diffLines, nil
}

View File

@ -552,6 +552,7 @@ func ViewPullFiles(ctx *context.Context) {
ctx.Data["Username"] = pull.MustHeadUserName()
ctx.Data["Reponame"] = pull.HeadRepo.Name
}
ctx.Data["AfterCommitID"] = endCommitID
diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(diffRepoPath,
startCommitID, endCommitID, setting.Git.MaxGitDiffLines,

View File

@ -11,8 +11,6 @@ import (
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
comment_service "code.gitea.io/gitea/services/comments"
pull_service "code.gitea.io/gitea/services/pull"
)
@ -31,64 +29,33 @@ func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) {
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
return
}
var comment *models.Comment
defer func() {
if comment != nil {
ctx.Redirect(comment.HTMLURL())
} else {
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
}
}()
signedLine := form.Line
if form.Side == "previous" {
signedLine *= -1
}
review := new(models.Review)
if form.IsReview {
var err error
// Check if the user has already a pending review for this issue
if review, err = models.GetCurrentReview(ctx.User, issue); err != nil {
if !models.IsErrReviewNotExist(err) {
ctx.ServerError("CreateCodeComment", err)
return
}
// No pending review exists
// Create a new pending review for this issue & user
if review, err = pull_service.CreateReview(models.CreateReviewOptions{
Type: models.ReviewTypePending,
Reviewer: ctx.User,
Issue: issue,
}); err != nil {
ctx.ServerError("CreateCodeComment", err)
return
}
}
}
if review.ID == 0 {
review.ID = form.Reply
}
//FIXME check if line, commit and treepath exist
comment, err := comment_service.CreateCodeComment(
comment, err := pull_service.CreateCodeComment(
ctx.User,
issue.Repo,
issue,
signedLine,
form.Content,
form.TreePath,
signedLine,
review.ID,
form.IsReview,
form.Reply,
)
if err != nil {
ctx.ServerError("CreateCodeComment", err)
return
}
// Send no notification if comment is pending
if !form.IsReview || form.Reply != 0 {
notification.NotifyCreateIssueComment(ctx.User, issue.Repo, issue, comment)
}
log.Trace("Comment created: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID)
if comment != nil {
ctx.Redirect(comment.HTMLURL())
} else {
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
}
}
// SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
@ -105,23 +72,17 @@ func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) {
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
return
}
var review *models.Review
var err error
reviewType := form.ReviewType()
switch reviewType {
case models.ReviewTypeUnknown:
ctx.ServerError("GetCurrentReview", fmt.Errorf("unknown ReviewType: %s", form.Type))
ctx.ServerError("ReviewType", fmt.Errorf("unknown ReviewType: %s", form.Type))
return
// can not approve/reject your own PR
case models.ReviewTypeApprove, models.ReviewTypeReject:
if issue.Poster.ID == ctx.User.ID {
var translated string
if reviewType == models.ReviewTypeApprove {
translated = ctx.Tr("repo.issues.review.self.approval")
} else {
@ -134,69 +95,16 @@ func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) {
}
}
review, err = models.GetCurrentReview(ctx.User, issue)
if err == nil {
review.Issue = issue
if errl := review.LoadCodeComments(); errl != nil {
ctx.ServerError("LoadCodeComments", err)
return
}
}
if ((err == nil && len(review.CodeComments) == 0) ||
(err != nil && models.IsErrReviewNotExist(err))) &&
form.HasEmptyContent() {
ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty"))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
return
}
_, comm, err := pull_service.SubmitReview(ctx.User, issue, reviewType, form.Content)
if err != nil {
if !models.IsErrReviewNotExist(err) {
ctx.ServerError("GetCurrentReview", err)
return
if models.IsContentEmptyErr(err) {
ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty"))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
} else {
ctx.ServerError("SubmitReview", err)
}
// No current review. Create a new one!
if review, err = pull_service.CreateReview(models.CreateReviewOptions{
Type: reviewType,
Issue: issue,
Reviewer: ctx.User,
Content: form.Content,
}); err != nil {
ctx.ServerError("CreateReview", err)
return
}
} else {
review.Content = form.Content
review.Type = reviewType
if err = pull_service.UpdateReview(review); err != nil {
ctx.ServerError("UpdateReview", err)
return
}
}
comm, err := models.CreateComment(&models.CreateCommentOptions{
Type: models.CommentTypeReview,
Doer: ctx.User,
Content: review.Content,
Issue: issue,
Repo: issue.Repo,
ReviewID: review.ID,
})
if err != nil || comm == nil {
ctx.ServerError("CreateComment", err)
return
}
if err = review.Publish(); err != nil {
ctx.ServerError("Publish", err)
return
}
pr, err := issue.GetPullRequest()
if err != nil {
ctx.ServerError("GetPullRequest", err)
return
}
notification.NotifyPullRequestReview(pr, review, comm)
ctx.Redirect(fmt.Sprintf("%s/pulls/%d#%s", ctx.Repo.RepoLink, issue.Index, comm.HashTag()))
}

View File

@ -20,7 +20,6 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/validation"
@ -67,18 +66,15 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
return
}
isNameChanged := false
oldRepoName := repo.Name
newRepoName := form.RepoName
// Check if repository name has been changed.
if repo.LowerName != strings.ToLower(newRepoName) {
isNameChanged = true
// Close the GitRepo if open
if ctx.Repo.GitRepo != nil {
ctx.Repo.GitRepo.Close()
ctx.Repo.GitRepo = nil
}
if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil {
if err := repo_service.ChangeRepositoryName(ctx.Repo.Owner, repo, newRepoName); err != nil {
ctx.Data["Err_RepoName"] = true
switch {
case models.IsErrRepoAlreadyExist(err):
@ -93,12 +89,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
return
}
err := models.NewRepoRedirect(ctx.Repo.Owner.ID, repo.ID, repo.Name, newRepoName)
if err != nil {
ctx.ServerError("NewRepoRedirect", err)
return
}
log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
}
// In case it's just a case change.
@ -127,10 +117,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
}
log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
if isNameChanged {
notification.NotifyRenameRepository(ctx.User, repo, oldRepoName)
}
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
ctx.Redirect(repo.Link() + "/settings")
@ -383,13 +369,12 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
return
}
oldOwnerID := ctx.Repo.Owner.ID
// Close the GitRepo if open
if ctx.Repo.GitRepo != nil {
ctx.Repo.GitRepo.Close()
ctx.Repo.GitRepo = nil
}
if err = models.TransferOwnership(ctx.User, newOwner, repo); err != nil {
if err = repo_service.TransferOwnership(ctx.User, newOwner, repo); err != nil {
if models.IsErrRepoAlreadyExist(err) {
ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil)
} else {
@ -398,12 +383,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
return
}
err = models.NewRepoRedirect(oldOwnerID, repo.ID, repo.Name, repo.Name)
if err != nil {
ctx.ServerError("NewRepoRedirect", err)
return
}
log.Trace("Repository transferred: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed"))
ctx.Redirect(setting.AppSubURL + "/" + newOwner + "/" + repo.Name)
@ -707,6 +686,7 @@ func GitHooks(ctx *context.Context) {
func GitHooksEdit(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
ctx.Data["PageIsSettingsGitHooks"] = true
ctx.Data["RequireSimpleMDE"] = true
name := ctx.Params(":name")
hook, err := ctx.Repo.GitRepo.GetHook(name)

View File

@ -864,6 +864,10 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", repo.Branches)
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
m.Group("/blob_excerpt", func() {
m.Get("/:sha", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob)
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
m.Group("/pulls/:index", func() {
m.Get(".diff", repo.DownloadPullDiff)
m.Get(".patch", repo.DownloadPullPatch)

View File

@ -5,15 +5,8 @@
package comments
import (
"bytes"
"fmt"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/gitdiff"
)
// CreateIssueComment creates a plain issue comment.
@ -35,60 +28,6 @@ func CreateIssueComment(doer *models.User, repo *models.Repository, issue *model
return comment, nil
}
// CreateCodeComment creates a plain code comment at the specified line / path
func CreateCodeComment(doer *models.User, repo *models.Repository, issue *models.Issue, content, treePath string, line, reviewID int64) (*models.Comment, error) {
var commitID, patch string
pr, err := models.GetPullRequestByIssueID(issue.ID)
if err != nil {
return nil, fmt.Errorf("GetPullRequestByIssueID: %v", err)
}
if err := pr.GetBaseRepo(); err != nil {
return nil, fmt.Errorf("GetHeadRepo: %v", err)
}
gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath())
if err != nil {
return nil, fmt.Errorf("OpenRepository: %v", err)
}
defer gitRepo.Close()
// FIXME validate treePath
// Get latest commit referencing the commented line
// No need for get commit for base branch changes
if line > 0 {
commit, err := gitRepo.LineBlame(pr.GetGitRefName(), gitRepo.Path, treePath, uint(line))
if err == nil {
commitID = commit.ID.String()
} else if !strings.Contains(err.Error(), "exit status 128 - fatal: no such path") {
return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %v", pr.GetGitRefName(), gitRepo.Path, treePath, line, err)
}
}
// Only fetch diff if comment is review comment
if reviewID != 0 {
headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil {
return nil, fmt.Errorf("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err)
}
patchBuf := new(bytes.Buffer)
if err := gitdiff.GetRawDiffForFile(gitRepo.Path, pr.MergeBase, headCommitID, gitdiff.RawDiffNormal, treePath, patchBuf); err != nil {
return nil, fmt.Errorf("GetRawDiffForLine[%s, %s, %s, %s]: %v", err, gitRepo.Path, pr.MergeBase, headCommitID, treePath)
}
patch = gitdiff.CutDiffAroundLine(patchBuf, int64((&models.Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines)
}
return models.CreateComment(&models.CreateCommentOptions{
Type: models.CommentTypeCode,
Doer: doer,
Repo: repo,
Issue: issue,
Content: content,
LineNum: line,
TreePath: treePath,
CommitSHA: commitID,
ReviewID: reviewID,
Patch: patch,
})
}
// UpdateComment updates information of comment.
func UpdateComment(c *models.Comment, doer *models.User, oldContent string) error {
if err := models.UpdateComment(c, doer); err != nil {

View File

@ -13,6 +13,7 @@ import (
"html/template"
"io"
"io/ioutil"
"net/url"
"os"
"os/exec"
"regexp"
@ -56,15 +57,42 @@ const (
DiffFileRename
)
// DiffLineExpandDirection represents the DiffLineSection expand direction
type DiffLineExpandDirection uint8
// DiffLineExpandDirection possible values.
const (
DiffLineExpandNone DiffLineExpandDirection = iota + 1
DiffLineExpandSingle
DiffLineExpandUpDown
DiffLineExpandUp
DiffLineExpandDown
)
// DiffLine represents a line difference in a DiffSection.
type DiffLine struct {
LeftIdx int
RightIdx int
Type DiffLineType
Content string
Comments []*models.Comment
LeftIdx int
RightIdx int
Type DiffLineType
Content string
Comments []*models.Comment
SectionInfo *DiffLineSectionInfo
}
// DiffLineSectionInfo represents diff line section meta data
type DiffLineSectionInfo struct {
Path string
LastLeftIdx int
LastRightIdx int
LeftIdx int
RightIdx int
LeftHunkSize int
RightHunkSize int
}
// BlobExceprtChunkSize represent max lines of excerpt
const BlobExceprtChunkSize = 20
// GetType returns the type of a DiffLine.
func (d *DiffLine) GetType() int {
return int(d.Type)
@ -91,6 +119,71 @@ func (d *DiffLine) GetLineTypeMarker() string {
return ""
}
// GetBlobExcerptQuery builds query string to get blob excerpt
func (d *DiffLine) GetBlobExcerptQuery() string {
query := fmt.Sprintf(
"last_left=%d&last_right=%d&"+
"left=%d&right=%d&"+
"left_hunk_size=%d&right_hunk_size=%d&"+
"path=%s",
d.SectionInfo.LastLeftIdx, d.SectionInfo.LastRightIdx,
d.SectionInfo.LeftIdx, d.SectionInfo.RightIdx,
d.SectionInfo.LeftHunkSize, d.SectionInfo.RightHunkSize,
url.QueryEscape(d.SectionInfo.Path))
return query
}
// GetExpandDirection gets DiffLineExpandDirection
func (d *DiffLine) GetExpandDirection() DiffLineExpandDirection {
if d.Type != DiffLineSection || d.SectionInfo == nil || d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx <= 1 {
return DiffLineExpandNone
}
if d.SectionInfo.LastLeftIdx <= 0 && d.SectionInfo.LastRightIdx <= 0 {
return DiffLineExpandUp
} else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx > BlobExceprtChunkSize && d.SectionInfo.RightHunkSize > 0 {
return DiffLineExpandUpDown
} else if d.SectionInfo.LeftHunkSize <= 0 && d.SectionInfo.RightHunkSize <= 0 {
return DiffLineExpandDown
}
return DiffLineExpandSingle
}
func getDiffLineSectionInfo(curFile *DiffFile, line string, lastLeftIdx, lastRightIdx int) *DiffLineSectionInfo {
var (
leftLine int
leftHunk int
rightLine int
righHunk int
)
ss := strings.Split(line, "@@")
ranges := strings.Split(ss[1][1:], " ")
leftRange := strings.Split(ranges[0], ",")
leftLine, _ = com.StrTo(leftRange[0][1:]).Int()
if len(leftRange) > 1 {
leftHunk, _ = com.StrTo(leftRange[1]).Int()
}
if len(ranges) > 1 {
rightRange := strings.Split(ranges[1], ",")
rightLine, _ = com.StrTo(rightRange[0]).Int()
if len(rightRange) > 1 {
righHunk, _ = com.StrTo(rightRange[1]).Int()
}
} else {
log.Warn("Parse line number failed: %v", line)
rightLine = leftLine
righHunk = leftHunk
}
return &DiffLineSectionInfo{
Path: curFile.Name,
LastLeftIdx: lastLeftIdx,
LastRightIdx: lastRightIdx,
LeftIdx: leftLine,
RightIdx: rightLine,
LeftHunkSize: leftHunk,
RightHunkSize: righHunk,
}
}
// escape a line's content or return <br> needed for copy/paste purposes
func getLineContent(content string) string {
if len(content) > 0 {
@ -248,6 +341,53 @@ func (diffFile *DiffFile) GetHighlightClass() string {
return highlight.FileNameToHighlightClass(diffFile.Name)
}
// GetTailSection creates a fake DiffLineSection if the last section is not the end of the file
func (diffFile *DiffFile) GetTailSection(gitRepo *git.Repository, leftCommitID, rightCommitID string) *DiffSection {
if diffFile.Type != DiffFileChange || diffFile.IsBin || diffFile.IsLFSFile {
return nil
}
leftCommit, err := gitRepo.GetCommit(leftCommitID)
if err != nil {
return nil
}
rightCommit, err := gitRepo.GetCommit(rightCommitID)
if err != nil {
return nil
}
lastSection := diffFile.Sections[len(diffFile.Sections)-1]
lastLine := lastSection.Lines[len(lastSection.Lines)-1]
leftLineCount := getCommitFileLineCount(leftCommit, diffFile.Name)
rightLineCount := getCommitFileLineCount(rightCommit, diffFile.Name)
if leftLineCount <= lastLine.LeftIdx || rightLineCount <= lastLine.RightIdx {
return nil
}
tailDiffLine := &DiffLine{
Type: DiffLineSection,
Content: " ",
SectionInfo: &DiffLineSectionInfo{
Path: diffFile.Name,
LastLeftIdx: lastLine.LeftIdx,
LastRightIdx: lastLine.RightIdx,
LeftIdx: leftLineCount,
RightIdx: rightLineCount,
}}
tailSection := &DiffSection{Lines: []*DiffLine{tailDiffLine}}
return tailSection
}
func getCommitFileLineCount(commit *git.Commit, filePath string) int {
blob, err := commit.GetBlobByPath(filePath)
if err != nil {
return 0
}
lineCount, err := blob.GetBlobLineCount()
if err != nil {
return 0
}
return lineCount
}
// Diff represents a difference between two git trees.
type Diff struct {
TotalAddition, TotalDeletion int
@ -510,19 +650,16 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
case line[0] == '@':
curSection = &DiffSection{}
curFile.Sections = append(curFile.Sections, curSection)
ss := strings.Split(line, "@@")
diffLine := &DiffLine{Type: DiffLineSection, Content: line}
curSection.Lines = append(curSection.Lines, diffLine)
// Parse line number.
ranges := strings.Split(ss[1][1:], " ")
leftLine, _ = com.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int()
if len(ranges) > 1 {
rightLine, _ = com.StrTo(strings.Split(ranges[1], ",")[0]).Int()
} else {
log.Warn("Parse line number failed: %v", line)
rightLine = leftLine
lineSectionInfo := getDiffLineSectionInfo(curFile, line, leftLine-1, rightLine-1)
diffLine := &DiffLine{
Type: DiffLineSection,
Content: line,
SectionInfo: lineSectionInfo,
}
curSection.Lines = append(curSection.Lines, diffLine)
// update line number.
leftLine = lineSectionInfo.LeftIdx
rightLine = lineSectionInfo.RightIdx
continue
case line[0] == '+':
curFile.Addition++
@ -599,6 +736,8 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
break
}
curFileLinesCount = 0
leftLine = 1
rightLine = 1
curFileLFSPrefix = false
// Check file diff type and is submodule.
@ -701,6 +840,7 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID
diffArgs = append(diffArgs, actualBeforeCommitID)
diffArgs = append(diffArgs, afterCommitID)
cmd = exec.Command(git.GitExecutable, diffArgs...)
beforeCommitID = actualBeforeCommitID
}
cmd.Dir = repoPath
cmd.Stderr = os.Stderr
@ -721,6 +861,12 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID
if err != nil {
return nil, fmt.Errorf("ParsePatch: %v", err)
}
for _, diffFile := range diff.Files {
tailSection := diffFile.GetTailSection(gitRepo, beforeCommitID, afterCommitID)
if tailSection != nil {
diffFile.Sections = append(diffFile.Sections, tailSection)
}
}
if err = cmd.Wait(); err != nil {
return nil, fmt.Errorf("Wait: %v", err)

View File

@ -76,6 +76,7 @@ func TestRelease_MirrorDelete(t *testing.T) {
assert.True(t, ok)
count, err := models.GetReleaseCountByRepoID(mirror.ID, findOptions)
assert.NoError(t, err)
assert.EqualValues(t, initCount+1, count)
release, err := models.GetRelease(repo.ID, "v0.2")
@ -86,5 +87,6 @@ func TestRelease_MirrorDelete(t *testing.T) {
assert.True(t, ok)
count, err = models.GetReleaseCountByRepoID(mirror.ID, findOptions)
assert.NoError(t, err)
assert.EqualValues(t, initCount, count)
}

View File

@ -424,8 +424,16 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
log.Error("setMerged [%d]: %v", pr.ID, err)
}
if err = models.MergePullRequestAction(doer, pr.Issue.Repo, pr.Issue); err != nil {
log.Error("MergePullRequestAction [%d]: %v", pr.ID, err)
if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: models.ActionMergePullRequest,
Content: fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
RepoID: pr.Issue.Repo.ID,
Repo: pr.Issue.Repo,
IsPrivate: pr.Issue.Repo.IsPrivate,
}); err != nil {
log.Error("NotifyWatchers [%d]: %v", pr.ID, err)
}
// Reset cached commit count

View File

@ -6,30 +6,144 @@
package pull
import (
"bytes"
"fmt"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/gitdiff"
)
// CreateReview creates a new review based on opts
func CreateReview(opts models.CreateReviewOptions) (*models.Review, error) {
review, err := models.CreateReview(opts)
// CreateCodeComment creates a comment on the code line
func CreateCodeComment(doer *models.User, issue *models.Issue, line int64, content string, treePath string, isReview bool, replyReviewID int64) (*models.Comment, error) {
// It's not a review, maybe a reply to a review comment or a single comment.
if !isReview {
if err := issue.LoadRepo(); err != nil {
return nil, err
}
comment, err := createCodeComment(
doer,
issue.Repo,
issue,
content,
treePath,
line,
replyReviewID,
)
if err != nil {
return nil, err
}
notification.NotifyCreateIssueComment(doer, issue.Repo, issue, comment)
return comment, nil
}
review, err := models.GetCurrentReview(doer, issue)
if err != nil {
if !models.IsErrReviewNotExist(err) {
return nil, err
}
review, err = models.CreateReview(models.CreateReviewOptions{
Type: models.ReviewTypePending,
Reviewer: doer,
Issue: issue,
})
if err != nil {
return nil, err
}
}
comment, err := createCodeComment(
doer,
issue.Repo,
issue,
content,
treePath,
line,
review.ID,
)
if err != nil {
return nil, err
}
notification.NotifyPullRequestReview(review.Issue.PullRequest, review, nil)
// NOTICE: it's a pending review, so the notifications will not be fired until user submit review.
return review, nil
return comment, nil
}
// UpdateReview updates a review
func UpdateReview(review *models.Review) error {
err := models.UpdateReview(review)
// createCodeComment creates a plain code comment at the specified line / path
func createCodeComment(doer *models.User, repo *models.Repository, issue *models.Issue, content, treePath string, line, reviewID int64) (*models.Comment, error) {
var commitID, patch string
if err := issue.LoadPullRequest(); err != nil {
return nil, fmt.Errorf("GetPullRequestByIssueID: %v", err)
}
pr := issue.PullRequest
if err := pr.GetBaseRepo(); err != nil {
return nil, fmt.Errorf("GetHeadRepo: %v", err)
}
gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath())
if err != nil {
return err
return nil, fmt.Errorf("OpenRepository: %v", err)
}
defer gitRepo.Close()
// FIXME validate treePath
// Get latest commit referencing the commented line
// No need for get commit for base branch changes
if line > 0 {
commit, err := gitRepo.LineBlame(pr.GetGitRefName(), gitRepo.Path, treePath, uint(line))
if err == nil {
commitID = commit.ID.String()
} else if !strings.Contains(err.Error(), "exit status 128 - fatal: no such path") {
return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %v", pr.GetGitRefName(), gitRepo.Path, treePath, line, err)
}
}
notification.NotifyPullRequestReview(review.Issue.PullRequest, review, nil)
return nil
// Only fetch diff if comment is review comment
if reviewID != 0 {
headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil {
return nil, fmt.Errorf("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err)
}
patchBuf := new(bytes.Buffer)
if err := gitdiff.GetRawDiffForFile(gitRepo.Path, pr.MergeBase, headCommitID, gitdiff.RawDiffNormal, treePath, patchBuf); err != nil {
return nil, fmt.Errorf("GetRawDiffForLine[%s, %s, %s, %s]: %v", err, gitRepo.Path, pr.MergeBase, headCommitID, treePath)
}
patch = gitdiff.CutDiffAroundLine(patchBuf, int64((&models.Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines)
}
return models.CreateComment(&models.CreateCommentOptions{
Type: models.CommentTypeCode,
Doer: doer,
Repo: repo,
Issue: issue,
Content: content,
LineNum: line,
TreePath: treePath,
CommitSHA: commitID,
ReviewID: reviewID,
Patch: patch,
NoAction: true,
})
}
// SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
func SubmitReview(doer *models.User, issue *models.Issue, reviewType models.ReviewType, content string) (*models.Review, *models.Comment, error) {
review, comm, err := models.SubmitReview(doer, issue, reviewType, content)
if err != nil {
return nil, nil, err
}
pr, err := issue.GetPullRequest()
if err != nil {
return nil, nil, err
}
notification.NotifyPullRequestReview(pr, review, comm)
return review, comm, nil
}

View File

@ -0,0 +1,16 @@
// 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 repository
import (
"path/filepath"
"testing"
"code.gitea.io/gitea/models"
)
func TestMain(m *testing.M) {
models.MainTest(m, filepath.Join("..", ".."))
}

View File

@ -0,0 +1,54 @@
// 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 repository
import (
"fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/notification"
)
// TransferOwnership transfers all corresponding setting from old user to new one.
func TransferOwnership(doer *models.User, newOwnerName string, repo *models.Repository) error {
if err := repo.GetOwner(); err != nil {
return err
}
oldOwner := repo.Owner
if err := models.TransferOwnership(doer, newOwnerName, repo); err != nil {
return err
}
if err := models.NewRepoRedirect(oldOwner.ID, repo.ID, repo.Name, repo.Name); err != nil {
return fmt.Errorf("NewRepoRedirect: %v", err)
}
notification.NotifyTransferRepository(doer, repo, oldOwner.Name)
return nil
}
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoName string) error {
oldRepoName := repo.Name
if err := models.ChangeRepositoryName(doer, repo, newRepoName); err != nil {
return err
}
if err := repo.GetOwner(); err != nil {
return err
}
if err := models.NewRepoRedirect(repo.Owner.ID, repo.ID, oldRepoName, newRepoName); err != nil {
return err
}
notification.NotifyRenameRepository(doer, repo, oldRepoName)
return nil
}

View File

@ -0,0 +1,50 @@
// 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 repository
import (
"sync"
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/notification/action"
"github.com/stretchr/testify/assert"
"github.com/unknwon/com"
)
var notifySync sync.Once
func registerNotifier() {
notifySync.Do(func() {
notification.RegisterNotifier(action.NewNotifier())
})
}
func TestTransferOwnership(t *testing.T) {
registerNotifier()
assert.NoError(t, models.PrepareTestDatabase())
doer := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)
repo.Owner = models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
assert.NoError(t, TransferOwnership(doer, "user2", repo))
transferredRepo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)
assert.EqualValues(t, 2, transferredRepo.OwnerID)
assert.False(t, com.IsExist(models.RepoPath("user3", "repo3")))
assert.True(t, com.IsExist(models.RepoPath("user2", "repo3")))
models.AssertExistsAndLoadBean(t, &models.Action{
OpType: models.ActionTransferRepo,
ActUserID: 2,
RepoID: 3,
Content: "user3/repo3",
})
models.CheckConsistencyFor(t, &models.Repository{}, &models.User{}, &models.Team{})
}

View File

@ -26,7 +26,6 @@
{{if .RequireGitGraph}}
<!-- graph -->
<script src="{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.js"></script>
<script src="{{StaticUrlPrefix}}/js/draw.js"></script>
{{end}}
<!-- Third-party libraries -->
@ -117,7 +116,7 @@
}
</script>
{{end}}
<script src="{{StaticUrlPrefix}}/vendor/plugins/emojify/emojify.min.js"></script>
<script src="{{StaticUrlPrefix}}/vendor/plugins/emojify/emojify.custom.js"></script>
<script src="{{StaticUrlPrefix}}/vendor/plugins/clipboard/clipboard.min.js"></script>
<script src="{{StaticUrlPrefix}}/vendor/plugins/vue/vue.min.js"></script>
@ -129,7 +128,7 @@
<script src="{{StaticUrlPrefix}}/vendor/plugins/moment/moment.min.js" charset="utf-8"></script>
<script src="{{StaticUrlPrefix}}/vendor/plugins/vue-calendar-heatmap/vue-calendar-heatmap.browser.js" charset="utf-8"></script>
<script type="text/javascript">
initHeatmap('user-heatmap', '{{.HeatmapUser}}');
window.initHeatmap('user-heatmap', '{{.HeatmapUser}}');
</script>
{{end}}
{{template "custom/footer" .}}

View File

@ -7,11 +7,10 @@ var urlsToCache = [
'{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.js',
'{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}',
'{{StaticUrlPrefix}}/js/semantic.dropdown.custom.js?v={{MD5 AppVer}}',
'{{StaticUrlPrefix}}/js/draw.js',
'{{StaticUrlPrefix}}/vendor/plugins/clipboard/clipboard.min.js',
'{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.js',
'{{StaticUrlPrefix}}/vendor/plugins/vue/vue.min.js',
'{{StaticUrlPrefix}}/vendor/plugins/emojify/emojify.min.js',
'{{StaticUrlPrefix}}/vendor/plugins/emojify/emojify.custom.js',
'{{StaticUrlPrefix}}/vendor/plugins/cssrelpreload/loadCSS.min.js',
'{{StaticUrlPrefix}}/vendor/plugins/cssrelpreload/cssrelpreload.min.js',
'{{StaticUrlPrefix}}/vendor/plugins/dropzone/dropzone.js',

View File

@ -0,0 +1,50 @@
{{if $.IsSplitStyle}}
{{range $k, $line := $.section.Lines}}
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
{{if eq .GetType 4}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}">
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
<i class="ui blob-excerpt fa fa-caret-down" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=down" data-anchor="{{$.Anchor}}"></i>
{{end}}
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }}
<i class="ui blob-excerpt fa fa-caret-up" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=up" data-anchor="{{$.Anchor}}"></i>
{{end}}
{{if or (eq $line.GetExpandDirection 2)}}
<i class="ui blob-excerpt octicon octicon-fold" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=" data-anchor="{{$.Anchor}}"></i>
{{end}}
</td>
<td colspan="5" class="lines-code lines-code-old "><span class="mono wrap{{if $.highlightClass}} language-{{$.highlightClass}}{{else}} nohighlight{{end}}">{{$.section.GetComputedInlineDiffFor $line}}</span></td>
{{else}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $.fileName}}L{{$line.LeftIdx}}{{end}}"></span></td>
<td class="blob-excerpt lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker=""></span>{{end}}</td>
<td class="blob-excerpt lines-code lines-code-old halfwidth"><span class="mono wrap{{if $.highlightClass}} language-{{$.highlightClass}}{{else}} nohighlight{{end}}">{{if $line.LeftIdx}}{{$.section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $.fileName}}R{{$line.RightIdx}}{{end}}"></span></td>
<td class="blob-excerpt lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker=""></span>{{end}}</td>
<td class="blob-excerpt lines-code lines-code-new halfwidth"><span class="mono wrap{{if $.highlightClass}} language-{{$.highlightClass}}{{else}} nohighlight{{end}}">{{if $line.RightIdx}}{{$.section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
{{end}}
</tr>
{{end}}
{{else}}
{{range $k, $line := $.section.Lines}}
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
{{if eq .GetType 4}}
<td colspan="2" class="lines-num">
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }}
<i class="ui blob-excerpt fa fa-caret-up" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=up" data-anchor="{{$.Anchor}}"></i>
{{end}}
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
<i class="ui blob-excerpt fa fa-caret-down" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=down" data-anchor="{{$.Anchor}}"></i>
{{end}}
{{if or (eq $line.GetExpandDirection 2)}}
<i class="ui blob-excerpt octicon octicon-fold" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=" data-anchor="{{$.Anchor}}"></i>
{{end}}
</td>
{{else}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $.fileName}}L{{$line.LeftIdx}}{{end}}"></span></td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $.fileName}}R{{$line.RightIdx}}{{end}}"></span></td>
{{end}}
<td class="blob-excerpt lines-type-marker"><span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td>
<td class="blob-excerpt lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><span class="mono wrap{{if $.highlightClass}} language-{{$.highlightClass}}{{else}} nohighlight{{end}}">{{$.section.GetComputedInlineDiffFor $line}}</span></td>
</tr>
{{end}}
{{end}}

View File

@ -81,6 +81,15 @@
{{else}}
<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}" id="diff-{{.Index}}">
<h4 class="ui top attached normal header">
{{$isImage := false}}
{{if $file.IsDeleted}}
{{$isImage = (call $.IsImageFileInBase $file.Name)}}
{{else}}
{{$isImage = (call $.IsImageFileInHead $file.Name)}}
{{end}}
{{if or (not $file.IsBin) $isImage}}
<i class="ui fold-code grey fa fa-chevron-down"></i>
{{end}}
<div class="diff-counter count">
{{if $file.IsBin}}
{{$.i18n.Tr "repo.diff.bin"}}
@ -104,12 +113,6 @@
</h4>
<div class="ui attached unstackable table segment">
{{if ne $file.Type 4}}
{{$isImage := false}}
{{if $file.IsDeleted}}
{{$isImage = (call $.IsImageFileInBase $file.Name)}}
{{else}}
{{$isImage = (call $.IsImageFileInHead $file.Name)}}
{{end}}
<div class="file-body file-code code-view code-diff {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}}">
<table>
<tbody>
@ -121,12 +124,27 @@
{{range $j, $section := $file.Sections}}
{{range $k, $line := $section.Lines}}
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td>
<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
<td class="lines-code lines-code-old halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 2))}}<a class="ui green button add-code-comment add-code-comment-left" data-path="{{$file.Name}}" data-side="left" data-idx="{{$line.LeftIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td>
<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
<td class="lines-code lines-code-new halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 3))}}<a class="ui green button add-code-comment add-code-comment-right" data-path="{{$file.Name}}" data-side="right" data-idx="{{$line.RightIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
{{if eq .GetType 4}}
<td class="lines-num lines-num-old">
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
<i class="ui blob-excerpt fa fa-caret-down" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=down" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i>
{{end}}
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }}
<i class="ui blob-excerpt fa fa-caret-up" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=up" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i>
{{end}}
{{if or (eq $line.GetExpandDirection 2)}}
<i class="ui blob-excerpt octicon octicon-fold" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i>
{{end}}
</td>
<td colspan="5" class="lines-code lines-code-old "><span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{$section.GetComputedInlineDiffFor $line}}</span></td>
{{else}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td>
<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
<td class="lines-code lines-code-old halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 2))}}<a class="ui green button add-code-comment add-code-comment-left" data-path="{{$file.Name}}" data-side="left" data-idx="{{$line.LeftIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td>
<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
<td class="lines-code lines-code-new halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 3))}}<a class="ui green button add-code-comment add-code-comment-right" data-path="{{$file.Name}}" data-side="right" data-idx="{{$line.RightIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
{{end}}
</tr>
{{if gt (len $line.Comments) 0}}
<tr class="add-code-comment">

View File

@ -39,7 +39,7 @@
{{end}}
{{end}}
{{if or (not $.HasComments) $.hidden}}
<button type="button" class="ui submit tiny basic button btn-cancel" onclick="cancelCodeComment(this);">{{$.root.i18n.Tr "cancel"}}</button>
<button type="button" class="ui submit tiny basic button btn-cancel" onclick="window.cancelCodeComment(this);">{{$.root.i18n.Tr "cancel"}}</button>
{{end}}
</div>
</div>

View File

@ -4,9 +4,17 @@
{{range $k, $line := $section.Lines}}
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
{{if eq .GetType 4}}
<td colspan="2" class="lines-num">
{{/* {{if gt $j 0}}<span class="fold octicon octicon-fold"></span>{{end}} */}}
</td>
<td colspan="2" class="lines-num">
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
<i class="ui blob-excerpt fa fa-caret-down" data-url="{{$.root.RepoLink}}/blob_excerpt/{{$.root.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=down" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i>
{{end}}
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }}
<i class="ui blob-excerpt fa fa-caret-up" data-url="{{$.root.RepoLink}}/blob_excerpt/{{$.root.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=up" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i>
{{end}}
{{if or (eq $line.GetExpandDirection 2)}}
<i class="ui blob-excerpt octicon octicon-fold" data-url="{{$.root.RepoLink}}/blob_excerpt/{{$.root.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i>
{{end}}
</td>
{{else}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td>

View File

@ -155,8 +155,8 @@
</form>
{{if $.IsStopwatchRunning}}
<div class="ui buttons fluid stop-cancel">
<button onclick="this.disabled=true;toggleStopwatch()" class="ui button stop">{{.i18n.Tr "repo.issues.stop_tracking"}}</button>
<button onclick="this.disabled=true;cancelStopwatch()" class="ui negative button cancel">{{.i18n.Tr "repo.issues.cancel_tracking"}}</button>
<button onclick="this.disabled=true;window.toggleStopwatch()" class="ui button stop">{{.i18n.Tr "repo.issues.stop_tracking"}}</button>
<button onclick="this.disabled=true;window.cancelStopwatch()" class="ui negative button cancel">{{.i18n.Tr "repo.issues.cancel_tracking"}}</button>
</div>
{{else}}
{{if .HasUserStopwatch}}
@ -165,8 +165,8 @@
</div>
{{end}}
<div class="ui buttons two fluid start-add">
<button onclick="this.disabled=true;toggleStopwatch()" class="ui button poping up start" data-content='{{.i18n.Tr "repo.issues.start_tracking"}}' data-position="top center" data-variation="small inverted">{{.i18n.Tr "repo.issues.start_tracking_short"}}</button>
<button onclick="timeAddManual()" class="ui button green poping up add-time" data-content='{{.i18n.Tr "repo.issues.add_time"}}' data-position="top center" data-variation="small inverted">{{.i18n.Tr "repo.issues.add_time_short"}}</button>
<button onclick="this.disabled=true;window.toggleStopwatch()" class="ui button poping up start" data-content='{{.i18n.Tr "repo.issues.start_tracking"}}' data-position="top center" data-variation="small inverted">{{.i18n.Tr "repo.issues.start_tracking_short"}}</button>
<button onclick="window.timeAddManual()" class="ui button green poping up add-time" data-content='{{.i18n.Tr "repo.issues.add_time"}}' data-position="top center" data-variation="small inverted">{{.i18n.Tr "repo.issues.add_time_short"}}</button>
<div class="ui mini modal">
<div class="header">{{.i18n.Tr "repo.issues.add_time"}}</div>
<div class="content">
@ -225,8 +225,8 @@
{{end}}
{{if and .IsIssueWriter (not .Repository.IsArchived)}}
<br/>
<a style="cursor:pointer;" onclick="toggleDeadlineForm();"><i class="edit icon"></i>{{$.i18n.Tr "repo.issues.due_date_form_edit"}}</a> -
<a style="cursor:pointer;" onclick="updateDeadline('');"><i class="remove icon"></i>{{$.i18n.Tr "repo.issues.due_date_form_remove"}}</a>
<a style="cursor:pointer;" onclick="window.toggleDeadlineForm();"><i class="edit icon"></i>{{$.i18n.Tr "repo.issues.due_date_form_edit"}}</a> -
<a style="cursor:pointer;" onclick="window.updateDeadline('');"><i class="remove icon"></i>{{$.i18n.Tr "repo.issues.due_date_form_remove"}}</a>
{{end}}
</p>
{{else}}
@ -235,7 +235,7 @@
{{if and .IsIssueWriter (not .Repository.IsArchived)}}
<div {{if ne .Issue.DeadlineUnix 0}} style="display: none;"{{end}} id="deadlineForm">
<form class="ui fluid action input" action="{{AppSubUrl}}/api/v1/repos/{{.Repository.Owner.Name}}/{{.Repository.Name}}/issues/{{.Issue.Index}}" method="post" id="update-issue-deadline-form" onsubmit="setDeadline();return false;">
<form class="ui fluid action input" action="{{AppSubUrl}}/api/v1/repos/{{.Repository.Owner.Name}}/{{.Repository.Name}}/issues/{{.Issue.Index}}" method="post" id="update-issue-deadline-form" onsubmit="window.setDeadline();return false;">
{{$.CsrfTokenHtml}}
<input required placeholder="{{.i18n.Tr "repo.issues.due_date_form"}}" {{if gt .Issue.DeadlineUnix 0}}value="{{.Issue.DeadlineUnix.Format "2006-01-02"}}"{{end}} type="date" name="deadlineDate" id="deadlineDate">
<button class="ui green icon button">
@ -280,7 +280,7 @@
<div class="text small">{{.Repository.OwnerName}}/{{.Repository.Name}}</div>
<div class="ui transparent label right floated nopadding">
{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
<a class="delete-dependency-button" onclick="deleteDependencyModal({{.Issue.ID}}, 'blocking');"
<a class="delete-dependency-button" onclick="window.deleteDependencyModal({{.Issue.ID}}, 'blocking');"
data-tooltip="{{$.i18n.Tr "repo.issues.dependency.remove_info"}}" data-inverted="">
<i class="delete icon text red nopadding nomargin"></i>
</a>
@ -307,7 +307,7 @@
<div class="text small">{{.Repository.OwnerName}}/{{.Repository.Name}}</div>
<div class="ui transparent label right floated nopadding">
{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
<a class="delete-dependency-button" onclick="deleteDependencyModal({{.Issue.ID}}, 'blockedBy');"
<a class="delete-dependency-button" onclick="window.deleteDependencyModal({{.Issue.ID}}, 'blockedBy');"
data-tooltip="{{$.i18n.Tr "repo.issues.dependency.remove_info"}}" data-inverted="">
<i class="delete icon text red nopadding nomargin"></i>
</a>

View File

@ -65,7 +65,7 @@
title="{{$provider.DisplayName}}{{if eq $provider.Name "openidConnect"}} ({{$key}}){{end}}"
class="{{$provider.Name}}"
src="{{AppSubUrl}}{{$provider.Image}}"
onclick="onOAuthLoginClick()"
onclick="window.onOAuthLoginClick()"
></a>
{{end}}
</div>

View File

@ -61,6 +61,12 @@
{{$.i18n.Tr "action.mirror_sync_create" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 20}}
{{$.i18n.Tr "action.mirror_sync_delete" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 21}}
{{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.approve_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 22}}
{{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.reject_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{end}}
</p>
{{if or (eq .GetOpType 5) (eq .GetOpType 18)}}
@ -80,7 +86,7 @@
<span class="text truncate issue title has-emoji">{{index .GetIssueInfos 1}}</span>
{{else if eq .GetOpType 7}}
<span class="text truncate issue title has-emoji">{{index .GetIssueInfos 1}}</span>
{{else if eq .GetOpType 10}}
{{else if or (eq .GetOpType 10) (eq .GetOpType 21) (eq .GetOpType 22)}}
<a href="{{.GetCommentLink}}" class="text truncate issue title has-emoji">{{.GetIssueTitle}}</a>
<p class="text light grey has-emoji">{{index .GetIssueInfos 1}}</p>
{{else if eq .GetOpType 11}}

15
web_src/js/draw.js Normal file
View File

@ -0,0 +1,15 @@
/* globals gitGraph */
$(() => {
const graphList = [];
if (!document.getElementById('graph-canvas')) {
return;
}
$('#graph-raw-list li span.node-relation').each(function () {
graphList.push($(this).text());
});
gitGraph(document.getElementById('graph-canvas'), graphList);
});

3309
web_src/js/index.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2438,6 +2438,10 @@ tbody.commit-list {
padding-bottom: 8px;
}
td.blob-excerpt {
background-color: #fafafa;
}
.issue-keyword {
border-bottom: 1px dotted #959da5;
display: inline-block;

View File

@ -108,3 +108,26 @@
font: 12px @monospaced-fonts, monospace;
color: rgba(0, 0, 0, 0.87);
}
.ui.fold-code {
margin-right: 1em;
padding-left: 5px;
cursor: pointer;
width: 22px;
font-size: 12px;
}
.ui.fold-code:hover {
color: #428bca;
}
.ui.blob-excerpt {
display: block;
line-height: 20px;
font-size: 16px;
cursor: pointer;
}
.ui.blob-excerpt:hover {
color: #428bca;
}

42
webpack.config.js Normal file
View File

@ -0,0 +1,42 @@
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
index: ['./web_src/js/index', './web_src/js/draw']
},
devtool: 'source-map',
output: {
path: path.resolve(__dirname, 'public/js'),
filename: 'index.js'
},
optimization: {
minimize: true,
minimizer: [new TerserPlugin({
sourceMap: true,
})],
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'entry',
corejs: 3,
}
]
]
}
}
}
]
}
};