code.gitea.io/gitea@v1.22.3/modules/templates/util_misc.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package templates
     5  
     6  import (
     7  	"context"
     8  	"html/template"
     9  	"mime"
    10  	"path/filepath"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	activities_model "code.gitea.io/gitea/models/activities"
    16  	repo_model "code.gitea.io/gitea/models/repo"
    17  	"code.gitea.io/gitea/modules/git"
    18  	giturl "code.gitea.io/gitea/modules/git/url"
    19  	"code.gitea.io/gitea/modules/json"
    20  	"code.gitea.io/gitea/modules/log"
    21  	"code.gitea.io/gitea/modules/repository"
    22  	"code.gitea.io/gitea/modules/svg"
    23  
    24  	"github.com/editorconfig/editorconfig-core-go/v2"
    25  )
    26  
    27  func SortArrow(normSort, revSort, urlSort string, isDefault bool) template.HTML {
    28  	// if needed
    29  	if len(normSort) == 0 || len(urlSort) == 0 {
    30  		return ""
    31  	}
    32  
    33  	if len(urlSort) == 0 && isDefault {
    34  		// if sort is sorted as default add arrow tho this table header
    35  		if isDefault {
    36  			return svg.RenderHTML("octicon-triangle-down", 16)
    37  		}
    38  	} else {
    39  		// if sort arg is in url test if it correlates with column header sort arguments
    40  		// the direction of the arrow should indicate the "current sort order", up means ASC(normal), down means DESC(rev)
    41  		if urlSort == normSort {
    42  			// the table is sorted with this header normal
    43  			return svg.RenderHTML("octicon-triangle-up", 16)
    44  		} else if urlSort == revSort {
    45  			// the table is sorted with this header reverse
    46  			return svg.RenderHTML("octicon-triangle-down", 16)
    47  		}
    48  	}
    49  	// the table is NOT sorted with this header
    50  	return ""
    51  }
    52  
    53  // IsMultilineCommitMessage checks to see if a commit message contains multiple lines.
    54  func IsMultilineCommitMessage(msg string) bool {
    55  	return strings.Count(strings.TrimSpace(msg), "\n") >= 1
    56  }
    57  
    58  // Actioner describes an action
    59  type Actioner interface {
    60  	GetOpType() activities_model.ActionType
    61  	GetActUserName(ctx context.Context) string
    62  	GetRepoUserName(ctx context.Context) string
    63  	GetRepoName(ctx context.Context) string
    64  	GetRepoPath(ctx context.Context) string
    65  	GetRepoLink(ctx context.Context) string
    66  	GetBranch() string
    67  	GetContent() string
    68  	GetCreate() time.Time
    69  	GetIssueInfos() []string
    70  }
    71  
    72  // ActionIcon accepts an action operation type and returns an icon class name.
    73  func ActionIcon(opType activities_model.ActionType) string {
    74  	switch opType {
    75  	case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
    76  		return "repo"
    77  	case activities_model.ActionCommitRepo:
    78  		return "git-commit"
    79  	case activities_model.ActionDeleteBranch:
    80  		return "git-branch"
    81  	case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
    82  		return "git-merge"
    83  	case activities_model.ActionCreatePullRequest:
    84  		return "git-pull-request"
    85  	case activities_model.ActionClosePullRequest:
    86  		return "git-pull-request-closed"
    87  	case activities_model.ActionCreateIssue:
    88  		return "issue-opened"
    89  	case activities_model.ActionCloseIssue:
    90  		return "issue-closed"
    91  	case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
    92  		return "issue-reopened"
    93  	case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
    94  		return "comment-discussion"
    95  	case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
    96  		return "mirror"
    97  	case activities_model.ActionApprovePullRequest:
    98  		return "check"
    99  	case activities_model.ActionRejectPullRequest:
   100  		return "file-diff"
   101  	case activities_model.ActionPublishRelease, activities_model.ActionPushTag, activities_model.ActionDeleteTag:
   102  		return "tag"
   103  	case activities_model.ActionPullReviewDismissed:
   104  		return "x"
   105  	default:
   106  		return "question"
   107  	}
   108  }
   109  
   110  // ActionContent2Commits converts action content to push commits
   111  func ActionContent2Commits(act Actioner) *repository.PushCommits {
   112  	push := repository.NewPushCommits()
   113  
   114  	if act == nil || act.GetContent() == "" {
   115  		return push
   116  	}
   117  
   118  	if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
   119  		log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
   120  	}
   121  
   122  	if push.Len == 0 {
   123  		push.Len = len(push.Commits)
   124  	}
   125  
   126  	return push
   127  }
   128  
   129  // MigrationIcon returns a SVG name matching the service an issue/comment was migrated from
   130  func MigrationIcon(hostname string) string {
   131  	switch hostname {
   132  	case "github.com":
   133  		return "octicon-mark-github"
   134  	default:
   135  		return "gitea-git"
   136  	}
   137  }
   138  
   139  type remoteAddress struct {
   140  	Address  string
   141  	Username string
   142  	Password string
   143  }
   144  
   145  func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
   146  	ret := remoteAddress{}
   147  	remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
   148  	if err != nil {
   149  		log.Error("GetRemoteURL %v", err)
   150  		return ret
   151  	}
   152  
   153  	u, err := giturl.Parse(remoteURL)
   154  	if err != nil {
   155  		log.Error("giturl.Parse %v", err)
   156  		return ret
   157  	}
   158  
   159  	if u.Scheme != "ssh" && u.Scheme != "file" {
   160  		if u.User != nil {
   161  			ret.Username = u.User.Username()
   162  			ret.Password, _ = u.User.Password()
   163  		}
   164  	}
   165  
   166  	// The URL stored in the git repo could contain authentication,
   167  	// erase it, or it will be shown in the UI.
   168  	u.User = nil
   169  	ret.Address = u.String()
   170  	// Why not use m.OriginalURL to set ret.Address?
   171  	// It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository.
   172  	// However, the old code has already stored authentication in m.OriginalURL when updating mirror settings.
   173  	// That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased.
   174  	// Instead of doing this, why not directly use the authentication-erased URL from the Git repository?
   175  	// It should be the same as long as there are no bugs.
   176  
   177  	return ret
   178  }
   179  
   180  func FilenameIsImage(filename string) bool {
   181  	mimeType := mime.TypeByExtension(filepath.Ext(filename))
   182  	return strings.HasPrefix(mimeType, "image/")
   183  }
   184  
   185  func TabSizeClass(ec *editorconfig.Editorconfig, filename string) string {
   186  	if ec != nil {
   187  		def, err := ec.GetDefinitionForFilename(filename)
   188  		if err == nil && def.TabWidth >= 1 && def.TabWidth <= 16 {
   189  			return "tab-size-" + strconv.Itoa(def.TabWidth)
   190  		}
   191  	}
   192  	return "tab-size-4"
   193  }