Commit 71ab011a1711db9a1a9ced2c2c92c8427ae6f624
Exists in
master
and in
4 other branches
Merge branch 'use_gollum_wikis' of https://github.com/DanKnox/gitlabhq into DanKnox-use_gollum_wikis
Conflicts: app/views/layouts/project_resource.html.haml app/views/wikis/edit.html.haml app/views/wikis/pages.html.haml app/views/wikis/show.html.haml spec/features/gitlab_flavored_markdown_spec.rb
Showing
30 changed files
with
1252 additions
and
86 deletions
Show diff stats
Gemfile
... | ... | @@ -99,6 +99,13 @@ gem "colored" |
99 | 99 | # GitLab settings |
100 | 100 | gem 'settingslogic' |
101 | 101 | |
102 | +# Wiki | |
103 | +# - Use latest master to resolve Gem dependency with Pygemnts | |
104 | +# github-linquist needs pygments 0.4.2 but Gollum 2.4.11 | |
105 | +# requires pygments 0.3.2. The latest master Gollum has been updated | |
106 | +# to use pygments 0.4.2. Change this after next Gollum release. | |
107 | +gem "gollum", "~> 2.4.0", git: "git://github.com/github/gollum.git" | |
108 | + | |
102 | 109 | # Misc |
103 | 110 | gem "foreman" |
104 | 111 | gem "git" | ... | ... |
Gemfile.lock
1 | 1 | GIT |
2 | + remote: git://github.com/github/gollum.git | |
3 | + revision: 544d499ab170c9d9b355b7a0160afc74139ee2a4 | |
4 | + specs: | |
5 | + gollum (2.4.11) | |
6 | + github-markdown (~> 0.5.3) | |
7 | + github-markup (>= 0.7.5, < 1.0.0) | |
8 | + grit (~> 2.5.0) | |
9 | + mustache (>= 0.99.4, < 1.0.0) | |
10 | + nokogiri (~> 1.5.6) | |
11 | + pygments.rb (~> 0.4.2) | |
12 | + sanitize (~> 2.0.3) | |
13 | + sinatra (~> 1.3.5) | |
14 | + stringex (~> 1.5.1) | |
15 | + useragent (~> 0.4.16) | |
16 | + | |
17 | +GIT | |
2 | 18 | remote: https://github.com/ctran/annotate_models.git |
3 | 19 | revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e |
4 | 20 | specs: |
... | ... | @@ -145,6 +161,7 @@ GEM |
145 | 161 | escape_utils (~> 0.2.3) |
146 | 162 | mime-types (~> 1.19) |
147 | 163 | pygments.rb (>= 0.2.13) |
164 | + github-markdown (0.5.3) | |
148 | 165 | github-markup (0.7.5) |
149 | 166 | gitlab-grack (1.0.0) |
150 | 167 | rack (~> 1.4.1) |
... | ... | @@ -176,6 +193,10 @@ GEM |
176 | 193 | grape-entity (0.2.0) |
177 | 194 | activesupport |
178 | 195 | multi_json (>= 1.3.2) |
196 | + grit (2.5.0) | |
197 | + diff-lcs (~> 1.1) | |
198 | + mime-types (~> 1.15) | |
199 | + posix-spawn (~> 0.3.6) | |
179 | 200 | grit_ext (0.6.2) |
180 | 201 | charlock_holmes (~> 0.6.9) |
181 | 202 | growl (1.0.3) |
... | ... | @@ -237,7 +258,8 @@ GEM |
237 | 258 | sprockets (~> 2.0) |
238 | 259 | multi_json (1.6.1) |
239 | 260 | multi_xml (0.5.3) |
240 | - multipart-post (1.2.0) | |
261 | + multipart-post (1.1.5) | |
262 | + mustache (0.99.4) | |
241 | 263 | mysql2 (0.3.11) |
242 | 264 | net-ldap (0.2.2) |
243 | 265 | nokogiri (1.5.6) |
... | ... | @@ -373,6 +395,8 @@ GEM |
373 | 395 | rspec-mocks (~> 2.12.0) |
374 | 396 | rubyntlm (0.1.1) |
375 | 397 | rubyzip (0.9.9) |
398 | + sanitize (2.0.3) | |
399 | + nokogiri (>= 1.4.4, < 1.6) | |
376 | 400 | sass (3.2.5) |
377 | 401 | sass-rails (3.2.5) |
378 | 402 | railties (~> 3.2.0) |
... | ... | @@ -429,6 +453,7 @@ GEM |
429 | 453 | tilt (~> 1.1, != 1.3.0) |
430 | 454 | stamp (0.5.0) |
431 | 455 | state_machine (1.1.2) |
456 | + stringex (1.5.1) | |
432 | 457 | temple (0.5.5) |
433 | 458 | test_after_commit (0.0.1) |
434 | 459 | therubyracer (0.10.2) |
... | ... | @@ -451,6 +476,7 @@ GEM |
451 | 476 | kgio (~> 2.6) |
452 | 477 | rack |
453 | 478 | raindrops (~> 0.7) |
479 | + useragent (0.4.16) | |
454 | 480 | virtus (0.5.4) |
455 | 481 | backports (~> 2.6.1) |
456 | 482 | descendants_tracker (~> 0.0.1) |
... | ... | @@ -499,6 +525,7 @@ DEPENDENCIES |
499 | 525 | gitlab_meta (= 5.0) |
500 | 526 | gitlab_omniauth-ldap (= 1.0.2) |
501 | 527 | gitlab_yaml_db (= 1.0.0) |
528 | + gollum (~> 2.4.0)! | |
502 | 529 | gon |
503 | 530 | grape (~> 0.3.1) |
504 | 531 | grape-entity (~> 0.2.0) | ... | ... |
app/assets/stylesheets/application.scss
app/controllers/wikis_controller.rb
... | ... | @@ -2,58 +2,94 @@ class WikisController < ProjectResourceController |
2 | 2 | before_filter :authorize_read_wiki! |
3 | 3 | before_filter :authorize_write_wiki!, only: [:edit, :create, :history] |
4 | 4 | before_filter :authorize_admin_wiki!, only: :destroy |
5 | + before_filter :load_gollum_wiki | |
5 | 6 | |
6 | 7 | def pages |
7 | - @wiki_pages = @project.wikis.group(:slug).ordered | |
8 | + @wiki_pages = @gollum_wiki.pages | |
8 | 9 | end |
9 | 10 | |
10 | 11 | def show |
11 | - @most_recent_wiki = @project.wikis.where(slug: params[:id]).ordered.first | |
12 | - if params[:version_id] | |
13 | - @wiki = @project.wikis.find(params[:version_id]) | |
14 | - else | |
15 | - @wiki = @most_recent_wiki | |
16 | - end | |
12 | + @wiki = @gollum_wiki.find_page(params[:id], params[:version_id]) | |
17 | 13 | |
18 | 14 | if @wiki |
19 | 15 | render 'show' |
20 | 16 | else |
21 | - if can?(current_user, :write_wiki, @project) | |
22 | - @wiki = @project.wikis.new(slug: params[:id]) | |
23 | - render 'edit' | |
24 | - else | |
25 | - render 'empty' | |
26 | - end | |
17 | + return render('empty') unless can?(current_user, :write_wiki, @project) | |
18 | + @wiki = WikiPage.new(@gollum_wiki) | |
19 | + @wiki.title = params[:id] | |
20 | + | |
21 | + render 'edit' | |
27 | 22 | end |
28 | 23 | end |
29 | 24 | |
30 | 25 | def edit |
31 | - @wiki = @project.wikis.where(slug: params[:id]).ordered.first | |
32 | - @wiki = Wiki.regenerate_from @wiki | |
26 | + @wiki = @gollum_wiki.find_page(params[:id]) | |
27 | + end | |
28 | + | |
29 | + def update | |
30 | + @wiki = @gollum_wiki.find_page(params[:id]) | |
31 | + | |
32 | + return render('empty') unless can?(current_user, :write_wiki, @project) | |
33 | + | |
34 | + if @wiki.update(content, format, message) | |
35 | + redirect_to [@project, @wiki], notice: 'Wiki was successfully updated.' | |
36 | + else | |
37 | + render 'edit' | |
38 | + end | |
33 | 39 | end |
34 | 40 | |
35 | 41 | def create |
36 | - @wiki = @project.wikis.new(params[:wiki]) | |
37 | - @wiki.user = current_user | |
38 | - | |
39 | - respond_to do |format| | |
40 | - if @wiki.save | |
41 | - format.html { redirect_to [@project, @wiki], notice: 'Wiki was successfully updated.' } | |
42 | - else | |
43 | - format.html { render action: "edit" } | |
44 | - end | |
42 | + @wiki = WikiPage.new(@gollum_wiki) | |
43 | + | |
44 | + if @wiki.create(wiki_params) | |
45 | + redirect_to project_wiki_path(@project, @wiki), notice: 'Wiki was successfully updated.' | |
46 | + else | |
47 | + render action: "edit" | |
45 | 48 | end |
46 | 49 | end |
47 | 50 | |
48 | 51 | def history |
49 | - @wiki_pages = @project.wikis.where(slug: params[:id]).ordered | |
52 | + unless @wiki = @gollum_wiki.find_page(params[:id]) | |
53 | + redirect_to project_wiki_path(@project, :home), notice: "Page not found" | |
54 | + end | |
50 | 55 | end |
51 | 56 | |
52 | 57 | def destroy |
53 | - @wikis = @project.wikis.where(slug: params[:id]).delete_all | |
58 | + @wiki = @gollum_wiki.find_page(params[:id]) | |
59 | + @wiki.delete if @wiki | |
60 | + redirect_to project_wiki_path(@project, :home), notice: "Page was successfully deleted" | |
61 | + end | |
54 | 62 | |
55 | - respond_to do |format| | |
56 | - format.html { redirect_to project_wiki_path(@project, :index), notice: "Page was successfully deleted" } | |
57 | - end | |
63 | + def git_access | |
58 | 64 | end |
65 | + | |
66 | + private | |
67 | + | |
68 | + def load_gollum_wiki | |
69 | + @gollum_wiki = GollumWiki.new(@project, current_user) | |
70 | + | |
71 | + # Call #wiki to make sure the Wiki Repo is initialized | |
72 | + @gollum_wiki.wiki | |
73 | + rescue GollumWiki::CouldNotCreateWikiError => ex | |
74 | + flash[:notice] = "Could not create Wiki Repository at this time. Please try again later." | |
75 | + redirect_to @project | |
76 | + return false | |
77 | + end | |
78 | + | |
79 | + def wiki_params | |
80 | + params[:wiki].slice(:title, :content, :format, :message) | |
81 | + end | |
82 | + | |
83 | + def content | |
84 | + params[:wiki][:content] | |
85 | + end | |
86 | + | |
87 | + def format | |
88 | + params[:wiki][:format] | |
89 | + end | |
90 | + | |
91 | + def message | |
92 | + params[:wiki][:message] | |
93 | + end | |
94 | + | |
59 | 95 | end | ... | ... |
app/helpers/gitlab_markdown_helper.rb
... | ... | @@ -49,4 +49,12 @@ module GitlabMarkdownHelper |
49 | 49 | |
50 | 50 | @markdown.render(text).html_safe |
51 | 51 | end |
52 | + | |
53 | + def render_wiki_content(wiki_page) | |
54 | + if wiki_page.format == :markdown | |
55 | + markdown(wiki_page.content) | |
56 | + else | |
57 | + wiki_page.formatted_content.html_safe | |
58 | + end | |
59 | + end | |
52 | 60 | end | ... | ... |
... | ... | @@ -0,0 +1,118 @@ |
1 | +class GollumWiki | |
2 | + | |
3 | + MARKUPS = { | |
4 | + "Markdown" => :markdown, | |
5 | + "RDoc" => :rdoc | |
6 | + } | |
7 | + | |
8 | + class CouldNotCreateWikiError < StandardError; end | |
9 | + | |
10 | + # Returns a string describing what went wrong after | |
11 | + # an operation fails. | |
12 | + attr_reader :error_message | |
13 | + | |
14 | + def initialize(project, user = nil) | |
15 | + @project = project | |
16 | + @user = user | |
17 | + end | |
18 | + | |
19 | + def path_with_namespace | |
20 | + @project.path_with_namespace + ".wiki" | |
21 | + end | |
22 | + | |
23 | + def url_to_repo | |
24 | + gitlab_shell.url_to_repo(path_with_namespace) | |
25 | + end | |
26 | + | |
27 | + def ssh_url_to_repo | |
28 | + url_to_repo | |
29 | + end | |
30 | + | |
31 | + def http_url_to_repo | |
32 | + http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') | |
33 | + end | |
34 | + | |
35 | + # Returns the Gollum::Wiki object. | |
36 | + def wiki | |
37 | + @wiki ||= begin | |
38 | + Gollum::Wiki.new(path_to_repo) | |
39 | + rescue Grit::NoSuchPathError | |
40 | + create_repo! | |
41 | + end | |
42 | + end | |
43 | + | |
44 | + # Returns an Array of Gitlab WikiPage instances or an | |
45 | + # empty Array if this Wiki has no pages. | |
46 | + def pages | |
47 | + wiki.pages.map { |page| WikiPage.new(self, page, true) } | |
48 | + end | |
49 | + | |
50 | + # Returns the last 30 Commit objects accross the entire | |
51 | + # repository. | |
52 | + def recent_history | |
53 | + Commit.fresh_commits(wiki.repo, 30) | |
54 | + end | |
55 | + | |
56 | + # Finds a page within the repository based on a tile | |
57 | + # or slug. | |
58 | + # | |
59 | + # title - The human readable or parameterized title of | |
60 | + # the page. | |
61 | + # | |
62 | + # Returns an initialized WikiPage instance or nil | |
63 | + def find_page(title, version = nil) | |
64 | + if page = wiki.page(title, version) | |
65 | + WikiPage.new(self, page, true) | |
66 | + else | |
67 | + nil | |
68 | + end | |
69 | + end | |
70 | + | |
71 | + def create_page(title, content, format = :markdown, message = nil) | |
72 | + commit = commit_details(:created, message, title) | |
73 | + | |
74 | + wiki.write_page(title, format, content, commit) | |
75 | + rescue Gollum::DuplicatePageError => e | |
76 | + @error_message = "Duplicate page: #{e.message}" | |
77 | + return false | |
78 | + end | |
79 | + | |
80 | + def update_page(page, content, format = :markdown, message = nil) | |
81 | + commit = commit_details(:updated, message, page.title) | |
82 | + | |
83 | + wiki.update_page(page, page.name, format, content, commit) | |
84 | + end | |
85 | + | |
86 | + def delete_page(page, message = nil) | |
87 | + wiki.delete_page(page, commit_details(:deleted, message, page.title)) | |
88 | + end | |
89 | + | |
90 | + private | |
91 | + | |
92 | + def create_repo! | |
93 | + if gitlab_shell.add_repository(path_with_namespace) | |
94 | + Gollum::Wiki.new(path_to_repo) | |
95 | + else | |
96 | + raise CouldNotCreateWikiError | |
97 | + end | |
98 | + end | |
99 | + | |
100 | + def commit_details(action, message = nil, title = nil) | |
101 | + commit_message = message || default_message(action, title) | |
102 | + | |
103 | + {email: @user.email, name: @user.name, message: commit_message} | |
104 | + end | |
105 | + | |
106 | + def default_message(action, title) | |
107 | + "#{@user.username} #{action} page: #{title}" | |
108 | + end | |
109 | + | |
110 | + def gitlab_shell | |
111 | + @gitlab_shell ||= Gitlab::Shell.new | |
112 | + end | |
113 | + | |
114 | + def path_to_repo | |
115 | + @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git") | |
116 | + end | |
117 | + | |
118 | +end | ... | ... |
... | ... | @@ -0,0 +1,181 @@ |
1 | +class WikiPage | |
2 | + include ActiveModel::Validations | |
3 | + include ActiveModel::Conversion | |
4 | + include StaticModel | |
5 | + extend ActiveModel::Naming | |
6 | + | |
7 | + def self.primary_key | |
8 | + 'slug' | |
9 | + end | |
10 | + | |
11 | + def self.model_name | |
12 | + ActiveModel::Name.new(self, nil, 'wiki') | |
13 | + end | |
14 | + | |
15 | + def to_key | |
16 | + [:slug] | |
17 | + end | |
18 | + | |
19 | + validates :title, presence: true | |
20 | + validates :content, presence: true | |
21 | + | |
22 | + # The Gitlab GollumWiki instance. | |
23 | + attr_reader :wiki | |
24 | + | |
25 | + # The raw Gollum::Page instance. | |
26 | + attr_reader :page | |
27 | + | |
28 | + # The attributes Hash used for storing and validating | |
29 | + # new Page values before writing to the Gollum repository. | |
30 | + attr_accessor :attributes | |
31 | + | |
32 | + def initialize(wiki, page = nil, persisted = false) | |
33 | + @wiki = wiki | |
34 | + @page = page | |
35 | + @persisted = persisted | |
36 | + @attributes = {}.with_indifferent_access | |
37 | + | |
38 | + set_attributes if persisted? | |
39 | + end | |
40 | + | |
41 | + # The escaped URL path of this page. | |
42 | + def slug | |
43 | + @attributes[:slug] | |
44 | + end | |
45 | + | |
46 | + alias :to_param :slug | |
47 | + | |
48 | + # The formatted title of this page. | |
49 | + def title | |
50 | + @attributes[:title] || "" | |
51 | + end | |
52 | + | |
53 | + # Sets the title of this page. | |
54 | + def title=(new_title) | |
55 | + @attributes[:title] = new_title | |
56 | + end | |
57 | + | |
58 | + # The raw content of this page. | |
59 | + def content | |
60 | + @attributes[:content] | |
61 | + end | |
62 | + | |
63 | + # The processed/formatted content of this page. | |
64 | + def formatted_content | |
65 | + @attributes[:formatted_content] | |
66 | + end | |
67 | + | |
68 | + # The markup format for the page. | |
69 | + def format | |
70 | + @attributes[:format] || :markdown | |
71 | + end | |
72 | + | |
73 | + # The commit message for this page version. | |
74 | + def message | |
75 | + version.try(:message) | |
76 | + end | |
77 | + | |
78 | + # The Gitlab Commit instance for this page. | |
79 | + def version | |
80 | + return nil unless persisted? | |
81 | + | |
82 | + @version ||= Commit.new(@page.version) | |
83 | + end | |
84 | + | |
85 | + # Returns an array of Gitlab Commit instances. | |
86 | + def versions | |
87 | + return [] unless persisted? | |
88 | + | |
89 | + @page.versions.map { |v| Commit.new(v) } | |
90 | + end | |
91 | + | |
92 | + # Returns the Date that this latest version was | |
93 | + # created on. | |
94 | + def created_at | |
95 | + @page.version.date | |
96 | + end | |
97 | + | |
98 | + # Returns boolean True or False if this instance | |
99 | + # is an old version of the page. | |
100 | + def historical? | |
101 | + @page.historical? | |
102 | + end | |
103 | + | |
104 | + # Returns boolean True or False if this instance | |
105 | + # has been fully saved to disk or not. | |
106 | + def persisted? | |
107 | + @persisted == true | |
108 | + end | |
109 | + | |
110 | + # Creates a new Wiki Page. | |
111 | + # | |
112 | + # attr - Hash of attributes to set on the new page. | |
113 | + # :title - The title for the new page. | |
114 | + # :content - The raw markup content. | |
115 | + # :format - Optional symbol representing the | |
116 | + # content format. Can be any type | |
117 | + # listed in the GollumWiki::MARKUPS | |
118 | + # Hash. | |
119 | + # :message - Optional commit message to set on | |
120 | + # the new page. | |
121 | + # | |
122 | + # Returns the String SHA1 of the newly created page | |
123 | + # or False if the save was unsuccessful. | |
124 | + def create(attr = {}) | |
125 | + @attributes.merge!(attr) | |
126 | + | |
127 | + save :create_page, title, content, format, message | |
128 | + end | |
129 | + | |
130 | + # Updates an existing Wiki Page, creating a new version. | |
131 | + # | |
132 | + # new_content - The raw markup content to replace the existing. | |
133 | + # format - Optional symbol representing the content format. | |
134 | + # See GollumWiki::MARKUPS Hash for available formats. | |
135 | + # message - Optional commit message to set on the new version. | |
136 | + # | |
137 | + # Returns the String SHA1 of the newly created page | |
138 | + # or False if the save was unsuccessful. | |
139 | + def update(new_content = "", format = :markdown, message = nil) | |
140 | + @attributes[:content] = new_content | |
141 | + @attributes[:format] = format | |
142 | + | |
143 | + save :update_page, @page, content, format, message | |
144 | + end | |
145 | + | |
146 | + # Destroys the WIki Page. | |
147 | + # | |
148 | + # Returns boolean True or False. | |
149 | + def delete | |
150 | + if wiki.delete_page(@page) | |
151 | + true | |
152 | + else | |
153 | + false | |
154 | + end | |
155 | + end | |
156 | + | |
157 | + private | |
158 | + | |
159 | + def set_attributes | |
160 | + attributes[:slug] = @page.escaped_url_path | |
161 | + attributes[:title] = @page.title | |
162 | + attributes[:content] = @page.raw_data | |
163 | + attributes[:formatted_content] = @page.formatted_data | |
164 | + attributes[:format] = @page.format | |
165 | + end | |
166 | + | |
167 | + def save(method, *args) | |
168 | + if valid? && wiki.send(method, *args) | |
169 | + @page = wiki.wiki.paged(title) | |
170 | + | |
171 | + set_attributes | |
172 | + | |
173 | + @persisted = true | |
174 | + else | |
175 | + errors.add(:base, wiki.error_message) if wiki.error_message | |
176 | + @persisted = false | |
177 | + end | |
178 | + @persisted | |
179 | + end | |
180 | + | |
181 | +end | ... | ... |
app/observers/project_observer.rb
... | ... | @@ -18,6 +18,11 @@ class ProjectObserver < ActiveRecord::Observer |
18 | 18 | project.path_with_namespace |
19 | 19 | ) |
20 | 20 | |
21 | + GitlabShellWorker.perform_async( | |
22 | + :remove_repository, | |
23 | + project.path_with_namespace + ".wiki" | |
24 | + ) | |
25 | + | |
21 | 26 | project.satellite.destroy |
22 | 27 | |
23 | 28 | log_info("Project \"#{project.name}\" was removed") | ... | ... |
app/views/layouts/project_resource.html.haml
... | ... | @@ -36,7 +36,7 @@ |
36 | 36 | %span.count.merge_counter= @project.merge_requests.opened.count |
37 | 37 | |
38 | 38 | = nav_link(html_options: {class: "#{project_wiki_tab_class}"}) do |
39 | - = link_to 'Wiki', project_wiki_path(@project, :index) | |
39 | + = link_to 'Wiki', project_wiki_path(@project, :home) | |
40 | 40 | |
41 | 41 | - if can? current_user, :admin_project, @project |
42 | 42 | = nav_link(html_options: {class: "#{project_tab_class}"}) do | ... | ... |
app/views/wikis/_form.html.haml
... | ... | @@ -8,9 +8,12 @@ |
8 | 8 | |
9 | 9 | .ui-box.ui-box-show |
10 | 10 | .ui-box-head |
11 | - = f.label :title | |
12 | - .input= f.text_field :title, class: 'span8' | |
13 | - = f.hidden_field :slug | |
11 | + %h3.page_title | |
12 | + .edit-wiki-header | |
13 | + = @wiki.title.titleize | |
14 | + = f.hidden_field :title, value: @wiki.title | |
15 | + = f.select :format, options_for_select(GollumWiki::MARKUPS, {selected: @wiki.format}), {}, class: "pull-right input-medium" | |
16 | + = f.label :format, class: "pull-right", style: "padding-right: 20px;" | |
14 | 17 | .ui-box-body |
15 | 18 | .input |
16 | 19 | %span.cgray |
... | ... | @@ -22,6 +25,9 @@ |
22 | 25 | .ui-box-bottom |
23 | 26 | = f.label :content |
24 | 27 | .input= f.text_area :content, class: 'span8 js-gfm-input' |
28 | + .ui-box-bottom | |
29 | + = f.label :commit_message | |
30 | + .input= f.text_field :message, class: 'span8' | |
25 | 31 | .actions |
26 | 32 | = f.submit 'Save', class: "btn-save btn" |
27 | 33 | = link_to "Cancel", project_wiki_path(@project, :index), class: "btn btn-cancel" | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +%span.pull-right | |
2 | + = link_to project_wiki_path(@project, :home), class: "btn btn-small grouped" do | |
3 | + Home | |
4 | + = link_to pages_project_wikis_path(@project), class: "btn btn-small grouped" do | |
5 | + Pages | |
6 | + - if (@wiki && @wiki.persisted?) | |
7 | + = link_to history_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do | |
8 | + History | |
9 | + - if can?(current_user, :write_wiki, @project) | |
10 | + - if @wiki && @wiki.persisted? | |
11 | + = link_to edit_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do | |
12 | + %i.icon-edit | |
13 | + Edit | |
14 | + = link_to git_access_project_wikis_path(@project), class: "btn btn-small grouped" do | |
15 | + %i.icon-download-alt | |
16 | + Git Access | ... | ... |
app/views/wikis/edit.html.haml
1 | -%h3.page_title Editing page | |
1 | +%h3.page_title | |
2 | + Editing page | |
3 | + = render partial: 'main_links' | |
2 | 4 | = render 'form' |
3 | 5 | |
4 | 6 | .pull-right |
5 | - - if can? current_user, :admin_wiki, @project | |
7 | + - if @wiki.persisted? && can?(current_user, :admin_wiki, @project) | |
6 | 8 | = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete, class: "btn btn-small btn-remove" do |
7 | 9 | Delete this page | ... | ... |
... | ... | @@ -0,0 +1,36 @@ |
1 | +%h3.page_title | |
2 | + Git Access | |
3 | + %strong= @gollum_wiki.path_with_namespace | |
4 | + = render partial: 'main_links' | |
5 | + | |
6 | +%br | |
7 | +.content | |
8 | + .project_clone_panel | |
9 | + .row | |
10 | + .span7 | |
11 | + .form-horizontal | |
12 | + .input-prepend.project_clone_holder | |
13 | + %button{class: "btn active", :"data-clone" => @gollum_wiki.ssh_url_to_repo} SSH | |
14 | + %button{class: "btn", :"data-clone" => @gollum_wiki.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase | |
15 | + = text_field_tag :project_clone, @gollum_wiki.url_to_repo, class: "one_click_select input-xxlarge", readonly: true | |
16 | + .git-empty | |
17 | + %fieldset | |
18 | + %legend Install Gollum: | |
19 | + %pre.dark | |
20 | + :preserve | |
21 | + gem install gollum | |
22 | + | |
23 | + %legend Clone Your Wiki: | |
24 | + %pre.dark | |
25 | + :preserve | |
26 | + git clone #{@gollum_wiki.path_with_namespace}.git | |
27 | + cd #{@gollum_wiki.path_with_namespace} | |
28 | + | |
29 | + %legend Start Gollum And Edit Locally: | |
30 | + %pre.dark | |
31 | + :preserve | |
32 | + gollum | |
33 | + == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin | |
34 | + >> Thin web server (v1.5.0 codename Knife) | |
35 | + >> Maximum connections set to 1024 | |
36 | + >> Listening on 0.0.0.0:4567, CTRL+C to stop | ... | ... |
app/views/wikis/history.html.haml
1 | 1 | %h3.page_title |
2 | 2 | %span.cgray History for |
3 | - = @wiki_pages.first.title | |
3 | + = @wiki.title.titleize | |
4 | + = render partial: 'main_links' | |
4 | 5 | %br |
5 | 6 | %table |
6 | 7 | %thead |
7 | 8 | %tr |
8 | 9 | %th Page version |
10 | + %th Author | |
11 | + %th Commit Message | |
9 | 12 | %th Last updated |
10 | - %th Updated by | |
13 | + %th Format | |
11 | 14 | %tbody |
12 | - - @wiki_pages.each_with_index do |wiki_page, i| | |
15 | + - @wiki.versions.each do |version| | |
16 | + - commit = CommitDecorator.new(version) | |
13 | 17 | %tr |
14 | 18 | %td |
15 | - %strong | |
16 | - = link_to project_wiki_path(@project, wiki_page, version_id: wiki_page.id) do | |
17 | - Version | |
18 | - = @wiki_pages.count - i | |
19 | + = link_to project_wiki_path(@project, @wiki, version_id: commit.id) do | |
20 | + = commit.short_id | |
21 | + %td= commit.author_link avatar: true, size: 24 | |
22 | + %td | |
23 | + = commit.title | |
19 | 24 | %td |
20 | - = wiki_page.created_at.to_s(:short) | |
21 | - (#{time_ago_in_words(wiki_page.created_at)} | |
22 | - ago) | |
23 | - %td= link_to_member(@project, wiki_page.user) | |
25 | + = time_ago_in_words(version.date) | |
26 | + ago | |
27 | + %td | |
28 | + %strong | |
29 | + = @wiki.page.wiki.page(@wiki.page.name, commit.id).try(:format) | ... | ... |
app/views/wikis/pages.html.haml
1 | 1 | = render 'wikis/nav' |
2 | -%h3.page_title All Pages | |
2 | +%h3.page_title | |
3 | + All Pages | |
4 | + = render partial: 'main_links' | |
3 | 5 | %br |
4 | 6 | %table |
5 | 7 | %thead |
6 | 8 | %tr |
7 | 9 | %th Title |
8 | - %th Slug | |
10 | + %th Format | |
9 | 11 | %th Last updated |
10 | 12 | %th Updated by |
11 | 13 | %tbody |
12 | 14 | - @wiki_pages.each do |wiki_page| |
13 | 15 | %tr |
14 | 16 | %td |
15 | - %strong= link_to wiki_page.title, project_wiki_path(@project, wiki_page) | |
16 | - %td= wiki_page.slug | |
17 | + %strong= link_to wiki_page.title.titleize, project_wiki_path(@project, wiki_page) | |
18 | + %td | |
19 | + %strong= wiki_page.format | |
17 | 20 | %td |
18 | 21 | = wiki_page.created_at.to_s(:short) do |
19 | 22 | (#{time_ago_in_words(wiki_page.created_at)} |
20 | 23 | ago) |
21 | - %td= link_to_member(@project, wiki_page.user) | |
24 | + - commit = CommitDecorator.decorate(wiki_page.version) | |
25 | + %td= commit.author_link avatar: true, size: 24 | ... | ... |
app/views/wikis/show.html.haml
1 | 1 | = render 'wikis/nav' |
2 | -- if @wiki != @most_recent_wiki | |
3 | - .alert | |
2 | +%h3.page_title | |
3 | + = @wiki.title.titleize | |
4 | + = render partial: 'main_links' | |
5 | +%br | |
6 | +- if @wiki.historical? | |
7 | + .warning_message | |
4 | 8 | This is an old version of this page. |
5 | 9 | You can view the #{link_to "most recent version", project_wiki_path(@project, @wiki)} or browse the #{link_to "history", history_project_wiki_path(@project, @wiki)}. |
6 | 10 | |
... | ... | @@ -19,6 +23,7 @@ |
19 | 23 | |
20 | 24 | .file_content.wiki |
21 | 25 | = preserve do |
22 | - = markdown @wiki.content | |
26 | + = render_wiki_content(@wiki) | |
23 | 27 | |
24 | -%p.time Last edited by #{link_to_member @project, @wiki.user}, #{time_ago_in_words @wiki.created_at} ago | |
28 | +- commit = CommitDecorator.new(@wiki.version) | |
29 | +%p.time Last edited by #{commit.author_link(avatar: true, size: 16)} #{time_ago_in_words @wiki.created_at} ago | ... | ... |
config/routes.rb
features/project/wiki.feature
... | ... | @@ -5,5 +5,32 @@ Feature: Project Wiki |
5 | 5 | Given I visit project wiki page |
6 | 6 | |
7 | 7 | Scenario: Add new page |
8 | - Given I create Wiki page | |
9 | - Then I should see newly created wiki page | |
8 | + Given I create the Wiki Home page | |
9 | + Then I should see the newly created wiki page | |
10 | + | |
11 | + Scenario: Edit existing page | |
12 | + Given I have an existing Wiki page | |
13 | + And I browse to that Wiki page | |
14 | + And I click on the Edit button | |
15 | + And I change the content | |
16 | + Then I should see the updated content | |
17 | + | |
18 | + Scenario: View page history | |
19 | + Given I have an existing wiki page | |
20 | + And That page has two revisions | |
21 | + And I browse to that Wiki page | |
22 | + And I click the History button | |
23 | + Then I should see both revisions | |
24 | + | |
25 | + Scenario: Destroy Wiki page | |
26 | + Given I have an existing wiki page | |
27 | + And I browse to that Wiki page | |
28 | + And I click on the Edit button | |
29 | + And I click on the "Delete this page" button | |
30 | + Then The page should be deleted | |
31 | + | |
32 | + Scenario: View all pages | |
33 | + Given I have an existing wiki page | |
34 | + And I browse to that Wiki page | |
35 | + And I click on the "Pages" button | |
36 | + Then I should see the existing page in the pages list | ... | ... |
features/steps/project/project_wiki.rb
... | ... | @@ -4,17 +4,73 @@ class ProjectWiki < Spinach::FeatureSteps |
4 | 4 | include SharedNote |
5 | 5 | include SharedPaths |
6 | 6 | |
7 | - Given 'I create Wiki page' do | |
8 | - fill_in "Title", :with => 'Test title' | |
7 | + Given 'I create the Wiki Home page' do | |
9 | 8 | fill_in "Content", :with => '[link test](test)' |
10 | 9 | click_on "Save" |
11 | 10 | end |
12 | 11 | |
13 | - Then 'I should see newly created wiki page' do | |
14 | - page.should have_content "Test title" | |
12 | + Then 'I should see the newly created wiki page' do | |
13 | + page.should have_content "Home" | |
15 | 14 | page.should have_content "link test" |
16 | 15 | |
17 | 16 | click_link "link test" |
18 | 17 | page.should have_content "Editing page" |
19 | 18 | end |
19 | + | |
20 | + Given 'I have an existing Wiki page' do | |
21 | + wiki.create_page("existing", "content", :markdown, "first commit") | |
22 | + @page = wiki.find_page("existing") | |
23 | + end | |
24 | + | |
25 | + And 'I browse to that Wiki page' do | |
26 | + visit project_wiki_path(project, @page) | |
27 | + end | |
28 | + | |
29 | + And 'I click on the Edit button' do | |
30 | + click_on "Edit" | |
31 | + end | |
32 | + | |
33 | + And 'I change the content' do | |
34 | + fill_in "Content", :with => 'Updated Wiki Content' | |
35 | + click_on "Save" | |
36 | + end | |
37 | + | |
38 | + Then 'I should see the updated content' do | |
39 | + page.should have_content "Updated Wiki Content" | |
40 | + end | |
41 | + | |
42 | + And 'That page has two revisions' do | |
43 | + @page.update("new content", :markdown, "second commit") | |
44 | + end | |
45 | + | |
46 | + And 'I click the History button' do | |
47 | + click_on "History" | |
48 | + end | |
49 | + | |
50 | + Then 'I should see both revisions' do | |
51 | + page.should have_content current_user.name | |
52 | + page.should have_content "first commit" | |
53 | + page.should have_content "second commit" | |
54 | + end | |
55 | + | |
56 | + And 'I click on the "Delete this page" button' do | |
57 | + click_on "Delete this page" | |
58 | + end | |
59 | + | |
60 | + Then 'The page should be deleted' do | |
61 | + page.should have_content "Page was successfully deleted" | |
62 | + end | |
63 | + | |
64 | + And 'I click on the "Pages" button' do | |
65 | + click_on "Pages" | |
66 | + end | |
67 | + | |
68 | + Then 'I should see the existing page in the pages list' do | |
69 | + page.should have_content current_user.name | |
70 | + page.should have_content @page.title.titleize | |
71 | + end | |
72 | + | |
73 | + def wiki | |
74 | + @gollum_wiki = GollumWiki.new(project, current_user) | |
75 | + end | |
20 | 76 | end | ... | ... |
features/steps/shared/paths.rb
... | ... | @@ -165,7 +165,7 @@ module SharedPaths |
165 | 165 | end |
166 | 166 | |
167 | 167 | Given "I visit my project's wiki page" do |
168 | - visit project_wiki_path(@project, :index) | |
168 | + visit project_wiki_path(@project, :home) | |
169 | 169 | end |
170 | 170 | |
171 | 171 | When 'I visit project hooks page' do |
... | ... | @@ -260,7 +260,7 @@ module SharedPaths |
260 | 260 | end |
261 | 261 | |
262 | 262 | Given 'I visit project wiki page' do |
263 | - visit project_wiki_path(@project, :index) | |
263 | + visit project_wiki_path(@project, :home) | |
264 | 264 | end |
265 | 265 | |
266 | 266 | def root_ref | ... | ... |
features/support/env.rb
... | ... | @@ -37,6 +37,9 @@ DatabaseCleaner.strategy = :truncation |
37 | 37 | Spinach.hooks.before_scenario do |
38 | 38 | # Use tmp dir for FS manipulations |
39 | 39 | Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path')) |
40 | + Gitlab::Shell.any_instance.stub(:add_repository) do |path| | |
41 | + create_temp_repo("#{Rails.root}/tmp/test-git-base-path/#{path}.git") | |
42 | + end | |
40 | 43 | FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path |
41 | 44 | FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path |
42 | 45 | DatabaseCleaner.start |
... | ... | @@ -51,3 +54,9 @@ Spinach.hooks.before_run do |
51 | 54 | |
52 | 55 | include FactoryGirl::Syntax::Methods |
53 | 56 | end |
57 | + | |
58 | +def create_temp_repo(path) | |
59 | + FileUtils.mkdir_p path | |
60 | + command = "git init --quiet --bare #{path};" | |
61 | + system(command) | |
62 | +end | ... | ... |
lib/api/internal.rb
... | ... | @@ -12,10 +12,18 @@ module Gitlab |
12 | 12 | # ref - branch name |
13 | 13 | # |
14 | 14 | get "/allowed" do |
15 | + # Check for *.wiki repositories. | |
16 | + # Strip out the .wiki from the pathname before finding the | |
17 | + # project. This applies the correct project permissions to | |
18 | + # the wiki repository as well. | |
19 | + project_path = params[:project] | |
20 | + project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/ | |
21 | + | |
15 | 22 | key = Key.find(params[:key_id]) |
16 | - project = Project.find_with_namespace(params[:project]) | |
23 | + project = Project.find_with_namespace(project_path) | |
17 | 24 | git_cmd = params[:action] |
18 | 25 | |
26 | + | |
19 | 27 | if key.is_deploy_key |
20 | 28 | project == key.project && git_cmd == 'git-upload-pack' |
21 | 29 | else | ... | ... |
... | ... | @@ -0,0 +1,20 @@ |
1 | +namespace :gitlab do | |
2 | + namespace :wiki do | |
3 | + | |
4 | + # This task will migrate all of the existing Wiki | |
5 | + # content stored in your database into the new | |
6 | + # Gollum Wiki system. A new repository named | |
7 | + # namespace/project.wiki.git will be created for | |
8 | + # each project that currently has Wiki pages in | |
9 | + # the database. | |
10 | + # | |
11 | + # Notes: | |
12 | + # * The existing Wiki content will remain in your | |
13 | + # database in-tact. | |
14 | + desc "GITLAB | Migrate Wiki content from database to Gollum repositories." | |
15 | + task :migrate => :environment do | |
16 | + wiki_migrator = WikiToGollumMigrator.new | |
17 | + wiki_migrator.migrate! | |
18 | + end | |
19 | + end | |
20 | +end | ... | ... |
... | ... | @@ -0,0 +1,103 @@ |
1 | +class WikiToGollumMigrator | |
2 | + | |
3 | + attr_reader :projects | |
4 | + | |
5 | + def initialize | |
6 | + @projects = [] | |
7 | + | |
8 | + Project.find_in_batches(batch_size: 50) do |batch| | |
9 | + batch.each { |p| @projects << p if p.wikis.any? } | |
10 | + end | |
11 | + end | |
12 | + | |
13 | + def migrate! | |
14 | + projects.each do |project| | |
15 | + log "\nMigrating Wiki for '#{project.path_with_namespace}'" | |
16 | + wiki = create_gollum_repo(project) | |
17 | + create_pages project, wiki | |
18 | + log "Project '#{project.path_with_namespace}' migrated. " + "[OK]".green | |
19 | + end | |
20 | + end | |
21 | + | |
22 | + private | |
23 | + | |
24 | + def create_gollum_repo(project) | |
25 | + GollumWiki.new(project, nil).wiki | |
26 | + end | |
27 | + | |
28 | + def create_pages(project, wiki) | |
29 | + pages = project.wikis.group(:slug).all | |
30 | + | |
31 | + pages.each do |page| | |
32 | + create_page_and_revisions(project, page) | |
33 | + end | |
34 | + end | |
35 | + | |
36 | + def create_page_and_revisions(project, page) | |
37 | + # Grab all revisions of the page | |
38 | + revisions = project.wikis.where(slug: page.slug).ordered.all | |
39 | + | |
40 | + # Remove the first revision created from the array | |
41 | + # and use it to create the Gollum page. Each successive revision | |
42 | + # will then be applied to the new Gollum page as an update. | |
43 | + first_rev = revisions.pop | |
44 | + | |
45 | + wiki = GollumWiki.new(project, page.user) | |
46 | + wiki_page = WikiPage.new(wiki) | |
47 | + | |
48 | + attributes = extract_attributes_from_page(first_rev) | |
49 | + | |
50 | + if wiki_page.create(attributes) | |
51 | + log " Created page '#{wiki_page.title}' " + "[OK]".green | |
52 | + | |
53 | + # Reverse the revisions to create them in the correct | |
54 | + # chronological order. | |
55 | + create_revisions(project, wiki_page, revisions.reverse) | |
56 | + else | |
57 | + log " Failed to create page '#{wiki_page.title}' " + "[FAILED]".red | |
58 | + end | |
59 | + end | |
60 | + | |
61 | + def create_revisions(project, page, revisions) | |
62 | + revisions.each do |revision| | |
63 | + log " Creating revisions..." | |
64 | + # Reinitialize a new GollumWiki instance for each page | |
65 | + # and revision created so the correct User is shown in | |
66 | + # the commit message. | |
67 | + wiki = GollumWiki.new(project, revision.user) | |
68 | + wiki_page = wiki.find_page(page.slug) | |
69 | + | |
70 | + attributes = extract_attributes_from_page(revision) | |
71 | + | |
72 | + content = attributes[:content] | |
73 | + | |
74 | + if wiki_page.update(content) | |
75 | + log " Created revision " + "[OK]".green | |
76 | + else | |
77 | + log " Failed to create revision " + "[FAILED]".red | |
78 | + end | |
79 | + end | |
80 | + end | |
81 | + | |
82 | + def extract_attributes_from_page(page) | |
83 | + attributes = page.attributes | |
84 | + .with_indifferent_access | |
85 | + .slice(:title, :content) | |
86 | + | |
87 | + # Change 'index' pages to 'home' pages to match Gollum standards | |
88 | + if attributes[:title].downcase == "index" | |
89 | + attributes[:title] = "home" unless home_already_exists?(project) | |
90 | + end | |
91 | + | |
92 | + attributes | |
93 | + end | |
94 | + | |
95 | + def home_already_exists?(project) | |
96 | + project.wikis.where(title: 'home').any? || project.wikis.where(title: 'Home').any? | |
97 | + end | |
98 | + | |
99 | + def log(message) | |
100 | + puts message | |
101 | + end | |
102 | + | |
103 | +end | ... | ... |
spec/features/gitlab_flavored_markdown_spec.rb
... | ... | @@ -207,25 +207,4 @@ describe "Gitlab Flavored Markdown" do |
207 | 207 | page.should have_link("##{issue.id}") |
208 | 208 | end |
209 | 209 | end |
210 | - | |
211 | - | |
212 | - describe "for wikis" do | |
213 | - before do | |
214 | - visit project_wiki_path(project, :index) | |
215 | - fill_in "Title", with: "Circumvent ##{issue.id}" | |
216 | - fill_in "Content", with: "# Other pages\n\n* [Foo](foo)\n* [Bar](bar)\n\nAlso look at ##{issue.id} :-)" | |
217 | - click_on "Save" | |
218 | - end | |
219 | - | |
220 | - it "should NOT render title in wikis#show" do | |
221 | - within(".content .file_title") do # page title | |
222 | - page.should have_content("Circumvent ##{issue.id}") | |
223 | - page.should_not have_link("##{issue.id}") | |
224 | - end | |
225 | - end | |
226 | - | |
227 | - it "should render content in wikis#show" do | |
228 | - page.should have_link("##{issue.id}") | |
229 | - end | |
230 | - end | |
231 | 210 | end | ... | ... |
spec/helpers/gitlab_markdown_helper_spec.rb
... | ... | @@ -363,4 +363,28 @@ describe GitlabMarkdownHelper do |
363 | 363 | markdown(":smile:").should include("src=\"#{url_to_image("emoji/smile")}") |
364 | 364 | end |
365 | 365 | end |
366 | + | |
367 | + describe "#render_wiki_content" do | |
368 | + before do | |
369 | + @wiki = stub('WikiPage') | |
370 | + @wiki.stub(:content).and_return('wiki content') | |
371 | + end | |
372 | + | |
373 | + it "should use Gitlab Flavored Markdown for markdown files" do | |
374 | + @wiki.stub(:format).and_return(:markdown) | |
375 | + | |
376 | + helper.should_receive(:markdown).with('wiki content') | |
377 | + | |
378 | + helper.render_wiki_content(@wiki) | |
379 | + end | |
380 | + | |
381 | + it "should use the Gollum renderer for all other file types" do | |
382 | + @wiki.stub(:format).and_return(:rdoc) | |
383 | + formatted_content_stub = stub('formatted_content') | |
384 | + formatted_content_stub.should_receive(:html_safe) | |
385 | + @wiki.stub(:formatted_content).and_return(formatted_content_stub) | |
386 | + | |
387 | + helper.render_wiki_content(@wiki) | |
388 | + end | |
389 | + end | |
366 | 390 | end | ... | ... |
... | ... | @@ -0,0 +1,114 @@ |
1 | +require "spec_helper" | |
2 | + | |
3 | +describe WikiToGollumMigrator do | |
4 | + | |
5 | + def create_wiki_for(project) | |
6 | + 3.times { @pages[project.id] << create_page(project) } | |
7 | + end | |
8 | + | |
9 | + def create_revisions_for(project) | |
10 | + @pages[project.id].each do |page| | |
11 | + create_revision(page) | |
12 | + end | |
13 | + end | |
14 | + | |
15 | + def create_page(project) | |
16 | + page = project.wikis.new(title: "Page #{rand(1000)}", content: "Content") | |
17 | + page.user = project.owner | |
18 | + page.slug = page.title.parameterize | |
19 | + page.save! | |
20 | + page | |
21 | + end | |
22 | + | |
23 | + def create_revision(page) | |
24 | + revision = page.dup | |
25 | + revision.content = "Updated Content" | |
26 | + revision.save! | |
27 | + end | |
28 | + | |
29 | + def create_temp_repo(path) | |
30 | + FileUtils.mkdir_p path | |
31 | + command = "git init --quiet --bare #{path};" | |
32 | + system(command) | |
33 | + end | |
34 | + | |
35 | + before do | |
36 | + @repo_path = "#{Rails.root}/tmp/test-git-base-path" | |
37 | + @projects = [] | |
38 | + @pages = Hash.new {|h,k| h[k] = Array.new } | |
39 | + | |
40 | + @projects << create(:project) | |
41 | + @projects << create(:project) | |
42 | + | |
43 | + @projects.each do |project| | |
44 | + create_wiki_for project | |
45 | + create_revisions_for project | |
46 | + end | |
47 | + | |
48 | + @project_without_wiki = create(:project) | |
49 | + end | |
50 | + | |
51 | + context "Before the migration" do | |
52 | + it "has two projects with valid wikis" do | |
53 | + @projects.each do |project| | |
54 | + pages = project.wikis.group(:slug).all | |
55 | + pages.count.should == 3 | |
56 | + end | |
57 | + end | |
58 | + | |
59 | + it "has two revision for each page" do | |
60 | + @projects.each do |project| | |
61 | + @pages[project.id].each do |page| | |
62 | + revisions = project.wikis.where(slug: page.slug) | |
63 | + revisions.count.should == 2 | |
64 | + end | |
65 | + end | |
66 | + end | |
67 | + end | |
68 | + | |
69 | + describe "#initialize" do | |
70 | + it "finds all projects that have existing wiki pages" do | |
71 | + Project.count.should == 3 | |
72 | + subject.projects.count.should == 2 | |
73 | + end | |
74 | + end | |
75 | + | |
76 | + context "#migrate!" do | |
77 | + before do | |
78 | + Gitlab::Shell.any_instance.stub(:add_repository) do |path| | |
79 | + create_temp_repo("#{@repo_path}/#{path}.git") | |
80 | + end | |
81 | + | |
82 | + subject.stub(:log).as_null_object | |
83 | + | |
84 | + subject.migrate! | |
85 | + end | |
86 | + | |
87 | + it "creates a new Gollum Wiki for each project" do | |
88 | + @projects.each do |project| | |
89 | + wiki_path = project.path_with_namespace + ".wiki.git" | |
90 | + full_path = @repo_path + "/" + wiki_path | |
91 | + File.exist?(full_path).should be_true | |
92 | + File.directory?(full_path).should be_true | |
93 | + end | |
94 | + end | |
95 | + | |
96 | + it "creates a gollum page for each unique Wiki page" do | |
97 | + @projects.each do |project| | |
98 | + wiki = GollumWiki.new(project, nil) | |
99 | + wiki.pages.count.should == 3 | |
100 | + end | |
101 | + end | |
102 | + | |
103 | + it "creates a new revision for each old revision of the page" do | |
104 | + @projects.each do |project| | |
105 | + wiki = GollumWiki.new(project, nil) | |
106 | + wiki.pages.each do |page| | |
107 | + page.versions.count.should == 2 | |
108 | + end | |
109 | + end | |
110 | + end | |
111 | + end | |
112 | + | |
113 | + | |
114 | +end | ... | ... |
... | ... | @@ -0,0 +1,196 @@ |
1 | +require "spec_helper" | |
2 | + | |
3 | +describe GollumWiki do | |
4 | + | |
5 | + def create_temp_repo(path) | |
6 | + FileUtils.mkdir_p path | |
7 | + command = "git init --quiet #{path};" | |
8 | + system(command) | |
9 | + end | |
10 | + | |
11 | + def remove_temp_repo(path) | |
12 | + FileUtils.rm_rf path | |
13 | + end | |
14 | + | |
15 | + def commit_details | |
16 | + commit = {name: user.name, email: user.email, message: "test commit"} | |
17 | + end | |
18 | + | |
19 | + def create_page(name, content) | |
20 | + subject.wiki.write_page(name, :markdown, content, commit_details) | |
21 | + end | |
22 | + | |
23 | + def destroy_page(page) | |
24 | + subject.wiki.delete_page(page, commit_details) | |
25 | + end | |
26 | + | |
27 | + let(:project) { create(:project) } | |
28 | + let(:repository) { project.repository } | |
29 | + let(:user) { project.owner } | |
30 | + let(:gitlab_shell) { Gitlab::Shell.new } | |
31 | + | |
32 | + subject { GollumWiki.new(project, user) } | |
33 | + | |
34 | + before do | |
35 | + create_temp_repo(subject.send(:path_to_repo)) | |
36 | + end | |
37 | + | |
38 | + describe "#path_with_namespace" do | |
39 | + it "returns the project path with namespace with the .wiki extension" do | |
40 | + subject.path_with_namespace.should == project.path_with_namespace + ".wiki" | |
41 | + end | |
42 | + end | |
43 | + | |
44 | + describe "#url_to_repo" do | |
45 | + it "returns the correct ssh url to the repo" do | |
46 | + subject.url_to_repo.should == gitlab_shell.url_to_repo(subject.path_with_namespace) | |
47 | + end | |
48 | + end | |
49 | + | |
50 | + describe "#ssh_url_to_repo" do | |
51 | + it "equals #url_to_repo" do | |
52 | + subject.ssh_url_to_repo.should == subject.url_to_repo | |
53 | + end | |
54 | + end | |
55 | + | |
56 | + describe "#http_url_to_repo" do | |
57 | + it "provides the full http url to the repo" do | |
58 | + gitlab_url = Gitlab.config.gitlab.url | |
59 | + repo_http_url = "#{gitlab_url}/#{subject.path_with_namespace}.git" | |
60 | + subject.http_url_to_repo.should == repo_http_url | |
61 | + end | |
62 | + end | |
63 | + | |
64 | + describe "#wiki" do | |
65 | + it "contains a Gollum::Wiki instance" do | |
66 | + subject.wiki.should be_a Gollum::Wiki | |
67 | + end | |
68 | + | |
69 | + before do | |
70 | + Gitlab::Shell.any_instance.stub(:add_repository) do | |
71 | + create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git") | |
72 | + end | |
73 | + project.stub(:path_with_namespace).and_return("non-existant") | |
74 | + end | |
75 | + | |
76 | + it "creates a new wiki repo if one does not yet exist" do | |
77 | + wiki = GollumWiki.new(project, user) | |
78 | + wiki.create_page("index", "test content").should_not == false | |
79 | + | |
80 | + FileUtils.rm_rf wiki.send(:path_to_repo) | |
81 | + end | |
82 | + | |
83 | + it "raises CouldNotCreateWikiError if it can't create the wiki repository" do | |
84 | + Gitlab::Shell.any_instance.stub(:add_repository).and_return(false) | |
85 | + expect { GollumWiki.new(project, user).wiki }.to raise_exception(GollumWiki::CouldNotCreateWikiError) | |
86 | + end | |
87 | + end | |
88 | + | |
89 | + describe "#pages" do | |
90 | + before do | |
91 | + create_page("index", "This is an awesome new Gollum Wiki") | |
92 | + @pages = subject.pages | |
93 | + end | |
94 | + | |
95 | + after do | |
96 | + destroy_page(@pages.first.page) | |
97 | + end | |
98 | + | |
99 | + it "returns an array of WikiPage instances" do | |
100 | + @pages.first.should be_a WikiPage | |
101 | + end | |
102 | + | |
103 | + it "returns the correct number of pages" do | |
104 | + @pages.count.should == 1 | |
105 | + end | |
106 | + end | |
107 | + | |
108 | + describe "#find_page" do | |
109 | + before do | |
110 | + create_page("index page", "This is an awesome Gollum Wiki") | |
111 | + end | |
112 | + | |
113 | + after do | |
114 | + destroy_page(subject.pages.first.page) | |
115 | + end | |
116 | + | |
117 | + it "returns the latest version of the page if it exists" do | |
118 | + page = subject.find_page("index page") | |
119 | + page.title.should == "index page" | |
120 | + end | |
121 | + | |
122 | + it "returns nil if the page does not exist" do | |
123 | + subject.find_page("non-existant").should == nil | |
124 | + end | |
125 | + | |
126 | + it "can find a page by slug" do | |
127 | + page = subject.find_page("index-page") | |
128 | + page.title.should == "index page" | |
129 | + end | |
130 | + | |
131 | + it "returns a WikiPage instance" do | |
132 | + page = subject.find_page("index page") | |
133 | + page.should be_a WikiPage | |
134 | + end | |
135 | + end | |
136 | + | |
137 | + describe "#create_page" do | |
138 | + after do | |
139 | + destroy_page(subject.pages.first.page) | |
140 | + end | |
141 | + | |
142 | + it "creates a new wiki page" do | |
143 | + subject.create_page("test page", "this is content").should_not == false | |
144 | + subject.pages.count.should == 1 | |
145 | + end | |
146 | + | |
147 | + it "returns false when a duplicate page exists" do | |
148 | + subject.create_page("test page", "content") | |
149 | + subject.create_page("test page", "content").should == false | |
150 | + end | |
151 | + | |
152 | + it "stores an error message when a duplicate page exists" do | |
153 | + 2.times { subject.create_page("test page", "content") } | |
154 | + subject.error_message.should =~ /Duplicate page:/ | |
155 | + end | |
156 | + | |
157 | + it "sets the correct commit message" do | |
158 | + subject.create_page("test page", "some content", :markdown, "commit message") | |
159 | + subject.pages.first.page.version.message.should == "commit message" | |
160 | + end | |
161 | + end | |
162 | + | |
163 | + describe "#update_page" do | |
164 | + before do | |
165 | + create_page("update-page", "some content") | |
166 | + @gollum_page = subject.wiki.paged("update-page") | |
167 | + subject.update_page(@gollum_page, "some other content", :markdown, "updated page") | |
168 | + @page = subject.pages.first.page | |
169 | + end | |
170 | + | |
171 | + after do | |
172 | + destroy_page(@page) | |
173 | + end | |
174 | + | |
175 | + it "updates the content of the page" do | |
176 | + @page.raw_data.should == "some other content" | |
177 | + end | |
178 | + | |
179 | + it "sets the correct commit message" do | |
180 | + @page.version.message.should == "updated page" | |
181 | + end | |
182 | + end | |
183 | + | |
184 | + describe "#delete_page" do | |
185 | + before do | |
186 | + create_page("index", "some content") | |
187 | + @page = subject.wiki.paged("index") | |
188 | + end | |
189 | + | |
190 | + it "deletes the page" do | |
191 | + subject.delete_page(@page) | |
192 | + subject.pages.count.should == 0 | |
193 | + end | |
194 | + end | |
195 | + | |
196 | +end | ... | ... |
... | ... | @@ -0,0 +1,164 @@ |
1 | +require "spec_helper" | |
2 | + | |
3 | +describe WikiPage do | |
4 | + | |
5 | + def create_temp_repo(path) | |
6 | + FileUtils.mkdir_p path | |
7 | + command = "git init --quiet #{path};" | |
8 | + system(command) | |
9 | + end | |
10 | + | |
11 | + def remove_temp_repo(path) | |
12 | + FileUtils.rm_rf path | |
13 | + end | |
14 | + | |
15 | + def commit_details | |
16 | + commit = {name: user.name, email: user.email, message: "test commit"} | |
17 | + end | |
18 | + | |
19 | + def create_page(name, content) | |
20 | + wiki.wiki.write_page(name, :markdown, content, commit_details) | |
21 | + end | |
22 | + | |
23 | + def destroy_page(title) | |
24 | + page = wiki.wiki.paged(title) | |
25 | + wiki.wiki.delete_page(page, commit_details) | |
26 | + end | |
27 | + | |
28 | + let(:project) { create(:project) } | |
29 | + let(:repository) { project.repository } | |
30 | + let(:user) { project.owner } | |
31 | + let(:wiki) { GollumWiki.new(project, user) } | |
32 | + | |
33 | + subject { WikiPage.new(wiki) } | |
34 | + | |
35 | + before do | |
36 | + create_temp_repo(wiki.send(:path_to_repo)) | |
37 | + end | |
38 | + | |
39 | + describe "#initialize" do | |
40 | + context "when initialized with an existing gollum page" do | |
41 | + before do | |
42 | + create_page("test page", "test content") | |
43 | + @page = wiki.wiki.paged("test page") | |
44 | + @wiki_page = WikiPage.new(wiki, @page, true) | |
45 | + end | |
46 | + | |
47 | + it "sets the slug attribute" do | |
48 | + @wiki_page.slug.should == "test-page" | |
49 | + end | |
50 | + | |
51 | + it "sets the title attribute" do | |
52 | + @wiki_page.title.should == "test page" | |
53 | + end | |
54 | + | |
55 | + it "sets the formatted content attribute" do | |
56 | + @wiki_page.content.should == "test content" | |
57 | + end | |
58 | + | |
59 | + it "sets the format attribute" do | |
60 | + @wiki_page.format.should == :markdown | |
61 | + end | |
62 | + | |
63 | + it "sets the message attribute" do | |
64 | + @wiki_page.message.should == "test commit" | |
65 | + end | |
66 | + | |
67 | + it "sets the version attribute" do | |
68 | + @wiki_page.version.should be_a Commit | |
69 | + end | |
70 | + end | |
71 | + end | |
72 | + | |
73 | + describe "validations" do | |
74 | + before do | |
75 | + subject.attributes = {title: 'title', content: 'content'} | |
76 | + end | |
77 | + | |
78 | + it "validates presence of title" do | |
79 | + subject.attributes.delete(:title) | |
80 | + subject.valid?.should be_false | |
81 | + end | |
82 | + | |
83 | + it "validates presence of content" do | |
84 | + subject.attributes.delete(:content) | |
85 | + subject.valid?.should be_false | |
86 | + end | |
87 | + end | |
88 | + | |
89 | + before do | |
90 | + @wiki_attr = {title: "Index", content: "Home Page", format: "markdown"} | |
91 | + end | |
92 | + | |
93 | + describe "#create" do | |
94 | + after do | |
95 | + destroy_page("Index") | |
96 | + end | |
97 | + | |
98 | + context "with valid attributes" do | |
99 | + it "saves the wiki page" do | |
100 | + subject.create(@wiki_attr) | |
101 | + wiki.find_page("Index").should_not be_nil | |
102 | + end | |
103 | + | |
104 | + it "returns true" do | |
105 | + subject.create(@wiki_attr).should == true | |
106 | + end | |
107 | + end | |
108 | + end | |
109 | + | |
110 | + describe "#update" do | |
111 | + before do | |
112 | + create_page("Update", "content") | |
113 | + @page = wiki.find_page("Update") | |
114 | + end | |
115 | + | |
116 | + after do | |
117 | + destroy_page("Update") | |
118 | + end | |
119 | + | |
120 | + context "with valid attributes" do | |
121 | + it "updates the content of the page" do | |
122 | + @page.update("new content") | |
123 | + @page = wiki.find_page("Update") | |
124 | + end | |
125 | + | |
126 | + it "returns true" do | |
127 | + @page.update("more content").should be_true | |
128 | + end | |
129 | + end | |
130 | + end | |
131 | + | |
132 | + describe "#destroy" do | |
133 | + before do | |
134 | + create_page("Delete Page", "content") | |
135 | + @page = wiki.find_page("Delete Page") | |
136 | + end | |
137 | + | |
138 | + it "should delete the page" do | |
139 | + @page.delete | |
140 | + wiki.pages.should be_empty | |
141 | + end | |
142 | + | |
143 | + it "should return true" do | |
144 | + @page.delete.should == true | |
145 | + end | |
146 | + end | |
147 | + | |
148 | + describe "#versions" do | |
149 | + before do | |
150 | + create_page("Update", "content") | |
151 | + @page = wiki.find_page("Update") | |
152 | + end | |
153 | + | |
154 | + after do | |
155 | + destroy_page("Update") | |
156 | + end | |
157 | + | |
158 | + it "returns an array of all commits for the page" do | |
159 | + 3.times { |i| @page.update("content #{i}") } | |
160 | + @page.versions.count.should == 4 | |
161 | + end | |
162 | + end | |
163 | + | |
164 | +end | ... | ... |