diff --git a/app/controllers/spree/admin/taxonomies_controller.rb b/app/controllers/spree/admin/taxonomies_controller.rb new file mode 100644 index 0000000000..3010db2a52 --- /dev/null +++ b/app/controllers/spree/admin/taxonomies_controller.rb @@ -0,0 +1,21 @@ +module Spree + module Admin + class TaxonomiesController < ResourceController + respond_to :json, :only => [:get_children] + + def get_children + @taxons = Taxon.find(params[:parent_id]).children + end + + private + + def location_after_save + if @taxonomy.created_at == @taxonomy.updated_at + edit_admin_taxonomy_url(@taxonomy) + else + admin_taxonomies_url + end + end + end + end +end diff --git a/app/controllers/spree/admin/taxons_controller.rb b/app/controllers/spree/admin/taxons_controller.rb new file mode 100644 index 0000000000..57ae146d2a --- /dev/null +++ b/app/controllers/spree/admin/taxons_controller.rb @@ -0,0 +1,118 @@ +module Spree + module Admin + class TaxonsController < Spree::Admin::BaseController + respond_to :html, :json, :js + + def search + if params[:ids] + @taxons = Spree::Taxon.where(id: params[:ids].split(',')) + else + @taxons = Spree::Taxon.limit(20).search(name_cont: params[:q]).result + end + end + + def create + @taxonomy = Taxonomy.find(params[:taxonomy_id]) + @taxon = @taxonomy.taxons.build(params[:taxon]) + if @taxon.save + respond_with(@taxon) do |format| + format.json { render json: @taxon.to_json } + end + else + flash[:error] = Spree.t('errors.messages.could_not_create_taxon') + respond_with(@taxon) do |format| + format.html do + if redirect_to @taxonomy + edit_admin_taxonomy_url(@taxonomy) + else + admin_taxonomies_url + end + end + end + end + end + + def edit + @taxonomy = Taxonomy.find(params[:taxonomy_id]) + @taxon = @taxonomy.taxons.find(params[:id]) + @permalink_part = @taxon.permalink.split("/").last + end + + def update + @taxonomy = Taxonomy.find(params[:taxonomy_id]) + @taxon = @taxonomy.taxons.find(params[:id]) + parent_id = params[:taxon][:parent_id] + new_position = params[:taxon][:position] + + if parent_id || new_position # taxon is being moved + new_parent = parent_id.nil? ? @taxon.parent : Taxon.find(parent_id.to_i) + new_position = new_position.nil? ? -1 : new_position.to_i + + # Bellow is a very complicated way of finding where in nested set we + # should actually move the taxon to achieve sane results, + # JS is giving us the desired position, which was awesome for previous setup, + # but now it's quite complicated to find where we should put it as we have + # to differenciate between moving to the same branch, up down and into + # first position. + new_siblings = new_parent.children + if new_position <= 0 && new_siblings.empty? + @taxon.move_to_child_of(new_parent) + elsif new_parent.id != @taxon.parent_id + if new_position.zero? + @taxon.move_to_left_of(new_siblings.first) + else + @taxon.move_to_right_of(new_siblings[new_position - 1]) + end + elsif new_position < new_siblings.index(@taxon) + @taxon.move_to_left_of(new_siblings[new_position]) # we move up + else + @taxon.move_to_right_of(new_siblings[new_position - 1]) # we move down + end + # Reset legacy position, if any extensions still rely on it + new_parent.children.reload.each{ |t| t.update_column(:position, t.position) } + + if parent_id + @taxon.reload + @taxon.set_permalink + @taxon.save! + @update_children = true + end + end + + if params.key? "permalink_part" + parent_permalink = @taxon.permalink.split("/")[0...-1].join("/") + parent_permalink += "/" if parent_permalink.present? + params[:taxon][:permalink] = parent_permalink + params[:permalink_part] + end + # check if we need to rename child taxons if parent name or permalink changes + if params[:taxon][:name] != @taxon.name || params[:taxon][:permalink] != @taxon.permalink + @update_children = true + end + + if @taxon.update_attributes(params[:taxon]) + flash[:success] = flash_message_for(@taxon, :successfully_updated) + end + + # rename child taxons + if @update_children + @taxon.descendants.each do |taxon| + taxon.reload + taxon.set_permalink + taxon.save! + end + end + + respond_with(@taxon) do |format| + format.html { redirect_to edit_admin_taxonomy_url(@taxonomy) } + format.json { render json: @taxon.to_json } + end + end + + def destroy + @taxon = Taxon.find(params[:id]) + @taxon.destroy + respond_with(@taxon) { |format| format.json { render json: '' } } + end + end + end +end diff --git a/app/helpers/spree/admin/taxons_helper.rb b/app/helpers/spree/admin/taxons_helper.rb new file mode 100644 index 0000000000..87ee61dbd5 --- /dev/null +++ b/app/helpers/spree/admin/taxons_helper.rb @@ -0,0 +1,9 @@ +module Spree + module Admin + module TaxonsHelper + def taxon_path(taxon) + taxon.ancestors.reverse.collect(&:name).join( " >> ") + end + end + end +end diff --git a/app/views/spree/admin/taxonomies/_form.html.haml b/app/views/spree/admin/taxonomies/_form.html.haml new file mode 100644 index 0000000000..81927c7c1a --- /dev/null +++ b/app/views/spree/admin/taxonomies/_form.html.haml @@ -0,0 +1,7 @@ +.field.align-center + = f.field_container :name do + = f.label :name, t("spree.name") + %span.required * + %br/ + = error_message_on :taxonomy, :name, class: 'fullwidth title' + = text_field :taxonomy, :name diff --git a/app/views/spree/admin/taxonomies/_js_head.html.erb b/app/views/spree/admin/taxonomies/_js_head.html.erb new file mode 100755 index 0000000000..e95f4bdeff --- /dev/null +++ b/app/views/spree/admin/taxonomies/_js_head.html.erb @@ -0,0 +1,13 @@ +<% content_for :head do %> + <%= javascript_tag "var taxonomy_id = #{@taxonomy.id}; + var loading = '#{escape_javascript t("spree.loading")}'; + var new_taxon = '#{escape_javascript t("spree.new_taxon")}'; + var server_error = '#{escape_javascript t("spree.server_error")}'; + var taxonomy_tree_error = '#{escape_javascript t("spree.taxonomy_tree_error")}'; + + $(document).ready(function(){ + setup_taxonomy_tree(taxonomy_id); + }); + " + %> +<% end %> diff --git a/app/views/spree/admin/taxonomies/_list.html.haml b/app/views/spree/admin/taxonomies/_list.html.haml new file mode 100644 index 0000000000..1574e8b718 --- /dev/null +++ b/app/views/spree/admin/taxonomies/_list.html.haml @@ -0,0 +1,19 @@ +%table#listing_taxonomies.index.sortable{"data-sortable-link" => update_positions_admin_taxonomies_url} + %colgroup + %col{style: "width: 85%"}/ + %col{style: "width: 15%"}/ + %thead + %tr + %th= t("spree.name") + %th.actions + %tbody + - @taxonomies.each do |taxonomy| + - tr_class = cycle('odd', 'even') + - tr_id = spree_dom_id(taxonomy) + %tr{class: tr_class, id: tr_id} + %td + %span.handle + = taxonomy.name + %td.actions + = link_to_edit taxonomy.id, no_text: true + = link_to_delete taxonomy, no_text: true diff --git a/app/views/spree/admin/taxonomies/_taxon.html.haml b/app/views/spree/admin/taxonomies/_taxon.html.haml new file mode 100644 index 0000000000..6c5e5273b9 --- /dev/null +++ b/app/views/spree/admin/taxonomies/_taxon.html.haml @@ -0,0 +1,7 @@ +- if taxon.children.length != 0 + %ul + - taxon.children.each do |child| + %li{id: "#{child.id}", rel: "taxon"} + %a{href: "#", style: "background-image: url(#{child.icon.url});"}= child.name + - if child.children.length > 0 + = render partial: 'taxon', locals: { taxon: child } diff --git a/app/views/spree/admin/taxonomies/edit.haml b/app/views/spree/admin/taxonomies/edit.haml new file mode 100755 index 0000000000..3deb328999 --- /dev/null +++ b/app/views/spree/admin/taxonomies/edit.haml @@ -0,0 +1,32 @@ += render partial: 'spree/admin/shared/configuration_menu' + += render partial: 'js_head' + +- content_for :page_title do + = t("spree.taxonomy_edit") + +- content_for :page_actions do + %li + = button_link_to t("spree.back_to_taxonomies_list"), spree.admin_taxonomies_path, icon: 'icon-arrow-left' + +#ajax_error.errorExplanation{style: "display:none;"} += form_for [:admin, @taxonomy] do |f| + %fieldset.no-border-top + = render partial: 'form', locals: { f: f } + %div + = label_tag nil, t("spree.tree") + %br/ + :javascript + Spree.routes.taxonomy_taxons_path = "#{spree.api_taxonomy_taxons_path(@taxonomy)}"; + Spree.routes.admin_taxonomy_taxons_path = "#{spree.admin_taxonomy_taxons_path(@taxonomy)}"; + #taxonomy_tree.tree + #progress{style: "display:none;"} + = image_tag 'select2-spinner.gif', title: 'Spinner', style: "vertical-align:bottom;" + = t("spree.updating") + \.. + .info= t("spree.taxonomy_tree_instruction") + %br/ + .filter-actions.actions + = button t('spree.actions.update'), 'icon-refresh' + %span.or= t("spree.or") + = button_link_to t('spree.actions.cancel'), admin_taxonomies_path, icon: 'icon-remove' diff --git a/app/views/spree/admin/taxonomies/index.html.haml b/app/views/spree/admin/taxonomies/index.html.haml new file mode 100644 index 0000000000..63041138d1 --- /dev/null +++ b/app/views/spree/admin/taxonomies/index.html.haml @@ -0,0 +1,11 @@ += render partial: 'spree/admin/shared/configuration_menu' + +- content_for :page_title do + = t("spree.taxonomies") + +- content_for :page_actions do + %li + = button_link_to t("spree.new_taxonomy"), spree.new_admin_taxonomy_url, icon: 'icon-plus', id: 'admin_new_taxonomy_link' + +#list-taxonomies + = render partial: 'list' diff --git a/app/views/spree/admin/taxonomies/new.html.haml b/app/views/spree/admin/taxonomies/new.html.haml new file mode 100644 index 0000000000..a84338ddf1 --- /dev/null +++ b/app/views/spree/admin/taxonomies/new.html.haml @@ -0,0 +1,15 @@ += render partial: 'spree/admin/shared/configuration_menu' + +- content_for :page_title do + = t("spree.new_taxonomy") + +- content_for :page_actions do + %li + = button_link_to t("spree.back_to_taxonomies_list"), spree.admin_taxonomies_path, icon: 'icon-arrow-left' + += form_for [:admin, @taxonomy] do |f| + = render partial: 'form', locals: { f: f } + %fieldset.no-border-top + %br/ + .filter-actions.actions + = button t("spree.create"), 'icon-ok' diff --git a/app/views/spree/admin/taxons/_form.html.haml b/app/views/spree/admin/taxons/_form.html.haml index 19a8af5936..72ba498a4d 100644 --- a/app/views/spree/admin/taxons/_form.html.haml +++ b/app/views/spree/admin/taxons/_form.html.haml @@ -1,11 +1,11 @@ -.row{"data-hook" => "admin_inside_taxon_form"} +.row .alpha.five.columns = f.field_container :name do = f.label :name, t(:name) %span.required * %br/ - = error_message_on :taxon, :name, :class => 'fullwidth title' - = text_field :taxon, :name, :class => 'fullwidth' + = error_message_on :taxon, :name, class: 'fullwidth title' + = text_field :taxon, :name, class: 'fullwidth' = f.field_container :permalink_part do = f.label :permalink_part, t(:permalink) %span.required * @@ -20,17 +20,17 @@ = f.field_container :meta_title do = f.label :meta_title, t(:meta_title) %br/ - = f.text_field :meta_title, :class => 'fullwidth', :rows => 6 + = f.text_field :meta_title, class: 'fullwidth', rows: 6 = f.field_container :meta_description do = f.label :meta_description, t(:meta_description) %br/ - = f.text_field :meta_description, :class => 'fullwidth', :rows => 6 + = f.text_field :meta_description, class: 'fullwidth', rows: 6 = f.field_container :meta_description do = f.label :meta_keywords, t(:meta_keywords) %br/ - = f.text_field :meta_keywords, :class => 'fullwidth', :rows => 6 + = f.text_field :meta_keywords, class: 'fullwidth', rows: 6 .omega.seven.columns = f.field_container :description do = f.label :description, t(:description) %br/ - = f.text_area :description, :class => 'fullwidth', :rows => 6 + = f.text_area :description, class: 'fullwidth', rows: 6 diff --git a/app/views/spree/admin/taxons/_taxon_table.html.haml b/app/views/spree/admin/taxons/_taxon_table.html.haml new file mode 100644 index 0000000000..7917db0c97 --- /dev/null +++ b/app/views/spree/admin/taxons/_taxon_table.html.haml @@ -0,0 +1,20 @@ +%table.index + %thead + %tr + %th= t("spree.name") + %th= t("spree.path") + %th + %tbody + - taxons.each do |taxon| + - tr_class = cycle('odd', 'even') + - tr_id = spree_dom_id(taxon) + %tr{class: tr_class, id: tr_id} + %td= taxon.name + %td= taxon_path taxon + %td.actions + = link_to_delete taxon, url: remove_admin_product_taxon_url(@product, taxon), name: icon('delete') + ' ' + t("spree.remove") + - if taxons.empty? + %tr + %td{colspan: "3"} + = t("spree.none") + \. diff --git a/app/views/spree/admin/taxons/edit.html.haml b/app/views/spree/admin/taxons/edit.html.haml new file mode 100644 index 0000000000..7e1e0a45e4 --- /dev/null +++ b/app/views/spree/admin/taxons/edit.html.haml @@ -0,0 +1,17 @@ += render partial: 'spree/admin/shared/configuration_menu' + +- content_for :page_title do + = t("spree.taxon_edit") + +- content_for :page_actions do + %li + = button_link_to t("spree.back_to_taxonomies_list"), spree.admin_taxonomies_path, icon: 'icon-arrow-left' + +- # Because otherwise the form would attempt to use to_param of @taxon +- form_url = admin_taxonomy_taxon_path(@taxonomy.id, @taxon.id) += form_for [:admin, @taxonomy, @taxon], method: :put, url: form_url, html: { multipart: true } do |f| + = render partial: 'form', locals: { f: f } + .form-buttons + = button t('spree.actions.update'), 'icon-refresh' + = t("spree.or") + = button_link_to t('spree.actions.cancel'), edit_admin_taxonomy_url(@taxonomy), icon: "icon-remove" diff --git a/app/views/spree/admin/taxons/search.rabl b/app/views/spree/admin/taxons/search.rabl new file mode 100644 index 0000000000..5214337d12 --- /dev/null +++ b/app/views/spree/admin/taxons/search.rabl @@ -0,0 +1,4 @@ +object false +child(@taxons => :taxons) do + attributes :name, :pretty_name, :id +end diff --git a/config/routes/spree.rb b/config/routes/spree.rb index c732615e87..68a1dd9dda 100644 --- a/config/routes/spree.rb +++ b/config/routes/spree.rb @@ -89,6 +89,23 @@ Spree::Core::Engine.routes.prepend do end end + # Configuration section + resources :taxonomies do + collection do + post :update_positions + end + member do + get :get_children + end + resources :taxons + end + + resources :taxons, :only => [] do + collection do + get :search + end + end + resources :tax_rates resource :tax_settings resources :tax_categories diff --git a/spec/features/admin/configuration/taxonomies_spec.rb b/spec/features/admin/configuration/taxonomies_spec.rb new file mode 100644 index 0000000000..7eb592587f --- /dev/null +++ b/spec/features/admin/configuration/taxonomies_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe "Taxonomies" do + include AuthenticationWorkflow + + before(:each) do + quick_login_as_admin + visit spree.admin_path + click_link "Configuration" + end + + context "show" do + it "should display existing taxonomies" do + create(:taxonomy, name: 'Brand') + create(:taxonomy, name: 'Categories') + click_link "Taxonomies" + within_row(1) { expect(page).to have_content("Brand") } + within_row(2) { expect(page).to have_content("Categories") } + end + end + + context "create" do + before(:each) do + click_link "Taxonomies" + click_link "admin_new_taxonomy_link" + end + + it "should allow an admin to create a new taxonomy" do + expect(page).to have_content("New Taxonomy") + fill_in "taxonomy_name", with: "sports" + click_button "Create" + expect(page).to have_content("successfully created!") + end + + it "should display validation errors" do + fill_in "taxonomy_name", with: "" + click_button "Create" + expect(page).to have_content("can't be blank") + end + end + + context "edit" do + it "should allow an admin to update an existing taxonomy" do + create(:taxonomy) + click_link "Taxonomies" + within_row(1) { click_icon :edit } + fill_in "taxonomy_name", with: "sports 99" + click_button "Update" + expect(page).to have_content("successfully updated!") + expect(page).to have_content("sports 99") + end + end +end