udongo 2.0.4 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/backend/application.js +1 -0
  3. data/app/assets/javascripts/backend/bootstrap.js +3 -3569
  4. data/app/assets/javascripts/backend/tree.js +106 -65
  5. data/app/assets/stylesheets/backend/application.scss +2 -0
  6. data/app/assets/stylesheets/backend/bootstrap.scss +29 -51
  7. data/app/assets/stylesheets/backend/components/_page_tree.scss +9 -0
  8. data/app/assets/stylesheets/backend/custom.scss +0 -0
  9. data/app/assets/stylesheets/backend/font-awesome.scss +644 -1977
  10. data/app/assets/stylesheets/backend/udongo.scss +1 -0
  11. data/app/controllers/backend/admins_controller.rb +2 -2
  12. data/app/controllers/{backend_controller.rb → backend/base_controller.rb} +13 -5
  13. data/app/controllers/backend/content/rows/columns_controller.rb +1 -1
  14. data/app/controllers/backend/content/rows/images_controller.rb +1 -1
  15. data/app/controllers/backend/content/rows/texts_controller.rb +1 -1
  16. data/app/controllers/backend/content/rows_controller.rb +1 -1
  17. data/app/controllers/backend/dashboard_controller.rb +1 -1
  18. data/app/controllers/backend/email_templates_controller.rb +12 -12
  19. data/app/controllers/backend/emails_controller.rb +1 -1
  20. data/app/controllers/backend/navigation/items_controller.rb +9 -11
  21. data/app/controllers/backend/navigations_controller.rb +1 -1
  22. data/app/controllers/backend/pages_controller.rb +17 -39
  23. data/app/controllers/backend/redirects_controller.rb +1 -1
  24. data/app/controllers/backend/seo_controller.rb +1 -1
  25. data/app/controllers/backend/sessions_controller.rb +1 -1
  26. data/app/controllers/backend/snippets_controller.rb +14 -13
  27. data/app/controllers/backend/tagbox_controller.rb +1 -1
  28. data/app/controllers/catch_all_controller.rb +1 -1
  29. data/app/controllers/concerns/backend/content_type_controller.rb +3 -1
  30. data/app/controllers/redirects_controller.rb +1 -1
  31. data/app/decorators/page_decorator.rb +1 -4
  32. data/app/forms/backend/email_template_translation_form.rb +1 -23
  33. data/app/forms/backend/navigation_item_translation_form.rb +1 -23
  34. data/app/forms/backend/page_translation_form.rb +13 -18
  35. data/app/forms/backend/snippet_translation_form.rb +1 -23
  36. data/app/forms/backend/translation_form.rb +22 -0
  37. data/app/helpers/udongo_helper.rb +0 -4
  38. data/app/mailers/general_mailer.rb +14 -4
  39. data/app/models/admin.rb +2 -2
  40. data/app/models/comment.rb +1 -2
  41. data/app/models/concerns/addressable.rb +40 -0
  42. data/app/models/concerns/addressable/config.rb +33 -0
  43. data/app/models/concerns/cacheable.rb +1 -1
  44. data/app/models/email.rb +2 -2
  45. data/app/models/email_template.rb +2 -1
  46. data/app/views/backend/_general_form_error.html.erb +6 -0
  47. data/app/views/backend/admins/_form.html.erb +3 -0
  48. data/app/views/backend/content/rows/columns/_dimension_fields.html.erb +3 -0
  49. data/app/views/backend/content/rows/columns/edit.html.erb +3 -5
  50. data/app/views/backend/content/rows/columns/new.html.erb +3 -5
  51. data/app/views/backend/content/rows/images/edit.html.erb +2 -0
  52. data/app/views/backend/dashboard/show.html.erb +1 -1
  53. data/app/views/backend/email_templates/_form.html.erb +29 -7
  54. data/app/views/backend/email_templates/_tabs.html.erb +1 -1
  55. data/app/views/backend/email_templates/edit_translation.html.erb +1 -0
  56. data/app/views/backend/navigation/items/_form.html.erb +4 -2
  57. data/app/views/backend/navigation/items/_tabs.html.erb +1 -1
  58. data/app/views/backend/navigation/items/edit_translation.html.erb +1 -0
  59. data/app/views/backend/pages/_form.html.erb +4 -2
  60. data/app/views/backend/pages/_tabs.html.erb +1 -1
  61. data/app/views/backend/pages/edit_translation.html.erb +1 -0
  62. data/app/views/backend/redirects/_form.html.erb +2 -0
  63. data/app/views/backend/snippets/_form.html.erb +3 -1
  64. data/app/views/backend/snippets/_tabs.html.erb +1 -1
  65. data/app/views/backend/snippets/edit.html.erb +1 -1
  66. data/app/views/backend/snippets/edit_translation.html.erb +1 -0
  67. data/app/views/backend/snippets/new.html.erb +1 -1
  68. data/app/views/layouts/backend/_top_navigation.html.erb +1 -2
  69. data/changelog.md +38 -0
  70. data/config/initializers/simple_form_bootstrap.rb +13 -9
  71. data/config/locales/en_backend.yml +1 -4
  72. data/config/locales/en_forms.yml +3 -0
  73. data/config/locales/nl_backend.yml +3 -4
  74. data/config/locales/nl_forms.yml +3 -0
  75. data/config/routes.rb +5 -7
  76. data/db/migrate/20160815100903_remove_form_models.rb +9 -0
  77. data/db/migrate/20161014135637_add_category_to_admin.rb +5 -0
  78. data/db/migrate/20161029124558_add_locale_to_admin.rb +6 -0
  79. data/db/migrate/20161029130557_add_bcc_and_cc_to_email_templates.rb +6 -0
  80. data/db/migrate/20161029171056_add_ccc_and_bcc_to_email.rb +6 -0
  81. data/lib/udongo/configs/flexible_content.rb +7 -0
  82. data/lib/udongo/configs/i18n.rb +5 -3
  83. data/lib/udongo/configs/i18ns/app.rb +12 -0
  84. data/lib/udongo/configs/i18ns/cms.rb +12 -0
  85. data/lib/udongo/flexible_content/column_width_calculator.rb +4 -2
  86. data/lib/udongo/pages/tree.rb +15 -0
  87. data/lib/udongo/pages/tree_node.rb +43 -0
  88. data/lib/udongo/version.rb +1 -1
  89. data/readme.md +103 -43
  90. data/vendor/assets/javascripts/backend/select2.min.js +3 -0
  91. data/vendor/assets/stylesheets/backend/jstree/default/style.scss +3 -3
  92. data/vendor/assets/stylesheets/backend/select2.min.scss +1 -0
  93. metadata +22 -30
  94. data/app/controllers/backend/forms/base_controller.rb +0 -14
  95. data/app/controllers/backend/forms/submissions_controller.rb +0 -8
  96. data/app/controllers/backend/forms_controller.rb +0 -7
  97. data/app/controllers/backend/webserver_controller.rb +0 -6
  98. data/app/decorators/form_decorator.rb +0 -16
  99. data/app/decorators/form_submission_decorator.rb +0 -3
  100. data/app/forms/backend/email_template_form.rb +0 -38
  101. data/app/forms/backend/navigation_item_form.rb +0 -23
  102. data/app/forms/backend/page_form.rb +0 -26
  103. data/app/forms/backend/snippet_form.rb +0 -35
  104. data/app/models/form.rb +0 -10
  105. data/app/models/form_field.rb +0 -15
  106. data/app/models/form_field_validation.rb +0 -11
  107. data/app/models/form_submission.rb +0 -15
  108. data/app/models/form_submission_data.rb +0 -5
  109. data/app/views/backend/_form_errors.html.erb +0 -15
  110. data/app/views/backend/forms/index.html.erb +0 -27
  111. data/app/views/backend/forms/submissions/_filter.html.erb +0 -16
  112. data/app/views/backend/forms/submissions/index.html.erb +0 -34
  113. data/lib/generators/udongo/form/form_generator.rb +0 -62
  114. data/lib/generators/udongo/form/templates/form.rb +0 -18
  115. data/lib/udongo/configs/forms.rb +0 -18
  116. data/lib/udongo/configs/routes.rb +0 -13
  117. data/lib/udongo/email_vars/form_submission.rb +0 -18
  118. data/lib/udongo/forms/submission_datagrid.rb +0 -27
  119. data/lib/udongo/forms/submission_filter.rb +0 -31
