project_wiki.rb 4.36 KB
Newer Older
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
1
2
3
4
class ProjectWiki
  include Gitlab::ShellAdapter

  MARKUPS = {
5
    'Markdown' => :markdown,
6
7
    'RDoc'     => :rdoc,
    'AsciiDoc' => :asciidoc
Douwe Maan's avatar
Douwe Maan committed
8
  }.freeze unless defined?(MARKUPS)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
9

10
  CouldNotCreateWikiError = Class.new(StandardError)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
11
12
13
14

  # Returns a string describing what went wrong after
  # an operation fails.
  attr_reader :error_message
Valery Sizov's avatar
Valery Sizov committed
15
  attr_reader :project
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
16
17
18
19
20
21

  def initialize(project, user = nil)
    @project = project
    @user = user
  end

Douwe Maan's avatar
Douwe Maan committed
22
23
24
  delegate :empty?, to: :pages
  delegate :repository_storage_path, to: :project

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
25
26
27
28
29
30
31
32
  def path
    @project.path + '.wiki'
  end

  def path_with_namespace
    @project.path_with_namespace + ".wiki"
  end

33
34
35
36
  def web_url
    Gitlab::Routing.url_helpers.namespace_project_wiki_url(@project.namespace, @project, :home)
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
37
38
39
40
41
42
43
44
  def url_to_repo
    gitlab_shell.url_to_repo(path_with_namespace)
  end

  def ssh_url_to_repo
    url_to_repo
  end

45
46
47
48
49
  def http_url_to_repo(user = nil)
    url = "#{Gitlab.config.gitlab.url}/#{path_with_namespace}.git"
    credentials = Gitlab::UrlSanitizer.http_credentials_for_user(user)

    Gitlab::UrlSanitizer.new(url, credentials: credentials).full_url
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
50
51
  end

52
  def wiki_base_path
53
    [Gitlab.config.gitlab.relative_url_root, "/", @project.path_with_namespace, "/wikis"].join('')
54
55
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
56
57
58
59
  # Returns the Gollum::Wiki object.
  def wiki
    @wiki ||= begin
      Gollum::Wiki.new(path_to_repo)
60
    rescue Rugged::OSError
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
61
62
63
64
      create_repo!
    end
  end

65
66
67
68
  def repository_exists?
    !!repository.exists?
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  # Returns an Array of Gitlab WikiPage instances or an
  # empty Array if this Wiki has no pages.
  def pages
    wiki.pages.map { |page| WikiPage.new(self, page, true) }
  end

  # Finds a page within the repository based on a tile
  # or slug.
  #
  # title - The human readable or parameterized title of
  #         the page.
  #
  # Returns an initialized WikiPage instance or nil
  def find_page(title, version = nil)
Marin Jankovski's avatar
Marin Jankovski committed
83
84
    page_title, page_dir = page_title_and_dir(title)
    if page = wiki.page(page_title, version, page_dir)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
85
86
87
88
89
90
      WikiPage.new(self, page, true)
    else
      nil
    end
  end

91
92
93
94
95
96
97
98
99
  def find_file(name, version = nil, try_on_disk = true)
    version = wiki.ref if version.nil? # Gollum::Wiki#file ?
    if wiki_file = wiki.file(name, version, try_on_disk)
      wiki_file
    else
      nil
    end
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
100
101
102
  def create_page(title, content, format = :markdown, message = nil)
    commit = commit_details(:created, message, title)

103
    wiki.write_page(title, format.to_sym, content, commit)
104
105

    update_project_activity
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
106
107
108
109
110
111
112
113
  rescue Gollum::DuplicatePageError => e
    @error_message = "Duplicate page: #{e.message}"
    return false
  end

  def update_page(page, content, format = :markdown, message = nil)
    commit = commit_details(:updated, message, page.title)

114
    wiki.update_page(page, page.name, format.to_sym, content, commit)
115
116

    update_project_activity
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
117
118
119
120
  end

  def delete_page(page, message = nil)
    wiki.delete_page(page, commit_details(:deleted, message, page.title))
121
122

    update_project_activity
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
123
124
  end

Marin Jankovski's avatar
Marin Jankovski committed
125
  def page_title_and_dir(title)
126
    title_array = title.split("/")
Marin Jankovski's avatar
Marin Jankovski committed
127
    title = title_array.pop
Stan Hu's avatar
Stan Hu committed
128
    [title, title_array.join("/")]
Marin Jankovski's avatar
Marin Jankovski committed
129
130
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
131
  def search_files(query)
132
    repository.search_files_by_content(query, default_branch)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
133
134
135
  end

  def repository
136
    @repository ||= Repository.new(path_with_namespace, @project)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
137
138
139
140
141
142
  end

  def default_branch
    wiki.class.default_ref
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
143
144
  def create_repo!
    if init_repo(path_with_namespace)
145
      wiki = Gollum::Wiki.new(path_to_repo)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
146
147
148
    else
      raise CouldNotCreateWikiError
    end
149
150
151
152

    repository.after_create

    wiki
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
153
  end
Gabriel Mazetto's avatar
Gabriel Mazetto committed
154

155
156
157
158
159
160
  def hook_attrs
    {
      web_url: web_url,
      git_ssh_url: ssh_url_to_repo,
      git_http_url: http_url_to_repo,
      path_with_namespace: path_with_namespace,
Gabriel Mazetto's avatar
Gabriel Mazetto committed
161
      default_branch: default_branch
162
163
    }
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
164

165
166
  private

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
167
  def init_repo(path_with_namespace)
168
    gitlab_shell.add_repository(project.repository_storage_path, path_with_namespace)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
169
170
171
172
173
  end

  def commit_details(action, message = nil, title = nil)
    commit_message = message || default_message(action, title)

174
    { email: @user.email, name: @user.name, message: commit_message }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
175
176
177
178
179
180
181
  end

  def default_message(action, title)
    "#{@user.username} #{action} page: #{title}"
  end

  def path_to_repo
182
    @path_to_repo ||= File.join(project.repository_storage_path, "#{path_with_namespace}.git")
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
183
  end
184
185
186
187

  def update_project_activity
    @project.touch(:last_activity_at)
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
188
end