diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 6818fe6a..83a7501c 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -72,4 +72,6 @@ $hover: #FDF5D9; @import "highlight.css.scss"; @import "highlight.black.css.scss"; +@import "wiki.scss"; + diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index b54e12c8..487925db 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -221,3 +221,7 @@ input.git_clone_url { width:270px; background:#fff !important; } + +.span12 hr{ + margin-top: 2px; +} diff --git a/app/assets/stylesheets/wiki.scss b/app/assets/stylesheets/wiki.scss new file mode 100644 index 00000000..acc37b96 --- /dev/null +++ b/app/assets/stylesheets/wiki.scss @@ -0,0 +1,5 @@ +p.time { + color: #999; + font-size: 90%; + margin: 30px 3px 3px 2px; +} diff --git a/app/controllers/wikis_controller.rb b/app/controllers/wikis_controller.rb new file mode 100644 index 00000000..5e8365cf --- /dev/null +++ b/app/controllers/wikis_controller.rb @@ -0,0 +1,68 @@ +class WikisController < ApplicationController + before_filter :project + before_filter :add_project_abilities + before_filter :authorize_read_wiki! + before_filter :authorize_write_wiki!, :except => [:show, :destroy] + before_filter :authorize_admin_wiki!, :only => :destroy + layout "project" + + def show + if params[:old_page_id] + @wiki = @project.wikis.find(params[:old_page_id]) + else + @wiki = @project.wikis.where(:slug => params[:id]).order("created_at").last + end + respond_to do |format| + if @wiki + format.html + else + @wiki = @project.wikis.new(:slug => params[:id]) + format.html { render "edit" } + end + end + end + + def edit + @wiki = @project.wikis.where(:slug => params[:id]).order("created_at").last + @wiki = Wiki.regenerate_from @wiki + 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 + end + end + + def history + @wikis = @project.wikis.where(:slug => params[:id]).order("created_at") + 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 + end + + protected + + def authorize_read_wiki! + can?(current_user, :read_wiki, @project) + end + + def authorize_write_wiki! + can?(current_user, :write_wiki, @project) + end + + def authorize_admin_wiki! + can?(current_user, :admin_wiki, @project) + end +end diff --git a/app/helpers/wikis_helper.rb b/app/helpers/wikis_helper.rb new file mode 100644 index 00000000..0c24f57a --- /dev/null +++ b/app/helpers/wikis_helper.rb @@ -0,0 +1,5 @@ +module WikisHelper + def markdown_to_html(text) + RDiscount.new(text).to_html.html_safe + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index f31b510d..c7fddec2 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -5,6 +5,7 @@ class Ability when "Issue" then issue_abilities(object, subject) when "Note" then note_abilities(object, subject) when "Snippet" then snippet_abilities(object, subject) + when "Wiki" then wiki_abilities(object, subject) else [] end end @@ -14,35 +15,40 @@ class Ability rules << [ :read_project, + :read_wiki, :read_issue, :read_snippet, :read_team_member, :read_merge_request, - :read_note - ] if project.allow_read_for?(user) - - rules << [ + :read_note, :write_project, :write_issue, :write_snippet, :write_merge_request, :write_note - ] if project.allow_write_for?(user) + ] if project.guest_access_for?(user) + + rules << [ + :download_code, + ] if project.report_access_for?(user) + + rules << [ + :write_wiki + ] if project.dev_access_for?(user) rules << [ :modify_issue, :modify_snippet, + :modify_wiki, :admin_project, :admin_issue, :admin_snippet, :admin_team_member, :admin_merge_request, - :admin_note - ] if project.allow_admin_for?(user) + :admin_note, + :admin_wiki + ] if project.master_access_for?(user) - rules << [ - :download_code, - ] if project.allow_pull_for?(user) rules.flatten end diff --git a/app/models/project.rb b/app/models/project.rb index 017ef2ce..f5b9b54c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -12,6 +12,7 @@ class Project < ActiveRecord::Base has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key" has_many :web_hooks, :dependent => :destroy has_many :protected_branches, :dependent => :destroy + has_many :wikis, :dependent => :destroy acts_as_taggable @@ -232,16 +233,20 @@ class Project < ActiveRecord::Base !users_projects.where(:user_id => user.id).empty? end - def allow_write_for?(user) + def guest_access_for?(user) !users_projects.where(:user_id => user.id).empty? end - def allow_admin_for?(user) - !users_projects.where(:user_id => user.id, :project_access => [UsersProject::MASTER]).empty? || owner_id == user.id + def report_access_for?(user) + !users_projects.where(:user_id => user.id, :project_access => [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty? end - def allow_pull_for?(user) - !users_projects.where(:user_id => user.id, :project_access => [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty? + def dev_access_for?(user) + !users_projects.where(:user_id => user.id, :project_access => [UsersProject::DEVELOPER, UsersProject::MASTER]).empty? + end + + def master_access_for?(user) + !users_projects.where(:user_id => user.id, :project_access => [UsersProject::MASTER]).empty? || owner_id == user.id end def root_ref diff --git a/app/models/wiki.rb b/app/models/wiki.rb new file mode 100644 index 00000000..62ac4cb8 --- /dev/null +++ b/app/models/wiki.rb @@ -0,0 +1,33 @@ +class Wiki < ActiveRecord::Base + belongs_to :project + belongs_to :user + + validates :content, :title, :user_id, :presence => true + validates :title, :length => 1..250 + + before_update :set_slug + + + def to_param + slug + end + + protected + + def set_slug + self.slug = self.title.parameterize + end + + class << self + def regenerate_from wiki + regenerated_field = [:slug, :content, :title] + + new_wiki = Wiki.new + regenerated_field.each do |field| + new_wiki.send("#{field}=", wiki.send(field)) + end + new_wiki + end + + end +end diff --git a/app/views/layouts/_project_menu.html.haml b/app/views/layouts/_project_menu.html.haml index 5260fcd1..465b550e 100644 --- a/app/views/layouts/_project_menu.html.haml +++ b/app/views/layouts/_project_menu.html.haml @@ -12,7 +12,6 @@ = link_to project_issues_filter_path(@project), :class => (controller.controller_name == "issues") ? "current" : nil do Issues %span.count= @project.issues.opened.count - - if @project.merge_requests_enabled = link_to project_merge_requests_path(@project), :class => (controller.controller_name == "merge_requests") ? "current" : nil do Merge Requests @@ -21,3 +20,7 @@ - if @project.wall_enabled = link_to wall_project_path(@project), :class => current_page?(:controller => "projects", :action => "wall", :id => @project) ? "current" : nil do Wall + + - if @project.wiki_enabled + = link_to project_wiki_path(@project, :index), :class => (controller.controller_name == "wikis") ? "current" : nil do + Wiki diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml index 167a7d00..6137aa5e 100644 --- a/app/views/projects/_form.html.haml +++ b/app/views/projects/_form.html.haml @@ -41,6 +41,10 @@ .clearfix = f.label :wall_enabled, "Wall" .input= f.check_box :wall_enabled + + .clearfix + = f.label :wiki_enabled, "Wiki" + .input= f.check_box :wiki_enabled .clearfix = f.label :description diff --git a/app/views/wikis/_form.html.haml b/app/views/wikis/_form.html.haml new file mode 100644 index 00000000..f88e67bf --- /dev/null +++ b/app/views/wikis/_form.html.haml @@ -0,0 +1,18 @@ += form_for [@project, @wiki] do |f| + -if @wiki.errors.any? + #error_explanation + %h2= "#{pluralize(@wiki.errors.count, "error")} prohibited this wiki from being saved:" + %ul + - @wiki.errors.full_messages.each do |msg| + %li= msg + + .clearfix + = f.label :title + .input= f.text_field :title, :class => :xxlarge + = f.hidden_field :slug + .clearfix + = f.label :content + .input= f.text_area :content, :class => :xxlarge + .actions + = f.submit 'Save', :class => "primary btn" + = link_to "Cancel", project_wiki_path(@project, :index), :class => "btn" diff --git a/app/views/wikis/edit.html.haml b/app/views/wikis/edit.html.haml new file mode 100644 index 00000000..df3274b1 --- /dev/null +++ b/app/views/wikis/edit.html.haml @@ -0,0 +1,3 @@ +%h1 Editing page + += render 'form' diff --git a/app/views/wikis/history.html.haml b/app/views/wikis/history.html.haml new file mode 100644 index 00000000..53bfe7ae --- /dev/null +++ b/app/views/wikis/history.html.haml @@ -0,0 +1,19 @@ +%h2 Versions +%table + %thead + %tr + %th # + %th last edit + %th created by + %tbody + - @wikis.each_with_index do |wiki_page, i| + %tr + %td= i + 1 + %td + = link_to wiki_page.created_at.to_s(:short), project_wiki_path(@project, wiki_page, :old_page_id => wiki_page.id) + ( + = time_ago_in_words(wiki_page.created_at) + ago + ) + %td= wiki_page.user.name + diff --git a/app/views/wikis/show.html.haml b/app/views/wikis/show.html.haml new file mode 100644 index 00000000..63a166fe --- /dev/null +++ b/app/views/wikis/show.html.haml @@ -0,0 +1,15 @@ +%h3 + = @wiki.title + = link_to edit_project_wiki_path(@project, @wiki), :class => "right btn small" do + Edit + - if can? current_user, :write_wiki, @project + = link_to history_project_wiki_path(@project, @wiki), :class => "right btn small" do + History +%hr + += markdown_to_html @wiki.content + +%p.time Last edited by #{@wiki.user.name}, in #{time_ago_in_words @wiki.created_at} +- if can? current_user, :write_wiki, @project + = link_to project_wiki_path(@project, @wiki), :confirm => "Are you sure you want to delete this page?", :method => :delete do + Delete this page diff --git a/config/routes.rb b/config/routes.rb index 5d880df1..653ec089 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,6 @@ Gitlab::Application.routes.draw do + # Optionally, enable Resque here require 'resque/server' mount Resque::Server.new, at: '/info/resque' @@ -55,6 +56,12 @@ Gitlab::Application.routes.draw do get "files" end + resources :wikis, :only => [:show, :edit, :destroy, :create] do + member do + get "history" + end + end + resource :repository do member do get "branches" diff --git a/db/migrate/20120216215008_create_wikis.rb b/db/migrate/20120216215008_create_wikis.rb new file mode 100644 index 00000000..38947df3 --- /dev/null +++ b/db/migrate/20120216215008_create_wikis.rb @@ -0,0 +1,11 @@ +class CreateWikis < ActiveRecord::Migration + def change + create_table :wikis do |t| + t.string :title + t.text :content + t.integer :project_id + + t.timestamps + end + end +end diff --git a/db/migrate/20120219130957_add_slug_to_wiki.rb b/db/migrate/20120219130957_add_slug_to_wiki.rb new file mode 100644 index 00000000..5f2d5970 --- /dev/null +++ b/db/migrate/20120219130957_add_slug_to_wiki.rb @@ -0,0 +1,6 @@ +class AddSlugToWiki < ActiveRecord::Migration + def change + add_column :wikis, :slug, :string + + end +end diff --git a/db/migrate/20120219140810_add_wiki_enabled_to_project.rb b/db/migrate/20120219140810_add_wiki_enabled_to_project.rb new file mode 100644 index 00000000..ebd71bea --- /dev/null +++ b/db/migrate/20120219140810_add_wiki_enabled_to_project.rb @@ -0,0 +1,6 @@ +class AddWikiEnabledToProject < ActiveRecord::Migration + def change + add_column :projects, :wiki_enabled, :boolean, :default => true, :null => false + + end +end diff --git a/db/migrate/20120219193300_add_user_to_wiki.rb b/db/migrate/20120219193300_add_user_to_wiki.rb new file mode 100644 index 00000000..8a6c0a06 --- /dev/null +++ b/db/migrate/20120219193300_add_user_to_wiki.rb @@ -0,0 +1,6 @@ +class AddUserToWiki < ActiveRecord::Migration + def change + add_column :wikis, :user_id, :integer + + end +end diff --git a/db/schema.rb b/db/schema.rb index 0ec8cfaf..c32df7ea 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20120216085842) do +ActiveRecord::Schema.define(:version => 20120219193300) do create_table "issues", :force => true do |t| t.string "title" @@ -80,6 +80,7 @@ ActiveRecord::Schema.define(:version => 20120216085842) do t.boolean "issues_enabled", :default => true, :null => false t.boolean "wall_enabled", :default => true, :null => false t.boolean "merge_requests_enabled", :default => true, :null => false + t.boolean "wiki_enabled", :default => true, :null => false end create_table "protected_branches", :force => true do |t| @@ -158,4 +159,14 @@ ActiveRecord::Schema.define(:version => 20120216085842) do t.datetime "updated_at" end + create_table "wikis", :force => true do |t| + t.string "title" + t.text "content" + t.integer "project_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "slug" + t.integer "user_id" + end + end diff --git a/spec/factories.rb b/spec/factories.rb index 15e54ed2..6d7a4cb7 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -59,3 +59,8 @@ end Factory.add(:web_hook, WebHook) do |obj| obj.url = Faker::Internet.url end + +Factory.add(:wikis, WebHook) do |obj| + obj.title = Faker::Lorem.sentence + obj.content = Faker::Lorem.sentence +end diff --git a/spec/requests/wikis_spec.rb b/spec/requests/wikis_spec.rb new file mode 100644 index 00000000..fd66b5e4 --- /dev/null +++ b/spec/requests/wikis_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe "Wiki" do + let(:project) { Factory :project } + + before do + login_as :user + project.add_access(@user, :read, :write) + end + + describe "Add pages" do + before do + visit project_wiki_path(project, :index) + end + + it "should see form" do + page.should have_content("Editing page") + end + + it "should see added page" do + fill_in "Title", :with => 'Test title' + fill_in "Content", :with => '[link test](test)' + click_on "Save" + + page.should have_content("Test title") + page.should have_content("link test") + + click_link "link test" + + page.should have_content("Editing page") + end + + end + +end