Commit 71ab011a1711db9a1a9ced2c2c92c8427ae6f624

Authored by Dmitriy Zaporozhets
2 parents 4f23c30a d69a37e0

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
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
... ... @@ -34,6 +34,7 @@
34 34 @import "sections/login.scss";
35 35 @import "sections/editor.scss";
36 36 @import "sections/admin.scss";
  37 +@import "sections/wiki.scss";
37 38  
38 39 @import "highlight/white.scss";
39 40 @import "highlight/dark.scss";
... ...
app/assets/stylesheets/sections/wiki.scss 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +h3.page_title .edit-wiki-header {
  2 + width: 780px;
  3 + margin-left: auto;
  4 + margin-right: auto;
  5 + padding-right: 7px;
  6 +}
... ...
app/controllers/wikis_controller.rb
... ... @@ -2,58 +2,94 @@ class WikisController &lt; 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
... ...
app/models/gollum_wiki.rb 0 → 100644
... ... @@ -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
... ...
app/models/wiki_page.rb 0 → 100644
... ... @@ -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 &lt; 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"
... ...
app/views/wikis/_main_links.html.haml 0 → 100644
... ... @@ -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
... ...
app/views/wikis/git_access.html.haml 0 → 100644
... ... @@ -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
... ... @@ -185,6 +185,8 @@ Gitlab::Application.routes.draw do
185 185 resources :wikis, only: [:show, :edit, :destroy, :create] do
186 186 collection do
187 187 get :pages
  188 + put ':id' => 'wikis#update'
  189 + get :git_access
188 190 end
189 191  
190 192 member do
... ...
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 &lt; 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
... ...
lib/tasks/gitlab/migrate_wiki.rake 0 → 100644
... ... @@ -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
... ...
lib/wiki_to_gollum_migrator.rb 0 → 100644
... ... @@ -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 &quot;Gitlab Flavored Markdown&quot; 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
... ...
spec/lib/wiki_to_gollum_migrator_spec.rb 0 → 100644
... ... @@ -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
... ...
spec/models/gollum_wiki_spec.rb 0 → 100644
... ... @@ -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
... ...
spec/models/wiki_page_spec.rb 0 → 100644
... ... @@ -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
... ...