1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
# frozen_string_literal: true
module Gitlab
module Template
class BaseTemplate
attr_accessor :category
def initialize(path, project = nil, category: nil)
@path = path
@category = category
@project = project
@finder = self.class.finder(project)
end
def name
File.basename(@path, self.class.extension)
end
alias_method :key, :name
def full_name
Pathname.new(@path)
.relative_path_from(self.class.base_dir)
.to_s
end
def content
blob = @finder.read(@path)
[description, blob].compact.join("\n")
end
def description
# override with a comment to be placed at the top of the blob.
end
def project_id
@project&.id
end
# Present for compatibility with license templates, which can replace text
# like `[fullname]` with a user-specified string. This is a no-op for
# other templates
def resolve!(_placeholders = {})
self
end
def to_json(*)
{ key: key, name: name, content: content }
end
def <=>(other)
name <=> other.name
end
class << self
def all(project = nil)
if categories.any?
categories.keys.flat_map { |cat| by_category(cat, project) }
else
by_category("", project)
end
end
def find(key, project = nil)
path = self.finder(project).find(key)
path.present? ? new(path, project) : nil
end
# Set categories as sub directories
# Example: { "category_name_1" => "directory_path_1", "category_name_2" => "directory_name_2" }
# Default is no category with all files in base dir of each class
def categories
{}
end
def extension
raise NotImplementedError
end
def base_dir
raise NotImplementedError
end
# Defines which strategy will be used to get templates files
# RepoTemplateFinder - Finds templates on project repository, templates are filtered per project
# GlobalTemplateFinder - Finds templates on gitlab installation source, templates can be used in all projects
def finder(project = nil)
raise NotImplementedError
end
def by_category(category, project = nil)
directory = category_directory(category)
files = finder(project).list_files_for(directory)
files.map { |f| new(f, project, category: category) }.sort
end
def category_directory(category)
return base_dir unless category.present?
File.join(base_dir, categories[category])
end
# `repository_template_names` - reads through Gitaly the actual templates names within a
# given project's repository. This is only used by issue and merge request templates,
# that need to call this once and then cache the returned value.
#
# `template_names` - is an alias to `repository_template_names`. It would read through
# Gitaly the actual template names within a given project's repository for all file templates
# other than `issue` and `merge request` description templates, which would instead
# overwrite the `template_names` method to return a redis cached version, by reading cached values
# from `repository.issue_template_names_hash` and `repository.merge_request_template_names_hash`
# methods.
def repository_template_names(project)
template_names_by_category(self.all(project))
end
alias_method :template_names, :repository_template_names
def template_names_by_category(items)
grouped = items.group_by(&:category)
categories = grouped.keys
categories.each_with_object({}) do |category, hash|
hash[category] = grouped[category].map do |item|
{ name: item.name, id: item.key, key: item.key, project_id: item.try(:project_id) }
end
end
end
def template_subsets(project = nil)
return [] if project && !project.repository.exists?
if categories.any?
categories.keys.map do |category|
files = self.by_category(category, project)
[category, files.map { |t| { key: t.key, name: t.name, content: t.content } }]
end.to_h
else
files = self.all(project)
files.map { |t| { key: t.key, name: t.name, content: t.content } }
end
end
end
end
end
end
|