diff --git a/README.md b/README.md index 53c1555..8fc7cec 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,5 @@ To set up the service, build a jar and add a config file as follows to the curre } } ``` -It is also recommended to replace the template file repo/issue/list.tmpl (as described [here](https://docs.gitea.io/en-us/customizing-gitea/#customizing-gitea-pages-and-resources)) -with one that contains a link to the helpdesk. As an example, my own service uses the following line: -```html -Helpdesk**** -``` -added next to the one with repo.issues.new \ No newline at end of file +It is also recommended to adjust the template files used in Gitea to reference the Helpdesk for users. +The templates used on my own instance can be found in templates/ \ No newline at end of file diff --git a/src/main/java/io/gitlab/jfronny/gitea/helpdesk/Main.java b/src/main/java/io/gitlab/jfronny/gitea/helpdesk/Main.java index e753a9c..89952f9 100644 --- a/src/main/java/io/gitlab/jfronny/gitea/helpdesk/Main.java +++ b/src/main/java/io/gitlab/jfronny/gitea/helpdesk/Main.java @@ -25,6 +25,7 @@ public class Main { public static final Pattern MAIL_PATTERN = Pattern.compile("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])"); public static final Pattern HOST_PATTERN = Pattern.compile("(?:[0-9a-zA-Z-._~]+|[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})(?::[0-9]{1,5})?"); public static final Pattern PATH_SEGMENT_PATTERN = Pattern.compile("[a-zA-Z0-9-_]+"); + public static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+"); public static void main(String[] args) throws IOException, SQLException, MessagingException, Config.IllegalConfigException, InterruptedException { GsonHolders.registerSerializer(); diff --git a/src/main/java/io/gitlab/jfronny/gitea/helpdesk/web/WebInterface.java b/src/main/java/io/gitlab/jfronny/gitea/helpdesk/web/WebInterface.java index cdd868e..c41b5d8 100644 --- a/src/main/java/io/gitlab/jfronny/gitea/helpdesk/web/WebInterface.java +++ b/src/main/java/io/gitlab/jfronny/gitea/helpdesk/web/WebInterface.java @@ -18,6 +18,8 @@ public class WebInterface implements AutoCloseable { private static final String TEMPLATE = Main.getResource("/web/template.html"); private static final String ISSUE_MISSING_REPO = Main.getResource("/web/issue/missing_repo.html"); private static final String ISSUE_SUCCESS = Main.getResource("/web/issue/success.html"); + private static final String REPLY_MISSING_REPO = Main.getResource("/web/reply/missing_repo.html"); + private static final String REPLY_SUCCESS = Main.getResource("/web/reply/success.html"); private static final String UNSUBSCRIBE_MISSING_ID = Main.getResource("/web/unsubscribe/missing_id.html"); private static final String UNSUBSCRIBE_FAILURE = Main.getResource("/web/unsubscribe/failure.html"); private static final String UNSUBSCRIBE_SUCCESS = Main.getResource("/web/unsubscribe/success.html"); @@ -96,6 +98,19 @@ public class WebInterface implements AutoCloseable { } return 0; }); + host.addContext("/comment", (req, resp) -> { + Map params = req.getParams(); + String owner = params.get("owner"); + String repo = params.get("repo"); + String issueId = params.get("id"); + if (owner == null || repo == null || !Main.PATH_SEGMENT_PATTERN.matcher(owner).matches() || !Main.PATH_SEGMENT_PATTERN.matcher(repo).matches() || !Main.NUMBER_PATTERN.matcher(issueId).matches()) { + resp.send(404, template(REPLY_MISSING_REPO)); + } else { + String mail = "mailto:" + addr[0] + "+comment+" + owner + '+' + repo + '+' + issueId + '@' + addr[1]; + resp.send(200, template(REPLY_SUCCESS.formatted(escapeHTML(mail)))); + } + return 0; + }); host.addContext("/theme.css", (req, resp) -> { byte[] content = THEME.getBytes(StandardCharsets.UTF_8); resp.sendHeaders(200, content.length, -1, "W/\"" + Integer.toHexString(THEME.hashCode()) + "\"", "text/css; charset=utf-8", null); diff --git a/src/main/resources/web/reply/missing_repo.html b/src/main/resources/web/reply/missing_repo.html new file mode 100644 index 0000000..8b799a5 --- /dev/null +++ b/src/main/resources/web/reply/missing_repo.html @@ -0,0 +1,2 @@ +

Invalid issue

+

Lacking either a proper owner, repo or issue id (plusses are not supported in either)

\ No newline at end of file diff --git a/src/main/resources/web/reply/success.html b/src/main/resources/web/reply/success.html new file mode 100644 index 0000000..1c71a5f --- /dev/null +++ b/src/main/resources/web/reply/success.html @@ -0,0 +1,7 @@ +

Creating a reply

+

By clicking on the following link, you agree that the name you use to send your E-Mail and its content will be displayed in the issue created by mailing.

+

You will receive a timely response containing a link to the issue.

+

Additionally, you will be informed if any comments are posted on the issue and may add your own by replying to them

+

If you wish to unsubscribe, that option will be present in the original reply you receive, but will not be repeated

+

Please note that only plain text messages without attachments are currently supported due to API limitations

+

Create Reply

\ No newline at end of file diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl new file mode 100644 index 0000000..da0de4d --- /dev/null +++ b/templates/repo/issue/list.tmpl @@ -0,0 +1,214 @@ +{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+
+
+ {{template "repo/issue/navbar" .}} +
+
+ {{template "repo/issue/search" .}} +
+ {{if not .Repository.IsArchived}} +
+ {{if .PageIsIssueList}} + {{if .IsSigned}} + {{.i18n.Tr "repo.issues.new"}} + {{end}} + Helpdesk + {{else}} + {{.i18n.Tr "repo.pulls.new"}} + {{end}} +
+ {{else}} + {{if not .PageIsIssueList}} + + {{end}} + {{end}} +
+
+
+
+ {{template "repo/issue/openclose" .}} +
+
+ +
+
+
+
+ {{template "repo/issue/openclose" .}} +
+ {{/* Ten wide does not cope well and makes the columns stack. + This seems to be related to jQuery's hide/show: in fact, switching + issue-actions and issue-filters and having this ten wide will show + this one correctly, but not the other one. */}} +
+ +
+
+ {{template "shared/issuelist" mergeinto . "listType" "repo"}} +
+
+{{template "base/footer" .}} diff --git a/templates/repo/issue/view.tmpl b/templates/repo/issue/view.tmpl new file mode 100644 index 0000000..d1ad136 --- /dev/null +++ b/templates/repo/issue/view.tmpl @@ -0,0 +1,36 @@ +{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+
+
+ {{template "repo/issue/navbar" .}} +
+ {{if and (not .Repository.IsArchived) (not .Issue.IsPull)}} +
+ {{if .PageIsIssueList}} + {{if .IsSigned}} + {{.i18n.Tr "repo.issues.new"}} + {{end}} + Helpdesk + {{else}} + {{.i18n.Tr "repo.pulls.new"}} + {{end}} +
+ {{end}} +
+
+ {{if .Issue.IsPull}} + {{template "repo/issue/view_title" .}} + {{template "repo/pulls/tab_menu" .}} +
+ {{template "repo/issue/view_content" .}} +
+ {{else}} +
+ {{template "repo/issue/view_content" .}} +
+ {{end}} +
+
+{{template "base/footer" .}} diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl new file mode 100644 index 0000000..5eb741f --- /dev/null +++ b/templates/repo/issue/view_content.tmpl @@ -0,0 +1,244 @@ +
+ {{if .Flash}} +
+ {{template "base/alert" .}} +
+ {{end}} + {{if not .Issue.IsPull}} + {{template "repo/issue/view_title" .}} + {{end}} + + + + + + + + + {{ $createdStr:= TimeSinceUnix .Issue.CreatedUnix $.i18n.Lang }} +
+ +
+ {{if .Issue.OriginalAuthor }} + + {{else}} + + {{avatar .Issue.Poster}} + + {{end}} +
+
+
+ {{if .Issue.OriginalAuthor }} + + {{svg (MigrationIcon .Repository.GetOriginalURLHostname)}} + {{ .Issue.OriginalAuthor }} + + + {{ .i18n.Tr "repo.issues.commented_at" (.Issue.HashTag|Escape) $createdStr | Safe }} + + + {{if .Repository.OriginalURL}} ({{$.i18n.Tr "repo.migrated_from" (.Repository.OriginalURL|Escape) (.Repository.GetOriginalURLHostname|Escape) | Safe }}){{end}} + + {{else}} + + {{avatar .Issue.Poster}} + + + {{.Issue.Poster.GetDisplayName}} + {{.i18n.Tr "repo.issues.commented_at" (.Issue.HashTag|Escape) $createdStr | Safe}} + + {{end}} +
+
+ {{if gt .Issue.ShowRole 0}} + {{if (.Issue.ShowRole.HasRole "Writer")}} +
+ {{$.i18n.Tr "repo.issues.collaborator"}} +
+ {{end}} + {{if (.Issue.ShowRole.HasRole "Owner")}} +
+ {{$.i18n.Tr "repo.issues.owner"}} +
+ {{end}} + {{end}} + {{if not $.Repository.IsArchived}} + {{template "repo/issue/view_content/add_reaction" Dict "ctx" $ "ActionURL" (Printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index)}} + {{template "repo/issue/view_content/context_menu" Dict "ctx" $ "item" .Issue "delete" false "issue" true "diff" false "IsCommentPoster" $.IsIssuePoster}} + {{end}} +
+
+
+
+ {{if .Issue.RenderedContent}} + {{.Issue.RenderedContent|Str2html}} + {{else}} + {{.i18n.Tr "repo.issues.no_content"}} + {{end}} +
+
{{.Issue.Content}}
+
+ {{if .Issue.Attachments}} + {{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Issue.Attachments "Content" .Issue.RenderedContent}} + {{end}} +
+ {{$reactions := .Issue.Reactions.GroupByType}} + {{if $reactions}} +
+ {{template "repo/issue/view_content/reactions" Dict "ctx" $ "ActionURL" (Printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) "Reactions" $reactions}} +
+ {{end}} +
+
+ + {{ template "repo/issue/view_content/comments" . }} + + {{if and .Issue.IsPull (not $.Repository.IsArchived)}} + {{ template "repo/issue/view_content/pull". }} + {{end}} + {{if .IsSigned}} + {{ if and (or .IsRepoAdmin .HasIssuesOrPullsWritePermission (not .Issue.IsLocked)) (not .Repository.IsArchived) }} +
+ + {{avatar .SignedUser}} + +
+
+ {{template "repo/issue/comment_tab" .}} + {{.CsrfTokenHtml}} + + +
+
+
+ {{ else if .Repository.IsArchived }} +
+ {{if .Issue.IsPull}} + {{.i18n.Tr "repo.archive.pull.nocomment"}} + {{else}} + {{.i18n.Tr "repo.archive.issue.nocomment"}} + {{end}} +
+ {{ end }} + {{else}} + {{if .Repository.IsArchived}} +
+ {{if .Issue.IsPull}} + {{.i18n.Tr "repo.archive.pull.nocomment"}} + {{else}} + {{.i18n.Tr "repo.archive.issue.nocomment"}} + {{end}} +
+ {{else}} + {{if .IsSigned}} + {{if .Repository.IsArchived}} +
+ + {{avatar .SignedUser}} + +
+
+ {{template "repo/issue/comment_tab" .}} + {{.CsrfTokenHtml}} + + +
+
+
+ {{end}} + {{else}} + {{if .Issue.IsPull}} +
+ {{.i18n.Tr "repo.issues.sign_in_require_desc" (.SignInLink|Escape) | Safe}} +
+ {{else}} + {{.i18n.Tr "repo.issues.create_comment"}} + {{end}} + {{end}} + {{end}} + {{end}} +
+
+ + {{ template "repo/issue/view_content/sidebar" . }} +
+ +
+
+ +
+
+ +
+
+ {{$.i18n.Tr "loading"}} +
+
+ {{if .IsAttachmentEnabled}} +
+ {{template "repo/upload" .}} +
+ {{end}} + +
+
+ +{{template "repo/issue/view_content/reference_issue_dialog" .}} + +
+ {{.i18n.Tr "repo.issues.no_content"}} +
+ +