Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
7c2ae3bb4d Bump active_storage_validations from 1.1.4 to 3.0.3
Bumps [active_storage_validations](https://github.com/igorkasyanchuk/active_storage_validations) from 1.1.4 to 3.0.3.
- [Release notes](https://github.com/igorkasyanchuk/active_storage_validations/releases)
- [Changelog](https://github.com/igorkasyanchuk/active_storage_validations/blob/master/CHANGES.md)
- [Commits](https://github.com/igorkasyanchuk/active_storage_validations/commits/3.0.03)

---
updated-dependencies:
- dependency-name: active_storage_validations
  dependency-version: 3.0.3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 09:26:58 +00:00
778 changed files with 14762 additions and 11568 deletions

2
.browserslistrc Normal file
View File

@@ -0,0 +1,2 @@
defaults
IE 11

1
.gitattributes vendored
View File

@@ -8,3 +8,4 @@
# Same thing for following files, but they don't have an sh extension
pre-commit eol=lf
webpack-dev-server eol=lf
install-bundler eol=lf

View File

@@ -1,4 +1,4 @@
## What? Why?
#### What? Why?
- Closes # <!-- Insert issue number here. -->
@@ -7,7 +7,7 @@
## What should we test?
#### What should we test?
<!-- List which features should be tested and how.
This can be similar to the Steps to Reproduce in the issue.
Also think of other parts of the app which could be affected
@@ -16,7 +16,7 @@
- Visit ... page.
-
## Release notes
#### Release notes
<!-- Please select one for your PR and delete the other. -->
@@ -33,12 +33,12 @@ Changelog Category (reviewers may add a label for the release notes):
The title of the pull request will be included in the release notes.
## Dependencies
#### Dependencies
<!-- Does this PR depend on another one?
Add the link or remove this section. -->
## Documentation updates
#### Documentation updates
<!-- Are there any wiki pages that need updating after merging this PR?
List them here or remove this section. -->

2
.rspec
View File

@@ -1 +1 @@
--require spec_helper
--require base_spec_helper

View File

@@ -1 +1 @@
3.4.8
3.2.9

View File

@@ -1,4 +1,4 @@
FROM ruby:3.4.8-alpine3.19 AS base
FROM ruby:3.2.9-alpine3.19 AS base
ENV LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
TZ=Europe/London \
@@ -31,4 +31,4 @@ FROM development-base
COPY . $RAILS_ROOT
COPY Gemfile Gemfile.lock ./
RUN bundle install --jobs "$(nproc)"
COPY --from=yarn-dependencies $RAILS_ROOT/node_modules ./node_modules
COPY --from=yarn-dependencies $RAILS_ROOT/node_modules ./node_modules

View File

@@ -83,8 +83,11 @@ RUN wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.z
# Copy code and install app dependencies
COPY . /usr/src/app/
# Install Bundler
RUN ./script/install-bundler
# Install front-end dependencies
RUN yarn install
# Run bundler install in parallel with the amount of available CPUs
RUN bundle install --jobs="$(nproc)"
RUN bundle install --jobs="$(nproc)"

View File

@@ -18,7 +18,7 @@ gem 'activemerchant'
gem 'angular-rails-templates'
gem 'ransack', '~> 4.1.0'
gem 'responders'
gem 'shakapacker', '7.2.3'
gem 'webpacker', '~> 5'
# Indirect dependency but we access it directly in JS specs.
# It turns out to be hard to upgrade but please do if you can.
@@ -49,7 +49,7 @@ gem 'acts_as_list', '1.0.4'
gem 'cancancan', '~> 1.15.0'
gem 'digest'
gem 'ffaker'
gem 'highline'
gem 'highline', '2.0.3' # Necessary for the install generator
gem 'json'
gem 'monetize', '~> 1.11'
gem 'paranoia', '~> 2.4'
@@ -57,7 +57,7 @@ gem 'state_machines-activerecord'
gem 'stringex', '~> 2.8.5', require: false
gem 'paypal-sdk-merchant', '1.117.2'
gem 'stripe', '~> 15'
gem 'stripe', '~> 13'
gem 'devise'
gem 'devise-encryptable'
@@ -126,8 +126,6 @@ gem 'angular_rails_csrf'
gem 'jquery-rails', '4.4.0'
gem 'jquery-ui-rails', '~> 4.2'
# TODO move away from sass-rails, master branch will get rid of dependency, so we can move to
# https://github.com/sass/embedded-host-node
gem "select2-rails", github: "openfoodfoundation/select2-rails", branch: "v349_with_thor_v1"
gem 'good_migrations'

View File

@@ -104,7 +104,7 @@ GEM
rails-html-sanitizer (~> 1.6)
active_model_serializers (0.8.4)
activemodel (>= 3.0)
active_storage_validations (3.0.2)
active_storage_validations (3.0.3)
activejob (>= 6.1.4)
activemodel (>= 6.1.4)
activestorage (>= 6.1.4)
@@ -159,8 +159,8 @@ GEM
zeitwerk (>= 2.4, < 3.0)
acts_as_list (1.0.4)
activerecord (>= 4.2)
addressable (2.8.8)
public_suffix (>= 2.0.2, < 8.0)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
aes_key_wrap (1.1.0)
afm (0.2.2)
angular-rails-templates (1.4.0)
@@ -177,8 +177,8 @@ GEM
ast (2.4.3)
attr_required (1.0.2)
aws-eventstream (1.4.0)
aws-partitions (1.1196.0)
aws-sdk-core (3.240.0)
aws-partitions (1.1191.0)
aws-sdk-core (3.239.2)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
@@ -189,7 +189,7 @@ GEM
aws-sdk-kms (1.118.0)
aws-sdk-core (~> 3, >= 3.239.1)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.208.0)
aws-sdk-s3 (1.206.0)
aws-sdk-core (~> 3, >= 3.234.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
@@ -234,7 +234,7 @@ GEM
marcel (~> 1.0)
nokogiri (~> 1.10, >= 1.10.4)
rubyzip (>= 1.3.0, < 3)
cgi (0.5.0)
cgi (0.5.1)
childprocess (5.0.0)
choice (0.2.0)
chronic (0.10.2)
@@ -249,10 +249,10 @@ GEM
combine_pdf (1.0.31)
matrix
ruby-rc4 (>= 0.1.5)
concurrent-ruby (1.3.5)
connection_pool (2.5.5)
concurrent-ruby (1.3.6)
connection_pool (3.0.2)
cookiejar (0.3.4)
crack (1.0.1)
crack (1.0.0)
bigdecimal
rexml
crass (1.0.6)
@@ -290,7 +290,7 @@ GEM
diff-lcs (1.6.2)
digest (3.2.1)
docile (1.4.1)
dotenv (3.2.0)
dotenv (3.1.8)
drb (2.2.3)
em-http-request (1.1.7)
addressable (>= 2.3.4)
@@ -380,7 +380,7 @@ GEM
temple (>= 0.8.2)
thor
tilt
haml_lint (0.68.0)
haml_lint (0.67.0)
haml (>= 5.0)
parallel (~> 1.10)
rainbow
@@ -389,8 +389,7 @@ GEM
hashdiff (1.2.1)
hashery (2.1.2)
hashie (5.0.0)
highline (3.1.2)
reline
highline (2.0.3)
htmlentities (4.4.2)
http_parser.rb (0.8.0)
i18n (1.14.7)
@@ -493,7 +492,7 @@ GEM
logger
mini_mime (1.1.5)
mini_portile2 (2.8.6)
minitest (5.26.2)
minitest (5.27.0)
monetize (1.13.0)
money (~> 6.12)
money (6.16.0)
@@ -553,7 +552,6 @@ GEM
webfinger (~> 2.0)
orm_adapter (0.5.0)
ostruct (0.6.1)
package_json (0.2.0)
pagy (9.4.0)
paper_trail (17.0.0)
activerecord (>= 7.1)
@@ -579,7 +577,7 @@ GEM
pp (0.6.3)
prettyprint
prettyprint (0.2.0)
prism (1.7.0)
prism (1.6.0)
private_address_check (0.5.0)
pry (0.15.2)
coderay (~> 1.1)
@@ -587,7 +585,7 @@ GEM
psych (5.2.6)
date
stringio
public_suffix (7.0.0)
public_suffix (6.0.2)
puffing-billy (4.0.2)
addressable (~> 2.5)
em-http-request (~> 1.1, >= 1.1.0)
@@ -616,7 +614,7 @@ GEM
rack-protection (3.2.0)
base64 (>= 0.1.0)
rack (~> 2.2, >= 2.2.4)
rack-proxy (0.7.7)
rack-proxy (0.7.6)
rack
rack-rewrite (1.5.1)
rack-session (1.0.2)
@@ -815,13 +813,7 @@ GEM
tilt (>= 1.1, < 3)
sd_notify (0.1.1)
securerandom (0.4.1)
semantic_range (3.1.0)
shakapacker (7.2.3)
activesupport (>= 5.2)
package_json
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
semantic_range (3.0.0)
shoulda-matchers (7.0.1)
activesupport (>= 7.1)
sidekiq (7.2.4)
@@ -877,20 +869,20 @@ GEM
redis (>= 4.0, < 6.0)
stringex (2.8.6)
stringio (3.1.8)
stripe (15.5.0)
stripe (13.5.1)
swd (2.0.3)
activesupport (>= 3)
attr_required (>= 0.0.5)
faraday (~> 2.0)
faraday-follow_redirects
sysexits (1.2.0)
temple (0.10.4)
temple (0.8.2)
terminal-table (4.0.0)
unicode-display_width (>= 1.1.1, < 4)
thor (1.4.0)
thread-local (1.1.0)
tilt (2.6.1)
timeout (0.4.4)
timeout (0.5.0)
tsort (0.2.0)
ttfunk (1.8.0)
bigdecimal (~> 3.1)
@@ -911,7 +903,7 @@ GEM
simplecov_json_formatter
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
unicode-emoji (4.1.0)
uniform_notifier (1.17.0)
uri (1.1.1)
valid_email2 (5.2.3)
@@ -922,8 +914,7 @@ GEM
public_suffix
validates_lengths_from_database (0.8.0)
activerecord (>= 4)
vcr (6.3.1)
base64
vcr (6.2.0)
view_component (4.1.1)
actionview (>= 7.1.0, < 8.2)
activesupport (>= 7.1.0, < 8.2)
@@ -945,10 +936,15 @@ GEM
activesupport
faraday (~> 2.0)
faraday-follow_redirects
webmock (3.26.1)
webmock (3.25.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (5.4.4)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
webrick (1.9.2)
websocket-driver (0.7.7)
base64
@@ -1021,7 +1017,7 @@ DEPENDENCIES
good_migrations
haml
haml_lint
highline
highline (= 2.0.3)
i18n
i18n-js (~> 3.9.0)
i18n-tasks
@@ -1085,7 +1081,6 @@ DEPENDENCIES
rubocop-rspec_rails
sd_notify
select2-rails!
shakapacker (= 7.2.3)
shoulda-matchers
sidekiq
sidekiq-scheduler
@@ -1099,7 +1094,7 @@ DEPENDENCIES
stimulus_reflex
stimulus_reflex_testing!
stringex (~> 2.8.5)
stripe (~> 15)
stripe (~> 13)
turbo-rails
turbo_power
undercover
@@ -1111,12 +1106,13 @@ DEPENDENCIES
web!
web-console
webmock
webpacker (~> 5)
whenever
wicked_pdf
wkhtmltopdf-binary
RUBY VERSION
ruby 3.4.8p72
ruby 3.2.9p265
BUNDLED WITH
4.0.3
2.4.19

View File

@@ -1,5 +1,5 @@
# Foreman Procfile. Start all dev server processes with: `foreman start`
rails: DEV_CACHING=true bundle exec rails s -p 3000
webpack: ./bin/shakacker-dev-server
webpack: ./bin/webpack-dev-server
sidekiq: DEV_CACHING=true bundle exec sidekiq -q mailers -q default

View File

@@ -1,5 +1,5 @@
# Foreman Procfile for Docker env. Start all dev server processes with: `bundle exec foreman start -f Procfile.docker`
webpack: SHAKAPACKER_DEV_SERVER_HOST=0.0.0.0 ./bin/shakapacker-dev-server
webpack: WEBPACKER_DEV_SERVER_HOST=0.0.0.0 ./bin/webpack-dev-server
sidekiq: DEV_CACHING=true bundle exec sidekiq -q mailers -q default
rails: SHAKAPACKER_DEV_SERVER_HOST=0.0.0.0 DEV_CACHING=true bundle exec rails s -p 3000 -b 0.0.0.0
rails: WEBPACKER_DEV_SERVER_HOST=0.0.0.0 DEV_CACHING=true bundle exec rails s -p 3000 -b 0.0.0.0

View File

@@ -38,13 +38,16 @@ angular.module("admin.indexUtils").directive "objForUpdate", (switchClass, pendi
# To ensure the customer is still updated, we check on the $destroy event to see if
# the attribute has changed, if so we queue up the change.
scope.$on '$destroy', (value) ->
currentValue = scope.object()[scope.attr] || ""
# No update
return if currentValue is scope.savedValue
return if scope.object()[scope.attr] is scope.savedValue
# For some reason the code attribute is removed from the object when cleared, so we add
# an emptyvalue so it gets updated properly
if scope.attr is "code" and scope.object()[scope.attr] is undefined
scope.object()["code"] = ""
# Queuing up change
addPendingChange(scope.attr, currentValue)
addPendingChange(scope.attr, scope.object()[scope.attr])
# private

View File

@@ -0,0 +1,2 @@
angular.module('Darkswarm').controller "TmpCtrl", ($scope)->
$scope.test = {foo: "bar"}

View File

@@ -1,4 +1,4 @@
= render ConfirmModalComponent.new(id: dom_id(@order, :ship), confirm_actions: "click->modal#close", confirm_reflexes: "click->Admin::OrdersReflex#ship", controller: "orders", reflex: "Admin::Orders#ship") do
= render ConfirmModalComponent.new(id: dom_id(@order, :ship), confirm_reflexes: "click->Admin::OrdersReflex#ship", controller: "orders", reflex: "Admin::Orders#ship") do
%div{class: "margin-bottom-30"}
%p= t('spree.admin.orders.shipment.mark_as_shipped_message_html')
%div{class: "margin-bottom-30"}

View File

@@ -1,16 +0,0 @@
# frozen_string_literal: true
class WebhookEndpointFormComponent < ViewComponent::Base
def initialize(webhooks:, webhook_type:)
@webhooks = webhooks
@webhook_type = webhook_type
end
private
attr_reader :webhooks, :webhook_type
def is_webhook_payment_status?
webhook_type == "payment_status_changed"
end
end

View File

@@ -1,27 +0,0 @@
-# Create new endpoints
- if webhooks.empty? # Only one allowed for now.
%tr
%td= t("components.webhook_endpoint_form.event_types.#{webhook_type}")
%td
= form_with(url: helpers.account_webhook_endpoints_path, id: "#{webhook_type}_webhook_endpoint") do |f|
= f.url_field :'webhook_endpoint[url]', id: "#{webhook_type}_webhook_endpoint_url", placeholder: t('components.webhook_endpoint_form.url.create_placeholder'), required: true, size: 64
= f.hidden_field :'webhook_endpoint[webhook_type]', id: "#{webhook_type}_webhook_endpoint_webhook_type", value: webhook_type
%td.actions
= button_tag t(:create), class: 'button primary tiny no-margin', form: "#{webhook_type}_webhook_endpoint"
-# Existing endpoints
- webhooks.each do |webhook_endpoint|
%tr
%td= t("components.webhook_endpoint_form.event_types.#{webhook_type}")
%td= webhook_endpoint.url
%td.actions.endpoints-actions
- if webhook_endpoint.persisted?
= button_to helpers.account_webhook_endpoint_path(webhook_endpoint), method: :delete,
class: "tiny alert no-margin",
data: { confirm: I18n.t(:are_you_sure) } do
= I18n.t(:delete)
- if is_webhook_payment_status?
= form_tag helpers.webhook_endpoint_test_account_path(webhook_endpoint), class: "button_to", 'data-turbo': true do
= button_tag type: "submit", class: "tiny alert no-margin", data: { confirm: I18n.t(:are_you_sure) } do
= I18n.t("components.webhook_endpoint_form.test_endpoint")

View File

@@ -162,18 +162,6 @@ module Admin
end
end
def destroy
if @object.destroy
flash.now[:success] = flash_message_for(@object, :successfully_removed)
else
flash.now[:error] = @object.errors.full_messages.to_sentence
end
respond_to do |format|
format.turbo_stream { render :destroy, status: :ok }
end
end
protected
def delete_custom_tab

View File

@@ -7,7 +7,6 @@ module Admin
before_action :init_filters_params
before_action :init_pagination_params
before_action :init_none_tag
def index
fetch_products
@@ -180,8 +179,6 @@ module Admin
product_query = OpenFoodNetwork::Permissions.new(spree_current_user)
.editable_products.merge(product_scope_with_includes).ransack(ransack_query).result
product_query = apply_tags_filter(product_query)
# Postgres requires ORDER BY expressions to appear in the SELECT list when using DISTINCT.
# When the current ransack sort uses the computed stock columns, include them in the select
# so the generated COUNT/DISTINCT query is valid.
@@ -228,51 +225,12 @@ module Admin
query.merge!(Spree::Variant::SEARCH_KEY => @search_term)
end
query.merge!(variants_primary_taxon_id_in: @category_id) if @category_id.present?
query.merge!(variants_tags_name_in: @tags) if @tags.present?
query.merge!(@q) if @q
query
end
# Apply tags filter with OR logic:
# - Products with variants having selected tags
# - OR products with variants having no tags (when "None" is selected)
#
# Note: This cannot be implemented using Ransack because Ransack applies
# AND semantics across associations and cannot express OR logic that combines
# the presence and absence of the same associated records.
def apply_tags_filter(base_query)
return base_query if @tags.blank?
tag_names = Array(@tags).dup
has_none_tag = (tag_names.delete(@none_tag_value) == @none_tag_value)
queries = []
if tag_names.any?
# Products with at least one variant having one of the selected tags
tagged_product_ids = Spree::Variant
.joins(taggings: :tag)
.where(tags: { name: tag_names })
.select(:product_id)
queries << base_query.where(id: tagged_product_ids)
end
if has_none_tag
# Products where no variants have any tags
tagged_product_ids = Spree::Variant
.joins(:taggings)
.select(:product_id)
queries << base_query.where.not(id: tagged_product_ids)
end
return base_query if queries.empty?
# Combine queries using ActiveRecord's or method
queries.reduce { |combined, query| combined.or(query) }
end
# Optimise by pre-loading required columns
def product_query_includes
[
@@ -331,10 +289,6 @@ module Admin
t('.error')
end
end
def init_none_tag
@none_tag_value = '""'
end
end
end
# rubocop:enable Metrics/ClassLength

View File

@@ -61,7 +61,7 @@ module Admin
def destroy
if @object.destroy
flash[:success] = Spree.t(:successfully_removed)
flash[:success] = flash_message_for(@object, :successfully_removed)
respond_with(@object) do |format|
format.html { redirect_to collection_url }
format.js { render partial: "spree/admin/shared/destroy" }
@@ -76,7 +76,7 @@ module Admin
protected
def resource_not_found
flash[:error] = Spree.t(:not_found)
flash[:error] = flash_message_for(model_class.new, :not_found)
redirect_to collection_url
end

View File

@@ -6,7 +6,7 @@ module Admin
class StripeAccountsController < Spree::Admin::BaseController
def connect
payload = params.permit(:enterprise_id).to_h
key = Rails.application.secret_key_base
key = Openfoodnetwork::Application.config.secret_token
url_params = { state: JWT.encode(payload, key, 'HS256'), scope: "read_write" }
redirect_to Stripe::OAuth.authorize_url(url_params)
end

View File

@@ -30,7 +30,7 @@ module Admin
status = :ok
if @rule.destroy
flash[:success] = Spree.t(:successfully_removed, resource: Spree.t(:tag_rule))
flash[:success] = Spree.t(:successfully_removed, resource: "Tag Rule")
else
flash.now[:error] = t(".destroy_error")
status = :internal_server_error

View File

@@ -14,7 +14,7 @@ module Admin
)
if @voucher.save
flash[:success] = I18n.t(:successfully_created, resource: Spree.t(:voucher))
flash[:success] = I18n.t(:successfully_created, resource: "Voucher")
redirect_to edit_admin_enterprise_path(@enterprise, anchor: :vouchers_panel)
else
render_error

View File

@@ -68,7 +68,7 @@ module Spree
destroy_before
if @object.destroy
flash[:success] = Spree.t(:successfully_removed)
flash[:success] = flash_message_for(@object, :successfully_removed)
end
redirect_to location_after_save

View File

@@ -11,10 +11,10 @@ module Spree
respond_to :html
PAYMENT_METHODS = %w{
Spree::PaymentMethod::Check
Spree::Gateway::PayPalExpress
Spree::Gateway::StripeSCA
Spree::PaymentMethod::Check
}.freeze
}.index_with(&:constantize).freeze
def create
force_environment
@@ -95,7 +95,7 @@ module Spree
@payment_method = PaymentMethod.find(params[:pm_id])
end
else
@payment_method = PaymentMethod.new(type: params[:provider_type])
@payment_method = PAYMENT_METHODS.fetch(params[:provider_type], PaymentMethod).new
end
render partial: 'provider_settings'
@@ -117,7 +117,7 @@ module Spree
end
def validate_payment_method_provider
valid_payment_methods = PAYMENT_METHODS
valid_payment_methods = Rails.application.config.spree.payment_methods.map(&:to_s)
return if valid_payment_methods.include?(params[:payment_method][:type])
flash[:error] = Spree.t(:invalid_payment_provider)
@@ -133,11 +133,13 @@ module Spree
end
def load_providers
providers = PAYMENT_METHODS.dup
providers = Gateway.providers.sort_by(&:name)
providers.delete("Spree::Gateway::StripeSCA") unless show_stripe?
unless show_stripe?
providers.reject! { |provider| stripe_provider?(provider) }
end
providers.map(&:constantize)
providers
end
# Show Stripe as an option if enabled, or if the
@@ -162,6 +164,10 @@ module Spree
@payment_method.try(:type) == "Spree::Gateway::StripeSCA"
end
def stripe_provider?(provider)
provider.name.ends_with?("StripeSCA")
end
def base_params
@base_params ||= PermittedAttributes::PaymentMethod.new(params[:payment_method]).
call.to_h.with_indifferent_access

View File

@@ -16,7 +16,7 @@ module Spree
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
if @object.destroy
flash[:success] = Spree.t(:successfully_removed)
flash[:success] = flash_message_for(@object, :successfully_removed)
end
# if destroy fails it won't show any errors to the user
redirect_to spree.admin_product_product_properties_url(params[:product_id], @url_filters)

View File

@@ -36,7 +36,7 @@ module Spree
end
@object.touch :deleted_at
flash[:success] = Spree.t(:successfully_removed)
flash[:success] = flash_message_for(@object, :successfully_removed)
respond_with(@object) do |format|
format.html { redirect_to collection_url }

View File

@@ -5,7 +5,7 @@ module Spree
class TaxCategoriesController < ::Admin::ResourceController
def destroy
if @object.destroy
flash[:success] = Spree.t(:successfully_removed)
flash[:success] = flash_message_for(@object, :successfully_removed)
respond_with(@object) do |format|
format.html { redirect_to collection_url }
format.js { render partial: "spree/admin/shared/destroy" }

View File

@@ -90,13 +90,11 @@ class VoucherAdjustmentsController < BaseController
voucher_code: voucher_params[:voucher_code], enterprise: @order.distributor
)
voucher = vine_voucher_validator.validate
errors = vine_voucher_validator.errors
return nil if errors[:not_found_voucher].present?
return nil if vine_voucher_validator.errors[:not_found_voucher].present?
if errors.present?
message = errors[:invalid_voucher] || I18n.t('checkout.errors.add_voucher_error')
@order.errors.add(:voucher_code, message)
if vine_voucher_validator.errors.present?
@order.errors.add(:voucher_code, I18n.t('checkout.errors.add_voucher_error'))
return nil
end

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
class WebhookEndpointsController < BaseController
before_action :load_resource, only: [:destroy, :test]
before_action :load_resource, only: :destroy
def create
webhook_endpoint = spree_current_user.webhook_endpoints.new(webhook_endpoint_params)
@@ -25,30 +25,12 @@ class WebhookEndpointsController < BaseController
redirect_to redirect_path
end
def test
at = Time.zone.now
test_payload = Payments::WebhookPayload.test_data.to_hash
WebhookDeliveryJob.perform_later(@webhook_endpoint.url, "payment.completed", test_payload, at:)
flash[:success] = t(".success")
respond_with do |format|
format.turbo_stream do
render turbo_stream: turbo_stream.update(
:flashes, partial: "shared/flashes", locals: { flashes: flash }
)
end
end
end
private
def load_resource
@webhook_endpoint = spree_current_user.webhook_endpoints.find(params[:id])
end
def webhook_endpoint_params
params.require(:webhook_endpoint).permit(:url, :webhook_type)
params.require(:webhook_endpoint).permit(:url)
end
def redirect_path

View File

@@ -72,8 +72,7 @@ module ApplicationHelper
end
end
# Update "v1" to invalidate existing cache key
def cache_key_with_locale(key, locale)
Array.wrap(key) + ["v2", locale.to_s, I18nDigests.for_locale(locale)]
Array.wrap(key) + [locale.to_s, I18nDigests.for_locale(locale)]
end
end

View File

@@ -63,11 +63,8 @@ module EnterprisesHelper
url = object_url(enterprise)
name = t(:delete)
options = {}
options[:data] = {
turbo: true,
'turbo-method': 'delete',
'turbo-confirm': enterprise_confirm_delete_message(enterprise)
}
options[:class] = "delete-resource"
options[:data] = { action: 'remove', confirm: enterprise_confirm_delete_message(enterprise) }
link_to_with_icon 'icon-trash', name, url, options
end

View File

@@ -12,10 +12,7 @@ class ApplicationRecord < ActiveRecord::Base
self.include_root_in_json = true
def self.image_service
return :local if ENV["S3_BUCKET"].blank?
return :amazon_public if ENV["S3_ENDPOINT"].blank?
:s3_compatible_storage_public
ENV["S3_BUCKET"].present? ? :amazon_public : :local
end
# We might have a development environment without S3 but with a database

View File

@@ -50,11 +50,11 @@ class Enterprise < ApplicationRecord
has_many :distributed_orders, class_name: 'Spree::Order',
foreign_key: 'distributor_id',
inverse_of: :distributor,
dependent: :restrict_with_error
dependent: :restrict_with_exception
belongs_to :address, class_name: 'Spree::Address'
belongs_to :business_address, optional: true, class_name: 'Spree::Address', dependent: :destroy
has_many :enterprise_fees, dependent: :restrict_with_error
has_many :enterprise_fees, dependent: :restrict_with_exception
has_many :enterprise_roles, dependent: :destroy
has_many :users, through: :enterprise_roles
belongs_to :owner, class_name: 'Spree::User',
@@ -62,18 +62,18 @@ class Enterprise < ApplicationRecord
has_many :distributor_payment_methods,
inverse_of: :distributor,
foreign_key: :distributor_id,
dependent: :restrict_with_error
dependent: :restrict_with_exception
has_many :distributor_shipping_methods,
inverse_of: :distributor,
foreign_key: :distributor_id,
dependent: :restrict_with_error
dependent: :restrict_with_exception
has_many :payment_methods, through: :distributor_payment_methods
has_many :shipping_methods, through: :distributor_shipping_methods
has_many :customers, dependent: :destroy
has_many :inventory_items, dependent: :destroy
has_many :tag_rules, dependent: :destroy
has_one :stripe_account, dependent: :destroy
has_many :vouchers, dependent: :restrict_with_error
has_many :vouchers, dependent: :restrict_with_exception
has_many :connected_apps, dependent: :destroy
has_many :dfc_permissions, dependent: :destroy
has_one :custom_tab, dependent: :destroy
@@ -111,14 +111,14 @@ class Enterprise < ApplicationRecord
end
validates :logo,
processable_file: true,
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
processable_image: true,
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
validates :promo_image,
processable_file: true,
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
processable_image: true,
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
validates :white_label_logo,
processable_file: true,
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
processable_image: true,
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
validates :terms_and_conditions, content_type: {
in: "application/pdf",
message: I18n.t(:enterprise_terms_and_conditions_type_error),

View File

@@ -29,11 +29,11 @@ class EnterpriseGroup < ApplicationRecord
has_one_attached :promo_image, service: image_service
validates :logo,
processable_file: true,
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
processable_image: true,
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
validates :promo_image,
processable_file: true,
content_type: ::Spree::Image::ACCEPTED_CONTENT_TYPES
processable_image: true,
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
scope :by_position, -> { order('position ASC') }
scope :on_front_page, -> { where(on_front_page: true) }

View File

@@ -31,6 +31,7 @@ module Spree
preference :admin_products_per_page, :integer, default: 10
# Should only be true if you don't need to track inventory
preference :allow_backorder_shipping, :boolean, default: false
preference :allow_checkout_on_gateway_error, :boolean, default: false
preference :allow_guest_checkout, :boolean, default: true
preference :currency_decimal_mark, :string, default: "."
preference :currency_symbol_position, :string, default: "before"

View File

@@ -2,8 +2,6 @@
module Spree
class Image < Asset
ACCEPTED_CONTENT_TYPES = %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
has_one_attached :attachment, service: image_service do |attachment|
attachment.variant :mini, resize_to_fill: [48, 48]
attachment.variant :small, resize_to_fill: [227, 227]
@@ -13,8 +11,8 @@ module Spree
validates :attachment,
attached: true,
processable_file: true,
content_type: ACCEPTED_CONTENT_TYPES
processable_image: true,
content_type: %r{\Aimage/(png|jpeg|gif|jpg|svg\+xml|webp)\Z}
validate :no_attachment_errors
def self.default_image_url(size)

View File

@@ -437,13 +437,18 @@ module Spree
#
# Returns:
# - true if all pending_payments processed successfully
# - true if a payment failed, ie. raised a GatewayError
# which gets rescued and converted to TRUE when
# :allow_checkout_gateway_error is set to true
# - false if a payment failed, ie. raised a GatewayError
# which gets rescued and converted to FALSE
# which gets rescued and converted to FALSE when
# :allow_checkout_on_gateway_error is set to false
#
def process_payments!
process_each_payment(&:process!)
rescue Core::GatewayError => e
errors.add(:base, e.message) && (return false)
result = !!Spree::Config[:allow_checkout_on_gateway_error]
errors.add(:base, e.message) && (return result)
end
def process_payments_offline!

View File

@@ -101,24 +101,6 @@ module Spree
end
after_transition to: :completed, do: :set_captured_at
after_transition do |payment, transition|
# Catch any exceptions to prevent any rollback potentially
# preventing payment from going through
ActiveSupport::Notifications.instrument(
"ofn.payment_transition", payment: payment, event: transition.to
)
rescue StandardError => e
Rails.logger.fatal "ActiveSupport::Notification.instrument failed params: " \
"<event_type:ofn.payment_transition> " \
"<payment_id:#{payment.id}> " \
"<event:#{transition.to}>"
Alert.raise(
e,
metadata: {
event_tye: "ofn.payment_transition", payment_id: payment.id, event: transition.to
}
)
end
end
def money

View File

@@ -52,6 +52,10 @@ module Spree
.where(environment: [Rails.env, "", nil])
}
def self.providers
Rails.application.config.spree.payment_methods
end
def configured?
!stripe? || stripe_configured?
end
@@ -89,8 +93,8 @@ module Spree
type.demodulize.downcase
end
def self.find_with_destroyed(*)
unscoped { find(*) }
def self.find_with_destroyed(*args)
unscoped { find(*args) }
end
def payment_profiles_supported?
@@ -114,8 +118,8 @@ module Spree
end
def self.clean_name
scope = "spree.admin.payment_methods.providers"
I18n.t(name.demodulize.downcase, scope:)
i18n_key = "spree.admin.payment_methods.providers.#{name.demodulize.downcase}"
I18n.t(i18n_key)
end
private

View File

@@ -2,11 +2,5 @@
# Records a webhook url to send notifications to
class WebhookEndpoint < ApplicationRecord
WEBHOOK_TYPES = %w(order_cycle_opened payment_status_changed).freeze
validates :url, presence: true
validates :webhook_type, presence: true, inclusion: { in: WEBHOOK_TYPES }
scope :order_cycle_opened, -> { where(webhook_type: "order_cycle_opened") }
scope :payment_status, -> { where(webhook_type: "payment_status_changed") }
end

View File

@@ -11,9 +11,7 @@ class ImageImporter
image = Spree::Image.create do |img|
PrivateAddressCheck.only_public_connections do
io = valid_url.open
content_type = Marcel::MimeType.for(io)
img.attachment.attach(io:, filename:, metadata:, content_type:)
img.attachment.attach(io: valid_url.open, filename:, metadata:)
end
end
product.image = image if image

View File

@@ -11,12 +11,10 @@ module OrderCycles
.merge(coordinator_name: order_cycle.coordinator.name)
# Endpoints for coordinator owner
webhook_endpoints = order_cycle.coordinator.owner.webhook_endpoints.order_cycle_opened
webhook_endpoints = order_cycle.coordinator.owner.webhook_endpoints
# Plus unique endpoints for distributor owners (ignore duplicates)
webhook_endpoints |= order_cycle.distributors.map(&:owner).flat_map { |owner|
owner.webhook_endpoints.order_cycle_opened
}
webhook_endpoints |= order_cycle.distributors.map(&:owner).flat_map(&:webhook_endpoints)
webhook_endpoints.each do |endpoint|
WebhookDeliveryJob.perform_later(endpoint.url, event, webhook_payload, at:)

View File

@@ -1,13 +0,0 @@
# frozen_string_literal: true
# Called by "ActiveSupport::Notifications" when an "ofn.payment_transition" occurs
# Event originate from Spree::Payment event machine
#
module Payments
class StatusChangedListenerService
def call(_name, started, _finished, _unique_id, payload)
event = "payment.#{payload[:event]}"
Payments::WebhookService.create_webhook_job(payment: payload[:payment], event:, at: started)
end
end
end

View File

@@ -1,84 +0,0 @@
# frozen_string_literal: true
module Payments
class WebhookPayload
def initialize(payment:, order:, enterprise:)
@payment = payment
@order = order
@enterprise = enterprise
end
def to_hash
{
payment: @payment.slice(:updated_at, :amount, :state),
enterprise: @enterprise.slice(:abn, :acn, :name)
.merge(address: @enterprise.address.slice(:address1, :address2, :city, :zipcode)),
order: @order.slice(:total, :currency).merge(line_items: line_items)
}.with_indifferent_access
end
def self.test_data
new(payment: test_payment, order: test_order, enterprise: test_enterprise)
end
def self.test_payment
{
updated_at: Time.zone.now,
amount: 0.00,
state: "completed"
}
end
def self.test_order
order = Spree::Order.new(
total: 0.00,
currency: "AUD",
)
tax_category = Spree::TaxCategory.new(name: "VAT")
product = Spree::Product.new(name: "Test product")
Spree::Variant.new(product:, display_name: "")
order.line_items << Spree::LineItem.new(
quantity: 1,
price: 20.00,
tax_category:,
product:,
unit_presentation: "1kg"
)
order
end
def self.test_enterprise
enterprise = Enterprise.new(
abn: "65797115831",
acn: "",
name: "TEST Enterprise",
)
enterprise.address = Spree::Address.new(
address1: "1 testing street",
address2: "",
city: "TestCity",
zipcode: "1234"
)
enterprise
end
private_class_method :test_payment, :test_order, :test_enterprise
private
def line_items
@order.line_items.map do |li|
li.slice(:quantity, :price)
.merge(
tax_category_name: li.tax_category&.name,
product_name: li.product.name,
name_to_display: li.display_name,
unit_to_display: li.unit_presentation
)
end
end
end
end

View File

@@ -1,30 +0,0 @@
# frozen_string_literal: true
# Create a webhook payload for a payment status event.
# The payload will be delivered asynchronously.
module Payments
class WebhookService
def self.create_webhook_job(payment:, event:, at:)
order = payment.order
payload = WebhookPayload.new(payment:, order:, enterprise: order.distributor).to_hash
coordinator = payment.order.order_cycle.coordinator
webhook_urls(coordinator).each do |url|
WebhookDeliveryJob.perform_later(url, event, payload, at:)
end
end
def self.webhook_urls(coordinator)
# url for coordinator owner
webhook_urls = coordinator.owner.webhook_endpoints.payment_status.pluck(:url)
# plus url for coordinator manager (ignore duplicate)
users_webhook_urls = coordinator.users.flat_map do |user|
user.webhook_endpoints.payment_status.pluck(:url)
end
webhook_urls | users_webhook_urls
end
end
end

View File

@@ -2,11 +2,6 @@
module Vine
class VoucherValidatorService
VINE_ERRORS = {
# https://github.com/openfoodfoundation/vine/blob/main/app/Enums/ApiResponse.php
"This voucher has expired." => :expired,
}.freeze
attr_reader :voucher_code, :errors
def initialize(voucher_code:, enterprise:)
@@ -47,10 +42,8 @@ module Vine
end
def handle_errors(response)
if [400, 409].include?(response[:status])
message = response[:body] && JSON.parse(response[:body]).dig("meta", "message")
key = VINE_ERRORS.fetch(message, :invalid_voucher)
errors[:invalid_voucher] = I18n.t("vine_voucher_validator_service.errors.#{key}")
if response[:status] == 400
errors[:invalid_voucher] = I18n.t("vine_voucher_validator_service.errors.invalid_voucher")
elsif response[:status] == 404
errors[:not_found_voucher] =
I18n.t("vine_voucher_validator_service.errors.not_found_voucher")

View File

@@ -22,7 +22,7 @@
%tbody
= f.fields_for :collection do |enterprise_form|
- enterprise = enterprise_form.object
%tr{class: "enterprise-#{enterprise.id}", id: "resource-#{enterprise.id}"}
%tr{class: "enterprise-#{enterprise.id}"}
%td= link_to enterprise.name, main_app.edit_admin_enterprise_path(enterprise)
%td
= enterprise_form.check_box :is_primary_producer

View File

@@ -1,4 +0,0 @@
- unless flash[:error]
= turbo_stream.remove "resource-#{@object.id}"
= turbo_stream.append "flashes" do
= render(partial: 'admin/shared/flashes', locals: { flashes: flash })

View File

@@ -18,7 +18,7 @@
%span{ "ofn-with-tip": '{{ orderCycle.producerNames }}', "ng-show": 'orderCycle.producers.length > 3' }
{{ orderCycle.producers.length }}
= t('.suppliers')
%span{ "ng-hide": 'orderCycle.producers.length > 3', "ng-bind-html": 'orderCycle.producerNames' }
%span{ "ng-hide": 'orderCycle.producers.length > 3', "ng-bind": 'orderCycle.producerNames' }
%td.coordinator{ "ng-show": 'columns.coordinator.visible', "ng-bind-html": 'orderCycle.coordinator.name' }
%td.shops{ "ng-show": 'columns.shops.visible' }
%span{ "ofn-with-tip": '{{ orderCycle.shopNames }}', "ng-show": 'orderCycle.shops.length > 3' }

View File

@@ -1,6 +1,6 @@
%fieldset.no-border-top
.add_producer_properties
= image_pack_tag 'images/spinner.gif', :plugin => 'spree', :style => 'display:none;', :id => 'busy_indicator'
= image_pack_tag 'spinner.gif', :plugin => 'spree', :style => 'display:none;', :id => 'busy_indicator'
%table.index.sortable{"data-sortable-link" => main_app.update_positions_admin_enterprise_producer_properties_url(@enterprise)}
%thead
%tr

View File

@@ -23,7 +23,7 @@
- select_tag_options = { class: "fullwidth",
multiple: true ,
data: { controller: "tom-select", "tom-select-placeholder-value": t(".select_tag"), "tom-select-options-value": '{ "maxItems": 5 , "plugins": { "remove_button": {} , "no_active_items": {}, "checkbox_options": { "checkedClassNames": ["ts-checked"], "uncheckedClassNames": ["ts-unchecked"] } } }' } }
= select_tag :tags_name_in, options_for_select(available_tags.unshift([t('.tags.none'), @none_tag_value]), tags), select_tag_options
= select_tag :tags_name_in, options_for_select(available_tags, tags), select_tag_options
.submit
.search-button
= button_tag t(".search"), class: "secondary icon-search relaxed", name: nil

View File

@@ -7,6 +7,5 @@
#no-products-actions
%a{ href: "/admin/products/new", class: "button icon-plus", icon: "icon-plus" }
= t(:new_product)
%a{ href: admin_product_import_path, class: "button icon-upload secondary", icon: "icon-upload" }
%a{ href: "/admin/products/import", class: "button icon-upload secondary", icon: "icon-upload" }
= t(".import_products")

View File

@@ -1,2 +1,2 @@
= cache do
%img.spinner{ src: image_pack_path("images/spinning-circles.svg"), style: "max-width: 100%" }
%img.spinner{ src: image_pack_path("spinning-circles.svg"), style: "max-width: 100%" }

View File

@@ -3,7 +3,7 @@
.row
.small-12.text-center.columns
%h1
%img{src: image_pack_path("images/logo-white-notext.png"), title: Spree::Config.site_name}
%img{src: image_pack_path("logo-white-notext.png"), title: Spree::Config.site_name}
%br/
%a.button.transparent{href: "/shops"}
= t :home_shop

View File

@@ -13,12 +13,12 @@
- else
= favicon_link_tag "/favicon-staging.ico"
%link{href: "https://fonts.googleapis.com/css?family=Roboto:400,300italic,400italic,300,700,700italic|Oswald:300,400,700", rel: "stylesheet", type: "text/css"}
%link{href: asset_pack_path("static/fonts/OFN-v2.woff"), rel: "preload", as: "font", crossorigin: "anonymous"}
%link{href: asset_pack_path("media/fonts/OFN-v2.woff"), rel: "preload", as: "font", crossorigin: "anonymous"}
= render "layouts/matomo_tag"
= language_meta_tags
= stylesheet_pack_tag "darkswarm", "data-turbo-track": "reload", media: "screen"
= javascript_pack_tag "application", "data-turbo-track": "reload", defer: false # do not use defer because our javascript currently depend on order of execution of loaded script.
= javascript_pack_tag "application", "data-turbo-track": "reload"
= render "layouts/shopfront_script" if @shopfront_layout
= render "layouts/bugsnag_js"

View File

@@ -16,11 +16,11 @@
= stylesheet_pack_tag "darkswarm", media: "screen"
= javascript_include_tag "darkswarm/all"
= javascript_pack_tag "application", defer: false # do not use defer because our javascript currently depend on order of execution of loaded script.
= javascript_pack_tag "application"
= csrf_meta_tags
%body.off-canvas{ style: "background-image: url(#{image_pack_path('images/tile-wide.png')})", "data-turbo": "false" }
%body.off-canvas{ style: "background-image: url(#{image_pack_path('tile-wide.png')})", "data-turbo": "false" }
.off-canvas-wrap{offcanvas: true}
.inner-wrap

View File

@@ -41,7 +41,7 @@
%p.word-wrap{"ng-if" => "::producer.whatsapp_phone"}
%a{"ng-href" => "{{::producer.whatsapp_url}}", target: "_blank"}
%img{ src: image_pack_path("images/social-logos/whatsapp.svg") }
%img{ src: image_pack_path("social-logos/whatsapp.svg") }
%span{"ng-bind" => "::producer.whatsapp_phone"}
%p.word-wrap{"ng-if" => "::producer.email_address"}

View File

@@ -6,7 +6,7 @@
%h4= t(".message")
.row
.small-12.medium-3.large-2.columns.text-right.hide-for-small-only
%img{:src => image_pack_path("images/potatoes.png") }
%img{:src => image_pack_path("potatoes.png") }
.small-12.medium-9.large-10.columns
%p
= t(".text")

View File

@@ -4,7 +4,7 @@
.row
.small-12.columns.text-center
.logo
%img{src: image_pack_path("images/logo-white-notext.png") }
%img{src: image_pack_path("logo-white-notext.png") }
.row
.small-12.medium-8.medium-offset-2.columns.text-center
.alert-box

View File

@@ -4,6 +4,6 @@
%span
= t '.cart'
%span.count
%img{ src: image_pack_path("images/menu/icn-cart.svg") }
%img{ src: image_pack_path("menu/icn-cart.svg") }
%span
{{ Cart.total_item_count() }}

View File

@@ -2,7 +2,7 @@
%nav.tab-bar.show-for-medium-down
%section.left
%a.left-off-canvas-toggle.menu-icon
= image_pack_tag "images/menu/btn-menu-mobile.png"
= image_pack_tag "menu/btn-menu-mobile.png"
%section.left
.ofn-logo
@@ -18,7 +18,7 @@
%span
= t '.cart'
%span.count
= image_pack_tag "images/menu/icn-cart.svg"
= image_pack_tag "menu/icn-cart.svg"
%span
{{ Cart.total_item_count() }}

View File

@@ -1,7 +1,7 @@
%li.user-menu.has-dropdown.not-click
%a{href: "#", class: "top-bar--menu-item-with-icon"}
%img{ src: image_pack_path("images/menu/icn-profile.svg") }
%img{ src: image_pack_path("menu/icn-profile.svg") }
%span
= t '.profile'

View File

@@ -1,6 +1,6 @@
= cache_with_locale do
%li#login-link{ "data-controller": "login-modal" }
%a{"auth": "login", "data-action": "click->login-modal#call" }
%img{ src: image_pack_path("images/menu/icn-login.svg") }
%img{ src: image_pack_path("menu/icn-login.svg") }
%span
= t 'label_login'

View File

@@ -9,7 +9,7 @@
"ng-debounce" => "200",
"disable-enter-with-blur" => true}
%a.clear{ type: 'button', "focus-search": true, "ng-show": 'query', "ng-click": 'clearQuery()' }
= image_pack_tag "images/icn-close.png"
= image_pack_tag "icn-close.png"
.hide-for-large-up
%button{ type: 'button', "ng-click": 'toggleFilterSidebar()' }

View File

@@ -27,7 +27,7 @@
%br
- if current_distributor.whatsapp_phone.present?
%a{href: current_distributor.whatsapp_url, target: "_blank" }
%img{ src: image_pack_path("images/social-logos/whatsapp.svg") }
%img{ src: image_pack_path("social-logos/whatsapp.svg") }
= current_distributor.whatsapp_phone
%br
- if current_distributor.website.present?

View File

@@ -41,5 +41,5 @@
%a.info.cvvLink{href: "/content/cvv", target: "_blank"}
(#{t(:what_is_this)})
.nine.columns
= image_pack_tag 'images/credit_cards/credit_card.gif', class: 'credit-card-image'
= image_pack_tag 'credit_cards/credit_card.gif', class: 'credit-card-image'
.clear

View File

@@ -12,7 +12,7 @@
= form_for @product, url: admin_product_url(@product, @url_filters), method: :put do |f|
%fieldset.no-border-top
.add_product_properties
= image_pack_tag 'images/select2-spinner.gif', plugin: 'spree', style: 'display:none;', id: 'busy_indicator'
= image_pack_tag 'select2-spinner.gif', plugin: 'spree', style: 'display:none;', id: 'busy_indicator'
%table.index.sortable{"data-sortable-link" => update_positions_admin_product_product_properties_url}
%thead

View File

@@ -25,7 +25,7 @@
= render "spree/admin/shared/translations"
= render "spree/admin/shared/routes"
= javascript_pack_tag "admin", "data-turbo-track": "reload", defer: false # do not use defer because our javascript currently depend on order of execution of loaded script.
= javascript_pack_tag "admin", "data-turbo-track": "reload"
%script
= raw "var AUTH_TOKEN = \"#{form_authenticity_token}\";"

View File

@@ -11,6 +11,6 @@
- databaseurl = "#{admin_states_path(format: :js)}?country_id="
%select#country.observe_field.select2.fullwidth{"data-base-url" => databaseurl, "data-update" => "#state-list"}
= options_from_collection_for_select(@countries, :id, :name, @country.id)
= image_pack_tag 'images/select2-spinner.gif', plugin: 'spree', style: 'display:none;', id: 'busy_indicator'
= image_pack_tag 'select2-spinner.gif', plugin: 'spree', style: 'display:none;', id: 'busy_indicator'
#state-list
= render partial: 'state_list'

View File

@@ -10,5 +10,24 @@
%th= t('.url.header')
%th.actions
%tbody
= render WebhookEndpointFormComponent.new(webhooks: @user.webhook_endpoints.order_cycle_opened, webhook_type: "order_cycle_opened")
= render WebhookEndpointFormComponent.new(webhooks: @user.webhook_endpoints.payment_status, webhook_type: "payment_status_changed")
-# Existing endpoints
- @user.webhook_endpoints.each do |webhook_endpoint|
%tr
%td= t('.event_types.order_cycle_opened') # For now, we only support one type.
%td= webhook_endpoint.url
%td.actions
- if webhook_endpoint.persisted?
= button_to account_webhook_endpoint_path(webhook_endpoint), method: :delete,
class: "tiny alert no-margin",
data: { confirm: I18n.t(:are_you_sure)} do
= I18n.t(:delete)
-# Create new
- if @user.webhook_endpoints.empty? # Only one allowed for now.
%tr
%td= t('.event_types.order_cycle_opened') # For now, we only support one type.
%td
= form_for(@user.webhook_endpoints.build, url: account_webhook_endpoints_path, id: 'new_webhook_endpoint') do |f|
= f.url_field :url, placeholder: t('.url.create_placeholder'), required: true, size: 64
%td.actions
= button_tag t(:create), class: 'button primary tiny no-margin', form: 'new_webhook_endpoint'

View File

@@ -2,19 +2,19 @@
// While in feature-toggle, we inherit all files from old admin design.
// Individual files may be copied in order to replace the old files.
@import "assets/stylesheets/normalize";
@import "assets/stylesheets/responsive-tables";
@import "assets/stylesheets/jquery.powertip";
@import "jquery-ui/themes/base/core";
@import "jquery-ui/themes/base/button";
@import "jquery-ui/themes/base/resizable";
@import "assets/stylesheets/jquery-ui-theme";
@import "jquery-ui/themes/base/dialog";
@import "vendor/assets/stylesheets/normalize";
@import "vendor/assets/stylesheets/responsive-tables";
@import "vendor/assets/stylesheets/jquery.powertip";
@import "~jquery-ui/themes/base/core";
@import "~jquery-ui/themes/base/button";
@import "~jquery-ui/themes/base/resizable";
@import "vendor/assets/stylesheets/jquery-ui-theme";
@import "~jquery-ui/themes/base/dialog";
@import "../shared/ng-tags-input.min";
@import "assets/stylesheets/select2";
@import "flatpickr/dist/flatpickr";
@import "flatpickr/dist/themes/material_blue";
@import "shortcut-buttons-flatpickr/dist/themes/light";
@import "vendor/assets/stylesheets/select2.css.scss";
@import "~flatpickr/dist/flatpickr";
@import "~flatpickr/dist/themes/material_blue";
@import "~shortcut-buttons-flatpickr/dist/themes/light";
@import "../admin/globals/functions";
@import "globals/palette"; // admin_v3
@@ -123,13 +123,13 @@
@import "shared/question-mark-icon";
@import "../admin/question-mark-tooltip";
@import "tom-select/src/scss/tom-select.default";
@import "~tom-select/src/scss/tom-select.default";
@import "components/tom_select"; // admin_v3
@import "modal_component/modal_component";
@import "vertical_ellipsis_menu_component/vertical_ellipsis_menu_component"; // admin_v3 and only V3
@import "tag_list_input_component/tag_list_input_component";
@import "admin/trix";
@import "app/components/modal_component/modal_component";
@import "app/components/vertical_ellipsis_menu_component/vertical_ellipsis_menu_component"; // admin_v3 and only V3
@import "app/components/tag_list_input_component/tag_list_input_component";
@import "app/webpacker/css/admin/trix.scss";
@import "terms_of_service_banner"; // admin_v3

View File

@@ -9,8 +9,7 @@
}
}
.saved_cards,
.no_cards {
.saved_cards, .no_cards {
margin-bottom: 1em;
}
@@ -27,7 +26,7 @@
}
}
.authorised_shops {
.authorised_shops{
table {
width: 100%;
}
@@ -40,9 +39,7 @@
a {
color: $clr-brick;
&:hover,
&:active,
&:focus {
&:hover, &:active, &:focus {
color: $clr-brick-med-bright;
}
}
@@ -63,8 +60,7 @@
}
}
i.ofn-i_059-producer,
i.ofn-i_060-producer-reversed {
i.ofn-i_059-producer, i.ofn-i_060-producer-reversed {
font-size: 3rem;
display: inline-block;
margin-right: 0.25rem;
@@ -96,8 +92,7 @@
visibility: hidden;
}
.transaction-group {
}
.transaction-group {}
table {
border-radius: $radius-medium $radius-medium 0 0;
@@ -166,15 +161,6 @@ table {
//
// Unfortunately we can't use Scss's interpolation
// https://sass-lang.com/documentation/interpolation. We're using a too old version perhaps?
right: calc(12px + 2 * 2px + 2 * 1px);
}
}
// Webhook Endpoints
td.endpoints-actions {
display: flex;
form {
padding-right: $padding-small;
right: calc(12px + 2*2px + 2*1px);
}
}

View File

@@ -1,10 +1,10 @@
@import 'assets/stylesheets/autocomplete';
@import 'assets/stylesheets/leaflet';
@import 'vendor/assets/stylesheets/autocomplete';
@import 'vendor/assets/stylesheets/leaflet';
@import 'variables';
@import '../shared/variables/layout';
@import '../shared/utilities';
@import 'foundation-sites/scss/foundation';
@import '~foundation-sites/scss/foundation';
@import 'big-input';
@import 'branding';
@@ -77,4 +77,4 @@ ofn-modal {
@import "../shared/question-mark-icon";
@import '../admin/shared/scroll_bar';
@import 'modal_component/modal_component';
@import 'app/components/modal_component/modal_component';

View File

@@ -1,4 +1,4 @@
@import "foundation-sites/scss/foundation/components/global";
@import "~foundation-sites/scss/foundation/components/global";
// Brand guide colours:
// International: #81c26e

View File

@@ -1,6 +1,72 @@
module.exports = function (api) {
const defaultConfigFunc = require("shakapacker/package/babel/preset.js");
const resultConfig = defaultConfigFunc(api);
module.exports = function(api) {
var validEnv = ['development', 'test', 'production']
var currentEnv = api.env()
var isDevelopmentEnv = api.env('development')
var isProductionEnv = api.env('production')
var isTestEnv = api.env('test')
return resultConfig;
};
if (!validEnv.includes(currentEnv)) {
throw new Error(
'Please specify a valid `NODE_ENV` or ' +
'`BABEL_ENV` environment variables. Valid values are "development", ' +
'"test", and "production". Instead, received: ' +
JSON.stringify(currentEnv) +
'.'
)
}
return {
presets: [
isTestEnv && [
'@babel/preset-env',
{
targets: {
node: 'current'
}
}
],
(isProductionEnv || isDevelopmentEnv) && [
'@babel/preset-env',
{
forceAllTransforms: true,
useBuiltIns: 'entry',
corejs: 3,
modules: false,
exclude: ['transform-typeof-symbol']
}
]
].filter(Boolean),
plugins: [
'babel-plugin-macros',
'@babel/plugin-syntax-dynamic-import',
isTestEnv && 'babel-plugin-dynamic-import-node',
'@babel/plugin-transform-destructuring',
[
'@babel/plugin-proposal-class-properties',
{
loose: true
}
],
[
'@babel/plugin-proposal-object-rest-spread',
{
useBuiltIns: true
}
],
[
'@babel/plugin-transform-runtime',
{
helpers: false
}
],
[
'@babel/plugin-transform-regenerator',
{
async: false
}
],
["@babel/plugin-proposal-private-property-in-object", { "loose": true }],
["@babel/plugin-proposal-private-methods", { "loose": true }]
].filter(Boolean)
}
}

View File

@@ -1,10 +1,4 @@
#!/usr/bin/env ruby
#
# Install dependencies and migrate data during production deployments or
# when updating your development environment.
#
# Take care when changing it and consider the ofn-install repository for
# the context of deployments.
require "fileutils"
# path to your application root.
@@ -22,13 +16,13 @@ FileUtils.chdir APP_ROOT do
# Add necessary setup steps to this file.
puts "== Installing dependencies =="
system! "script/install-bundler"
# Check first (it's quicker), then install new gems if necessary
system("bundle check 2> /dev/null") || system!(BUNDLE_ENV, "bundle install")
# Install JavaScript dependencies
system!("script/nodenv-install.sh")
system!("bin/yarn")
system("script/nodenv-install.sh")
system("bin/yarn")
# puts "\n== Copying sample files =="
# unless File.exist?("config/database.yml")
@@ -37,16 +31,6 @@ FileUtils.chdir APP_ROOT do
puts "\n== Preparing database, removing old logs and tempfiles =="
system! "bin/rails db:prepare log:clear tmp:clear"
system! "rm -f tmp/capybara/screenshots/*.png"
system! "rm -f tmp/invoices/*.pdf"
system! "rm -f tmp/javascripts/*.js"
system! "rm -f tmp/karma_unit.js*"
system! "rm -f tmp/product_import-*"
puts "\n== Removing any precompiled assets that would be out of date now =="
system! "rm -rf public/assets/"
system! "rm -rf public/packs/"
system! "rm -rf public/packs-test/"
puts "\n== Restarting application server =="
# system! "bin/rails restart"

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env ruby
ENV["RAILS_ENV"] ||= "development"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
require "bundler/setup"
require "shakapacker"
require "shakapacker/webpack_runner"
APP_ROOT = File.expand_path("..", __dir__)
Dir.chdir(APP_ROOT) do
Shakapacker::WebpackRunner.run(ARGV)
end

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env ruby
ENV["RAILS_ENV"] ||= "development"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
require "bundler/setup"
require "shakapacker"
require "shakapacker/dev_server_runner"
APP_ROOT = File.expand_path("..", __dir__)
Dir.chdir(APP_ROOT) do
Shakapacker::DevServerRunner.run(ARGV)
end

19
bin/webpack Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env ruby
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= "development"
ENV["NODE_OPTIONS"] ||= "--openssl-legacy-provider"
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "bundler/setup"
require "webpacker"
require "webpacker/webpack_runner"
APP_ROOT = File.expand_path("..", __dir__)
Dir.chdir(APP_ROOT) do
Webpacker::WebpackRunner.run(ARGV)
end

19
bin/webpack-dev-server Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env ruby
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= "development"
ENV["NODE_OPTIONS"] ||= "--openssl-legacy-provider"
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "bundler/setup"
require "webpacker"
require "webpacker/dev_server_runner"
APP_ROOT = File.expand_path("..", __dir__)
Dir.chdir(APP_ROOT) do
Webpacker::DevServerRunner.run(ARGV)
end

View File

@@ -1,16 +1,9 @@
#!/usr/bin/env ruby
APP_ROOT = File.expand_path("..", __dir__)
APP_ROOT = File.expand_path('..', __dir__)
Dir.chdir(APP_ROOT) do
yarn = ENV["PATH"].split(File::PATH_SEPARATOR).
select { |dir| File.expand_path(dir) != __dir__ }.
product(["yarn", "yarnpkg", "yarn.cmd", "yarn.ps1"]).
map { |dir, file| File.expand_path(file, dir) }.
find { |file| File.executable?(file) }
if yarn
exec yarn, *ARGV
else
begin
exec "yarnpkg", *ARGV
rescue Errno::ENOENT
$stderr.puts "Yarn executable was not detected in the system."
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
exit 1

View File

@@ -53,9 +53,9 @@ services:
OFN_REDIS_URL: redis://redis/
OFN_REDIS_JOBS_URL: redis://redis
OFN_REDIS_TEST_URL: redis://redis/3
SHAKAPACKER_DEV_SERVER_HOST: webpack
SHAKAPACKER_DEV_SERVER_PORT: 3035
SHAKAPACKER_DEV_SERVER_PUBLIC: localhost:3035
WEBPACKER_DEV_SERVER_HOST: webpack
WEBPACKER_DEV_SERVER_PORT: 3035
WEBPACKER_DEV_SERVER_PUBLIC: localhost:3035
command: >
sh -lc 'rm -f tmp/pids/server.pid;
@@ -89,7 +89,7 @@ services:
build: .
command: >
sh -lc 'until [ -f /bundles/.Gemfile.lock.sha ] && sha256sum -c /bundles/.Gemfile.lock.sha >/dev/null 2>&1; do sleep 0.5; done;
exec ./bin/shakapacker-dev-server'
exec ./bin/webpack-dev-server'
ports:
- "3035:3035"
volumes:
@@ -102,7 +102,7 @@ services:
retries: 30
environment:
SHAKAPACKER_DEV_SERVER_HOST: 0.0.0.0
WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
volumes:
gems:
postgres:

View File

@@ -58,15 +58,10 @@ module Openfoodnetwork
Spree::Core::Engine.routes.default_url_options[:host] = ENV["SITE_URL"] if Rails.env == 'test'
end
# We reload the routes here
# so that the appended/prepended routes are available to the application.
config.after_initialize do
# We reload the routes here
# so that the appended/prepended routes are available to the application.
Rails.application.routes_reloader.reload!
# Subscribe to payment transition events
ActiveSupport::Notifications.subscribe(
"ofn.payment_transition", Payments::StatusChangedListenerService.new
)
end
initializer "spree.environment", before: :load_config_initializers do |app|
@@ -76,6 +71,14 @@ module Openfoodnetwork
end
end
initializer "spree.register.payment_methods" do |app|
Rails.application.reloader.to_prepare do
app.config.spree.payment_methods = [
Spree::PaymentMethod::Check
]
end
end
initializer "spree.mail.settings" do |_app|
Rails.application.reloader.to_prepare do
Spree::Core::MailSettings.init
@@ -123,6 +126,14 @@ module Openfoodnetwork
end
end
# Register Spree payment methods
initializer "spree.gateway.payment_methods", :after => "spree.register.payment_methods" do |app|
Rails.application.reloader.to_prepare do
app.config.spree.payment_methods << Spree::Gateway::StripeSCA
app.config.spree.payment_methods << Spree::Gateway::PayPalExpress
end
end
initializer "ofn.reports" do |app|
module ::Reporting; end
Rails.application.reloader.to_prepare do
@@ -195,16 +206,7 @@ module Openfoodnetwork
Rails.autoloaders.main.ignore(Rails.root.join('app/webpacker'))
config.active_storage.service =
if ENV["S3_BUCKET"].present?
if ENV["S3_ENDPOINT"].present?
:s3_compatible_storage
else
:amazon
end
else
:local
end
config.active_storage.service = ENV["S3_BUCKET"].present? ? :amazon : :local
config.active_storage.content_types_to_serve_as_binary -= ["image/svg+xml"]
config.active_storage.variable_content_types += ["image/svg+xml"]
config.active_storage.url_options = config.action_controller.default_url_options

View File

@@ -74,6 +74,9 @@ Rails.application.configure do
allowed_warnings = [
# List strings here to allow matching deprecations.
#
# `Rails.application.secrets` is deprecated in favor of `Rails.application.credentials` and will be removed in Rails 7.2
"Rails.application.secrets",
"Passing the class as positional argument",
# Spree::CreditCard model aliases `cc_type` and has a method called `cc_type=` defined. Starting in Rails 7.2 `brand=` will not be calling `cc_type=` anymore. You may want to additionally define `brand=` to preserve the current behavior.

View File

@@ -7,7 +7,7 @@
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https
policy.font_src :self, :https, :data, "fonts.gstatic.com"
policy.img_src :self, :https, :data, ENV.fetch("S3_CORS_POLICY_DOMAIN", "*.s3.amazonaws.com")
policy.img_src :self, :https, :data, "*.s3.amazonaws.com"
policy.img_src :self, :http, :data, ENV["SITE_URL"] if Rails.env.development?
policy.object_src :none
policy.frame_ancestors :none

View File

@@ -27,6 +27,18 @@ end
Flipper.register(:new_2024_07_03) do |actor|
actor.respond_to?(:created_at?) && actor.created_at >= Time.zone.parse("2024-07-03")
end
Flipper.register(:enterprise_created_before_2025_08_11) do |actor|
# This group applies to enterprises only, so we return false if the actor is not an Enterprise
next false unless actor.actor.instance_of? Enterprise
actor.respond_to?(:created_at?) && actor.created_at < Time.zone.parse("2025-08-11")
end
Flipper.register(:enterprise_created_after_2025_08_11) do |actor|
# This group applies to enterprises only, so we return false if the actor is not an Enterprise
next false unless actor.actor.instance_of? Enterprise
actor.respond_to?(:created_at?) && actor.created_at >= Time.zone.parse("2025-08-11")
end
Flipper.register(:enterprise_with_no_inventory) do |actor|
# This group applies to enterprises only, so we return false if the actor is not an Enterprise

View File

@@ -11,7 +11,7 @@
# `button_to` view helper will render `<button>` element, regardless of whether
# or not the content is passed as the first argument or as a block.
Rails.application.config.action_view.button_to_generates_button_tag = true
# Rails.application.config.action_view.button_to_generates_button_tag = true
# `stylesheet_link_tag` view helper will not render the media attribute by default.
Rails.application.config.action_view.apply_stylesheet_media_default = false

View File

@@ -93,7 +93,7 @@
# serializer. Therefore, this setting should only be enabled after all replicas
# have been successfully upgraded to Rails 7.1.
#++
Rails.application.config.active_job.use_big_decimal_serializer = true
# Rails.application.config.active_job.use_big_decimal_serializer = true
###
# Specify if an `ArgumentError` should be raised if `Rails.cache` `fetch` or

View File

@@ -6,4 +6,7 @@
# no regular words or you'll be exposed to dictionary attacks.
# Rails 4+ key for signing and encrypting cookies.
Openfoodnetwork::Application.credentials.secret_key_base = ENV["SECRET_TOKEN"]
Openfoodnetwork::Application.config.secret_key_base = ENV["SECRET_TOKEN"]
# Legacy secret_token variable. This is still used directly for encryption.
Openfoodnetwork::Application.config.secret_token = ENV["SECRET_TOKEN"]

View File

@@ -0,0 +1,10 @@
module WebpackImageExtension
def image_pack_path(image)
# The Webpacker::Helper#resolve_path_to_image method is incredibly useful
# for nicely fetching Webpacker image paths, but it's private.
resolve_path_to_image(image)
end
end
Webpacker::Helper.include WebpackImageExtension

View File

@@ -716,7 +716,6 @@ ar:
label: التصنيفات
tags:
label: الاوسمة
none: لا شيء
search: بحث
sort:
pagination:
@@ -4359,10 +4358,13 @@ ar:
webhook_endpoints:
title: نقاط نهاية Webhook
description: قد تؤدي الأحداث في النظام إلى تشغيل webhooks للأنظمة الخارجية.
event_types:
order_cycle_opened: تم فتح دورة الطلب
event_type:
header: نوع الحدث
url:
header: عنوان URL لنقطة النهاية
create_placeholder: 'أدخل عنوان URL لنقطة نهاية webhook '
developer_settings:
title: إعدادات المطور
form:
@@ -4562,8 +4564,3 @@ ar:
add_tag_rule_modal:
select_rule_type: "اختر نوع القاعدة:"
add_rule: "أضف قاعدة "
webhook_endpoint_form:
url:
create_placeholder: 'أدخل عنوان URL لنقطة نهاية webhook '
event_types:
order_cycle_opened: تم فتح دورة الطلب

View File

@@ -89,6 +89,17 @@ ca:
messages:
blank: "no es pot deixar en blanc"
errors:
messages:
content_type_invalid: "té un tipus de contingut no vàlid"
file_size_out_of_range: "la mida %{file_size} no és entre el rang necessari"
limit_out_of_range: "el total està fora del marge"
image_metadata_missing: "no és una imatge vàlida"
aspect_ratio_not_square: "ha de ser una imatge quadrada"
aspect_ratio_not_portrait: "ha de ser una imatge vertical"
aspect_ratio_not_landscape: "ha de ser una imatge horitzontal"
aspect_ratio_is_not: "ha de tenir una relació d'aspecte de %{aspect_ratio} "
aspect_ratio_unknown: "té una relació d'aspecte desconeguda"
image_not_processable: "no és una imatge vàlida"
not_found:
title: "La pàgina que esteu cridant no existeix (404)"
message_html: "<b>Si us plau torna-ho a provar</b> Això podria ser un problema temporal. Feu clic al botó Enrere per tornar a la pantalla anterior o torneu a <a href='/'>Inici</a> i torneu-ho a provar. <b>Contacteu amb suport</b><p> Si el problema persisteix o és urgent, si us plau, digueu-nos-ho. Esn pots enviar un correua a <a href='https://openfoodnetwork.org/ofn-local/' target='blank'>suport<p>katuma.org </a>.</p><p> Ens ajuda molt si pots donar tants detalls com sigui possible sobre de què estaves fent quan va aparèixer l'error.</p>"
@@ -762,7 +773,6 @@ ca:
label: Categories
tags:
label: Etiquetes
none: Cap
search: Cerca
sort:
pagination:
@@ -4239,6 +4249,8 @@ ca:
title: Clau API
webhook_endpoints:
title: Webhook Endpoints
event_types:
order_cycle_opened: Cicle de comanda obert
form:
account_settings: Configuració del compte
show:
@@ -4381,6 +4393,3 @@ ca:
add_tag_rule_modal:
select_rule_type: "Selecciona un tipus de regla:"
add_rule: "Afegeix una regla"
webhook_endpoint_form:
event_types:
order_cycle_opened: Cicle de comanda obert

View File

@@ -115,6 +115,27 @@ cy:
blank: "ni chaniateir ei adael yn wag"
too_short: "rhy fyr (o leiaf %{count} cymeriad)"
errors:
messages:
content_type_invalid: "math o gynnwys annilys"
file_size_out_of_range: "nid yw'r maint %{file_size} rhwng yr ystod ofynnol"
limit_out_of_range: "mae'r cyfanswm rhif tu hwnt i'r ystod"
image_metadata_missing: "nid yw'n llun dilys"
dimension_min_inclusion: "mae'n gorfod bod yn fwy neu'r un faint â %{width} x %{height} picsel."
dimension_max_inclusion: "mae'n gorfod bod yn llai neu'r un faint â %{width} x %{height} picsel."
dimension_width_inclusion: "nid yw lled yn cael ei gynnwys rhwng %{min} ac %{max} picsel."
dimension_height_inclusion: "nid yw uchder yn cael ei gynnwys rhwng %{min} acd %{max} picsel."
dimension_width_greater_than_or_equal_to: "mae'r lled yn gorfod bod yn fwy neu'r un faint â %{length} picsel."
dimension_height_greater_than_or_equal_to: "mae'r uchder yn gorfod bod yn fwy neu'r un faint â %{length} picsel."
dimension_width_less_than_or_equal_to: "mae'r lled yn gorfod bod yn llai neu'r un faint â %{length} picsel."
dimension_height_less_than_or_equal_to: "mae'r uchder yn gorfod bod yn llai neu'r un faint â %{length} picsel."
dimension_width_equal_to: "mae'r lled yn gorfod bod yr un faint â %{length} picsel."
dimension_height_equal_to: "mae'r uchder yn gorfod bod yr un faint â %{length} picsel."
aspect_ratio_not_square: "mae'n gorfod bod yn llun sgwâr"
aspect_ratio_not_portrait: "mae'n gorfod bod yn llun siâp portread"
aspect_ratio_not_landscape: "mae'n gorfod bod yn llun siâp tirlun"
aspect_ratio_is_not: "mae'n rhaid cael cymhareb agwedd o %{aspect_ratio}"
aspect_ratio_unknown: "yn meddu ar gymhareb agwedd anhysbys"
image_not_processable: "nid yw'n llun dilys"
not_found:
title: "Nid yw'r dudalen rydych yn chwilio amdani'n bodoli (404)"
message_html: "<b>Rhowch gynnig arall</b> <p>. Hwyrach taw problem dros dro yw hon. Cliciwch y botwm yn ôl i ddychwelyd at y sgrin flaenorol, neu ewch yn ôl ir <a href='/'> Hafan </a>, a rhowch gynnig arall </p>. <b> Cysylltwch âr ddesg</b> <p>Gymorth os bydd y broblem yn parhau neu os bydd yn fater brys, cofiwch ddweud wrthym amdano. Mae ein manylion cyswllt ar gael ar dudalen Lleol <a href='https://openfoodnetwork.org/ofn-local/' target='blank'> y Rhwydwaith Bwyd Agored byd-eang </a>. </p> <p> Bydd o gymorth mawr inni os gallwch roi cymaint o fanylion â phosibl am y dudalen sydd ar goll.</p>"
@@ -881,7 +902,6 @@ cy:
label: Categorïau
tags:
label: Tagiau
none: Dim
search: Chwilio
sort:
pagination:
@@ -4014,7 +4034,6 @@ cy:
logourl: "url Logo"
are_you_sure_delete: "Ydych chi'n siŵr eich bod chi am ddileu'r cofnod hwn?"
confirm_delete: "Cadarnhau Dileu"
voucher: "Taleb"
configurations: "Cyfluniadau"
general_settings: "Gosodiadau Cyffredinol"
site_name: "Enw'r Safle"
@@ -4739,10 +4758,13 @@ cy:
webhook_endpoints:
title: Webhook Endpoints
description: Events in the system may trigger webhooks to external systems.
event_types:
order_cycle_opened: Order Cycle Opened
event_type:
header: Event type
url:
header: Endpoint URL
create_placeholder: Enter the URL of the remote webhook endpoint
developer_settings:
title: Gosodiadau'r Datblygwr
form:
@@ -4916,11 +4938,6 @@ cy:
add_tag_rule_modal:
select_rule_type: "Dewis math o reol:"
add_rule: "Ychwanegu Rheol"
webhook_endpoint_form:
url:
create_placeholder: Enter the URL of the remote webhook endpoint
event_types:
order_cycle_opened: Order Cycle Opened
invisible_captcha:
sentence_for_humans: "Gadewch yn wag os gwelwch yn dda."
timestamp_error_message: "Rhowch gynnig arall ar ôl 5 eiliad."

View File

@@ -89,6 +89,27 @@ de_CH:
messages:
blank: "darf nicht leer sein"
errors:
messages:
content_type_invalid: "hat ein ungültiges Datenformat"
file_size_out_of_range: "Dateigrösse %{file_size} liegt ausserhalb des zulässigen Bereichs"
limit_out_of_range: "Gesamtzahl liegt ausserhalb des zulässigen Bereichs"
image_metadata_missing: "ist kein gültiges Bild"
dimension_min_inclusion: "muss grösser oder gleich %{width} x %{height} Pixel sein"
dimension_max_inclusion: "muss kleiner oder gleich %{width} x %{height} Pixel sein"
dimension_width_inclusion: "Breite liegt nicht zwischen %{min} und %{max} Pixel"
dimension_height_inclusion: "Höhe liegt nicht zwischen %{min} und %{max} Pixel"
dimension_width_greater_than_or_equal_to: "Breite muss grösser oder gleich %{length} Pixel sein"
dimension_height_greater_than_or_equal_to: "Höhe muss grösser oder gleich %{length} Pixel sein"
dimension_width_less_than_or_equal_to: "Breite muss kleiner oder gleich %{length} Pixel sein"
dimension_height_less_than_or_equal_to: "Höhe muss kleiner oder gleich %{length} Pixel sein"
dimension_width_equal_to: "Breite muss %{length} Pixel sein"
dimension_height_equal_to: "Höhe muss %{length} Pixel sein"
aspect_ratio_not_square: "muss ein quadratisches Bild sein"
aspect_ratio_not_portrait: "muss ein Bild im Hochformat sein"
aspect_ratio_not_landscape: "muss ein Bild im Querformat sein"
aspect_ratio_is_not: "muss ein Seitenverhältnis von %{aspect_ratio} haben"
aspect_ratio_unknown: "hat ein unbekanntes Seitenverhältnis"
image_not_processable: "ist kein gültiges Bild"
not_found:
title: "Die gesuchte Seite existiert nicht (404)"
stripe:
@@ -731,7 +752,6 @@ de_CH:
label: Lieferkategorien
tags:
label: Stichwörter
none: Keine
search: Suche
sort:
pagination:
@@ -3595,7 +3615,6 @@ de_CH:
logourl: "URL des Logos"
are_you_sure_delete: "Möchten Sie diesen Datensatz wirklich löschen?"
confirm_delete: "Löschen bestätigen"
voucher: "Gutschein"
configurations: "Konfiguration"
general_settings: "Allgemeine Einstellungen"
site_name: "Name der Website"

View File

@@ -105,6 +105,27 @@ de_DE:
blank: "darf nicht leer sein."
too_short: "ist zu kurz (mindestens %{count} Zeichen)"
errors:
messages:
content_type_invalid: "hat ein ungültiges Datenformat"
file_size_out_of_range: "Dateigröße %{file_size} liegt außerhalb des zulässigen Bereichs"
limit_out_of_range: "Gesamtzahl liegt außerhalb des zulässigen Bereichs"
image_metadata_missing: "ist kein gültiges Bild"
dimension_min_inclusion: "muss größer oder gleich %{width} x %{height} Pixel sein"
dimension_max_inclusion: "muss kleiner oder gleich %{width} x %{height} Pixel sein"
dimension_width_inclusion: "Breite liegt nicht zwischen %{min} und %{max} Pixel"
dimension_height_inclusion: "Höhe liegt nicht zwischen %{min} und %{max} Pixel"
dimension_width_greater_than_or_equal_to: "Breite muss größer oder gleich %{length} Pixel sein"
dimension_height_greater_than_or_equal_to: "Höhe muss größer oder gleich %{length} Pixel sein"
dimension_width_less_than_or_equal_to: "Breite muss kleiner oder gleich %{length} Pixel sein"
dimension_height_less_than_or_equal_to: "Höhe muss kleiner oder gleich %{length} Pixel sein"
dimension_width_equal_to: "Breite muss %{length} Pixel sein"
dimension_height_equal_to: "Höhe muss %{length} Pixel sein"
aspect_ratio_not_square: "muss ein quadratisches Bild sein"
aspect_ratio_not_portrait: "muss ein Bild im Hochformat sein"
aspect_ratio_not_landscape: "muss ein Bild im Querformat sein"
aspect_ratio_is_not: "muss ein Seitenverhältnis von %{aspect_ratio} haben"
aspect_ratio_unknown: "hat ein unbekanntes Seitenverhältnis"
image_not_processable: "ist kein gültiges Bild"
not_found:
title: "Die gesuchte Seite existiert nicht (404). "
message_html: "<b>Bitte versuchen Sie es erneut</b><p> Dies könnte ein vorübergehendes Problem sein. Bitte klicken Sie auf die Zurück-Schaltfläche, um zum vorherigen Bildschirm zurückzukehren, oder gehen Sie zurück zur <a href='/'>Startseite</a> und versuchen Sie es erneut.</p> <b>Kontaktieren Sie den Support</b><p> Wenn das Problem weiterhin besteht oder dringend ist, teilen Sie uns dies bitte mit. Unsere Kontaktdaten finden Sie auf der <a href='https://openfoodnetwork.org/ofn-local/' target='blank'>lokalen Seite des globalen Open Food Network</a> .</p><p> Es hilft uns sehr, wenn Sie so detailliert wie möglich angeben können, worum es auf der fehlenden Seite geht.</p>"
@@ -774,17 +795,14 @@ de_DE:
cancellation_text: "Produktvariante beibehalten"
filters:
search_products: Nach Produkten suchen
search_for_producers: Produzenten suchen
all_producers: Alle Produzenten
search_for_categories: Kategorie suchen
all_categories: Alle Kategorien
producers:
label: Unsere Produzenten
categories:
label: Produktkategorie
label: Lieferkategorien
tags:
label: Stichwörter
none: Nichts
search: Suche
sort:
pagination:
@@ -827,7 +845,6 @@ de_DE:
category_field_name: "Kategorie"
tax_category_field_name: "Steuerkategorie"
producer_field_name: "Produzent"
select_unit_scale: Einheit auswählen
add_a_tag: Stichwort hinzufügen
clone:
success: Das Produkt wurde dupliziert.
@@ -1081,7 +1098,6 @@ de_DE:
images:
legend: "Bilder"
logo: Logo
logo_size: "300 x 300 Pixel"
inventory_settings:
legend: "Katalog"
text1: Sie verwalten optional Ihre Lagerbestände und Preise auch in Ihrem
@@ -1243,7 +1259,7 @@ de_DE:
vouchers:
legend: Gutscheine
voucher_code: Gutschein-Code
rate: Gutscheinwert
rate: Steuersatz
label: Bezeichnung
purpose: Zweck
expiry: Gültigkeit
@@ -1500,7 +1516,6 @@ de_DE:
shared:
attachment_field:
logo_label: "Logo"
logo_hint: 300 x 300 Pixel
logo_remove: "Bild löschen"
logo_remove_confirm: "Das Logo wird sofort nach Ihrer Bestätigung gelöscht."
promo_image_note1: 'BITTE BEACHTEN SIE:'
@@ -3063,8 +3078,6 @@ de_DE:
report_header_transaction_fee: Transaktionsgebühr (keine Steuern)
report_header_total_untaxable_admin: Summe nicht steuerpflichtiger Admin-Anpassungen (keine Steuern)
report_header_total_taxable_admin: Summe steuerpflichtiger Admin-Anpassungen (inkl. Steuern)
report_header_voucher_label: Gutschein-Code
report_header_voucher_amount: "Gutscheinwert"
report_line_cost_of_produce: Produktkosten
report_line_line_items: Einzelposten
report_header_last_completed_order_date: Letzte Bestellung
@@ -3782,7 +3795,6 @@ de_DE:
logourl: "URL des Logos"
are_you_sure_delete: "Möchten Sie diesen Datensatz wirklich löschen?"
confirm_delete: "Löschen bestätigen"
voucher: "Gutschein"
configurations: "Konfiguration"
general_settings: "Allgemeine Einstellungen"
site_name: "Name der Website"
@@ -4236,7 +4248,6 @@ de_DE:
product_name: Produktname
primary_taxon_form:
product_category: Produktkategorie
search_for_categories: "Lieferkategorie suchen"
group_buy_form:
group_buy: "Gruppenkauf aktivieren?"
bulk_unit_size: Erforderliche Gesamtbestellmenge (in Gramm, Liter oder Stück)
@@ -4489,10 +4500,13 @@ de_DE:
webhook_endpoints:
title: Webhook-Endpunkte
description: Ereignisse im System können Webhooks zu externen Systemen auslösen.
event_types:
order_cycle_opened: Bestellzyklus eröffnet
event_type:
header: Ereignistyp
url:
header: Endpunkt-URL
create_placeholder: Geben Sie die URL des Remote-Webhook-Endpunkts ein
developer_settings:
title: Entwicklereinstellungen
form:
@@ -4640,8 +4654,3 @@ de_DE:
add_tag_rule_modal:
select_rule_type: "Wählen Sie einen Regelart aus:"
add_rule: "Regel hinzufügen"
webhook_endpoint_form:
url:
create_placeholder: Geben Sie die URL des Remote-Webhook-Endpunkts ein
event_types:
order_cycle_opened: Bestellzyklus eröffnet

View File

@@ -106,6 +106,27 @@ el:
blank: "δεν μπορεί να είναι κενό"
too_short: "είναι πολύ σύντομο (το ελάχιστο είναι %{count} χαρακτήρες)"
errors:
messages:
content_type_invalid: "περιέχει λανθασμένο τύπο περιεχομένου"
file_size_out_of_range: "μέγεθος %{file_size} δεν είναι εντός της απαιτούμενης εμβέλειας"
limit_out_of_range: "Ο συνολικός αριθμός βρίσκεται εκτός εύρους"
image_metadata_missing: "δεν είναι έγκυρη εικόνα"
dimension_min_inclusion: "πρέπει να είναι μεγαλύτερο η ίσο με %{width} x %{height} pixels."
dimension_max_inclusion: "πρέπει να είναι μικρότερο από ή ίσο με %{width} x %{height} pixels."
dimension_width_inclusion: "Το πλάτος δεν είναι μεταξύ %{min} και %{max} pixels."
dimension_height_inclusion: "Το ύψος δεν είναι μεταξύ %{min} και %{max} pixels. "
dimension_width_greater_than_or_equal_to: "Το πλάτος πρέπει να είναι μεγαλύτερο από η ίσο με %{length} pixels."
dimension_height_greater_than_or_equal_to: "Το ύψος πρέπει να είναι μεγαλύτερο από η ίσο με %{length} pixels."
dimension_width_less_than_or_equal_to: "Το πλάτος πρέπει να είναι μικρότερο από ή ίσο με %{length} pixels."
dimension_height_less_than_or_equal_to: "Το ύψος πρέπει να είναι μικρότερο από η ίσο με %{length} pixels."
dimension_width_equal_to: "Το πλάτος πρέπει να είναι ίσο με %{length} pixels."
dimension_height_equal_to: "Το ύψος πρέπει να είναι ίσο με %{length} pixels."
aspect_ratio_not_square: "πρέπει να είναι τετράγωνη εικόνα. "
aspect_ratio_not_portrait: "πρέπει να είναι εικόνα πορτρέτο. "
aspect_ratio_not_landscape: "πρέπει να είναι εικόνα τοπίου."
aspect_ratio_is_not: "πρέπει να έχει λόγο διαστάσεων %{aspect_ratio}"
aspect_ratio_unknown: "έχει άγνωστη αναλογία εμφάνισης (aspect ratio)."
image_not_processable: "δεν είναι έγκυρη εικόνα"
not_found:
title: "Η συγκεκριμένη σελίδα δεν υπάρχει (404) "
message_html: "<b>Παρακαλούμε προσπαθήστε ξανά</b> <p>Πιθανόν να πρόκειται για προσωρινό σφάλμα. Παρακαλούμε επιστρέψτε στην προηγούμενη σελίδα ή πηγαίνετε πίσω στην <a href='/'>Αρχική σελίδα</a> και προσπαθήστε ξανά.</p> <b>Επικοινωνήστε με την εξυπηρέτηση πελατών</b> <p>Εάν το πρόβλημα παραμένει η επείγει, παρακαλούμε να μας ενημερώσετε. Βρείτε τα στοιχεία επικοινωνίας μας στο <a href='https://openfoodnetwork.org/ofn-local/' target='blank'>Open Food Network Local page</a>. </p><p>Θα μας είναι απόλυτα χρήσιμο το να μας δώσετε όσες περισσότερες πληροφορίες γίνεται αναφορικά με τα στοιχεία της σελίδας που λείπουν.</p>"
@@ -796,7 +817,6 @@ el:
label: Κατηγορίες
tags:
label: Ετικέτες
none: Κανένας
search: Αναζήτηση
sort:
pagination:
@@ -3778,7 +3798,6 @@ el:
logourl: "Logourl"
are_you_sure_delete: "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν την εγγραφή;"
confirm_delete: "Επιβεβαίωση διαγραφής"
voucher: "Κουπόνι"
configurations: "Διαμορφώσεις"
general_settings: "Γενικές Ρυθμίσεις"
site_name: "Ονομα ιστοσελίδας"
@@ -4494,8 +4513,11 @@ el:
webhook_endpoints:
title: Webhook Endpoints
description: Συμβάντα στο σύστημα μπορεί να πυροδοτήσουν webhooks σε εξωτερικά συστήματα.
event_types:
order_cycle_opened: Ο Κύκλος παραγγελίας άνοιξε
url:
header: Endpoint URL
create_placeholder: Εισάγετε το URL απο το απομακρυσμένο webhook endpoint.
developer_settings:
title: Ρυθμίσεις Developer
form:
@@ -4643,11 +4665,6 @@ el:
add_tag_rule_modal:
select_rule_type: "Επιλέξτε έναν τύπο κανόνα:"
add_rule: "Προσθήκη κανόνα"
webhook_endpoint_form:
url:
create_placeholder: Εισάγετε το URL απο το απομακρυσμένο webhook endpoint.
event_types:
order_cycle_opened: Ο Κύκλος παραγγελίας άνοιξε
invisible_captcha:
sentence_for_humans: "Παρακαλώ αφήστε κενό"
timestamp_error_message: "Παρακαλώ δοκιμάστε ξανά σε 5 δευτερόλεπτα."

View File

@@ -138,66 +138,26 @@ en:
# Used by active_storage_validations
errors:
messages:
content_type_invalid:
one: "has an invalid content type (authorized content type is %{authorized_human_content_types})"
other: "has an invalid content type (authorized content types are %{authorized_human_content_types})"
content_type_spoofed:
one: "has a content type that is not equivalent to the one that is detected through its content (authorized content type is %{authorized_human_content_types})"
other: "has a content type that is not equivalent to the one that is detected through its content (authorized content types are %{authorized_human_content_types})"
file_size_not_less_than: "file size must be less than %{max} (current size is %{file_size})"
file_size_not_less_than_or_equal_to: "file size must be less than or equal to %{max} (current size is %{file_size})"
file_size_not_greater_than: "file size must be greater than %{min} (current size is %{file_size})"
file_size_not_greater_than_or_equal_to: "file size must be greater than or equal to %{min} (current size is %{file_size})"
file_size_not_between: "file size must be between %{min} and %{max} (current size is %{file_size})"
file_size_not_equal_to: "file size must be equal to %{exact} (current size is %{file_size})"
total_file_size_not_less_than: "total file size must be less than %{max} (current size is %{total_file_size})"
total_file_size_not_less_than_or_equal_to: "total file size must be less than or equal to %{max} (current size is %{total_file_size})"
total_file_size_not_greater_than: "total file size must be greater than %{min} (current size is %{total_file_size})"
total_file_size_not_greater_than_or_equal_to: "total file size must be greater than or equal to %{min} (current size is %{total_file_size})"
total_file_size_not_between: "total file size must be between %{min} and %{max} (current size is %{total_file_size})"
total_file_size_not_equal_to: "total file size must be equal to %{exact} (current size is %{total_file_size})"
duration_not_less_than: "duration must be less than %{max} (current duration is %{duration})"
duration_not_less_than_or_equal_to: "duration must be less than or equal to %{max} (current duration is %{duration})"
duration_not_greater_than: "duration must be greater than %{min} (current duration is %{duration})"
duration_not_greater_than_or_equal_to: "duration must be greater than or equal to %{min} (current duration is %{duration})"
duration_not_between: "duration must be between %{min} and %{max} (current duration is %{duration})"
duration_not_equal_to: "duration must be equal to %{exact} (current duration is %{duration})"
limit_out_of_range:
zero: "no files attached (must have between %{min} and %{max} files)"
one: "only 1 file attached (must have between %{min} and %{max} files)"
other: "total number of files must be between %{min} and %{max} files (there are %{count} files attached)"
limit_min_not_reached:
zero: "no files attached (must have at least %{min} files)"
one: "only 1 file attached (must have at least %{min} files)"
other: "%{count} files attached (must have at least %{min} files)"
limit_max_exceeded:
zero: "no files attached (maximum is %{max} files)"
one: "too many files attached (maximum is %{max} files, got %{count})"
other: "too many files attached (maximum is %{max} files, got %{count})"
attachment_missing: "is missing its attachment"
media_metadata_missing: "is not a valid media file"
dimension_min_not_included_in: "must be greater than or equal to %{width} x %{height} pixels"
dimension_max_not_included_in: "must be less than or equal to %{width} x %{height} pixels"
dimension_width_not_included_in: "width is not included between %{min} and %{max} pixels"
dimension_height_not_included_in: "height is not included between %{min} and %{max} pixels"
dimension_width_not_greater_than_or_equal_to: "width must be greater than or equal to %{length} pixels"
dimension_height_not_greater_than_or_equal_to: "height must be greater than or equal to %{length} pixels"
dimension_width_not_less_than_or_equal_to: "width must be less than or equal to %{length} pixels"
dimension_height_not_less_than_or_equal_to: "height must be less than or equal to %{length} pixels"
dimension_width_not_equal_to: "width must be equal to %{length} pixels"
dimension_height_not_equal_to: "height must be equal to %{length} pixels"
aspect_ratio_not_square: "must be square (current file is %{width}x%{height}px)"
aspect_ratio_not_portrait: "must be portrait (current file is %{width}x%{height}px)"
aspect_ratio_not_landscape: "must be landscape (current file is %{width}x%{height}px)"
aspect_ratio_not_x_y: "must be %{authorized_aspect_ratios} (current file is %{width}x%{height}px)"
aspect_ratio_invalid: "has an invalid aspect ratio (valid aspect ratios are %{authorized_aspect_ratios})"
file_not_processable: "is not identified as a valid media file"
pages_not_less_than: "page count must be less than %{max} (current page count is %{pages})"
pages_not_less_than_or_equal_to: "page count must be less than or equal to %{max} (current page count is %{pages})"
pages_not_greater_than: "page count must be greater than %{min} (current page count is %{pages})"
pages_not_greater_than_or_equal_to: "page count must be greater than or equal to %{min} (current page count is %{pages})"
pages_not_between: "page count must be between %{min} and %{max} (current page count is %{pages})"
pages_not_equal_to: "page count must be equal to %{exact} (current page count is %{pages})"
content_type_invalid: "has an invalid content type"
file_size_out_of_range: "size %{file_size} is not between required range"
limit_out_of_range: "total number is out of range"
image_metadata_missing: "is not a valid image"
dimension_min_inclusion: "must be greater than or equal to %{width} x %{height} pixel."
dimension_max_inclusion: "must be less than or equal to %{width} x %{height} pixel."
dimension_width_inclusion: "width is not included between %{min} and %{max} pixel."
dimension_height_inclusion: "height is not included between %{min} and %{max} pixel."
dimension_width_greater_than_or_equal_to: "width must be greater than or equal to %{length} pixel."
dimension_height_greater_than_or_equal_to: "height must be greater than or equal to %{length} pixel."
dimension_width_less_than_or_equal_to: "width must be less than or equal to %{length} pixel."
dimension_height_less_than_or_equal_to: "height must be less than or equal to %{length} pixel."
dimension_width_equal_to: "width must be equal to %{length} pixel."
dimension_height_equal_to: "height must be equal to %{length} pixel."
aspect_ratio_not_square: "must be a square image"
aspect_ratio_not_portrait: "must be a portrait image"
aspect_ratio_not_landscape: "must be a landscape image"
aspect_ratio_is_not: "must have an aspect ratio of %{aspect_ratio}"
aspect_ratio_unknown: "has an unknown aspect ratio"
image_not_processable: "is not a valid image"
not_found:
title: "The page you were looking for doesn't exist (404)"
message_html: "<b>Please try again</b>
@@ -627,7 +587,6 @@ en:
errors:
vine_api: "There was an error communicating with the API, please try again later."
invalid_voucher: "The voucher is not valid"
expired: "The voucher has expired"
not_found_voucher: "Sorry, we couldn't find that voucher, please check the code."
vine_voucher_redeemer_service:
errors:
@@ -1018,7 +977,6 @@ en:
label: Categories
tags:
label: Tags
none: None
search: Search
sort:
pagination:
@@ -4130,8 +4088,6 @@ en:
destroy:
success: Webhook endpoint successfully deleted
error: Webhook endpoint failed to delete
test:
success: Some test data will be sent to the webhook url
spree:
order_updated: "Order Updated"
@@ -4239,8 +4195,6 @@ en:
logourl: "Logourl"
are_you_sure_delete: "Are you sure you want to delete this record?"
confirm_delete: "Confirm Deletion"
tag_rule: "Tag Rule"
voucher: "Voucher"
configurations: "Configurations"
general_settings: "General Settings"
@@ -4984,10 +4938,13 @@ en:
webhook_endpoints:
title: Webhook Endpoints
description: Events in the system may trigger webhooks to external systems.
event_types:
order_cycle_opened: Order Cycle Opened
event_type:
header: Event type
url:
header: Endpoint URL
create_placeholder: Enter the URL of the remote webhook endpoint
developer_settings:
title: Developer Settings
form:
@@ -5136,13 +5093,7 @@ en:
add_tag_rule_modal:
select_rule_type: "Select a rule type:"
add_rule: "Add Rule"
webhook_endpoint_form:
url:
create_placeholder: Enter the URL of the remote webhook endpoint
event_types:
order_cycle_opened: Order Cycle Opened
payment_status_changed: Post webhook on Payment status change
test_endpoint: Test webhook endpoint
# Gem to prevent bot form submissions
invisible_captcha:

View File

@@ -561,7 +561,6 @@ en_AU:
label: Categories
tags:
label: Tags
none: None
search: Search
sort:
pagination:

View File

@@ -526,7 +526,6 @@ en_BE:
label: Categories
tags:
label: Tags
none: None
search: Search
product_variant_row:
new_variant: New variant

View File

@@ -116,66 +116,26 @@ en_CA:
too_short: "is too short (minimum is %{count} characters)"
errors:
messages:
content_type_invalid:
one: "has an invalid content type (authorized content type is %{authorized_human_content_types})"
other: "has an invalid content type (authorized content types are %{authorized_human_content_types})"
content_type_spoofed:
one: "has a content type that is not equivalent to the one that is detected through its content (authorized content type is %{authorized_human_content_types})"
other: "has a content type that is not equivalent to the one that is detected through its content (authorized content types are %{authorized_human_content_types})"
file_size_not_less_than: "file size must be less than %{max} (current size is %{file_size})"
file_size_not_less_than_or_equal_to: "file size must be less than or equal to %{max} (current size is %{file_size})"
file_size_not_greater_than: "file size must be greater than %{min} (current size is %{file_size})"
file_size_not_greater_than_or_equal_to: "file size must be greater than or equal to %{min} (current size is %{file_size})"
file_size_not_between: "file size must be between %{min} and %{max} (current size is %{file_size})"
file_size_not_equal_to: "file size must be equal to %{exact} (current size is %{file_size})"
total_file_size_not_less_than: "total file size must be less than %{max} (current size is %{total_file_size})"
total_file_size_not_less_than_or_equal_to: "total file size must be less than or equal to %{max} (current size is %{total_file_size})"
total_file_size_not_greater_than: "total file size must be greater than %{min} (current size is %{total_file_size})"
total_file_size_not_greater_than_or_equal_to: "total file size must be greater than or equal to %{min} (current size is %{total_file_size})"
total_file_size_not_between: "total file size must be between %{min} and %{max} (current size is %{total_file_size})"
total_file_size_not_equal_to: "total file size must be equal to %{exact} (current size is %{total_file_size})"
duration_not_less_than: "duration must be less than %{max} (current duration is %{duration})"
duration_not_less_than_or_equal_to: "duration must be less than or equal to %{max} (current duration is %{duration})"
duration_not_greater_than: "duration must be greater than %{min} (current duration is %{duration})"
duration_not_greater_than_or_equal_to: "duration must be greater than or equal to %{min} (current duration is %{duration})"
duration_not_between: "duration must be between %{min} and %{max} (current duration is %{duration})"
duration_not_equal_to: "duration must be equal to %{exact} (current duration is %{duration})"
limit_out_of_range:
zero: "no files attached (must have between %{min} and %{max} files)"
one: "only 1 file attached (must have between %{min} and %{max} files)"
other: "total number of files must be between %{min} and %{max} files (there are %{count} files attached)"
limit_min_not_reached:
zero: "no files attached (must have at least %{min} files)"
one: "only 1 file attached (must have at least %{min} files)"
other: "%{count} files attached (must have at least %{min} files)"
limit_max_exceeded:
zero: "no files attached (maximum is %{max} files)"
one: "too many files attached (maximum is %{max} files, got %{count})"
other: "too many files attached (maximum is %{max} files, got %{count})"
attachment_missing: "is missing its attachment"
media_metadata_missing: "is not a valid media file"
dimension_min_not_included_in: "must be greater than or equal to %{width} x %{height} pixels"
dimension_max_not_included_in: "must be less than or equal to %{width} x %{height} pixels"
dimension_width_not_included_in: "width is not included between %{min} and %{max} pixels"
dimension_height_not_included_in: "height is not included between %{min} and %{max} pixels"
dimension_width_not_greater_than_or_equal_to: "width must be greater than or equal to %{length} pixels"
dimension_height_not_greater_than_or_equal_to: "height must be greater than or equal to %{length} pixels"
dimension_width_not_less_than_or_equal_to: "width must be less than or equal to %{length} pixels"
dimension_height_not_less_than_or_equal_to: "height must be less than or equal to %{length} pixels"
dimension_width_not_equal_to: "width must be equal to %{length} pixels"
dimension_height_not_equal_to: "height must be equal to %{length} pixels"
aspect_ratio_not_square: "must be square (current file is %{width}x%{height}px)"
aspect_ratio_not_portrait: "must be portrait (current file is %{width}x%{height}px)"
aspect_ratio_not_landscape: "must be landscape (current file is %{width}x%{height}px)"
aspect_ratio_not_x_y: "must be %{authorized_aspect_ratios} (current file is %{width}x%{height}px)"
aspect_ratio_invalid: "has an invalid aspect ratio (valid aspect ratios are %{authorized_aspect_ratios})"
file_not_processable: "is not identified as a valid media file"
pages_not_less_than: "page count must be less than %{max} (current page count is %{pages})"
pages_not_less_than_or_equal_to: "page count must be less than or equal to %{max} (current page count is %{pages})"
pages_not_greater_than: "page count must be greater than %{min} (current page count is %{pages})"
pages_not_greater_than_or_equal_to: "page count must be greater than or equal to %{min} (current page count is %{pages})"
pages_not_between: "page count must be between %{min} and %{max} (current page count is %{pages})"
pages_not_equal_to: "page count must be equal to %{exact} (current page count is %{pages})"
content_type_invalid: "has an invalid content type"
file_size_out_of_range: "size %{file_size} is not within required range"
limit_out_of_range: "total number is out of range"
image_metadata_missing: "is not a valid image"
dimension_min_inclusion: "must be greater than or equal to %{width} x %{height} pixels."
dimension_max_inclusion: "must be less than or equal to %{width} x %{height} pixels."
dimension_width_inclusion: "width is not between %{min} and %{max} pixels."
dimension_height_inclusion: "height is not between %{min} and %{max} pixels."
dimension_width_greater_than_or_equal_to: "width must be greater than or equal to %{length} pixels."
dimension_height_greater_than_or_equal_to: "height must be greater than or equal to %{length} pixels."
dimension_width_less_than_or_equal_to: "width must be less than or equal to %{length} pixels."
dimension_height_less_than_or_equal_to: "height must be less than or equal to %{length} pixels."
dimension_width_equal_to: "width must be equal to %{length} pixels."
dimension_height_equal_to: "height must be equal to %{length} pixels."
aspect_ratio_not_square: "must be a square image"
aspect_ratio_not_portrait: "must be a portrait image"
aspect_ratio_not_landscape: "must be a landscape image"
aspect_ratio_is_not: "must have an aspect ration of %{aspect_ratio}"
aspect_ratio_unknown: "has an unknown aspect ration"
image_not_processable: "is not a valid image"
not_found:
title: "The page you were looking for doesn't exist (404)"
message_html: "<b>Please try again</b> <p>This might be a temporary problem. Please click the back button to return to the previous screen or go back to <a href='/'>Home</a>and try again.</p> <b>Contact support <p>If the problem persists or is urgent, please tell us about it. Find our contact details from the global <a href='https://openfoodnetwork.org/ofn-local/' target='blank'>Open Food Network Local page</a>.</p> <p>It really helps us if you can give as much detail as possible about what the missing page is about.</p>"
@@ -581,7 +541,6 @@ en_CA:
errors:
vine_api: "There was an error communicating with the API, please try again later."
invalid_voucher: "The voucher is not valid"
expired: "The voucher has expired"
not_found_voucher: "Sorry, we couldn't find that voucher, please check the code."
vine_voucher_redeemer_service:
errors:
@@ -947,7 +906,6 @@ en_CA:
label: Categories
tags:
label: Tags
none: None
search: Search
sort:
pagination:
@@ -3971,8 +3929,6 @@ en_CA:
destroy:
success: Webhook endpoint successfully deleted
error: Webhood endpoint failed to delete
test:
success: Some test data will be sent to the webhook url
spree:
order_updated: "Order Updated"
cannot_perform_operation: "Can not perform this operation"
@@ -4079,8 +4035,6 @@ en_CA:
logourl: "Logourl"
are_you_sure_delete: "Are you sure you want to delete this record?"
confirm_delete: "Confirm Deletion"
tag_rule: "Tag Rule"
voucher: "Voucher"
configurations: "Configurations"
general_settings: "General Settings"
site_name: "Site Name"
@@ -4806,10 +4760,13 @@ en_CA:
webhook_endpoints:
title: Webhook Endpoints
description: Events in the system may trigger webhooks to external systems.
event_types:
order_cycle_opened: Order Cycle Opened
event_type:
header: Event type
url:
header: Endpoint URL
create_placeholder: Enter the URL of the remove webhook endpoint
developer_settings:
title: Developer Settings
form:
@@ -4958,13 +4915,6 @@ en_CA:
add_tag_rule_modal:
select_rule_type: "Select a rule type:"
add_rule: "Add Rule"
webhook_endpoint_form:
url:
create_placeholder: Enter the URL of the remove webhook endpoint
event_types:
order_cycle_opened: Order Cycle Opened
payment_status_changed: Post webhook on Payment status change
test_endpoint: Test webhook endpoint
invisible_captcha:
sentence_for_humans: "Please leave empty"
timestamp_error_message: "Please try again after 5 seconds."

Some files were not shown because too many files have changed in this diff Show More