@@ -10,6 +10,7 @@
10
10
  @import 'components/breadcrumb';
11
11
  @import 'components/form';
12
12
  @import 'components/nav';
13
+ @import 'components/page_tree';
13
14
 
14
15
  // Page specific styles
15
16
  @import 'pages/login';
@@ -1,4 +1,4 @@
1
- class Backend::AdminsController < BackendController
1
+ class Backend::AdminsController < Backend::BaseController
2
2
  before_action :find_admin, only: [:show, :edit, :update, :destroy]
3
3
  before_action -> { breadcrumb.add t('b.admins'), backend_admins_path }
4
4
 
@@ -45,7 +45,7 @@ class Backend::AdminsController < BackendController
45
45
 
46
46
  def allowed_params
47
47
  params[:admin].permit(
48
- :first_name, :last_name, :email, :password, :password_confirmation
48
+ :locale, :first_name, :last_name, :email, :password, :password_confirmation
49
49
  )
50
50
  end
51
51
 
@@ -1,8 +1,8 @@
1
- class BackendController < ActionController::Base
1
+ class Backend::BaseController < ActionController::Base
2
2
  include Udongo::Cryptography
3
3
 
4
4
  layout 'backend/application'
5
- before_action :check_login
5
+ before_action :interface_locale, :check_login
6
6
 
