diff --git a/app/assets/javascripts/admin/products/products.js.coffee b/app/assets/javascripts/admin/products/products.js.coffee index 1952c8dded..402db8b7ff 100644 --- a/app/assets/javascripts/admin/products/products.js.coffee +++ b/app/assets/javascripts/admin/products/products.js.coffee @@ -1 +1 @@ -angular.module("admin.products", ["admin.utils"]) \ No newline at end of file +angular.module("admin.products", ["textAngular", "admin.utils"]) \ No newline at end of file diff --git a/app/assets/javascripts/admin/utils/directives/textangular_strip.js.coffee b/app/assets/javascripts/admin/utils/directives/textangular_strip.js.coffee new file mode 100644 index 0000000000..8e60221376 --- /dev/null +++ b/app/assets/javascripts/admin/utils/directives/textangular_strip.js.coffee @@ -0,0 +1,5 @@ +angular.module("admin.utils").directive "textangularStrip", () -> + restrict: 'CA' + link: (scope, element, attrs) -> + scope.stripFormatting = ($html) -> + return String($html).replace(/<[^>]+>/gm, '') diff --git a/app/assets/javascripts/templates/partials/contact.html.haml b/app/assets/javascripts/templates/partials/contact.html.haml index d173bcf366..9779dd0f3e 100644 --- a/app/assets/javascripts/templates/partials/contact.html.haml +++ b/app/assets/javascripts/templates/partials/contact.html.haml @@ -3,9 +3,9 @@ %p.modal-header {{'contact' | t}} %p{"ng-if" => "::enterprise.phone", "ng-bind" => "::enterprise.phone"} - %p.word-wrap{"ng-if" => "::enterprise.email_address"} + %p{"ng-if" => "::enterprise.email_address"} %a{"ng-href" => "{{::enterprise.email_address | stripUrl}}", target: "_blank", mailto: true} %span.email{"ng-bind" => "::enterprise.email_address | stripUrl"} - %p.word-wrap{"ng-if" => "enterprise.website"} + %p{"ng-if" => "enterprise.website"} %a{"ng-href" => "http://{{::enterprise.website | stripUrl}}", target: "_blank", "ng-bind" => "::enterprise.website | stripUrl"} diff --git a/app/assets/javascripts/templates/product_modal.html.haml b/app/assets/javascripts/templates/product_modal.html.haml index 4b21c054a4..6282bc439e 100644 --- a/app/assets/javascripts/templates/product_modal.html.haml +++ b/app/assets/javascripts/templates/product_modal.html.haml @@ -14,9 +14,9 @@ .filter-shopfront.property-selectors.inline-block %filter-selector{ 'selector-set' => "productPropertySelectors", objects: "[product] | propertiesWithValuesOf" } - %div{"ng-if" => "product.description"} + %div{"ng-if" => "product.description_html"} %hr - %p.text-small{"ng-bind" => "::product.description"} + %p.text-small{"ng-bind-html" => "::product.description_html"} %hr .columns.small-12.large-6 diff --git a/app/assets/stylesheets/admin/openfoodnetwork.css.scss b/app/assets/stylesheets/admin/openfoodnetwork.css.scss index 4b5703d317..836d31b9fd 100644 --- a/app/assets/stylesheets/admin/openfoodnetwork.css.scss +++ b/app/assets/stylesheets/admin/openfoodnetwork.css.scss @@ -200,6 +200,13 @@ table#listing_enterprise_groups { // textAngular wysiwyg text-angular { + .ta-toolbar { + border: 1px solid #cdd9e4; + padding: 0.4em; + margin-bottom: -1px; + background-color: #f1f1f1; + border-radius: 0.25em 0.25em 0 0; + } .ta-scroll-window > .ta-bind { max-height: 400px; min-height: 100px; @@ -210,12 +217,18 @@ text-angular { } .ta-scroll-window.form-control { min-height: 100px; + box-shadow: none !important; } .btn-group { display: inline; margin-right: 8px; button { padding: 5px 10px; + margin-right: 0.25em; + } + button.active:not(:hover) { + box-shadow: 0 0 0.7em rgba(0,0,0,0.3) inset; + background-color: #4583bf; } } } diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index e49db570cb..3814365447 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -175,7 +175,7 @@ class AbilityDecorator def add_order_management_abilities(user) # Enterprise User can only access orders that they are a distributor for can [:index, :create], Spree::Order - can [:read, :update, :fire, :resend, :invoice, :print], Spree::Order do |order| + can [:read, :update, :fire, :resend, :invoice, :print, :print_ticket], Spree::Order do |order| # We allow editing orders with a nil distributor as this state occurs # during the order creation process from the admin backend order.distributor.nil? || user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user) diff --git a/app/models/spree/app_configuration_decorator.rb b/app/models/spree/app_configuration_decorator.rb index 2dfda8949c..4f8a80bf96 100644 --- a/app/models/spree/app_configuration_decorator.rb +++ b/app/models/spree/app_configuration_decorator.rb @@ -6,7 +6,6 @@ Spree::AppConfiguration.class_eval do # Terms of Service Preferences preference :enterprises_require_tos, :boolean, default: false - preference :enterprise_tos_link, :string, default: "/Terms-of-service.pdf" # Tax Preferences preference :products_require_tax_category, :boolean, default: false diff --git a/app/overrides/spree/admin/general_settings/edit/tos_settings.html.haml.deface b/app/overrides/spree/admin/general_settings/edit/tos_settings.html.haml.deface index 9d573c3ac1..e0a588d740 100644 --- a/app/overrides/spree/admin/general_settings/edit/tos_settings.html.haml.deface +++ b/app/overrides/spree/admin/general_settings/edit/tos_settings.html.haml.deface @@ -2,10 +2,6 @@ %fieldset.enterprise_toc.no-border-bottom %legend{:align => "center"}= t(:enterprise_terms_of_service) - - [:enterprise_tos_link, :enterprises_require_tos].each do |pref| - - type = Spree::Config.preference_type(pref) - .field - = label_tag(pref, t(pref) + ': ') + tag(:br) if type != :boolean - = preference_field_tag(pref, Spree::Config[pref], :type => type) - = label_tag(pref, t(pref)) + tag(:br) if type == :boolean - + .field + = preference_field_tag(:enterprises_require_tos, Spree::Config[:enterprises_require_tos], :type => Spree::Config.preference_type(:enterprises_require_tos)) + = label_tag(:enterprises_require_tos, t(:enterprises_require_tos)) + tag(:br) diff --git a/app/overrides/spree/admin/product_properties/index/add_producer_properties_warning_and_table.html.haml.deface b/app/overrides/spree/admin/product_properties/index/add_producer_properties_warning_and_table.html.haml.deface index 8d2e598c43..c31a74b9e4 100644 --- a/app/overrides/spree/admin/product_properties/index/add_producer_properties_warning_and_table.html.haml.deface +++ b/app/overrides/spree/admin/product_properties/index/add_producer_properties_warning_and_table.html.haml.deface @@ -9,8 +9,8 @@ %table.index %thead %tr{"data-hook" => "producer_properties_header"} - %th= t(:inherited_property) - %th= t(:value) + %th= t('admin.products.properties.inherited_property') + %th= t('admin.description') %th.actions %tbody#producer_properties{"data-hook" => ""} - @product.supplier.producer_properties.each do |producer_property| diff --git a/app/overrides/spree/admin/product_properties/index/rename_form_headings.html.haml.deface b/app/overrides/spree/admin/product_properties/index/rename_form_headings.html.haml.deface new file mode 100644 index 0000000000..011580c1bb --- /dev/null +++ b/app/overrides/spree/admin/product_properties/index/rename_form_headings.html.haml.deface @@ -0,0 +1,5 @@ +/ replace "tr[data-hook='product_properties_header']" +%tr{"data-hook" => "product_properties_header"} + %th= t('admin.products.properties.property_name') + %th= t('admin.description') + %th.actions diff --git a/app/overrides/spree/admin/products/_form/add_description_wysiwyg.html.haml.deface b/app/overrides/spree/admin/products/_form/add_description_wysiwyg.html.haml.deface new file mode 100644 index 0000000000..d04e787482 --- /dev/null +++ b/app/overrides/spree/admin/products/_form/add_description_wysiwyg.html.haml.deface @@ -0,0 +1,3 @@ +/ replace "[data-hook=admin_product_form_left] code[erb-loud]:contains('f.text_area :description')" +%text-angular{'id' => 'product_description', 'name' => 'product[description]', 'class' => 'text-angular', 'textangular-strip' => true, 'ta-paste' => "stripFormatting($html)", 'ta-toolbar' => "[['bold','italics','clear']]"} + = sanitize(@product.description) diff --git a/app/overrides/spree/admin/products/edit/add_angular.deface b/app/overrides/spree/admin/products/edit/add_angular.deface new file mode 100644 index 0000000000..926c47c0ec --- /dev/null +++ b/app/overrides/spree/admin/products/edit/add_angular.deface @@ -0,0 +1,2 @@ +add_to_attributes 'fieldset.no-border-top' +attributes 'ng-app' => 'admin.products' \ No newline at end of file diff --git a/app/overrides/spree/admin/products/new/replace_form.html.haml.deface b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface index 3fd5a74cb1..01894a8965 100644 --- a/app/overrides/spree/admin/products/new/replace_form.html.haml.deface +++ b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface @@ -72,7 +72,7 @@ = f.field_container :description do = f.label :product_description, t(:product_description) %br/ - = f.text_area :description, class: 'fullwidth', rows: 3 + %text-angular{'id' => 'product_description', 'name' => 'product[description]', 'class' => 'text-angular', 'textangular-strip' => true, 'ta-paste' => "stripFormatting($html)", 'ta-toolbar' => "[['bold','italics','clear']]"} = f.error_message_on :description .four.columns.omega{ style: "text-align: center" } %fieldset.no-border-bottom{ id: "image" } diff --git a/app/serializers/api/product_serializer.rb b/app/serializers/api/product_serializer.rb index aa353173dc..7b371c5be1 100644 --- a/app/serializers/api/product_serializer.rb +++ b/app/serializers/api/product_serializer.rb @@ -37,7 +37,7 @@ class Api::CachedProductSerializer < ActiveModel::Serializer include ActionView::Helpers::SanitizeHelper attributes :id, :name, :permalink - attributes :on_demand, :group_buy, :notes, :description + attributes :on_demand, :group_buy, :notes, :description, :description_html attributes :properties_with_values has_many :variants, serializer: Api::VariantSerializer @@ -49,10 +49,17 @@ class Api::CachedProductSerializer < ActiveModel::Serializer has_many :images, serializer: Api::ImageSerializer has_one :supplier, serializer: Api::IdSerializer + #return an unformatted descripton def description strip_tags object.description end + #return a sanitized html description + def description_html + d = sanitize(object.description, options = {tags: "p, b, strong, em, i"}) + d.to_s.html_safe + end + def properties_with_values object.properties_including_inherited end diff --git a/app/views/admin/producer_properties/_form.html.haml b/app/views/admin/producer_properties/_form.html.haml index 249301ae40..7af322557d 100644 --- a/app/views/admin/producer_properties/_form.html.haml +++ b/app/views/admin/producer_properties/_form.html.haml @@ -4,8 +4,8 @@ %table.index.sortable{"data-hook" => "", "data-sortable-link" => main_app.update_positions_admin_enterprise_producer_properties_url(@enterprise)} %thead %tr{"data-hook" => "producer_properties_header"} - %th{colspan: "2"}= t('.property') - %th= t('.value') + %th{colspan: "2"}= t('admin.products.properties.property_name') + %th= t('admin.description') %th.actions %tbody#producer_properties{"data-hook" => ""} = f.fields_for :producer_properties do |pp_form| diff --git a/app/views/registration/index.html.haml b/app/views/registration/index.html.haml index 4f23ac8700..fbfb0c175c 100644 --- a/app/views/registration/index.html.haml +++ b/app/views/registration/index.html.haml @@ -6,7 +6,7 @@ = inject_enterprise_attributes - steps = %w{about contact details finished images introduction} -- steps += %w{limit_reached logo promo social steps type} +- steps += %w{logo promo social steps type} - steps.each do |step| = render partial: "registration/steps/#{step}" = render "modal" diff --git a/app/views/registration/limit_reached.html.haml b/app/views/registration/limit_reached.html.haml index bfaec6da3d..35132cb769 100644 --- a/app/views/registration/limit_reached.html.haml +++ b/app/views/registration/limit_reached.html.haml @@ -1,2 +1,4 @@ += render partial: "registration/steps/limit_reached" + / Directive which loads the modal %div{ "ofn-registration-limit-modal" => true } diff --git a/app/views/registration/steps/_introduction.html.haml b/app/views/registration/steps/_introduction.html.haml index c663886a03..57055bc2ea 100644 --- a/app/views/registration/steps/_introduction.html.haml +++ b/app/views/registration/steps/_introduction.html.haml @@ -37,7 +37,7 @@ .small-12.columns{'ng-hide' => '!tos_required' } %p.tos-message #{t(:enterprise_tos_message)} - %a{href: "#{Spree::Config.enterprise_tos_link}", target: "_blank" } #{t(:enterprise_tos_link_text)} + %a{href: ContentConfig.footer_tos_url, target: "_blank" } #{t(:enterprise_tos_link_text)} %p.tos-checkbox %input{ type: 'checkbox', name: 'accept_terms', id: 'accept_terms', ng: { model: "tos_accepted" } } %label{for: "accept_terms"} #{t(:enterprise_tos_agree)} diff --git a/config/application.rb b/config/application.rb index f9d6151e10..82191824ca 100644 --- a/config/application.rb +++ b/config/application.rb @@ -105,6 +105,7 @@ module Openfoodnetwork config.assets.precompile += ['mail/all.css'] config.assets.precompile += ['search/all.css', 'search/*.js'] config.assets.precompile += ['shared/*'] + config.assets.precompile += ['qz/*'] config.active_support.escape_html_entities_in_json = true end diff --git a/config/locales/en.yml b/config/locales/en.yml index 475942a692..63eff8f8f2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -109,6 +109,7 @@ en: columns: Columns actions: Actions viewing: "Viewing: %{current_view_name}" + description: Description whats_this: What's this? @@ -223,6 +224,9 @@ en: inherits_properties?: Inherits Properties? available_on: Available On av_on: "Av. On" + properties: + property_name: Property Name + inherited_property: Inherited Property variants: to_order_tip: "Items made to order do not have a set stock level, such as loaves of bread made fresh to order." @@ -503,9 +507,6 @@ en: advanced_settings: Advanced Settings update_and_close: Update and Close producer_properties: - form: - property: Property - value: Value index: title: Producer Properties shared: diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index 2d5e4dcbe8..2cebd58dce 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -35,7 +35,7 @@ feature %q{ fill_in 'product_on_hand', with: 5 select 'Test Tax Category', from: 'product_tax_category_id' select 'Test Shipping Category', from: 'product_shipping_category_id' - fill_in 'product_description', with: "A description..." + page.find("input[name='product\[description\]']", visible: false).set('A description...') click_button 'Create' @@ -75,7 +75,8 @@ feature %q{ check 'product_on_demand' select 'Test Tax Category', from: 'product_tax_category_id' select 'Test Shipping Category', from: 'product_shipping_category_id' - fill_in 'product_description', with: "In demand, and on_demand! The hottest cakes in town." + #fill_in 'product_description', with: "In demand, and on_demand! The hottest cakes in town." + page.first("input[name='product\[description\]']", visible: false).set('In demand, and on_demand! The hottest cakes in town.') click_button 'Create' diff --git a/spec/features/consumer/registration_spec.rb b/spec/features/consumer/registration_spec.rb index f6a90f6f61..c4e7af36ee 100644 --- a/spec/features/consumer/registration_spec.rb +++ b/spec/features/consumer/registration_spec.rb @@ -98,6 +98,23 @@ feature "Registration", js: true do expect(e.instagram).to eq "@InStAgRaM" end + context "when the user has no more remaining enterprises" do + before do + user.update_attributes(enterprise_limit: 0) + end + + it "displays the limit reached page" do + visit registration_path + + expect(page).to have_selector "dd", text: "Login" + switch_to_login_tab + + # Enter Login details + fill_in "Email", with: user.email + fill_in "Password", with: user.password + click_login_and_ensure_content I18n.t('limit_reached_headline') + end + end end describe "Terms of Service agreement" do diff --git a/spec/features/consumer/shopping/products_spec.rb b/spec/features/consumer/shopping/products_spec.rb new file mode 100644 index 0000000000..d8b0c2b792 --- /dev/null +++ b/spec/features/consumer/shopping/products_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +feature "As a consumer I want to view products", js: true do + include AuthenticationWorkflow + include WebHelper + include ShopWorkflow + include UIComponentHelper + + describe "Viewing a product" do + let(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) } + let(:supplier) { create(:supplier_enterprise) } + let(:oc1) { create(:simple_order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise), orders_close_at: 2.days.from_now) } + let(:product) { create(:simple_product, supplier: supplier) } + let(:variant) { product.variants.first } + let(:order) { create(:order, distributor: distributor) } + let(:exchange1) { oc1.exchanges.to_enterprises(distributor).outgoing.first } + + before do + set_order order + end + + describe "viewing HTML product descriptions" do + before do + exchange1.update_attribute :pickup_time, "monday" + add_variant_to_order_cycle(exchange1, variant) + end + + it "shows HTML product description" do + product.description = "

Formatted product description.

" + product.save! + + visit shop_path + select "monday", :from => "order_cycle_id" + + open_product_modal product + modal_should_be_open_for product + + within(".reveal-modal") do + html.should include("

Formatted product description.

") + end + end + + it "does not show unsecure HTML" do + product.description = "

Safe

" + product.save! + + visit shop_path + select "monday", :from => "order_cycle_id" + + open_product_modal product + modal_should_be_open_for product + + within(".reveal-modal") do + html.should include("

Safe

") + html.should_not include("") + end + end + end + end +end \ No newline at end of file