From ea9b3687db46bf876a6f966e61bfddc1e6d25ef3 Mon Sep 17 00:00:00 2001 From: Dan Knox Date: Sun, 3 Mar 2013 19:43:52 -0800 Subject: [PATCH 1/7] Replace current Wiki system with Gollum Wikis. This commit replaces the old database backed Wiki system with the excellent Gollum git based Wiki system. The UI has been updated to allow for utilizing the extra features that Gollum provides. Specifically: * Edit page now allows you to choose the content format. * Edit page allows you to provide a commit message for the change. * History page now shows Format, Commit Message, and Commit Hash. * A new Git Access page has been added with the Wiki Repo URL. * The default page has been changed to Home from Index to match the Gollum standard. The old Wiki model has been left in tact to provide for the development of a migration script that will move all content stored in the old Wiki system into new Gollum Wikis. --- Gemfile | 7 + Gemfile.lock | 29 ++- app/assets/stylesheets/application.scss | 1 + app/assets/stylesheets/sections/wiki.scss | 6 + app/controllers/wikis_controller.rb | 94 ++++++--- app/models/gollum_wiki.rb | 125 +++++++++++ app/models/wiki_page.rb | 181 ++++++++++++++++ app/observers/project_observer.rb | 5 + app/views/layouts/project_resource.html.haml | 2 +- app/views/wikis/_form.html.haml | 12 +- app/views/wikis/_main_links.html.haml | 16 ++ app/views/wikis/edit.html.haml | 8 +- app/views/wikis/git_access.html.haml | 36 ++++ app/views/wikis/history.html.haml | 28 ++- app/views/wikis/pages.html.haml | 14 +- app/views/wikis/show.html.haml | 19 +- config/routes.rb | 2 + lib/api/internal.rb | 10 +- .../features/gitlab_flavored_markdown_spec.rb | 20 -- spec/models/gollum_wiki_spec.rb | 196 ++++++++++++++++++ spec/models/wiki_page_spec.rb | 164 +++++++++++++++ 21 files changed, 888 insertions(+), 87 deletions(-) create mode 100644 app/assets/stylesheets/sections/wiki.scss create mode 100644 app/models/gollum_wiki.rb create mode 100644 app/models/wiki_page.rb create mode 100644 app/views/wikis/_main_links.html.haml create mode 100644 app/views/wikis/git_access.html.haml create mode 100644 spec/models/gollum_wiki_spec.rb create mode 100644 spec/models/wiki_page_spec.rb diff --git a/Gemfile b/Gemfile index 324e1ce2..a532a7dc 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 36447188..0e35997a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,19 @@ +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 @@ -139,6 +155,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) @@ -170,6 +187,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) @@ -231,7 +252,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) @@ -365,6 +387,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) @@ -418,6 +442,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) @@ -440,6 +465,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) @@ -487,6 +513,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 6b500b88..d6f2fa96 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -33,6 +33,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 00000000..175911d7 --- /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 69280291..940b1e97 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 + @wiki = WikiPage.new(@gollum_wiki) - 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 + 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 - - respond_to do |format| - format.html { redirect_to project_wiki_path(@project, :index), notice: "Page was successfully deleted" } - end + @wiki = @gollum_wiki.find_page(params[:id]) + @wiki.delete if @wiki + redirect_to project_wiki_path(@project, :home), 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/models/gollum_wiki.rb b/app/models/gollum_wiki.rb new file mode 100644 index 00000000..91641ff1 --- /dev/null +++ b/app/models/gollum_wiki.rb @@ -0,0 +1,125 @@ +class GollumWiki + + MARKUPS = { + "Markdown" => :markdown, + "Textile" => :textile, + "RDoc" => :rdoc, + "Org-mode" => :org, + "Creole" => :creole, + "reStructuredText" => :rest, + "AsciiDoc" => :asciidoc, + "MediaWiki" => :mediawiki, + "Pod" => :post + } + + 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 00000000..adc77b22 --- /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 4b1f8295..89dc97ac 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 37d0f16f..3c53c0f2 100644 --- a/app/views/layouts/project_resource.html.haml +++ b/app/views/layouts/project_resource.html.haml @@ -41,6 +41,6 @@ - if @project.wiki_enabled = nav_link(controller: :wikis) do - = link_to 'Wiki', project_wiki_path(@project, :index) + = link_to 'Wiki', project_wiki_path(@project, :home) .content= yield diff --git a/app/views/wikis/_form.html.haml b/app/views/wikis/_form.html.haml index 7758b129..6fa41db4 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 00000000..262ed746 --- /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 9e221aba..1e78d16e 100644 --- a/app/views/wikis/edit.html.haml +++ b/app/views/wikis/edit.html.haml @@ -1,8 +1,10 @@ -%h3.page_title Editing page +%h3.page_title + Editing page + = render partial: 'main_links' %hr = render 'form' .pull-right - if 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 \ No newline at end of file + = link_to project_wikis_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 00000000..353d86f2 --- /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 18df8e1d..60920710 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 - = wiki_page.created_at.to_s(:short) - (#{time_ago_in_words(wiki_page.created_at)} - ago) - %td= link_to_member(@project, wiki_page.user) + = commit.title + %td + = 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 2e0f091c..fe35a2ed 100644 --- a/app/views/wikis/pages.html.haml +++ b/app/views/wikis/pages.html.haml @@ -1,20 +1,24 @@ -%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 7ff8b5cc..54d2a728 100644 --- a/app/views/wikis/show.html.haml +++ b/app/views/wikis/show.html.haml @@ -1,16 +1,8 @@ %h3.page_title - = @wiki.title - %span.pull-right - = link_to pages_project_wikis_path(@project), class: "btn btn-small grouped" do - Pages - - if can? current_user, :write_wiki, @project - = link_to history_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do - History - = link_to edit_project_wiki_path(@project, @wiki), class: "btn btn-small grouped" do - %i.icon-edit - Edit + = @wiki.title.titleize + = render partial: 'main_links' %br -- if @wiki != @most_recent_wiki +- 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)}. @@ -18,6 +10,7 @@ .file_holder .file_content.wiki = preserve do - = markdown @wiki.content + = @wiki.formatted_content.html_safe -%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 b06fda8f..2c9f0fd9 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/lib/api/internal.rb b/lib/api/internal.rb index c85c01f8..654dbe8c 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/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb index 769fcd68..05cef191 100644 --- a/spec/features/gitlab_flavored_markdown_spec.rb +++ b/spec/features/gitlab_flavored_markdown_spec.rb @@ -208,24 +208,4 @@ describe "Gitlab Flavored Markdown" do 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 h3") 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/models/gollum_wiki_spec.rb b/spec/models/gollum_wiki_spec.rb new file mode 100644 index 00000000..87601683 --- /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 00000000..67f2a6da --- /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 From 1479f1722702c955ed3ee9456107c6a1a7277c7b Mon Sep 17 00:00:00 2001 From: Dan Knox Date: Sun, 10 Mar 2013 14:37:26 -0700 Subject: [PATCH 2/7] Add Spinach coverage for Gollum Wiki system and correct the Delete link. The previously failing Spinach steps have been fixed with this commit. I have also added new steps that cover the entire usage of the Wiki system. The new Spinach steps revealed a minor bug in the Delete page process. The path for the "Delete this page" button was previously set to `project_wikis_page(@project, @wiki)` when it should have been using the singular `project_wiki_page(@project, @wiki)` path helper. The link has been corrected and all steps are now passing. --- app/views/wikis/edit.html.haml | 4 +- features/project/wiki.feature | 31 ++++++++++++- features/steps/project/project_wiki.rb | 64 ++++++++++++++++++++++++-- features/steps/shared/paths.rb | 4 +- features/support/env.rb | 9 ++++ 5 files changed, 102 insertions(+), 10 deletions(-) diff --git a/app/views/wikis/edit.html.haml b/app/views/wikis/edit.html.haml index 1e78d16e..84d8d27d 100644 --- a/app/views/wikis/edit.html.haml +++ b/app/views/wikis/edit.html.haml @@ -5,6 +5,6 @@ = render 'form' .pull-right - - if can? current_user, :admin_wiki, @project - = link_to project_wikis_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete, class: "btn btn-small btn-remove" do + - 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/features/project/wiki.feature b/features/project/wiki.feature index f052e2f2..45761f09 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 902e9ce1..1a811bad 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 431d5299..30a3fcaf 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -161,7 +161,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 @@ -256,7 +256,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 2fd7ffdb..b83f0d12 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -32,6 +32,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 @@ -46,3 +49,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 From f0aa54e0fbce27600aa02a1ee5465e2ab5c18ccc Mon Sep 17 00:00:00 2001 From: Dan Knox Date: Sun, 10 Mar 2013 19:10:44 -0700 Subject: [PATCH 3/7] Create Wiki migration task. This commit adds a new Rake task for migrating all of your existing Wiki content from your database into new Gollum repositories. The bulk of the logic happens within the `WikiToGollumMigrator` class which is decently test covered and located in the lib directory. The new Rake task can be executed by running: `bundle exec rake gitlab:wiki:migrate` It will output a nice log of every project that it migrates along with success or failure messages. I have used it on my own installation to migrate my Wikis successfully. --- lib/tasks/gitlab/migrate_wiki.rake | 20 ++++ lib/wiki_to_gollum_migrator.rb | 103 ++++++++++++++++++++ spec/lib/wiki_to_gollum_migrator_spec.rb | 114 +++++++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 lib/tasks/gitlab/migrate_wiki.rake create mode 100644 lib/wiki_to_gollum_migrator.rb create mode 100644 spec/lib/wiki_to_gollum_migrator_spec.rb diff --git a/lib/tasks/gitlab/migrate_wiki.rake b/lib/tasks/gitlab/migrate_wiki.rake new file mode 100644 index 00000000..9b2f34c6 --- /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 00000000..6083533b --- /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/lib/wiki_to_gollum_migrator_spec.rb b/spec/lib/wiki_to_gollum_migrator_spec.rb new file mode 100644 index 00000000..a784d836 --- /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 From 7665b1de7eed4addd7b94786c84e6674710e6377 Mon Sep 17 00:00:00 2001 From: Dan Knox Date: Wed, 13 Mar 2013 23:31:08 -0700 Subject: [PATCH 4/7] Use Gitlab Markdown for Markdown files and Gollum to render the rest. This commit enables the usage of the Gitlab Markdown post processing on all Markdown formatted files. For file types that do not contain Markdown, it defaults to the Gollum native renderer to process the content. --- app/helpers/gitlab_markdown_helper.rb | 8 +++++++ app/views/wikis/show.html.haml | 2 +- spec/helpers/gitlab_markdown_helper_spec.rb | 24 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 1a3d34eb..375f8861 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/views/wikis/show.html.haml b/app/views/wikis/show.html.haml index 54d2a728..8e987069 100644 --- a/app/views/wikis/show.html.haml +++ b/app/views/wikis/show.html.haml @@ -10,7 +10,7 @@ .file_holder .file_content.wiki = preserve do - = @wiki.formatted_content.html_safe + = render_wiki_content(@wiki) - 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/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 1f5fabfb..ac49e4d6 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 From 6d25484417b14776739eff91e6fa009b2b989cd7 Mon Sep 17 00:00:00 2001 From: Dan Knox Date: Wed, 13 Mar 2013 23:34:50 -0700 Subject: [PATCH 5/7] Limit available Wiki formats to Markdown and RDoc. --- app/models/gollum_wiki.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/models/gollum_wiki.rb b/app/models/gollum_wiki.rb index 91641ff1..2aecce99 100644 --- a/app/models/gollum_wiki.rb +++ b/app/models/gollum_wiki.rb @@ -2,14 +2,7 @@ class GollumWiki MARKUPS = { "Markdown" => :markdown, - "Textile" => :textile, - "RDoc" => :rdoc, - "Org-mode" => :org, - "Creole" => :creole, - "reStructuredText" => :rest, - "AsciiDoc" => :asciidoc, - "MediaWiki" => :mediawiki, - "Pod" => :post + "RDoc" => :rdoc } class CouldNotCreateWikiError < StandardError; end From d69a37e0b7163f5a03fcc58fdb6ec0ed1eb20862 Mon Sep 17 00:00:00 2001 From: Dan Knox Date: Thu, 14 Mar 2013 00:07:01 -0700 Subject: [PATCH 6/7] Fix whitespace on MARKUPS constant in GollumWiki class. --- app/models/gollum_wiki.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/gollum_wiki.rb b/app/models/gollum_wiki.rb index 2aecce99..95326505 100644 --- a/app/models/gollum_wiki.rb +++ b/app/models/gollum_wiki.rb @@ -1,8 +1,8 @@ class GollumWiki MARKUPS = { - "Markdown" => :markdown, - "RDoc" => :rdoc + "Markdown" => :markdown, + "RDoc" => :rdoc } class CouldNotCreateWikiError < StandardError; end From 94f04939990400c60be3d345d69d42d0f05e8355 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 15 Mar 2013 17:21:01 +0200 Subject: [PATCH 7/7] few fixes afte merge gollum wiki --- app/views/wikis/_nav.html.haml | 2 +- app/views/wikis/show.html.haml | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/app/views/wikis/_nav.html.haml b/app/views/wikis/_nav.html.haml index 36ab1053..f4b0b190 100644 --- a/app/views/wikis/_nav.html.haml +++ b/app/views/wikis/_nav.html.haml @@ -1,7 +1,7 @@ %ul.nav.nav-tabs - if @project.wiki_enabled = nav_link(controller: 'wikis') do - = link_to 'Wiki', project_wiki_path(@project, :index) + = link_to 'Wiki', project_wiki_path(@project, :home) - if @project.wall_enabled = nav_link(path: 'projects#wall') do diff --git a/app/views/wikis/show.html.haml b/app/views/wikis/show.html.haml index 9d87fdf1..b660a15e 100644 --- a/app/views/wikis/show.html.haml +++ b/app/views/wikis/show.html.haml @@ -9,18 +9,6 @@ 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)}. .file_holder - .file_title - = @wiki.title - %span.options - = link_to pages_project_wikis_path(@project), class: "btn btn-tiny grouped" do - Pages - - if can? current_user, :write_wiki, @project - = link_to history_project_wiki_path(@project, @wiki), class: "btn btn-tiny grouped" do - History - = link_to edit_project_wiki_path(@project, @wiki), class: "btn btn-tiny grouped" do - %i.icon-edit - Edit - .file_content.wiki = preserve do = render_wiki_content(@wiki)