7
7
  def breadcrumb
8
8
  @breadcrumb ||= Udongo::Breadcrumb.new
@@ -18,13 +18,21 @@ class BackendController < ActionController::Base
18
18
  Udongo::Notification.new(notice).translate(actor)
19
19
  end
20
20
 
21
- def default_locale
22
- Udongo.config.i18n.default_locale
21
+ def default_app_locale
22
+ Udongo.config.i18n.app.default_locale
23
23
  end
24
- helper_method :default_locale
24
+ helper_method :default_app_locale
25
25
 
26
26
  private
27
27
 
28
+ def interface_locale
29
+ if current_admin && current_admin.locale.present?
30
+ I18n.locale = current_admin.locale
31
+ else
32
+ I18n.locale = Udongo.config.i18n.cms.default_interface_locale
33
+ end
34
+ end
35
+
28
36
  def check_login
29
37
  unless current_admin
30
38
  session[:backend_redirect] = request.url
@@ -1,4 +1,4 @@
1
- class Backend::Content::Rows::ColumnsController < BackendController
1
+ class Backend::Content::Rows::ColumnsController < Backend::BaseController
2
2
  include Concerns::Backend::PositionableController
3
3
 
4
4
  before_action :find_row
@@ -1,4 +1,4 @@
1
- class Backend::Content::Rows::ImagesController < BackendController
1
+ class Backend::Content::Rows::ImagesController < Backend::BaseController
2
2
  include Concerns::Backend::ContentTypeController
3
3
 
4
4
  model ContentImage
@@ -1,4 +1,4 @@
1
- class Backend::Content::Rows::TextsController < BackendController
1
+ class Backend::Content::Rows::TextsController < Backend::BaseController
2
2
  include Concerns::Backend::ContentTypeController
3
3
 
4
4
  model ContentText
@@ -1,4 +1,4 @@
1
- class Backend::Content::RowsController < BackendController
1
+ class Backend::Content::RowsController < Backend::BaseController
2
2
  include Concerns::Backend::PositionableController
3
3
  before_action :find_model, only: [:update_position, :destroy]
4
4
 
@@ -1,2 +1,2 @@
1
- class Backend::DashboardController < BackendController
1
+ class Backend::DashboardController < Backend::BaseController
2
2
  end
@@ -1,4 +1,4 @@
1
- class Backend::EmailTemplatesController < BackendController
1
+ class Backend::EmailTemplatesController < Backend::BaseController
2
2
  include Concerns::Backend::TranslatableController
3
3
  include Concerns::Backend::PositionableController
4
4
 
@@ -10,27 +10,21 @@ class Backend::EmailTemplatesController < BackendController
10
10
  end
11
11
 
12
12
  def new
13
- @form = Backend::EmailTemplateForm.new(EmailTemplate.new)
13
+ @model = EmailTemplate.new
14
14
  end
15
15
 
16
16
  def create
17
- @form = Backend::EmailTemplateForm.new(EmailTemplate.new)
17
+ @model = EmailTemplate.new allowed_params
18
18
 
