@@ -29,6 +29,7 @@ import (
2929 shared_user "code.gitea.io/gitea/routers/web/shared/user"
3030 "code.gitea.io/gitea/services/context"
3131 "code.gitea.io/gitea/services/convert"
32+ "code.gitea.io/gitea/services/export"
3233 issue_service "code.gitea.io/gitea/services/issue"
3334 pull_service "code.gitea.io/gitea/services/pull"
3435)
@@ -258,14 +259,13 @@ func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
258259 return user .ID
259260}
260261
261- // SearchRepoIssuesJSON lists the issues of a repository
262262// This function was copied from API (decouple the web and API routes),
263263// it is only used by frontend to search some dependency or related issues
264- func SearchRepoIssuesJSON (ctx * context.Context ) {
264+ func SearchRepoIssues (ctx * context.Context ) (issues_model. IssueList , int64 ) {
265265 before , since , err := context .GetQueryBeforeSince (ctx .Base )
266266 if err != nil {
267267 ctx .HTTPError (http .StatusUnprocessableEntity , err .Error ())
268- return
268+ return nil , 0
269269 }
270270
271271 var isClosed optional.Option [bool ]
@@ -295,7 +295,7 @@ func SearchRepoIssuesJSON(ctx *context.Context) {
295295 }
296296 if ! issues_model .IsErrMilestoneNotExist (err ) {
297297 ctx .HTTPError (http .StatusInternalServerError , err .Error ())
298- return
298+ return nil , 0
299299 }
300300 id , err := strconv .ParseInt (part [i ], 10 , 64 )
301301 if err != nil {
@@ -329,15 +329,15 @@ func SearchRepoIssuesJSON(ctx *context.Context) {
329329 // FIXME: we should be more efficient here
330330 createdByID := getUserIDForFilter (ctx , "created_by" )
331331 if ctx .Written () {
332- return
332+ return nil , 0
333333 }
334334 assignedByID := getUserIDForFilter (ctx , "assigned_by" )
335335 if ctx .Written () {
336- return
336+ return nil , 0
337337 }
338338 mentionedByID := getUserIDForFilter (ctx , "mentioned_by" )
339339 if ctx .Written () {
340- return
340+ return nil , 0
341341 }
342342
343343 searchOpt := & issue_indexer.SearchOptions {
@@ -380,18 +380,39 @@ func SearchRepoIssuesJSON(ctx *context.Context) {
380380 ids , total , err := issue_indexer .SearchIssues (ctx , searchOpt )
381381 if err != nil {
382382 ctx .HTTPError (http .StatusInternalServerError , "SearchIssues" , err .Error ())
383- return
383+ return nil , 0
384384 }
385385 issues , err := issues_model .GetIssuesByIDs (ctx , ids , true )
386386 if err != nil {
387387 ctx .HTTPError (http .StatusInternalServerError , "FindIssuesByIDs" , err .Error ())
388- return
388+ return nil , 0
389389 }
390390
391+ return issues , total
392+ }
393+
394+ // SearchRepoIssuesJSON lists the issues of a repository
395+ func SearchRepoIssuesJSON (ctx * context.Context ) {
396+ issues , total := SearchRepoIssues (ctx )
397+
391398 ctx .SetTotalCountHeader (total )
392399 ctx .JSON (http .StatusOK , convert .ToIssueList (ctx , ctx .Doer , issues ))
393400}
394401
402+ func ExportIssues (ctx * context.Context ) {
403+ issues , total := SearchRepoIssues (ctx )
404+
405+ if total == 0 {
406+ return
407+ }
408+
409+ f := export .IssuesToExcel (ctx , issues )
410+
411+ ctx .Resp .Header ().Set ("Content-Type" , "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" )
412+ ctx .Resp .Header ().Set ("Content-Disposition" , `attachment; filename="issues.xlsx"` )
413+ _ = f .Write (ctx .Resp )
414+ }
415+
395416func BatchDeleteIssues (ctx * context.Context ) {
396417 issues := getActionIssues (ctx )
397418 if ctx .Written () {
0 commit comments