summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-03-15 16:55:07 +0200
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-03-15 16:55:07 +0200
commit71ab011a1711db9a1a9ced2c2c92c8427ae6f624 (patch)
tree95e38662d7f022061c2c809f4b6c53067a5df468
parent4f23c30ace844cdcd2c59da1a758c8a6788ca6f3 (diff)
parentd69a37e0b7163f5a03fcc58fdb6ec0ed1eb20862 (diff)
downloadgitlab-ce-71ab011a1711db9a1a9ced2c2c92c8427ae6f624.tar.gz
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
-rw-r--r--Gemfile7
-rw-r--r--Gemfile.lock29
-rw-r--r--app/assets/stylesheets/application.scss1
-rw-r--r--app/assets/stylesheets/sections/wiki.scss6
-rw-r--r--app/controllers/wikis_controller.rb94
-rw-r--r--app/helpers/gitlab_markdown_helper.rb8
-rw-r--r--app/models/gollum_wiki.rb118
-rw-r--r--app/models/wiki_page.rb181
-rw-r--r--app/observers/project_observer.rb5
-rw-r--r--app/views/layouts/project_resource.html.haml2
-rw-r--r--app/views/wikis/_form.html.haml12
-rw-r--r--app/views/wikis/_main_links.html.haml16
-rw-r--r--app/views/wikis/edit.html.haml6
-rw-r--r--app/views/wikis/git_access.html.haml36
-rw-r--r--app/views/wikis/history.html.haml28
-rw-r--r--app/views/wikis/pages.html.haml14
-rw-r--r--app/views/wikis/show.html.haml13
-rw-r--r--config/routes.rb2
-rw-r--r--features/project/wiki.feature31
-rw-r--r--features/steps/project/project_wiki.rb64
-rw-r--r--features/steps/shared/paths.rb4
-rw-r--r--features/support/env.rb9
-rw-r--r--lib/api/internal.rb10
-rw-r--r--lib/tasks/gitlab/migrate_wiki.rake20
-rw-r--r--lib/wiki_to_gollum_migrator.rb103
-rw-r--r--spec/features/gitlab_flavored_markdown_spec.rb21
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb24
-rw-r--r--spec/lib/wiki_to_gollum_migrator_spec.rb114
-rw-r--r--spec/models/gollum_wiki_spec.rb196
-rw-r--r--spec/models/wiki_page_spec.rb164
30 files changed, 1252 insertions, 86 deletions
diff --git a/Gemfile b/Gemfile
index a9f69acc575..1815dfd52ac 100644
--- a/Gemfile
+++ b/Gemfile
@@ -99,6 +99,13 @@ gem "colored"
# GitLab settings
gem 'settingslogic'
+# Wiki
+# - Use latest master to resolve Gem dependency with Pygemnts
+# github-linquist needs pygments 0.4.2 but Gollum 2.4.11
+# requires pygments 0.3.2. The latest master Gollum has been updated
+# to use pygments 0.4.2. Change this after next Gollum release.
+gem "gollum", "~> 2.4.0", git: "git://github.com/github/gollum.git"
+
# Misc
gem "foreman"
gem "git"
diff --git a/Gemfile.lock b/Gemfile.lock
index a19cbfdd90a..ebaab14f615 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,4 +1,20 @@
GIT
+ remote: git://github.com/github/gollum.git
+ revision: 544d499ab170c9d9b355b7a0160afc74139ee2a4
+ specs:
+ gollum (2.4.11)
+ github-markdown (~> 0.5.3)
+ github-markup (>= 0.7.5, < 1.0.0)
+ grit (~> 2.5.0)
+ mustache (>= 0.99.4, < 1.0.0)
+ nokogiri (~> 1.5.6)
+ pygments.rb (~> 0.4.2)
+ sanitize (~> 2.0.3)
+ sinatra (~> 1.3.5)
+ stringex (~> 1.5.1)
+ useragent (~> 0.4.16)
+
+GIT
remote: https://github.com/ctran/annotate_models.git
revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
specs:
@@ -145,6 +161,7 @@ GEM
escape_utils (~> 0.2.3)
mime-types (~> 1.19)
pygments.rb (>= 0.2.13)
+ github-markdown (0.5.3)
github-markup (0.7.5)
gitlab-grack (1.0.0)
rack (~> 1.4.1)
@@ -176,6 +193,10 @@ GEM
grape-entity (0.2.0)
activesupport
multi_json (>= 1.3.2)
+ grit (2.5.0)
+ diff-lcs (~> 1.1)
+ mime-types (~> 1.15)
+ posix-spawn (~> 0.3.6)
grit_ext (0.6.2)
charlock_holmes (~> 0.6.9)
growl (1.0.3)
@@ -237,7 +258,8 @@ GEM
sprockets (~> 2.0)
multi_json (1.6.1)
multi_xml (0.5.3)
- multipart-post (1.2.0)
+ multipart-post (1.1.5)
+ mustache (0.99.4)
mysql2 (0.3.11)
net-ldap (0.2.2)
nokogiri (1.5.6)
@@ -373,6 +395,8 @@ GEM
rspec-mocks (~> 2.12.0)
rubyntlm (0.1.1)
rubyzip (0.9.9)
+ sanitize (2.0.3)
+ nokogiri (>= 1.4.4, < 1.6)
sass (3.2.5)
sass-rails (3.2.5)
railties (~> 3.2.0)
@@ -429,6 +453,7 @@ GEM
tilt (~> 1.1, != 1.3.0)
stamp (0.5.0)
state_machine (1.1.2)
+ stringex (1.5.1)
temple (0.5.5)
test_after_commit (0.0.1)
therubyracer (0.10.2)
@@ -451,6 +476,7 @@ GEM
kgio (~> 2.6)
rack
raindrops (~> 0.7)
+ useragent (0.4.16)
virtus (0.5.4)
backports (~> 2.6.1)
descendants_tracker (~> 0.0.1)
@@ -499,6 +525,7 @@ DEPENDENCIES
gitlab_meta (= 5.0)
gitlab_omniauth-ldap (= 1.0.2)
gitlab_yaml_db (= 1.0.0)
+ gollum (~> 2.4.0)!
gon
grape (~> 0.3.1)
grape-entity (~> 0.2.0)
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index d1d51e15992..f7cd2b559d3 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -34,6 +34,7 @@
@import "sections/login.scss";
@import "sections/editor.scss";
@import "sections/admin.scss";
+@import "sections/wiki.scss";
@import "highlight/white.scss";
@import "highlight/dark.scss";
diff --git a/app/assets/stylesheets/sections/wiki.scss b/app/assets/stylesheets/sections/wiki.scss
new file mode 100644
index 00000000000..175911d7312
--- /dev/null
+++ b/app/assets/stylesheets/sections/wiki.scss
@@ -0,0 +1,6 @@
+h3.page_title .edit-wiki-header {
+ width: 780px;
+ margin-left: auto;
+ margin-right: auto;
+ padding-right: 7px;
+}
diff --git a/app/controllers/wikis_controller.rb b/app/controllers/wikis_controller.rb
index 69280291003..940b1e97340 100644
--- a/app/controllers/wikis_controller.rb
+++ b/app/controllers/wikis_controller.rb
@@ -2,58 +2,94 @@ class WikisController < ProjectResourceController
before_filter :authorize_read_wiki!
before_filter :authorize_write_wiki!, only: [:edit, :create, :history]
before_filter :authorize_admin_wiki!, only: :destroy
+ before_filter :load_gollum_wiki
def pages
- @wiki_pages = @project.wikis.group(:slug).ordered
+ @wiki_pages = @gollum_wiki.pages
end
def show
- @most_recent_wiki = @project.wikis.where(slug: params[:id]).ordered.first
- if params[:version_id]
- @wiki = @project.wikis.find(params[:version_id])
- else
- @wiki = @most_recent_wiki
- end
+ @wiki = @gollum_wiki.find_page(params[:id], params[:version_id])
if @wiki
render 'show'
else
- if can?(current_user, :write_wiki, @project)
- @wiki = @project.wikis.new(slug: params[:id])
- render 'edit'
- else
- render 'empty'
- end
+ return render('empty') unless can?(current_user, :write_wiki, @project)
+ @wiki = WikiPage.new(@gollum_wiki)
+ @wiki.title = params[:id]
+
+ render 'edit'
end
end
def edit
- @wiki = @project.wikis.where(slug: params[:id]).ordered.first
- @wiki = Wiki.regenerate_from @wiki
+ @wiki = @gollum_wiki.find_page(params[:id])
+ end
+
+ def update
+ @wiki = @gollum_wiki.find_page(params[:id])
+
+ return render('empty') unless can?(current_user, :write_wiki, @project)
+
+ if @wiki.update(content, format, message)
+ redirect_to [@project, @wiki], notice: 'Wiki was successfully updated.'
+ else
+ render 'edit'
+ end
end
def create
- @wiki = @project.wikis.new(params[:wiki])
- @wiki.user = current_user
-
- respond_to do |format|
- if @wiki.save
- format.html { redirect_to [@project, @wiki], notice: 'Wiki was successfully updated.' }
- else
- format.html { render action: "edit" }
- end
+ @wiki = WikiPage.new(@gollum_wiki)
+
+ if @wiki.create(wiki_params)
+ redirect_to project_wiki_path(@project, @wiki), notice: 'Wiki was successfully updated.'
+ else
+ render action: "edit"
end
end
def history
- @wiki_pages = @project.wikis.where(slug: params[:id]).ordered
+ unless @wiki = @gollum_wiki.find_page(params[:id])
+ redirect_to project_wiki_path(@project, :home), notice: "Page not found"
+ end
end
def destroy
- @wikis = @project.wikis.where(slug: params[:id]).delete_all
+ @wiki = @gollum_wiki.find_page(params[:id])
+ @wiki.delete if @wiki
+ redirect_to project_wiki_path(@project, :home), notice: "Page was successfully deleted"
+ end
- respond_to do |format|
- format.html { redirect_to project_wiki_path(@project, :index), notice: "Page was successfully deleted" }
- end
+ def git_access
end
+
+ private
+
+ def load_gollum_wiki
+ @gollum_wiki = GollumWiki.new(@project, current_user)
+
+ # Call #wiki to make sure the Wiki Repo is initialized
+ @gollum_wiki.wiki
+ rescue GollumWiki::CouldNotCreateWikiError => ex
+ flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
+ redirect_to @project
+ return false
+ end
+
+ def wiki_params
+ params[:wiki].slice(:title, :content, :format, :message)
+ end
+
+ def content
+ params[:wiki][:content]
+ end
+
+ def format
+ params[:wiki][:format]
+ end
+
+ def message
+ params[:wiki][:message]
+ end
+
end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 1a3d34eb886..375f8861dae 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -49,4 +49,12 @@ module GitlabMarkdownHelper
@markdown.render(text).html_safe
end
+
+ def render_wiki_content(wiki_page)
+ if wiki_page.format == :markdown
+ markdown(wiki_page.content)
+ else
+ wiki_page.formatted_content.html_safe
+ end
+ end
end
diff --git a/app/models/gollum_wiki.rb b/app/models/gollum_wiki.rb
new file mode 100644
index 00000000000..95326505365
--- /dev/null
+++ b/app/models/gollum_wiki.rb
@@ -0,0 +1,118 @@
+class GollumWiki
+
+ MARKUPS = {
+ "Markdown" => :markdown,
+ "RDoc" => :rdoc
+ }
+
+ class CouldNotCreateWikiError < StandardError; end
+
+ # Returns a string describing what went wrong after
+ # an operation fails.
+ attr_reader :error_message
+
+ def initialize(project, user = nil)
+ @project = project
+ @user = user
+ end
+
+ def path_with_namespace
+ @project.path_with_namespace + ".wiki"
+ end
+
+ def url_to_repo
+ gitlab_shell.url_to_repo(path_with_namespace)
+ end
+
+ def ssh_url_to_repo
+ url_to_repo
+ end
+
+ def http_url_to_repo
+ http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
+ end
+
+ # Returns the Gollum::Wiki object.
+ def wiki
+ @wiki ||= begin
+ Gollum::Wiki.new(path_to_repo)
+ rescue Grit::NoSuchPathError
+ create_repo!
+ end
+ end
+
+ # Returns an Array of Gitlab WikiPage instances or an
+ # empty Array if this Wiki has no pages.
+ def pages
+ wiki.pages.map { |page| WikiPage.new(self, page, true) }
+ end
+
+ # Returns the last 30 Commit objects accross the entire
+ # repository.
+ def recent_history
+ Commit.fresh_commits(wiki.repo, 30)
+ end
+
+ # Finds a page within the repository based on a tile
+ # or slug.
+ #
+ # title - The human readable or parameterized title of
+ # the page.
+ #
+ # Returns an initialized WikiPage instance or nil
+ def find_page(title, version = nil)
+ if page = wiki.page(title, version)
+ WikiPage.new(self, page, true)
+ else
+ nil
+ end
+ end
+
+ def create_page(title, content, format = :markdown, message = nil)
+ commit = commit_details(:created, message, title)
+
+ wiki.write_page(title, format, content, commit)
+ rescue Gollum::DuplicatePageError => e
+ @error_message = "Duplicate page: #{e.message}"
+ return false
+ end
+
+ def update_page(page, content, format = :markdown, message = nil)
+ commit = commit_details(:updated, message, page.title)
+
+ wiki.update_page(page, page.name, format, content, commit)
+ end
+
+ def delete_page(page, message = nil)
+ wiki.delete_page(page, commit_details(:deleted, message, page.title))
+ end
+
+ private
+
+ def create_repo!
+ if gitlab_shell.add_repository(path_with_namespace)
+ Gollum::Wiki.new(path_to_repo)
+ else
+ raise CouldNotCreateWikiError
+ end
+ end
+
+ def commit_details(action, message = nil, title = nil)
+ commit_message = message || default_message(action, title)
+
+ {email: @user.email, name: @user.name, message: commit_message}
+ end
+
+ def default_message(action, title)
+ "#{@user.username} #{action} page: #{title}"
+ end
+
+ def gitlab_shell
+ @gitlab_shell ||= Gitlab::Shell.new
+ end
+
+ def path_to_repo
+ @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
+ end
+
+end
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
new file mode 100644
index 00000000000..adc77b22231
--- /dev/null
+++ b/app/models/wiki_page.rb
@@ -0,0 +1,181 @@
+class WikiPage
+ include ActiveModel::Validations
+ include ActiveModel::Conversion
+ include StaticModel
+ extend ActiveModel::Naming
+
+ def self.primary_key
+ 'slug'
+ end
+
+ def self.model_name
+ ActiveModel::Name.new(self, nil, 'wiki')
+ end
+
+ def to_key
+ [:slug]
+ end
+
+ validates :title, presence: true
+ validates :content, presence: true
+
+ # The Gitlab GollumWiki instance.
+ attr_reader :wiki
+
+ # The raw Gollum::Page instance.
+ attr_reader :page
+
+ # The attributes Hash used for storing and validating
+ # new Page values before writing to the Gollum repository.
+ attr_accessor :attributes
+
+ def initialize(wiki, page = nil, persisted = false)
+ @wiki = wiki
+ @page = page
+ @persisted = persisted
+ @attributes = {}.with_indifferent_access
+
+ set_attributes if persisted?
+ end
+
+ # The escaped URL path of this page.
+ def slug
+ @attributes[:slug]
+ end
+
+ alias :to_param :slug
+
+ # The formatted title of this page.
+ def title
+ @attributes[:title] || ""
+ end
+
+ # Sets the title of this page.
+ def title=(new_title)
+ @attributes[:title] = new_title
+ end
+
+ # The raw content of this page.
+ def content
+ @attributes[:content]
+ end
+
+ # The processed/formatted content of this page.
+ def formatted_content
+ @attributes[:formatted_content]
+ end
+
+ # The markup format for the page.
+ def format
+ @attributes[:format] || :markdown
+ end
+
+ # The commit message for this page version.
+ def message
+ version.try(:message)
+ end
+
+ # The Gitlab Commit instance for this page.
+ def version
+ return nil unless persisted?
+
+ @version ||= Commit.new(@page.version)
+ end
+
+ # Returns an array of Gitlab Commit instances.
+ def versions
+ return [] unless persisted?
+
+ @page.versions.map { |v| Commit.new(v) }
+ end
+
+ # Returns the Date that this latest version was
+ # created on.
+ def created_at
+ @page.version.date
+ end
+
+ # Returns boolean True or False if this instance
+ # is an old version of the page.
+ def historical?
+ @page.historical?
+ end
+
+ # Returns boolean True or False if this instance
+ # has been fully saved to disk or not.
+ def persisted?
+ @persisted == true
+ end
+
+ # Creates a new Wiki Page.
+ #
+ # attr - Hash of attributes to set on the new page.
+ # :title - The title for the new page.
+ # :content - The raw markup content.
+ # :format - Optional symbol representing the
+ # content format. Can be any type
+ # listed in the GollumWiki::MARKUPS
+ # Hash.
+ # :message - Optional commit message to set on
+ # the new page.
+ #
+ # Returns the String SHA1 of the newly created page
+ # or False if the save was unsuccessful.
+ def create(attr = {})
+ @attributes.merge!(attr)
+
+ save :create_page, title, content, format, message
+ end
+
+ # Updates an existing Wiki Page, creating a new version.
+ #
+ # new_content - The raw markup content to replace the existing.
+ # format - Optional symbol representing the content format.
+ # See GollumWiki::MARKUPS Hash for available formats.
+ # message - Optional commit message to set on the new version.
+ #
+ # Returns the String SHA1 of the newly created page
+ # or False if the save was unsuccessful.
+ def update(new_content = "", format = :markdown, message = nil)
+ @attributes[:content] = new_content
+ @attributes[:format] = format
+
+ save :update_page, @page, content, format, message
+ end
+
+ # Destroys the WIki Page.
+ #
+ # Returns boolean True or False.
+ def delete
+ if wiki.delete_page(@page)
+ true
+ else
+ false
+ end
+ end
+
+ private
+
+ def set_attributes
+ attributes[:slug] = @page.escaped_url_path
+ attributes[:title] = @page.title
+ attributes[:content] = @page.raw_data
+ attributes[:formatted_content] = @page.formatted_data
+ attributes[:format] = @page.format
+ end
+
+ def save(method, *args)
+ if valid? && wiki.send(method, *args)
+ @page = wiki.wiki.paged(title)
+
+ set_attributes
+
+ @persisted = true
+ else
+ errors.add(:base, wiki.error_message) if wiki.error_message
+ @persisted = false
+ end
+ @persisted
+ end
+
+end
diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb
index 4b1f8295dc2..89dc97ac140 100644
--- a/app/observers/project_observer.rb
+++ b/app/observers/project_observer.rb
@@ -18,6 +18,11 @@ class ProjectObserver < ActiveRecord::Observer
project.path_with_namespace
)
+ GitlabShellWorker.perform_async(
+ :remove_repository,
+ project.path_with_namespace + ".wiki"
+ )
+
project.satellite.destroy
log_info("Project \"#{project.name}\" was removed")
diff --git a/app/views/layouts/project_resource.html.haml b/app/views/layouts/project_resource.html.haml
index e4558b69362..dfbb6a0394e 100644
--- a/app/views/layouts/project_resource.html.haml
+++ b/app/views/layouts/project_resource.html.haml
@@ -36,7 +36,7 @@
%span.count.merge_counter= @project.merge_requests.opened.count
= nav_link(html_options: {class: "#{project_wiki_tab_class}"}) do
- = link_to 'Wiki', project_wiki_path(@project, :index)
+ = link_to 'Wiki', project_wiki_path(@project, :home)
- if can? current_user, :admin_project, @project
= nav_link(html_options: {class: "#{project_tab_class}"}) do
diff --git a/app/views/wikis/_form.html.haml b/app/views/wikis/_form.html.haml
index 7758b129b07..6fa41db4f7a 100644
--- a/app/views/wikis/_form.html.haml
+++ b/app/views/wikis/_form.html.haml
@@ -8,9 +8,12 @@
.ui-box.ui-box-show
.ui-box-head
- = f.label :title
- .input= f.text_field :title, class: 'span8'
- = f.hidden_field :slug
+ %h3.page_title
+ .edit-wiki-header
+ = @wiki.title.titleize
+ = f.hidden_field :title, value: @wiki.title
+ = f.select :format, options_for_select(GollumWiki::MARKUPS, {selected: @wiki.format}), {}, class: "pull-right input-medium"
+ = f.label :format, class: "pull-right", style: "padding-right: 20px;"
.ui-box-body
.input
%span.cgray
@@ -22,6 +25,9 @@
.ui-box-bottom
= f.label :content
.input= f.text_area :content, class: 'span8 js-gfm-input'
+ .ui-box-bottom
+ = f.label :commit_message
+ .input= f.text_field :message, class: 'span8'
.actions
= f.submit 'Save', class: "btn-save btn"
= link_to "Cancel", project_wiki_path(@project, :index), class: "btn btn-cancel"
diff --git a/app/views/wikis/_main_links.html.haml b/app/views/wikis/_main_links.html.haml
new file mode 100644
index 00000000000..262ed74681c
--- /dev/null
+++ b/app/views/wikis/_main_links.html.haml
@@ -0,0 +1,16 @@
+%span.pull-right
+ = link_to project_wiki_path(@project, :home), class: "btn btn-small grouped" do
+ Home
+ = link_to pages_project_wikis_path(@project), class: "btn btn-small grouped" do
+ Pages
+ - if (@wiki && @wiki.persisted?)
+ = link_to history_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do
+ History
+ - if can?(current_user, :write_wiki, @project)
+ - if @wiki && @wiki.persisted?
+ = link_to edit_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do
+ %i.icon-edit
+ Edit
+ = link_to git_access_project_wikis_path(@project), class: "btn btn-small grouped" do
+ %i.icon-download-alt
+ Git Access
diff --git a/app/views/wikis/edit.html.haml b/app/views/wikis/edit.html.haml
index 501ba092317..dd200a35522 100644
--- a/app/views/wikis/edit.html.haml
+++ b/app/views/wikis/edit.html.haml
@@ -1,7 +1,9 @@
-%h3.page_title Editing page
+%h3.page_title
+ Editing page
+ = render partial: 'main_links'
= render 'form'
.pull-right
- - if can? current_user, :admin_wiki, @project
+ - if @wiki.persisted? && can?(current_user, :admin_wiki, @project)
= 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
Delete this page
diff --git a/app/views/wikis/git_access.html.haml b/app/views/wikis/git_access.html.haml
new file mode 100644
index 00000000000..353d86f2d4d
--- /dev/null
+++ b/app/views/wikis/git_access.html.haml
@@ -0,0 +1,36 @@
+%h3.page_title
+ Git Access
+ %strong= @gollum_wiki.path_with_namespace
+ = render partial: 'main_links'
+
+%br
+.content
+ .project_clone_panel
+ .row
+ .span7
+ .form-horizontal
+ .input-prepend.project_clone_holder
+ %button{class: "btn active", :"data-clone" => @gollum_wiki.ssh_url_to_repo} SSH
+ %button{class: "btn", :"data-clone" => @gollum_wiki.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase
+ = text_field_tag :project_clone, @gollum_wiki.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
+ .git-empty
+ %fieldset
+ %legend Install Gollum:
+ %pre.dark
+ :preserve
+ gem install gollum
+
+ %legend Clone Your Wiki:
+ %pre.dark
+ :preserve
+ git clone #{@gollum_wiki.path_with_namespace}.git
+ cd #{@gollum_wiki.path_with_namespace}
+
+ %legend Start Gollum And Edit Locally:
+ %pre.dark
+ :preserve
+ gollum
+ == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
+ >> Thin web server (v1.5.0 codename Knife)
+ >> Maximum connections set to 1024
+ >> Listening on 0.0.0.0:4567, CTRL+C to stop
diff --git a/app/views/wikis/history.html.haml b/app/views/wikis/history.html.haml
index 18df8e1d71b..609207106ab 100644
--- a/app/views/wikis/history.html.haml
+++ b/app/views/wikis/history.html.haml
@@ -1,23 +1,29 @@
%h3.page_title
%span.cgray History for
- = @wiki_pages.first.title
+ = @wiki.title.titleize
+ = render partial: 'main_links'
%br
%table
%thead
%tr
%th Page version
+ %th Author
+ %th Commit Message
%th Last updated
- %th Updated by
+ %th Format
%tbody
- - @wiki_pages.each_with_index do |wiki_page, i|
+ - @wiki.versions.each do |version|
+ - commit = CommitDecorator.new(version)
%tr
%td
- %strong
- = link_to project_wiki_path(@project, wiki_page, version_id: wiki_page.id) do
- Version
- = @wiki_pages.count - i
+ = link_to project_wiki_path(@project, @wiki, version_id: commit.id) do
+ = commit.short_id
+ %td= commit.author_link avatar: true, size: 24
+ %td
+ = commit.title
%td
- = wiki_page.created_at.to_s(:short)
- (#{time_ago_in_words(wiki_page.created_at)}
- ago)
- %td= link_to_member(@project, wiki_page.user)
+ = time_ago_in_words(version.date)
+ ago
+ %td
+ %strong
+ = @wiki.page.wiki.page(@wiki.page.name, commit.id).try(:format)
diff --git a/app/views/wikis/pages.html.haml b/app/views/wikis/pages.html.haml
index 7bf57adc106..eb65599d087 100644
--- a/app/views/wikis/pages.html.haml
+++ b/app/views/wikis/pages.html.haml
@@ -1,21 +1,25 @@
= render 'wikis/nav'
-%h3.page_title All Pages
+%h3.page_title
+ All Pages
+ = render partial: 'main_links'
%br
%table
%thead
%tr
%th Title
- %th Slug
+ %th Format
%th Last updated
%th Updated by
%tbody
- @wiki_pages.each do |wiki_page|
%tr
%td
- %strong= link_to wiki_page.title, project_wiki_path(@project, wiki_page)
- %td= wiki_page.slug
+ %strong= link_to wiki_page.title.titleize, project_wiki_path(@project, wiki_page)
+ %td
+ %strong= wiki_page.format
%td
= wiki_page.created_at.to_s(:short) do
(#{time_ago_in_words(wiki_page.created_at)}
ago)
- %td= link_to_member(@project, wiki_page.user)
+ - commit = CommitDecorator.decorate(wiki_page.version)
+ %td= commit.author_link avatar: true, size: 24
diff --git a/app/views/wikis/show.html.haml b/app/views/wikis/show.html.haml
index ea8de155154..9d87fdf1498 100644
--- a/app/views/wikis/show.html.haml
+++ b/app/views/wikis/show.html.haml
@@ -1,6 +1,10 @@
= render 'wikis/nav'
-- if @wiki != @most_recent_wiki
- .alert
+%h3.page_title
+ = @wiki.title.titleize
+ = render partial: 'main_links'
+%br
+- if @wiki.historical?
+ .warning_message
This is an old version of this page.
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)}.
@@ -19,6 +23,7 @@
.file_content.wiki
= preserve do
- = markdown @wiki.content
+ = render_wiki_content(@wiki)
-%p.time Last edited by #{link_to_member @project, @wiki.user}, #{time_ago_in_words @wiki.created_at} ago
+- commit = CommitDecorator.new(@wiki.version)
+%p.time Last edited by #{commit.author_link(avatar: true, size: 16)} #{time_ago_in_words @wiki.created_at} ago
diff --git a/config/routes.rb b/config/routes.rb
index 27977ae30a9..2e6d31c23e1 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -185,6 +185,8 @@ Gitlab::Application.routes.draw do
resources :wikis, only: [:show, :edit, :destroy, :create] do
collection do
get :pages
+ put ':id' => 'wikis#update'
+ get :git_access
end
member do
diff --git a/features/project/wiki.feature b/features/project/wiki.feature
index f052e2f244c..45761f09f51 100644
--- a/features/project/wiki.feature
+++ b/features/project/wiki.feature
@@ -5,5 +5,32 @@ Feature: Project Wiki
Given I visit project wiki page
Scenario: Add new page
- Given I create Wiki page
- Then I should see newly created wiki page
+ Given I create the Wiki Home page
+ Then I should see the newly created wiki page
+
+ Scenario: Edit existing page
+ Given I have an existing Wiki page
+ And I browse to that Wiki page
+ And I click on the Edit button
+ And I change the content
+ Then I should see the updated content
+
+ Scenario: View page history
+ Given I have an existing wiki page
+ And That page has two revisions
+ And I browse to that Wiki page
+ And I click the History button
+ Then I should see both revisions
+
+ Scenario: Destroy Wiki page
+ Given I have an existing wiki page
+ And I browse to that Wiki page
+ And I click on the Edit button
+ And I click on the "Delete this page" button
+ Then The page should be deleted
+
+ Scenario: View all pages
+ Given I have an existing wiki page
+ And I browse to that Wiki page
+ And I click on the "Pages" button
+ Then I should see the existing page in the pages list
diff --git a/features/steps/project/project_wiki.rb b/features/steps/project/project_wiki.rb
index 902e9ce158c..1a811bad798 100644
--- a/features/steps/project/project_wiki.rb
+++ b/features/steps/project/project_wiki.rb
@@ -4,17 +4,73 @@ class ProjectWiki < Spinach::FeatureSteps
include SharedNote
include SharedPaths
- Given 'I create Wiki page' do
- fill_in "Title", :with => 'Test title'
+ Given 'I create the Wiki Home page' do
fill_in "Content", :with => '[link test](test)'
click_on "Save"
end
- Then 'I should see newly created wiki page' do
- page.should have_content "Test title"
+ Then 'I should see the newly created wiki page' do
+ page.should have_content "Home"
page.should have_content "link test"
click_link "link test"
page.should have_content "Editing page"
end
+
+ Given 'I have an existing Wiki page' do
+ wiki.create_page("existing", "content", :markdown, "first commit")
+ @page = wiki.find_page("existing")
+ end
+
+ And 'I browse to that Wiki page' do
+ visit project_wiki_path(project, @page)
+ end
+
+ And 'I click on the Edit button' do
+ click_on "Edit"
+ end
+
+ And 'I change the content' do
+ fill_in "Content", :with => 'Updated Wiki Content'
+ click_on "Save"
+ end
+
+ Then 'I should see the updated content' do
+ page.should have_content "Updated Wiki Content"
+ end
+
+ And 'That page has two revisions' do
+ @page.update("new content", :markdown, "second commit")
+ end
+
+ And 'I click the History button' do
+ click_on "History"
+ end
+
+ Then 'I should see both revisions' do
+ page.should have_content current_user.name
+ page.should have_content "first commit"
+ page.should have_content "second commit"
+ end
+
+ And 'I click on the "Delete this page" button' do
+ click_on "Delete this page"
+ end
+
+ Then 'The page should be deleted' do
+ page.should have_content "Page was successfully deleted"
+ end
+
+ And 'I click on the "Pages" button' do
+ click_on "Pages"
+ end
+
+ Then 'I should see the existing page in the pages list' do
+ page.should have_content current_user.name
+ page.should have_content @page.title.titleize
+ end
+
+ def wiki
+ @gollum_wiki = GollumWiki.new(project, current_user)
+ end
end
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 2713e20a91e..444e1d0c2fd 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -165,7 +165,7 @@ module SharedPaths
end
Given "I visit my project's wiki page" do
- visit project_wiki_path(@project, :index)
+ visit project_wiki_path(@project, :home)
end
When 'I visit project hooks page' do
@@ -260,7 +260,7 @@ module SharedPaths
end
Given 'I visit project wiki page' do
- visit project_wiki_path(@project, :index)
+ visit project_wiki_path(@project, :home)
end
def root_ref
diff --git a/features/support/env.rb b/features/support/env.rb
index 6f1e4df39fc..f6f88955625 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -37,6 +37,9 @@ DatabaseCleaner.strategy = :truncation
Spinach.hooks.before_scenario do
# Use tmp dir for FS manipulations
Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path'))
+ Gitlab::Shell.any_instance.stub(:add_repository) do |path|
+ create_temp_repo("#{Rails.root}/tmp/test-git-base-path/#{path}.git")
+ end
FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
DatabaseCleaner.start
@@ -51,3 +54,9 @@ Spinach.hooks.before_run do
include FactoryGirl::Syntax::Methods
end
+
+def create_temp_repo(path)
+ FileUtils.mkdir_p path
+ command = "git init --quiet --bare #{path};"
+ system(command)
+end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 22ac49c6be8..81451638090 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -12,10 +12,18 @@ module Gitlab
# ref - branch name
#
get "/allowed" do
+ # Check for *.wiki repositories.
+ # Strip out the .wiki from the pathname before finding the
+ # project. This applies the correct project permissions to
+ # the wiki repository as well.
+ project_path = params[:project]
+ project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/
+
key = Key.find(params[:key_id])
- project = Project.find_with_namespace(params[:project])
+ project = Project.find_with_namespace(project_path)
git_cmd = params[:action]
+
if key.is_deploy_key
project == key.project && git_cmd == 'git-upload-pack'
else
diff --git a/lib/tasks/gitlab/migrate_wiki.rake b/lib/tasks/gitlab/migrate_wiki.rake
new file mode 100644
index 00000000000..9b2f34c6b19
--- /dev/null
+++ b/lib/tasks/gitlab/migrate_wiki.rake
@@ -0,0 +1,20 @@
+namespace :gitlab do
+ namespace :wiki do
+
+ # This task will migrate all of the existing Wiki
+ # content stored in your database into the new
+ # Gollum Wiki system. A new repository named
+ # namespace/project.wiki.git will be created for
+ # each project that currently has Wiki pages in
+ # the database.
+ #
+ # Notes:
+ # * The existing Wiki content will remain in your
+ # database in-tact.
+ desc "GITLAB | Migrate Wiki content from database to Gollum repositories."
+ task :migrate => :environment do
+ wiki_migrator = WikiToGollumMigrator.new
+ wiki_migrator.migrate!
+ end
+ end
+end
diff --git a/lib/wiki_to_gollum_migrator.rb b/lib/wiki_to_gollum_migrator.rb
new file mode 100644
index 00000000000..6083533b7e2
--- /dev/null
+++ b/lib/wiki_to_gollum_migrator.rb
@@ -0,0 +1,103 @@
+class WikiToGollumMigrator
+
+ attr_reader :projects
+
+ def initialize
+ @projects = []
+
+ Project.find_in_batches(batch_size: 50) do |batch|
+ batch.each { |p| @projects << p if p.wikis.any? }
+ end
+ end
+
+ def migrate!
+ projects.each do |project|
+ log "\nMigrating Wiki for '#{project.path_with_namespace}'"
+ wiki = create_gollum_repo(project)
+ create_pages project, wiki
+ log "Project '#{project.path_with_namespace}' migrated. " + "[OK]".green
+ end
+ end
+
+ private
+
+ def create_gollum_repo(project)
+ GollumWiki.new(project, nil).wiki
+ end
+
+ def create_pages(project, wiki)
+ pages = project.wikis.group(:slug).all
+
+ pages.each do |page|
+ create_page_and_revisions(project, page)
+ end
+ end
+
+ def create_page_and_revisions(project, page)
+ # Grab all revisions of the page
+ revisions = project.wikis.where(slug: page.slug).ordered.all
+
+ # Remove the first revision created from the array
+ # and use it to create the Gollum page. Each successive revision
+ # will then be applied to the new Gollum page as an update.
+ first_rev = revisions.pop
+
+ wiki = GollumWiki.new(project, page.user)
+ wiki_page = WikiPage.new(wiki)
+
+ attributes = extract_attributes_from_page(first_rev)
+
+ if wiki_page.create(attributes)
+ log " Created page '#{wiki_page.title}' " + "[OK]".green
+
+ # Reverse the revisions to create them in the correct
+ # chronological order.
+ create_revisions(project, wiki_page, revisions.reverse)
+ else
+ log " Failed to create page '#{wiki_page.title}' " + "[FAILED]".red
+ end
+ end
+
+ def create_revisions(project, page, revisions)
+ revisions.each do |revision|
+ log " Creating revisions..."
+ # Reinitialize a new GollumWiki instance for each page
+ # and revision created so the correct User is shown in
+ # the commit message.
+ wiki = GollumWiki.new(project, revision.user)
+ wiki_page = wiki.find_page(page.slug)
+
+ attributes = extract_attributes_from_page(revision)
+
+ content = attributes[:content]
+
+ if wiki_page.update(content)
+ log " Created revision " + "[OK]".green
+ else
+ log " Failed to create revision " + "[FAILED]".red
+ end
+ end
+ end
+
+ def extract_attributes_from_page(page)
+ attributes = page.attributes
+ .with_indifferent_access
+ .slice(:title, :content)
+
+ # Change 'index' pages to 'home' pages to match Gollum standards
+ if attributes[:title].downcase == "index"
+ attributes[:title] = "home" unless home_already_exists?(project)
+ end
+
+ attributes
+ end
+
+ def home_already_exists?(project)
+ project.wikis.where(title: 'home').any? || project.wikis.where(title: 'Home').any?
+ end
+
+ def log(message)
+ puts message
+ end
+
+end
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb
index 1da18275989..a6485328219 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/gitlab_flavored_markdown_spec.rb
@@ -207,25 +207,4 @@ describe "Gitlab Flavored Markdown" do
page.should have_link("##{issue.id}")
end
end
-
-
- describe "for wikis" do
- before do
- visit project_wiki_path(project, :index)
- fill_in "Title", with: "Circumvent ##{issue.id}"
- fill_in "Content", with: "# Other pages\n\n* [Foo](foo)\n* [Bar](bar)\n\nAlso look at ##{issue.id} :-)"
- click_on "Save"
- end
-
- it "should NOT render title in wikis#show" do
- within(".content .file_title") do # page title
- page.should have_content("Circumvent ##{issue.id}")
- page.should_not have_link("##{issue.id}")
- end
- end
-
- it "should render content in wikis#show" do
- page.should have_link("##{issue.id}")
- end
- end
end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 1f5fabfbb8e..ac49e4d6da0 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -363,4 +363,28 @@ describe GitlabMarkdownHelper do
markdown(":smile:").should include("src=\"#{url_to_image("emoji/smile")}")
end
end
+
+ describe "#render_wiki_content" do
+ before do
+ @wiki = stub('WikiPage')
+ @wiki.stub(:content).and_return('wiki content')
+ end
+
+ it "should use Gitlab Flavored Markdown for markdown files" do
+ @wiki.stub(:format).and_return(:markdown)
+
+ helper.should_receive(:markdown).with('wiki content')
+
+ helper.render_wiki_content(@wiki)
+ end
+
+ it "should use the Gollum renderer for all other file types" do
+ @wiki.stub(:format).and_return(:rdoc)
+ formatted_content_stub = stub('formatted_content')
+ formatted_content_stub.should_receive(:html_safe)
+ @wiki.stub(:formatted_content).and_return(formatted_content_stub)
+
+ helper.render_wiki_content(@wiki)
+ end
+ end
end
diff --git a/spec/lib/wiki_to_gollum_migrator_spec.rb b/spec/lib/wiki_to_gollum_migrator_spec.rb
new file mode 100644
index 00000000000..a784d836d62
--- /dev/null
+++ b/spec/lib/wiki_to_gollum_migrator_spec.rb
@@ -0,0 +1,114 @@
+require "spec_helper"
+
+describe WikiToGollumMigrator do
+
+ def create_wiki_for(project)
+ 3.times { @pages[project.id] << create_page(project) }
+ end
+
+ def create_revisions_for(project)
+ @pages[project.id].each do |page|
+ create_revision(page)
+ end
+ end
+
+ def create_page(project)
+ page = project.wikis.new(title: "Page #{rand(1000)}", content: "Content")
+ page.user = project.owner
+ page.slug = page.title.parameterize
+ page.save!
+ page
+ end
+
+ def create_revision(page)
+ revision = page.dup
+ revision.content = "Updated Content"
+ revision.save!
+ end
+
+ def create_temp_repo(path)
+ FileUtils.mkdir_p path
+ command = "git init --quiet --bare #{path};"
+ system(command)
+ end
+
+ before do
+ @repo_path = "#{Rails.root}/tmp/test-git-base-path"
+ @projects = []
+ @pages = Hash.new {|h,k| h[k] = Array.new }
+
+ @projects << create(:project)
+ @projects << create(:project)
+
+ @projects.each do |project|
+ create_wiki_for project
+ create_revisions_for project
+ end
+
+ @project_without_wiki = create(:project)
+ end
+
+ context "Before the migration" do
+ it "has two projects with valid wikis" do
+ @projects.each do |project|
+ pages = project.wikis.group(:slug).all
+ pages.count.should == 3
+ end
+ end
+
+ it "has two revision for each page" do
+ @projects.each do |project|
+ @pages[project.id].each do |page|
+ revisions = project.wikis.where(slug: page.slug)
+ revisions.count.should == 2
+ end
+ end
+ end
+ end
+
+ describe "#initialize" do
+ it "finds all projects that have existing wiki pages" do
+ Project.count.should == 3
+ subject.projects.count.should == 2
+ end
+ end
+
+ context "#migrate!" do
+ before do
+ Gitlab::Shell.any_instance.stub(:add_repository) do |path|
+ create_temp_repo("#{@repo_path}/#{path}.git")
+ end
+
+ subject.stub(:log).as_null_object
+
+ subject.migrate!
+ end
+
+ it "creates a new Gollum Wiki for each project" do
+ @projects.each do |project|
+ wiki_path = project.path_with_namespace + ".wiki.git"
+ full_path = @repo_path + "/" + wiki_path
+ File.exist?(full_path).should be_true
+ File.directory?(full_path).should be_true
+ end
+ end
+
+ it "creates a gollum page for each unique Wiki page" do
+ @projects.each do |project|
+ wiki = GollumWiki.new(project, nil)
+ wiki.pages.count.should == 3
+ end
+ end
+
+ it "creates a new revision for each old revision of the page" do
+ @projects.each do |project|
+ wiki = GollumWiki.new(project, nil)
+ wiki.pages.each do |page|
+ page.versions.count.should == 2
+ end
+ end
+ end
+ end
+
+
+end
diff --git a/spec/models/gollum_wiki_spec.rb b/spec/models/gollum_wiki_spec.rb
new file mode 100644
index 00000000000..87601683275
--- /dev/null
+++ b/spec/models/gollum_wiki_spec.rb
@@ -0,0 +1,196 @@
+require "spec_helper"
+
+describe GollumWiki do
+
+ def create_temp_repo(path)
+ FileUtils.mkdir_p path
+ command = "git init --quiet #{path};"
+ system(command)
+ end
+
+ def remove_temp_repo(path)
+ FileUtils.rm_rf path
+ end
+
+ def commit_details
+ commit = {name: user.name, email: user.email, message: "test commit"}
+ end
+
+ def create_page(name, content)
+ subject.wiki.write_page(name, :markdown, content, commit_details)
+ end
+
+ def destroy_page(page)
+ subject.wiki.delete_page(page, commit_details)
+ end
+
+ let(:project) { create(:project) }
+ let(:repository) { project.repository }
+ let(:user) { project.owner }
+ let(:gitlab_shell) { Gitlab::Shell.new }
+
+ subject { GollumWiki.new(project, user) }
+
+ before do
+ create_temp_repo(subject.send(:path_to_repo))
+ end
+
+ describe "#path_with_namespace" do
+ it "returns the project path with namespace with the .wiki extension" do
+ subject.path_with_namespace.should == project.path_with_namespace + ".wiki"
+ end
+ end
+
+ describe "#url_to_repo" do
+ it "returns the correct ssh url to the repo" do
+ subject.url_to_repo.should == gitlab_shell.url_to_repo(subject.path_with_namespace)
+ end
+ end
+
+ describe "#ssh_url_to_repo" do
+ it "equals #url_to_repo" do
+ subject.ssh_url_to_repo.should == subject.url_to_repo
+ end
+ end
+
+ describe "#http_url_to_repo" do
+ it "provides the full http url to the repo" do
+ gitlab_url = Gitlab.config.gitlab.url
+ repo_http_url = "#{gitlab_url}/#{subject.path_with_namespace}.git"
+ subject.http_url_to_repo.should == repo_http_url
+ end
+ end
+
+ describe "#wiki" do
+ it "contains a Gollum::Wiki instance" do
+ subject.wiki.should be_a Gollum::Wiki
+ end
+
+ before do
+ Gitlab::Shell.any_instance.stub(:add_repository) do
+ create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git")
+ end
+ project.stub(:path_with_namespace).and_return("non-existant")
+ end
+
+ it "creates a new wiki repo if one does not yet exist" do
+ wiki = GollumWiki.new(project, user)
+ wiki.create_page("index", "test content").should_not == false
+
+ FileUtils.rm_rf wiki.send(:path_to_repo)
+ end
+
+ it "raises CouldNotCreateWikiError if it can't create the wiki repository" do
+ Gitlab::Shell.any_instance.stub(:add_repository).and_return(false)
+ expect { GollumWiki.new(project, user).wiki }.to raise_exception(GollumWiki::CouldNotCreateWikiError)
+ end
+ end
+
+ describe "#pages" do
+ before do
+ create_page("index", "This is an awesome new Gollum Wiki")
+ @pages = subject.pages
+ end
+
+ after do
+ destroy_page(@pages.first.page)
+ end
+
+ it "returns an array of WikiPage instances" do
+ @pages.first.should be_a WikiPage
+ end
+
+ it "returns the correct number of pages" do
+ @pages.count.should == 1
+ end
+ end
+
+ describe "#find_page" do
+ before do
+ create_page("index page", "This is an awesome Gollum Wiki")
+ end
+
+ after do
+ destroy_page(subject.pages.first.page)
+ end
+
+ it "returns the latest version of the page if it exists" do
+ page = subject.find_page("index page")
+ page.title.should == "index page"
+ end
+
+ it "returns nil if the page does not exist" do
+ subject.find_page("non-existant").should == nil
+ end
+
+ it "can find a page by slug" do
+ page = subject.find_page("index-page")
+ page.title.should == "index page"
+ end
+
+ it "returns a WikiPage instance" do
+ page = subject.find_page("index page")
+ page.should be_a WikiPage
+ end
+ end
+
+ describe "#create_page" do
+ after do
+ destroy_page(subject.pages.first.page)
+ end
+
+ it "creates a new wiki page" do
+ subject.create_page("test page", "this is content").should_not == false
+ subject.pages.count.should == 1
+ end
+
+ it "returns false when a duplicate page exists" do
+ subject.create_page("test page", "content")
+ subject.create_page("test page", "content").should == false
+ end
+
+ it "stores an error message when a duplicate page exists" do
+ 2.times { subject.create_page("test page", "content") }
+ subject.error_message.should =~ /Duplicate page:/
+ end
+
+ it "sets the correct commit message" do
+ subject.create_page("test page", "some content", :markdown, "commit message")
+ subject.pages.first.page.version.message.should == "commit message"
+ end
+ end
+
+ describe "#update_page" do
+ before do
+ create_page("update-page", "some content")
+ @gollum_page = subject.wiki.paged("update-page")
+ subject.update_page(@gollum_page, "some other content", :markdown, "updated page")
+ @page = subject.pages.first.page
+ end
+
+ after do
+ destroy_page(@page)
+ end
+
+ it "updates the content of the page" do
+ @page.raw_data.should == "some other content"
+ end
+
+ it "sets the correct commit message" do
+ @page.version.message.should == "updated page"
+ end
+ end
+
+ describe "#delete_page" do
+ before do
+ create_page("index", "some content")
+ @page = subject.wiki.paged("index")
+ end
+
+ it "deletes the page" do
+ subject.delete_page(@page)
+ subject.pages.count.should == 0
+ end
+ end
+
+end
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
new file mode 100644
index 00000000000..67f2a6da42d
--- /dev/null
+++ b/spec/models/wiki_page_spec.rb
@@ -0,0 +1,164 @@
+require "spec_helper"
+
+describe WikiPage do
+
+ def create_temp_repo(path)
+ FileUtils.mkdir_p path
+ command = "git init --quiet #{path};"
+ system(command)
+ end
+
+ def remove_temp_repo(path)
+ FileUtils.rm_rf path
+ end
+
+ def commit_details
+ commit = {name: user.name, email: user.email, message: "test commit"}
+ end
+
+ def create_page(name, content)
+ wiki.wiki.write_page(name, :markdown, content, commit_details)
+ end
+
+ def destroy_page(title)
+ page = wiki.wiki.paged(title)
+ wiki.wiki.delete_page(page, commit_details)
+ end
+
+ let(:project) { create(:project) }
+ let(:repository) { project.repository }
+ let(:user) { project.owner }
+ let(:wiki) { GollumWiki.new(project, user) }
+
+ subject { WikiPage.new(wiki) }
+
+ before do
+ create_temp_repo(wiki.send(:path_to_repo))
+ end
+
+ describe "#initialize" do
+ context "when initialized with an existing gollum page" do
+ before do
+ create_page("test page", "test content")
+ @page = wiki.wiki.paged("test page")
+ @wiki_page = WikiPage.new(wiki, @page, true)
+ end
+
+ it "sets the slug attribute" do
+ @wiki_page.slug.should == "test-page"
+ end
+
+ it "sets the title attribute" do
+ @wiki_page.title.should == "test page"
+ end
+
+ it "sets the formatted content attribute" do
+ @wiki_page.content.should == "test content"
+ end
+
+ it "sets the format attribute" do
+ @wiki_page.format.should == :markdown
+ end
+
+ it "sets the message attribute" do
+ @wiki_page.message.should == "test commit"
+ end
+
+ it "sets the version attribute" do
+ @wiki_page.version.should be_a Commit
+ end
+ end
+ end
+
+ describe "validations" do
+ before do
+ subject.attributes = {title: 'title', content: 'content'}
+ end
+
+ it "validates presence of title" do
+ subject.attributes.delete(:title)
+ subject.valid?.should be_false
+ end
+
+ it "validates presence of content" do
+ subject.attributes.delete(:content)
+ subject.valid?.should be_false
+ end
+ end
+
+ before do
+ @wiki_attr = {title: "Index", content: "Home Page", format: "markdown"}
+ end
+
+ describe "#create" do
+ after do
+ destroy_page("Index")
+ end
+
+ context "with valid attributes" do
+ it "saves the wiki page" do
+ subject.create(@wiki_attr)
+ wiki.find_page("Index").should_not be_nil
+ end
+
+ it "returns true" do
+ subject.create(@wiki_attr).should == true
+ end
+ end
+ end
+
+ describe "#update" do
+ before do
+ create_page("Update", "content")
+ @page = wiki.find_page("Update")
+ end
+
+ after do
+ destroy_page("Update")
+ end
+
+ context "with valid attributes" do
+ it "updates the content of the page" do
+ @page.update("new content")
+ @page = wiki.find_page("Update")
+ end
+
+ it "returns true" do
+ @page.update("more content").should be_true
+ end
+ end
+ end
+
+ describe "#destroy" do
+ before do
+ create_page("Delete Page", "content")
+ @page = wiki.find_page("Delete Page")
+ end
+
+ it "should delete the page" do
+ @page.delete
+ wiki.pages.should be_empty
+ end
+
+ it "should return true" do
+ @page.delete.should == true
+ end
+ end
+
+ describe "#versions" do
+ before do
+ create_page("Update", "content")
+ @page = wiki.find_page("Update")
+ end
+
+ after do
+ destroy_page("Update")
+ end
+
+ it "returns an array of all commits for the page" do
+ 3.times { |i| @page.update("content #{i}") }
+ @page.versions.count.should == 4
+ end
+ end
+
+end