diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 1aeb90c489..461715ba17 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -303,7 +303,8 @@ class WikiController < ApplicationController end end @page.destroy - redirect_to :action => 'index', :project_id => @project + + redirect_to @wiki.pages.any? ? {:action => 'index', :project_id => @project} : project_path(@project) end # Export wiki to a single html file diff --git a/app/controllers/wiki_menu_items_controller.rb b/app/controllers/wiki_menu_items_controller.rb index ee65450409..867088f9f7 100644 --- a/app/controllers/wiki_menu_items_controller.rb +++ b/app/controllers/wiki_menu_items_controller.rb @@ -48,7 +48,13 @@ class WikiMenuItemsController < ApplicationController get_data_from_params(params) if wiki_menu_setting == 'no_item' - @wiki_menu_item.destroy unless @wiki_menu_item.nil? + unless @wiki_menu_item.nil? + if @wiki_menu_item.is_only_main_item? + redirect_to(select_main_menu_item_project_wiki_path(@project, @page.id)) and return + else + @wiki_menu_item.destroy + end + end else @wiki_menu_item.wiki_id = @page.wiki.id @wiki_menu_item.name = params[:wiki_menu_item][:name] @@ -58,18 +64,7 @@ class WikiMenuItemsController < ApplicationController @wiki_menu_item.parent_id = parent_wiki_menu_item elsif wiki_menu_setting == 'main_item' @wiki_menu_item.parent_id = nil - - if params[:wiki_menu_item][:new_wiki_page] == "1" - @wiki_menu_item.new_wiki_page = true - elsif params[:wiki_menu_item][:new_wiki_page] == "0" - @wiki_menu_item.new_wiki_page = false - end - - if params[:wiki_menu_item][:index_page] == "1" - @wiki_menu_item.index_page = true - elsif params[:wiki_menu_item][:index_page] == "0" - @wiki_menu_item.index_page = false - end + assign_wiki_menu_item_params @wiki_menu_item end end @@ -83,10 +78,27 @@ class WikiMenuItemsController < ApplicationController end end + def select_main_menu_item + wiki_page_title = params[:id] + @possible_wiki_pages = @project.wiki.pages.all(:include => :parent).reject{|page| page.title == wiki_page_title || page.menu_item.present? && page.menu_item.is_main_item?} + end + + def replace_main_menu_item + current_page = WikiPage.find params[:id] + current_menu_item = current_page.menu_item + + if page = WikiPage.find_by_id(params[:wiki_page][:id]) + create_main_menu_item_for_wiki_page(page, current_menu_item.options) + end + + current_menu_item.destroy + + redirect_to action: :edit, id: current_page.title + end + private def get_data_from_params(params) - @project = Project.find(params[:project_id]) @page_title = params[:id] wiki_id = @project.wiki.id @@ -102,4 +114,31 @@ class WikiMenuItemsController < ApplicationController @page.nearest_parent_menu_item(:is_main_item => true).try :id end end + + def assign_wiki_menu_item_params(menu_item) + if params[:wiki_menu_item][:new_wiki_page] == "1" + menu_item.new_wiki_page = true + elsif params[:wiki_menu_item][:new_wiki_page] == "0" + menu_item.new_wiki_page = false + end + + if params[:wiki_menu_item][:index_page] == "1" + menu_item.index_page = true + elsif params[:wiki_menu_item][:index_page] == "0" + menu_item.index_page = false + end + end + + def create_main_menu_item_for_wiki_page(page, options={}) + wiki = page.wiki + + menu_item = if item = page.menu_item + item.tap {|item| item.parent_id = nil} + else + wiki.wiki_menu_items.build(title: page.title, name: page.pretty_title) + end + + menu_item.options = options + menu_item.save + end end diff --git a/app/models/project.rb b/app/models/project.rb index aec2b2fd15..628c2338d6 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -718,6 +718,12 @@ class Project < ActiveRecord::Base enabled_modules.collect(&:name) end + def disable_module(module_name) + if module_enabled? module_name + self.enabled_module_names = enabled_module_names - [module_name.to_s] + end + end + safe_attributes 'name', 'description', 'summary', diff --git a/app/models/wiki_menu_item.rb b/app/models/wiki_menu_item.rb index 3accc221b6..c200de3412 100644 --- a/app/models/wiki_menu_item.rb +++ b/app/models/wiki_menu_item.rb @@ -39,7 +39,7 @@ class WikiMenuItem < ActiveRecord::Base :order => 'id ASC'} } - attr_accessible :name, :title + attr_accessible :name, :title, :wiki_id validates_presence_of :title validates_format_of :title, :with => /\A[^,\.\/\?\;\|\:]*\z/ @@ -84,4 +84,8 @@ class WikiMenuItem < ActiveRecord::Base def is_sub_item? !parent_id.nil? end + + def is_only_main_item? + self.class.main_items(wiki.id) == [self] + end end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index e6ac96ae48..2c9360d0ac 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -67,10 +67,17 @@ class WikiPage < ActiveRecord::Base :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id" } + scope :main_pages, lambda {|wiki_id| + { conditions: {wiki_id: wiki_id, parent_id: nil} } + } + # Wiki pages that are protected by default DEFAULT_PROTECTED_PAGES = %w(sidebar) after_destroy :delete_wiki_menu_item + after_destroy do |wiki_page| + wiki_page.wiki.project.disable_module(:wiki) and wiki_page.wiki.destroy if is_only_wiki_page? + end def check_and_mark_as_protected if new_record? && DEFAULT_PROTECTED_PAGES.include?(title.to_s.downcase) @@ -249,6 +256,10 @@ class WikiPage < ActiveRecord::Base def validate_same_project errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id) end + + def is_only_wiki_page? + wiki.pages.reject {|page| page == self}.empty? + end end class WikiDiff < Redmine::Helpers::Diff diff --git a/app/views/wiki_menu_items/select_main_menu_item.html.erb b/app/views/wiki_menu_items/select_main_menu_item.html.erb new file mode 100644 index 0000000000..c88066ec66 --- /dev/null +++ b/app/views/wiki_menu_items/select_main_menu_item.html.erb @@ -0,0 +1,45 @@ +<%#-- copyright +OpenProject is a project management system. +Copyright (C) 2012-2013 the OpenProject Foundation (OPF) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See doc/COPYRIGHT.rdoc for more details. + +++#%> + +

