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