diff --git a/.gitignore b/.gitignore index 0f2c82fccf..3446dff95d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ config/mail.yml *~ db/*.sqlite* db/schema.rb +db/db_development +db/db_test +db/db_test-journal .*.swp .*.swo .DS_Store diff --git a/Gemfile b/Gemfile index b8121c7ee5..63c15f3769 100644 --- a/Gemfile +++ b/Gemfile @@ -57,4 +57,10 @@ group :development, :test do gem 'cucumber-rails-training-wheels' gem 'database_cleaner' gem 'capybara' + gem "pry" +end + +group :development do + gem "better_errors" + gem "binding_of_caller" end diff --git a/Gemfile.lock b/Gemfile.lock index 64eb36cc1c..67c9103a1a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,5 +1,5 @@ GEM - remote: http://rubygems.org/ + remote: https://rubygems.org/ specs: RedCloth (4.2.9) abstract (1.0.0) @@ -34,6 +34,11 @@ GEM addressable (2.3.2) archive-tar-minitar (0.5.2) arel (2.0.10) + better_errors (0.0.8) + coderay + erubis + binding_of_caller (0.7.2) + debug_inspector (>= 0.0.1) bluecloth (2.2.0) builder (2.1.2) capybara (1.1.2) @@ -60,6 +65,7 @@ GEM cucumber-rails (>= 1.1.1) daemons (1.1.9) database_cleaner (0.8.0) + debug_inspector (0.0.2) diff-lcs (1.1.3) erubis (2.6.6) abstract (>= 1.0.0) @@ -87,6 +93,8 @@ GEM i18n (>= 0.4.0) mime-types (~> 1.16) treetop (~> 1.4.8) + method_source (0.6.7) + ruby_parser (>= 2.3.1) mime-types (1.19) mini_magick (1.3.3) subexec (~> 0.0.4) @@ -94,6 +102,11 @@ GEM nokogiri (1.5.5) pg (0.14.1) polyglot (0.3.3) + pry (0.9.7.4) + coderay (~> 0.9.8) + method_source (~> 0.6.7) + ruby_parser (>= 2.3.1) + slop (~> 2.1.0) rack (1.2.5) rack-mount (0.6.14) rack (>= 1.0.0) @@ -141,6 +154,8 @@ GEM ruby-debug-base19 (>= 0.11.19) ruby_core_source (0.1.5) archive-tar-minitar (>= 0.5.2) + ruby_parser (3.8.1) + sexp_processor (~> 4.1) rubypants (0.2.0) rubyzip (0.9.9) selenium-webdriver (2.25.0) @@ -148,10 +163,12 @@ GEM libwebsocket (~> 0.1.3) multi_json (~> 1.0) rubyzip + sexp_processor (4.7.0) simplecov (0.6.4) multi_json (~> 1.0) simplecov-html (~> 0.5.3) simplecov-html (0.5.3) + slop (2.1.0) sqlite3 (1.3.6) subexec (0.0.4) thin (1.5.0) @@ -179,6 +196,8 @@ DEPENDENCIES acts_as_list acts_as_tree_rails3 addressable (~> 2.1) + better_errors + binding_of_caller bluecloth (~> 2.1) capybara coderay (~> 0.9) @@ -193,6 +212,7 @@ DEPENDENCIES kaminari mini_magick (~> 1.3.3) pg + pry rails (~> 3.0.10) rake (~> 0.9.2) recaptcha @@ -205,3 +225,6 @@ DEPENDENCIES thin uuidtools (~> 2.1.1) webrat + +BUNDLED WITH + 1.11.2 diff --git a/app/controllers/admin/categories_controller.rb b/app/controllers/admin/categories_controller.rb index b7026f8f29..6913a7f8da 100644 --- a/app/controllers/admin/categories_controller.rb +++ b/app/controllers/admin/categories_controller.rb @@ -25,7 +25,7 @@ def destroy def new_or_edit @categories = Category.find(:all) - @category = Category.find(params[:id]) + @category = params[:id] ? (Category.find(params[:id])) : (Category.new) @category.attributes = params[:category] if request.post? respond_to do |format| @@ -43,7 +43,7 @@ def new_or_edit end def save_category - if @category.save! + if @category.save flash[:notice] = _('Category was successfully saved.') else flash[:error] = _('Category could not be saved.') diff --git a/app/controllers/admin/content_controller.rb b/app/controllers/admin/content_controller.rb index 6ef4ebf969..7db41da773 100644 --- a/app/controllers/admin/content_controller.rb +++ b/app/controllers/admin/content_controller.rb @@ -1,4 +1,5 @@ require 'base64' +require 'pry' module Admin; end class Admin::ContentController < Admin::BaseController @@ -113,6 +114,12 @@ def autosave render :text => nil end + def merge + @article = Article.find(params["id"]) + @article.merge_with(params[:merge_with]) + redirect_to :action => 'edit', id: @article.id + end + protected def get_fresh_or_existing_draft_for_article @@ -140,6 +147,7 @@ def do_add_or_remove_fu def real_action_for(action); { 'add' => :<<, 'remove' => :delete}[action]; end def new_or_edit + @articles = Article.all if current_user.admin? && params[:action] == "edit" id = params[:id] id = params[:article][:id] if params[:article] && params[:article][:id] @article = Article.get_or_build_article(id) diff --git a/app/models/article.rb b/app/models/article.rb index c80469b734..6fb89c411f 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -80,6 +80,17 @@ def has_child? Article.exists?({:parent_id => self.id}) end + def merge_with(merge_id) + Article.transaction do + merge_article = Article.find(merge_id) + self.body += merge_article.body + self.comments += merge_article.comments + merge_article.comments.reload + merge_article.destroy + self.save + end + end + attr_accessor :draft, :keywords has_state(:state, diff --git a/app/views/admin/shared/_edit.html.erb b/app/views/admin/shared/_edit.html.erb index a4f213ddb7..7c20ed00a9 100644 --- a/app/views/admin/shared/_edit.html.erb +++ b/app/views/admin/shared/_edit.html.erb @@ -4,3 +4,13 @@ <%= render :partial => "form" %> <% end %> + +<% if current_user.admin? && params[:id] %> +

You can associate the following resources

+
Merge Articles
+ <%= form_tag({action: :merge, :id => @article.id}, {:remote => true, :class => className}) do %> + <% options = options_from_collection_for_select(@articles, 'id', 'title') %> + <%= select_tag 'merge_with', options %> + <%= submit_tag("Merge", {:class => 'btn primary'}) %> + <% end %> +<% end %> \ No newline at end of file diff --git a/features/article_merge_admin.feature b/features/article_merge_admin.feature new file mode 100644 index 0000000000..162be11607 --- /dev/null +++ b/features/article_merge_admin.feature @@ -0,0 +1,54 @@ +Feature: Article Merge + + As a blog administrator + In order to keep my blog organized + I want to be able to merge similar articles on my blog + + Background: + Given the blog is set up + Given I am logged into the admin panel + Given the following article records + | title | author | body | user_id | published | + | Hello! | Ada Lovelace | Hello, world! | 2 | true | + | Goodbye! | Admin | Goodbye, waterfalls! | 1 | true | + Given the following comment records + | author | body | article_id | published | + | Ada Lovelace | Hello, article! | 3 | true | + | Charles Babbage | Goodbye, article. | 4 | true | + + Scenario: Display Merge Articles Button to Admin on Edit Page + Given I am on the edit article page for "Hello!" + Then I should see "Merge Articles" + And I should see "Merge" button + + Scenario: Do not Display Merge Articles Button to Admin on New Page + And I am on the new article page + Then I should not see "Merge Articles" + And I should not see "Merge" button + + Scenario: Merge text of two articles + Given I am on the edit article page for "Hello!" + And I select "Goodbye!" from the dropdown + And I press "Merge" + Then I should be on the edit article page for "Hello!" + When I go to the article page for "Hello!" + Then I should see "Goodbye, waterfalls!" + And I should see "Hello, world!" + + Scenario: Merge the comments of two articles + Given I am on the article page for "Hello!" + And the "Hello!" article should have 1 comment + When I go to the edit article page for "Hello!" + And I select "Goodbye!" from the dropdown + And I press "Merge" + Then the "Hello!" article should have 2 comments + + Scenario: Delete the merged article + Given I am on the home page + Then I should see "Goodbye!" + When I go to the edit article page for "Hello!" + And I select "Goodbye!" from the dropdown + And I press "Merge" + When I go to the homepage + Then I should not see "Goodbye!" + diff --git a/features/article_merge_not_admin.feature b/features/article_merge_not_admin.feature new file mode 100644 index 0000000000..4c5788802f --- /dev/null +++ b/features/article_merge_not_admin.feature @@ -0,0 +1,23 @@ +Feature: Article Merge + + As a blog contributor + In order to keep my blog organized + I want to be able to merge similar articles on my blog + + Background: + Given the blog is set up + Given I am logged in as contributor to the admin panel + Given the following article records + | title | author | body | user_id | + | Hello, world! | Ada Lovelace | Hello, world! | 2 | + | Goodbye! | Admin | Goodbye, waterfalls! | 1 | + + Scenario: Do not Display Merge Articles Button on Edit Page + Given I am on the edit article page for "Hello, world!" + Then I should not see "Merge Articles" + And I should not see "Merge" button + + Scenario: Do not Display Merge Articles Button on New Page + Given I am on the new article page + Then I should not see "Merge Articles" + And I should not see "Merge" button diff --git a/features/category.feature b/features/category.feature new file mode 100644 index 0000000000..682202be5d --- /dev/null +++ b/features/category.feature @@ -0,0 +1,44 @@ +Feature: Categories + As a blog administrator + In order to share my thoughts with the world + I want to be able to create and manage categories to my blog + + Background: + Given the blog is set up + And I am logged into the admin panel + + Scenario: Successfully add new category + Given I am on the new category page + When I fill in "category_name" with "Foobar" + And I press "Save" + Then I should be on the new category page + And I should see "Foobar" + And page should have success message "Category was successfully saved." + + Scenario: Error message with blank new category + Given I am on the new category page + And I press "Save" + Then I should be on the new category page + And page should have error message "Category could not be saved." + + Scenario: Update a category + Given the following category records + | name | + | Foobar | + And I am on the edit page for "Foobar" + When I fill in "category_name" with "Foobaz" + And I press "Save" + Then I should be on the new category page + And I should see "Foobaz" + And page should have success message "Category was successfully saved." + + Scenario: Error message with blank update category + Given the following category records + | name | + | Foobar | + And I am on the edit page for "Foobar" + When I fill in "category_name" with "" + And I press "Save" + Then I should be on the new category page + And I should see "Foobar" + And page should have error message "Category could not be saved." \ No newline at end of file diff --git a/features/step_definitions/web_steps.rb b/features/step_definitions/web_steps.rb index 6315105872..e673d8392f 100644 --- a/features/step_definitions/web_steps.rb +++ b/features/step_definitions/web_steps.rb @@ -41,6 +41,12 @@ def with_scope(locator) :profile_id => 1, :name => 'admin', :state => 'active'}) + User.create!({:login => 'ada', + :password => 'lovelace', + :email => 'ada@lovelace.com', + :profile_id => 2, + :name => 'ada', + :state => 'active'}) end And /^I am logged into the admin panel$/ do @@ -55,6 +61,18 @@ def with_scope(locator) end end +Given /^I am logged in as contributor to the admin panel$/ do + visit '/accounts/login' + fill_in 'user_login', :with => 'ada' + fill_in 'user_password', :with => 'lovelace' + click_button 'Login' + if page.respond_to? :should + page.should have_content('Login successful') + else + assert page.has_content?('Login successful') + end +end + # Single-line step scoper When /^(.*) within (.*[^:])$/ do |step, parent| with_scope(parent) { When step } @@ -276,3 +294,30 @@ def with_scope(locator) Then /^show me the page$/ do save_and_open_page end + +Then /^page should have (.+) message "([^\"]*)"$/ do |type, text| + page.has_css?("div.#{type}", :text => text, :visible => true) +end + +Given /^the following (.+) records$/ do |factory, table| + table.hashes.each do |hash| + Factory(factory, hash) + end +end + +Then /^I should see "([^"]*)" button/ do |name| + should have_button name +end + +Then /^I should not see "(.*?)" button$/ do |name| + should have_no_button name +end + +Given /^I select "(.*?)" from the dropdown$/ do |title| + select title, from: "merge_with" +end + +Given /^the "(.*?)" article should have (\d+) comments*$/ do |title, num| + article = Article.where(title: title).first + article.comments.count.should == num.to_i +end diff --git a/features/support/env.rb b/features/support/env.rb index 492ce27b53..f7498fa955 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -5,6 +5,7 @@ # files. require 'cucumber/rails' +require "#{Rails.root}/spec/factories" # Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In # order to ease the transition to Capybara we set the default here. If you'd diff --git a/features/support/paths.rb b/features/support/paths.rb index e7e00e5d89..4fd76c9ab3 100644 --- a/features/support/paths.rb +++ b/features/support/paths.rb @@ -17,6 +17,15 @@ def path_to(page_name) '/' when /^the new article page$/ '/admin/content/new' + when /^the new category page$/ + '/admin/categories/new' + when /^the article page for "(.*)"$/ + "#{Article.where(title: $1).first.short_url}" + when /^the edit article page for "(.*)"$/ + "/admin/content/edit/#{Article.where(title: $1).first.id}" + + when /^the edit page for "(.*)"$/ + "/admin/categories/edit/#{Category.find_by_name($1).id}" # Add more mappings here. # Here is an example that pulls values out of the Regexp: diff --git a/features/write_article.feature b/features/write_article.feature index 9d1c789980..ff5482ca22 100644 --- a/features/write_article.feature +++ b/features/write_article.feature @@ -16,4 +16,4 @@ Feature: Write Articles When I go to the home page Then I should see "Foobar" When I follow "Foobar" - Then I should see "Lorem Ipsum" + Then I should see "Lorem Ipsum" \ No newline at end of file diff --git a/public/javascripts/ckeditor/config.bak b/public/javascripts/ckeditor/config.bak old mode 100644 new mode 100755 index 187db086f2..492800fd66 --- a/public/javascripts/ckeditor/config.bak +++ b/public/javascripts/ckeditor/config.bak @@ -8,7 +8,7 @@ CKEDITOR.editorConfig = function( config ) config.PreserveSessionOnFileBrowser = true; // Define changes to default configuration here. For example: //config.language = ''; - config.uiColor = '#E0ECFF'; + config.uiColor = '#eee'; config.toolbar = 'Basic'; config.entities_greek = false; config.entities_latin = false; diff --git a/public/javascripts/ckeditor/config.js b/public/javascripts/ckeditor/config.js old mode 100644 new mode 100755 index 187db086f2..492800fd66 --- a/public/javascripts/ckeditor/config.js +++ b/public/javascripts/ckeditor/config.js @@ -8,7 +8,7 @@ CKEDITOR.editorConfig = function( config ) config.PreserveSessionOnFileBrowser = true; // Define changes to default configuration here. For example: //config.language = ''; - config.uiColor = '#E0ECFF'; + config.uiColor = '#eee'; config.toolbar = 'Basic'; config.entities_greek = false; config.entities_latin = false; diff --git a/spec/controllers/admin/categories_controller_spec.rb b/spec/controllers/admin/categories_controller_spec.rb index bb290f4a0d..77efe4b168 100644 --- a/spec/controllers/admin/categories_controller_spec.rb +++ b/spec/controllers/admin/categories_controller_spec.rb @@ -13,7 +13,36 @@ it "test_index" do get :index - assert_response :redirect, :action => 'index' + assert_response :redirect, :action => 'new' + end + + describe "test_new" do + before(:each) do + get :new + end + + it 'should render template new' do + assert_template 'new' + end + + it 'should create a category with empty attributes' do + assigns(:category).should_not be_nil + assert !assigns(:category).valid? + assigns(:categories).should_not be_nil + end + + it "successfully creates with valid params" do + post :new, { "category"=>{"name"=>"Foobar"} } + expect(flash[:notice]).to eq("Category was successfully saved.") + assert_response :redirect, :action => 'index' + end + + it "fails to create with invalid params" do + post :new, { "category"=>{"name"=>""}} + expect(flash[:error]).to eq("Category could not be saved.") + assert_response :redirect, :action => 'new' + end + end describe "test_edit" do @@ -34,10 +63,20 @@ end end - it "test_update" do - post :edit, :id => Factory(:category).id - assert_response :redirect, :action => 'index' + describe "test_update" do + it "successfully updates with valid params" do + post :edit, :id => Factory(:category).id + expect(flash[:notice]).to eq("Category was successfully saved.") + assert_response :redirect, :action => 'index' + end + + it "fails to update with invalid params" do + post :edit, { :id => Factory(:category).id, "category"=>{"name"=>"", "keywords"=>"", "permalink"=>"general", "description"=>""}} + expect(flash[:error]).to eq("Category could not be saved.") + assert_response :redirect, :action => 'new' + end end + describe "test_destroy with GET" do before(:each) do