19
- if @form.save params[:email_template]
20
- redirect_to edit_translation_backend_email_template_path(@form.email_template, translation_locale: default_locale), notice: translate_notice(:added, :email_template)
19
+ if @model.save
20
+ redirect_to edit_translation_backend_email_template_path(@model, translation_locale: default_app_locale), notice: translate_notice(:added, :email_template)
21
21
  else
22
22
  render :new
23
23
  end
24
24
  end
25
25
 
26
- def edit
27
- @form = Backend::EmailTemplateForm.new(@model)
28
- end
29
-
30
26
  def update
31
- @form = Backend::EmailTemplateForm.new(@model)
32
-
33
- if @form.save params[:email_template]
27
+ if @model.update_attributes allowed_params
34
28
  redirect_to edit_backend_email_template_path(@model), notice: translate_notice(:edited, :email_template)
35
29
  else
36
30
  render :edit
@@ -43,6 +37,12 @@ class Backend::EmailTemplatesController < BackendController
43
37
  @model = EmailTemplate.find params[:id]
44
38
  end
45
39
 
40
+ def allowed_params
41
+ params[:email_template].permit(
42
+ :identifier, :description, :from_name, :from_email, :cc, :bcc
43
+ )
44
+ end
45
+
46
46
  def translation_form
47
47
  Backend::EmailTemplateTranslationForm.new(
48
48
  @model, @model.translation(params[:translation_locale])
@@ -1,4 +1,4 @@
1
- class Backend::EmailsController < BackendController
1
+ class Backend::EmailsController < Backend::BaseController
2
2
  include Concerns::PaginationController
3
3
 
4
4
  before_action -> { breadcrumb.add t('b.emails'), backend_emails_path }
@@ -1,4 +1,4 @@
1
- class Backend::Navigation::ItemsController < BackendController
1
+ class Backend::Navigation::ItemsController < Backend::BaseController
2
2
  include Concerns::Backend::TranslatableController
3
3
  include Concerns::Backend::PositionableController
4
4
 
@@ -7,27 +7,21 @@ class Backend::Navigation::ItemsController < BackendController
7
7
  before_action { breadcrumb.add t('b.navigation'), backend_navigations_path }
8
8
 
9
9
  def new
10
- @form = Backend::NavigationItemForm.new(@navigation.items.new.decorate)
10
+ @model = @navigation.items.new.decorate
11
11
  end
12
12
 
13
13
  def create
14
- @form = Backend::NavigationItemForm.new(@navigation.items.new.decorate)
14
+ @model = @navigation.items.new(allowed_params).decorate
15
15
 
16
- if @form.save params[:navigation_item]
16
+ if @model.save
17
17
  redirect_to backend_navigations_path, notice: translate_notice(:added, :navigation_item)
18
18
  else
19
19
  render :new
20
20
  end
21
21
  end
22
22
 
23
- def edit
24
- @form = Backend::NavigationItemForm.new(@model)
25
- end
26
-
27
23
  def update
28
- @form = Backend::NavigationItemForm.new(@model)
29
-
30
- if @form.save params[:navigation_item]
24
+ if @model.update_attributes allowed_params
31
25
  redirect_to backend_navigations_path, notice: translate_notice(:changes_saved)
32
26
  else
33
27
  render :edit
@@ -49,6 +43,10 @@ class Backend::Navigation::ItemsController < BackendController
49
43
  @model = NavigationItem.find(params[:id]).decorate
50
44
  end
51
45
 
46
+ def allowed_params
47
+ params[:navigation_item].permit(:page_id, :extra)
48
+ end
49
+
52
50
  def translation_form
53
51
  Backend::NavigationItemTranslationForm.new(
54
52
  @model, @model.translation(params[:translation_locale])
@@ -1,4 +1,4 @@
1
- class Backend::NavigationsController < BackendController
1
+ class Backend::NavigationsController < Backend::BaseController
2
2
  before_action -> { breadcrumb.add t('b.navigation'), backend_navigations_path }
3
3
 
4
4
  def index
@@ -1,7 +1,7 @@
1
- class Backend::PagesController < BackendController
1
+ class Backend::PagesController < Backend::BaseController
2
2
  include Concerns::Backend::TranslatableController
3
3
 
4
- before_action :find_model, only: [:edit, :update, :tree_drag_and_drop, :destroy]
4
+ before_action :find_model, except: [:index, :new, :create]
5
5
  before_action -> { breadcrumb.add t('b.pages'), backend_pages_path }
6
6
 
7
7
  def index
@@ -10,39 +10,38 @@ class Backend::PagesController < BackendController
10
10
  respond_to do |format|
11
11
  format.html
12
12
  format.json {
13
- render json: page_tree_data.to_json
13
+ render json: Udongo::Pages::Tree.new(self).data
14
14
  }
15
15
  end
16
16
  end
17
17
 
18
18
  def new
19
- @form = Backend::PageForm.new(Page.new.decorate)
19
+ @model = Page.new.decorate
20
20
  end
21
21
 
22
22
  def create
23
- @form = Backend::PageForm.new(Page.new.decorate)
23
+ @model = Page.new(allowed_params).decorate
24
24
 
25
- if @form.save params[:page]
26
- redirect_to edit_backend_page_path(@form.page), notice: translate_notice(:added, :page)
25
+ if @model.save
26
+ redirect_to edit_backend_page_path(@model), notice: translate_notice(:added, :page)
27
27
  else
28
28
  render :new
29
29
  end
30
30
  end
31
31
 
32
- def edit
33
- @form = Backend::PageForm.new(@model)
34
- end
35
-
36
32
  def update
37
- @form = Backend::PageForm.new(@model)
38
-
39
- if @form.save params[:page]
33
+ if @model.update_attributes allowed_params
40
34
  redirect_to edit_backend_page_path(@model), notice: translate_notice(:edited, :page)
41
35
  else
42
36
  render :edit
43
37
  end
44
38
  end
45
39
 
40
+ def toggle_visibility
41
+ @model.visible? ? @model.hide! : @model.show!
42
+ render json: { toggled: @model }
43
+ end
44
+
46
45
  def tree_drag_and_drop
47
46
  # TODO (Dave) - check if this page is draggable.
48
47
  render json: { moved: @model.set_position(params[:position], params[:parent_id]) }
@@ -53,20 +52,16 @@ class Backend::PagesController < BackendController
53
52
  render json: { trashed: @model.destroy }
54
53
  end
55
54
 
56
- def page_tree_data(parent_id: nil)
57
- Page.where(parent_id: parent_id).inject([]) do |data, p|
58
- hash = node_data p
59
- hash[:children] = page_tree_data(parent_id: p.id) if p.children.any?
60
- data << hash
61
- end
62
- end
63
-
64
55
  private
65
56
 
66
57
  def find_model
67
58
  @model = Page.find(params[:id]).decorate
68
59
  end
69
60
 
61
+ def allowed_params
62
+ params[:page].permit(:description, :parent_id, :visible)
63
+ end
64
+
70
65
  def translation_form
71
66
  Backend::PageTranslationForm.new(
72
67
  @model,
@@ -74,21 +69,4 @@ class Backend::PagesController < BackendController
74
69
  @model.seo(params[:translation_locale])
75
70
  )
76
71
  end
77
-
78
- def node_data(page)
79
- {
80
- text: page.description,
81
- type: :file,
82
- state: { selected: false },
83
- data: {
84
- id: page.id,
85
- url: edit_backend_page_path(page),
86
- delete_url: backend_page_path(page, format: :json),
87
- deletable: page.deletable?,
88
- draggable: page.draggable?,
89
- update_position_url: tree_drag_and_drop_backend_page_path(page)
90
- },
91
- children: []
92
- }
93
- end
94
72
  end
@@ -1,4 +1,4 @@
1
- class Backend::RedirectsController < BackendController
1
+ class Backend::RedirectsController < Backend::BaseController
2
2
  include Concerns::PaginationController
3
3
 
4
4
  before_action -> { breadcrumb.add t('b.redirects'), backend_redirects_path }
@@ -1,4 +1,4 @@
1
- class Backend::SeoController < BackendController
1
+ class Backend::SeoController < Backend::BaseController
2
2
  def slugify
3
3
  result = params[:value] ? params[:value].parameterize : nil
4
4
  render json: { result: result }
@@ -1,4 +1,4 @@
1
- class Backend::SessionsController < BackendController
1
+ class Backend::SessionsController < Backend::BaseController
2
2
  skip_before_action :check_login
3
3
  layout 'backend/login'
4
4
 
@@ -1,4 +1,4 @@
1
- class Backend::SnippetsController < BackendController
1
+ class Backend::SnippetsController < Backend::BaseController
2
2
  include Concerns::Backend::TranslatableController
3
3
 
4
4
  before_action :find_model, only: [:edit, :update]
@@ -9,28 +9,22 @@ class Backend::SnippetsController < BackendController
9
9
  end
10
10
 
11
11
  def new
12
- @form = Backend::SnippetForm.new(Snippet.new)
12
+ @model = Snippet.new
13
13
  end
14
14
 
15
15
  def create
16
- @form = Backend::SnippetForm.new(Snippet.new)
16
+ @model = Snippet.new allowed_params
17
17
 
18
- if @form.save params[:snippet]
19
- redirect_to edit_translation_backend_snippet_path(@form.snippet, translation_locale: default_locale),
18
+ if @model.save
19
+ redirect_to edit_translation_backend_snippet_path(@model, translation_locale: default_app_locale),
20
20
  notice: translate_notice(:added, :snippet)
21
21
  else
22
22
  render :new
23
23
  end
24
24
  end
25
25
 
26
- def edit
27
- @form = Backend::SnippetForm.new(@model)
28
- end
29
-
30
26
  def update
31
- @form = Backend::SnippetForm.new(@model)
32
-
33
- if @form.save params[:snippet]
27
+ if @model.update_attributes allowed_params
34
28
  redirect_to edit_backend_snippet_path(@model),
35
29
  notice: translate_notice(:edited, :snippet)
36
30
  else
@@ -44,7 +38,14 @@ class Backend::SnippetsController < BackendController
44
38
  @model = Snippet.find params[:id]
45
39
  end
46
40
 
41
+ def allowed_params
42
+ params[:snippet].permit(:identifier, :description)
43
+ end
44
+
47
45
  def translation_form
48
- Backend::SnippetTranslationForm.new(@model, @model.translation(params[:translation_locale]))
46
+ Backend::SnippetTranslationForm.new(
47
+ @model,
48
+ @model.translation(params[:translation_locale])
49
+ )
49
50
  end
50
51
  end
@@ -1,4 +1,4 @@
1
- class Backend::TagboxController < BackendController
1
+ class Backend::TagboxController < Backend::BaseController
2
2
  def index
3
3
  tags = Tag.by_locale(locale).pluck(:name).map do |name|
4
4
  { label: name, value: name }
@@ -6,7 +6,7 @@ class CatchAllController < ActionController::Base
6
6
  redirect.used!
7
7
  redirect_to redirect.destination_uri, status: redirect.status_code
8
8
  else
9
- render text: 'Page not found', status: 404
9
+ render plain: 'Page not found', status: 404
10
10
  end
11
11
  end
12
12
  end
@@ -28,7 +28,9 @@ module Concerns
28
28
  def content_path
29
29
  column = @model.column
30
30
  path = "edit_translation_backend_#{column.row.rowable.class.to_s.downcase}_path"
31
- send(path, column.row.rowable, locale, anchor: "content-row-#{column.row.id}")
31
+ # TODO this needs to be the locale that you were editing, not the interface locale.
32
+ # You can reproduce this by editing flexible content in NL when your interface is english.
33
+ send(path, column.row.rowable, @model.column.row.locale, anchor: "content-row-#{column.row.id}")
32
34
  end
33
35
 
34
36
  def update
@@ -6,7 +6,7 @@ class RedirectsController < ActionController::Base
6
6
  redirect.used!
7
7
  redirect_to redirect.destination_uri, status: redirect.status_code
8
8
  else
9
- render text: 'No such redirect or disabled.', status: 404
9
+ render plain: 'No such redirect or disabled.', status: 404
10
10
  end
11
11
  end
12
12
 
@@ -21,9 +21,6 @@ class PageDecorator < ApplicationDecorator
21
21
  end
22
22
  end
23
23
 
24
- str = ''
25
- str << "/#{locale}" if Udongo.config.routes.prefix_with_locale?
26
- str << "/#{slugs.reverse.join('/')}"
27
- str
24
+ "/#{locale}/#{slugs.reverse.join('/')}"
28
25
  end
29
26
  end