<%= t(:label_select_main_menu_item) %>

+ +<%= labelled_tabular_form_for :wiki_page, url: { action: 'replace_main_menu_item' } do |f| %> +
+

+ <%= f.select :id, + "".html_safe + wiki_page_options_for_select(@possible_wiki_pages), + {label: WikiPage.human_attribute_name(:title) }, + {size: "#{@possible_wiki_pages.size + 1}", id: 'main-menu-item-select'} %> +

+ +
+ + + <%= submit_tag t(:button_save) %> +<% end %> diff --git a/config/locales/de.yml b/config/locales/de.yml index a151b4a256..04220175de 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -601,6 +601,7 @@ de: label_calendar: "Kalender" label_calendar_show: "Kalender anzeigen" label_category: "Kategorie" + label_select_main_menu_item: Neuen Hauptmenüpunkt auwählen label_change_plural: "Änderungen" label_change_properties: "Eigenschaften ändern" label_change_status: "Statuswechsel" diff --git a/config/locales/en.yml b/config/locales/en.yml index 1daadea715..b716150df7 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -596,6 +596,7 @@ en: label_calendar: "Calendar" label_calendar_show: "Show Calendar" label_category: "Category" + label_select_main_menu_item: Select new main menu item label_change_plural: "Changes" label_change_properties: "Change properties" label_change_status: "Change status" diff --git a/config/routes.rb b/config/routes.rb index e5cbbe50ec..0e9f51c71f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -201,6 +201,8 @@ OpenProject::Application.routes.draw do post :protect post :add_attachment get :list_attachments + get :select_main_menu_item, to: 'wiki_menu_items#select_main_menu_item' + post :replace_main_menu_item, to: 'wiki_menu_items#replace_main_menu_item' end end # as routes for index and show are swapped diff --git a/features/step_definitions/wiki_menu_item_steps.rb b/features/step_definitions/wiki_menu_item_steps.rb new file mode 100644 index 0000000000..1889992138 --- /dev/null +++ b/features/step_definitions/wiki_menu_item_steps.rb @@ -0,0 +1,32 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2013 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See doc/COPYRIGHT.rdoc for more details. +#++ + +Given /^the wiki menu item of the wiki page "(.*?)" of project "(.*?)" has been deleted$/ do |item_name, project_name| + project = Project.find_by_name project_name + WikiPage.where(title: item_name, wiki_id: project.wiki.id).first.delete_wiki_menu_item +end diff --git a/features/wiki/wiki_index.feature b/features/wiki/wiki_index.feature index a2321c029a..78816d1d4f 100644 --- a/features/wiki/wiki_index.feature +++ b/features/wiki/wiki_index.feature @@ -64,7 +64,3 @@ Feature: Viewing the wiki index page When I go to the wiki index page below the "ParentWikiPage" page of the project called "project1" Then I should see "Index by title" within "#content" And there should be no menu item selected - - - - diff --git a/features/wiki_menu_items/wiki_menu_items.feature b/features/wiki_menu_items/wiki_menu_items.feature index f14184a15c..70b8d84633 100644 --- a/features/wiki_menu_items/wiki_menu_items.feature +++ b/features/wiki_menu_items/wiki_menu_items.feature @@ -29,13 +29,14 @@ Feature: Wiki menu items Background: Given there is 1 project with the following: - | name | Awesome Project | - | identifier | awesome-project | + | name | Awesome Project | + | identifier | awesome-project | And there is a role "member" And the role "member" may have the following rights: - | view_wiki_pages | - | edit_wiki_pages | - | manage_wiki_menu | + | view_wiki_pages | + | edit_wiki_pages | + | delete_wiki_pages | + | manage_wiki_menu | And there is 1 user with the following: | login | bob | And the user "bob" is a "member" in the project "Awesome Project" @@ -116,3 +117,19 @@ Feature: Wiki menu items And I choose "Do not show this wikipage in project navigation" And I press "Save" Then I should not see "Wiki" within "#main-menu" + + @javascript + Scenario: When I delete the last wiki page with a menu item I can select a new menu item and the menu item is replaced + Given the project "Awesome Project" has a wiki menu item with the following: + | title | AwesomePage | + | name | AwesomePage | + And the wiki menu item of the wiki page "Wiki" of project "Awesome Project" has been deleted + When I go to the wiki page "AwesomePage" for the project called "Awesome Project" + And I click on "More functions" + And I click on "Configure menu item" + And I choose "Do not show this wikipage in project navigation" + And I press "Save" + And I select "Wiki" from "main-menu-item-select" + And I press "Save" + Then I should not see "AwesomePage" within "#main-menu" + Then I should see "Wiki" within "#main-menu" diff --git a/lib/redmine.rb b/lib/redmine.rb index fb457f7eab..9b88ea11b7 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -152,7 +152,7 @@ Redmine::AccessControl.map do |map| map.project_module :wiki do |map| map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member - map.permission :manage_wiki_menu, {:wiki_menu_items => [:edit, :update]}, :require => :member + map.permission :manage_wiki_menu, {:wiki_menu_items => [:edit, :update, :select_main_menu_item, :replace_main_menu_item]}, :require => :member map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member map.permission :change_wiki_parent_page, {:wiki => [:edit_parent_page, :update_parent_page]}, :require => :member diff --git a/spec/controllers/wiki_controller_spec.rb b/spec/controllers/wiki_controller_spec.rb index 0bddfd7808..fb5067216f 100644 --- a/spec/controllers/wiki_controller_spec.rb +++ b/spec/controllers/wiki_controller_spec.rb @@ -49,7 +49,7 @@ describe WikiController do # creating pages @existing_page = FactoryGirl.create(:wiki_page, :wiki_id => @project.wiki.id, - :title => 'ExisitingPage') + :title => 'ExistingPage') # creating page contents FactoryGirl.create(:wiki_content, :page_id => @existing_page.id, @@ -187,7 +187,31 @@ describe WikiController do end end end - end + + describe 'destroy' do + describe 'successful action' do + context 'when it is not the only wiki page' do + let(:wiki) { @project.wiki } + + before do + another_wiki_page = FactoryGirl.create :wiki_page, wiki: wiki + end + + it 'redirects to wiki#index' do + delete :destroy, project_id: @project, id: @existing_page + response.should redirect_to action: 'index', project_id: @project + end + end + + context 'when it is the only wiki page' do + it 'redirects to projects#show' do + delete :destroy, project_id: @project, id: @existing_page + response.should redirect_to project_path(@project) + end + end + end + end + end # describe 'actions' describe 'view related stuff' do render_views diff --git a/spec/controllers/wiki_menu_items_controller_spec.rb b/spec/controllers/wiki_menu_items_controller_spec.rb index 445133d557..cfe7814ace 100644 --- a/spec/controllers/wiki_menu_items_controller_spec.rb +++ b/spec/controllers/wiki_menu_items_controller_spec.rb @@ -35,20 +35,8 @@ describe WikiMenuItemsController do let(:project) { FactoryGirl.create(:project).reload } # a wiki is created for project, but the object doesn't know of it (FIXME?) let(:wiki) { project.wiki } - # wiki pages let(:wiki_page) { FactoryGirl.create(:wiki_page, :wiki => wiki) } # first wiki page without child pages - let!(:top_level_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => wiki_page.title) } - let(:another_wiki_page) { FactoryGirl.create(:wiki_page, :wiki => wiki) } # second wiki page with two child pages - let!(:another_wiki_page_top_level_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => another_wiki_page.title) } - - # child pages of another_wiki_page - let(:child_page) { FactoryGirl.create(:wiki_page, :parent => another_wiki_page, :wiki => wiki) } - let!(:child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => child_page.title) } - let(:another_child_page) { FactoryGirl.create(:wiki_page, :parent => another_wiki_page, :wiki => wiki) } - let!(:another_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => another_child_page.title, :parent => top_level_wiki_menu_item) } - - let(:grand_child_page) { FactoryGirl.create(:wiki_page, :parent => child_page, :wiki => wiki) } - let!(:grand_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => grand_child_page.title) } + let!(:top_level_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :with_menu_item_options, :wiki => wiki, :title => wiki_page.title) } before :each do # log in user @@ -56,6 +44,19 @@ describe WikiMenuItemsController do end describe :edit do + # more wiki pages with menu items + let(:another_wiki_page) { FactoryGirl.create(:wiki_page, :wiki => wiki) } # second wiki page with two child pages + let!(:another_wiki_page_top_level_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => another_wiki_page.title) } + + # child pages of another_wiki_page + let(:child_page) { FactoryGirl.create(:wiki_page, :parent => another_wiki_page, :wiki => wiki) } + let!(:child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => child_page.title) } + let(:another_child_page) { FactoryGirl.create(:wiki_page, :parent => another_wiki_page, :wiki => wiki) } + let!(:another_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => another_child_page.title, :parent => top_level_wiki_menu_item) } + + let(:grand_child_page) { FactoryGirl.create(:wiki_page, :parent => child_page, :wiki => wiki) } + let!(:grand_child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => grand_child_page.title) } + context 'when no parent wiki menu item has been configured yet' do context 'and it is a child page' do before { get :edit, project_id: project.id, id: child_page.title } @@ -91,4 +92,48 @@ describe WikiMenuItemsController do end end end + + shared_context 'when there is one more wiki page with a child page' do + let!(:another_wiki_page) { FactoryGirl.create(:wiki_page, :wiki => wiki) } # second wiki page with two child pages + let!(:child_page) { FactoryGirl.create(:wiki_page, :parent => another_wiki_page, :wiki => wiki) } + end + + describe :select_main_menu_item do + include_context 'when there is one more wiki page with a child page' + + before { get :select_main_menu_item, project_id: project, id: wiki_page.title } + subject { assigns['possible_wiki_pages'] } + + context 'when selecting a new wiki page to replace the current main menu item' do + it { should include another_wiki_page } + it { should include child_page } + it { should_not include wiki_page } + end + end + + describe :replace_main_menu_item do + include_context 'when there is one more wiki page with a child page' + + let(:selected_page) { child_page } + let(:new_menu_item) { selected_page.menu_item } + + before do + post :replace_main_menu_item, project_id: project, + id: wiki_page.id, + wiki_page: { id: selected_page.id } + end + + it 'destroys the current wiki menu item' do + wiki_page.menu_item.should be_nil + end + + it 'creates a new main menu item for the selected wiki page' do + selected_page.menu_item.should be_present + selected_page.menu_item.parent.should be_nil + end + + it 'transfers the menu item options to the selected wiki page' do + new_menu_item.options.should == { index_page: true, new_wiki_page: true } + end + end end diff --git a/spec/factories/wiki_menu_item_factory.rb b/spec/factories/wiki_menu_item_factory.rb index 60d0f97681..58ff08be2d 100644 --- a/spec/factories/wiki_menu_item_factory.rb +++ b/spec/factories/wiki_menu_item_factory.rb @@ -32,5 +32,10 @@ FactoryGirl.define do sequence(:name) {|n| "Item No. #{n}" } sequence(:title) {|n| "Wiki Title #{n}" } + + trait :with_menu_item_options do + index_page true + new_wiki_page true + end end end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 110b8c41a0..985c23fdeb 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -29,9 +29,11 @@ require 'spec_helper' describe WikiPage do + let(:project) { FactoryGirl.create(:project).reload } # a wiki is created for project, but the object doesn't know of it (FIXME?) + let(:wiki) { project.wiki } + let(:wiki_page) { FactoryGirl.create(:wiki_page, :wiki => wiki) } + describe '#nearest_parent_menu_item' do - let(:wiki_page) { FactoryGirl.create(:wiki_page) } - let(:wiki) { wiki_page.wiki } let!(:wiki_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => wiki_page.title) } let(:child_page) { FactoryGirl.create(:wiki_page, :parent => wiki_page, :wiki => wiki) } let!(:child_page_wiki_menu_item) { FactoryGirl.create(:wiki_menu_item, :wiki => wiki, :title => child_page.title, :parent => wiki_page_wiki_menu_item) } @@ -50,4 +52,37 @@ describe WikiPage do end end end + + describe '#destroy' do + context 'when the only wiki page is destroyed' do + before do + wiki_page.destroy + project.reload + end + + it 'deactivates the wiki module' do + project.module_enabled?(:wiki).should be_false + end + + it 'destroys the project wiki' do + project.wiki.should be_nil + end + end + + context 'when one of two wiki pages is destroyed' do + before do + another_wiki_page = FactoryGirl.create(:wiki_page, :wiki => wiki) + wiki_page.destroy + project.reload + end + + it 'does not deactivate the wiki module' do + project.module_enabled?(:wiki).should be_true + end + + it 'does not destroy the project wiki' do + project.wiki.should be_present + end + end + end end