alchemy_cms 3.1.0.beta5 → 3.1.0.beta6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +23 -0
  3. data/app/assets/stylesheets/alchemy/frame.scss +4 -0
  4. data/app/assets/stylesheets/alchemy/icons.scss +2 -0
  5. data/app/controllers/alchemy/admin/languages_controller.rb +5 -6
  6. data/app/controllers/alchemy/api/base_controller.rb +1 -1
  7. data/app/controllers/alchemy/api/contents_controller.rb +1 -1
  8. data/app/controllers/alchemy/api/elements_controller.rb +1 -1
  9. data/app/controllers/alchemy/api/pages_controller.rb +1 -1
  10. data/app/models/alchemy/page.rb +1 -0
  11. data/app/models/alchemy/page/page_natures.rb +3 -3
  12. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +4 -0
  13. data/app/views/alchemy/admin/layoutpages/edit.html.erb +5 -1
  14. data/app/views/alchemy/admin/pages/_form.html.erb +5 -1
  15. data/app/views/alchemy/admin/pages/_locked_page.html.erb +3 -0
  16. data/app/views/alchemy/admin/pages/_page.html.erb +5 -1
  17. data/app/views/alchemy/admin/pages/_page_status.html.erb +3 -0
  18. data/app/views/alchemy/admin/pages/info.html.erb +6 -1
  19. data/app/views/alchemy/admin/resources/_form.html.erb +1 -1
  20. data/config/locales/alchemy.de.yml +1 -0
  21. data/config/locales/alchemy.en.yml +2 -1
  22. data/config/routes.rb +1 -4
  23. data/lib/alchemy/capistrano.rb +3 -4
  24. data/lib/alchemy/errors.rb +0 -4
  25. data/lib/alchemy/mount_point.rb +30 -30
  26. data/lib/alchemy/resource.rb +18 -2
  27. data/lib/alchemy/test_support/factories.rb +2 -2
  28. data/lib/alchemy/version.rb +1 -1
  29. data/lib/rails/generators/alchemy/deploy_script/deploy_script_generator.rb +25 -9
  30. data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +0 -1
  31. data/spec/controllers/admin/languages_controller_spec.rb +35 -11
  32. data/spec/controllers/alchemy/api/contents_controller_spec.rb +1 -1
  33. data/spec/controllers/alchemy/api/elements_controller_spec.rb +1 -1
  34. data/spec/controllers/alchemy/api/pages_controller_spec.rb +1 -1
  35. data/spec/dummy/config/alchemy/page_layouts.yml +1 -1
  36. data/spec/dummy/config/application.rb +0 -2
  37. data/spec/dummy/config/environments/test.rb +1 -1
  38. data/spec/libraries/mount_point_spec.rb +41 -26
  39. data/spec/libraries/page_layout_spec.rb +9 -3
  40. data/spec/libraries/resource_spec.rb +24 -0
  41. data/spec/models/page_spec.rb +25 -26
  42. metadata +3 -4
  43. data/app/views/layouts/alchemy/login.html.erb +0 -2
  44. data/config/initializers/inflections.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d78b3cd4366f28718898767bfd2212e69395d267
4
- data.tar.gz: 140a0432a7d56ebf61bea01496a2a595dc4d01d2
3
+ metadata.gz: ee349e3c1603a85fa273cfbb168e88190db26c8f
4
+ data.tar.gz: c3288e7a9f128291320f6646dd41ab78f65f23ea
5
5
  SHA512:
6
- metadata.gz: 27f13644e9cb1b682087d700fc8bc8832bc631eec3050afceb6de0c208b2529240bf4c305e6834d6be3a0108cd5e0450c4a2760711fcc1280db8e100b12aaa39
7
- data.tar.gz: cf7ea49cb254531558138aa382e3cdba384e318dcadf7545abc467bb04393308a6fa71c5713faf3747ef6d38a4d2a92156d9217a1cfa4d3bfe7a509c352cc93c
6
+ metadata.gz: a4b86afe18307827a65c0ecff2894ac39df62a39430d8c9da38d455a49ace528caf9a94b0b91b7e93477a2188ea407c21c700f161ac067e11b26411481f6e5f1
7
+ data.tar.gz: 52dcf130529e8494da6e49babc0ab1bed1ce6e752aea27aaadce37081dee615892f62c4f5ff4cccd3ae00bf32de1ea4e7b001beb848ca08f6d022428a91a1d44
@@ -0,0 +1,23 @@
1
+ # EditorConfig is awesome: http://EditorConfig.org
2
+
3
+ # top-most EditorConfig file
4
+ root = true
5
+
6
+ # Unix-style newlines with a newline ending every file
7
+ [*]
8
+ charset = utf-8
9
+ end_of_line = lf
10
+ indent_size = 2
11
+ indent_style = space
12
+ insert_final_newline = true
13
+ trim_trailing_whitespace = true
14
+ tab_width = 2
15
+
16
+ [**.rb]
17
+ max_line_length = 80
18
+
19
+ [**.js, **.coffee]
20
+ max_line_length = 120
21
+
22
+ [*.md]
23
+ trim_trailing_whitespace = false
@@ -275,6 +275,10 @@ div#user_info {
275
275
  > a {
276
276
  padding: 0px 8px;
277
277
  }
278
+
279
+ .inline.icon {
280
+ vertical-align: text-bottom;
281
+ }
278
282
  }
279
283
  }
280
284
 
@@ -302,6 +302,8 @@
302
302
 
303
303
  &.warning, &.warn, &.alert {
304
304
  background-position: 0 -168px;
305
+
306
+ &.inline { vertical-align: baseline; }
305
307
  }
306
308
 
307
309
  &.word {
@@ -1,18 +1,17 @@
1
1
  module Alchemy
2
2
  module Admin
3
- class LanguagesController < Alchemy::Admin::ResourcesController
3
+ class LanguagesController < ResourcesController
4
4
 
5
5
  def new
6
- @language = Alchemy::Language.new
7
- @language.page_layout = (configured_page_layout or @language.page_layout)
6
+ @language = Language.new
7
+ @language.page_layout = configured_page_layout || @language.page_layout
8
8
  end
9
9
 
10
- protected
10
+ private
11
11
 
12
12
  def configured_page_layout
13
- Alchemy::Config.get(:default_language).try('[]', 'page_layout')
13
+ Config.get(:default_language).try('[]', 'page_layout')
14
14
  end
15
-
16
15
  end
17
16
  end
18
17
  end
@@ -1,5 +1,5 @@
1
1
  module Alchemy
2
- class API::BaseController < Alchemy::BaseController
2
+ class Api::BaseController < Alchemy::BaseController
3
3
  layout false
4
4
  respond_to :json
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Alchemy
2
- class API::ContentsController < API::BaseController
2
+ class Api::ContentsController < Api::BaseController
3
3
 
4
4
  # Returns all contents as json object
5
5
  #
@@ -1,5 +1,5 @@
1
1
  module Alchemy
2
- class API::ElementsController < API::BaseController
2
+ class Api::ElementsController < Api::BaseController
3
3
 
4
4
  # Returns all elements as json object
5
5
  #
@@ -1,5 +1,5 @@
1
1
  module Alchemy
2
- class API::PagesController < API::BaseController
2
+ class Api::PagesController < Api::BaseController
3
3
  before_action :load_page, only: [:show]
4
4
 
5
5
  # Returns all pages as json object
@@ -36,6 +36,7 @@
36
36
  module Alchemy
37
37
  class Page < ActiveRecord::Base
38
38
  include Alchemy::Hints
39
+ include Alchemy::Logger
39
40
  include Alchemy::Touching
40
41
 
41
42
  DEFAULT_ATTRIBUTES_FOR_COPY = {
@@ -67,10 +67,10 @@ module Alchemy
67
67
  return {} if self.systempage?
68
68
  description = PageLayout.get(self.page_layout)
69
69
  if description.nil?
70
- raise PageLayoutDefinitionError, "Description could not be found for page layout named #{self.page_layout}. Please check page_layouts.yml file."
71
- else
72
- description
70
+ log_warning "Page layout description for `#{self.page_layout}` not found. Please check `page_layouts.yml` file."
71
+ return {}
73
72
  end
73
+ description
74
74
  end
75
75
  alias_method :definition, :layout_description
76
76
 
@@ -1,7 +1,11 @@
1
1
  <li class="page_level_<%= layoutpage.level %>" id="page_<%= layoutpage.id %>">
2
2
  <div class="sitemap_page<%= layoutpage.locked ? ' locked' : '' %>">
3
3
  <div class="sitemap_left_images">
4
+ <% if layoutpage.layout_description.blank? %>
5
+ <span class="inline warning icon" title="<%= _t(:page_layout_description_missing) %>"></span>
6
+ <% else %>
4
7
  <%= render_icon(:page) %>
8
+ <% end %>
5
9
  </div>
6
10
  <div class="sitemap_right_tools">
7
11
  <%- if can?(:configure, layoutpage) -%>
@@ -1,5 +1,9 @@
1
1
  <%= alchemy_form_for [:admin, @page], class: 'edit_page' do |f| %>
2
- <%= f.input :page_layout, collection: @page_layouts, label: _t(:page_type), include_blank: false, input_html: {class: 'alchemy_selectbox'} %>
2
+ <%= f.input :page_layout,
3
+ collection: @page_layouts,
4
+ label: @page.layout_description.blank? ? (%(<span class="inline warning icon" title="#{ _t(:page_layout_description_missing) }"></span>&nbsp;) + _t(:page_type)).html_safe : _t(:page_type),
5
+ include_blank: false,
6
+ input_html: {class: 'alchemy_selectbox'} %>
3
7
  <%= f.input :name, autofocus: true %>
4
8
  <% if @page.taggable? %>
5
9
  <div class="input string">
@@ -1,5 +1,9 @@
1
1
  <%= alchemy_form_for [:admin, @page], class: 'edit_page' do |f| %>
2
- <%= f.input :page_layout, collection: @page_layouts, label: _t(:page_type), include_blank: false, input_html: {class: 'alchemy_selectbox'} %>
2
+ <%= f.input :page_layout,
3
+ collection: @page_layouts,
4
+ label: @page.layout_description.blank? ? (%(<span class="inline warning icon" title="#{ _t(:page_layout_description_missing) }"></span>&nbsp;) + _t(:page_type)).html_safe : _t(:page_type),
5
+ include_blank: false,
6
+ input_html: {class: 'alchemy_selectbox'} %>
3
7
  <div class="input check_boxes">
4
8
  <label class="control-label"><%= _t(:page_status) %></label>
5
9
  <div class="control_group">
@@ -3,6 +3,9 @@
3
3
  <% else %>
4
4
  <div class="subnavi_tab wide" id="locked_page_<%= locked_page.id %>">
5
5
  <%= link_to alchemy.edit_admin_page_path(locked_page) do %>
6
+ <% if locked_page.layout_description.blank? %>
7
+ <span class="inline warning icon" title="<%= _t(:page_layout_description_missing) %>"></span>
8
+ <% end %>
6
9
  <span class="page_name" title="<%= locked_page.name %>">
7
10
  <%= truncate locked_page.name, length: 15 %>
8
11
  </span>
@@ -2,7 +2,11 @@
2
2
  <div class="sitemap_page<%= page.locked ? ' locked' : '' %>" name="<%= page.name %>">
3
3
  <div class="sitemap_left_images">
4
4
  <%= sitemap_folder_link(page) unless page.level == 1 || page.children.blank? || @sorting %>
5
- <div class="page icon <%= @sorting && page.level > 1 ? 'handle' : nil %>"></div>
5
+ <% if page.layout_description.blank? %>
6
+ <span class="inline warning icon" title="<%= _t(:page_layout_description_missing) %>"></span>
7
+ <% else %>
8
+ <div class="page icon <%= @sorting && page.level > 1 ? 'handle' : nil %>"></div>
9
+ <% end %>
6
10
  </div>
7
11
  <div class="sitemap_right_tools">
8
12
  <%- unless @sorting -%>
@@ -1,4 +1,7 @@
1
1
  <div class="page_status_and_name" id="page_<%= @page.id %>_status">
2
+ <% if @page.layout_description.blank? %>
3
+ <span class="inline warning icon" title="<%= _t(:page_layout_description_missing) %>"></span>
4
+ <% end %>
2
5
  <span class="page_name"><%= @page.name %></span>
3
6
  <%- if multi_language? -%>
4
7
  <span class="page_language" title="<%= @page.language.name %>">
@@ -5,7 +5,12 @@
5
5
  <% end %>
6
6
  <% end %>
7
7
  <div class="value">
8
- <label><%= Alchemy::Page.human_attribute_name(:page_layout) %></label>
8
+ <label>
9
+ <% if @page.layout_description.blank? %>
10
+ <span class="inline warning icon" title="<%= _t(:page_layout_description_missing) %>"></span>
11
+ <% end %>
12
+ <%= Alchemy::Page.human_attribute_name(:page_layout) %>
13
+ </label>
9
14
  <p><%= @page.layout_display_name %></p>
10
15
  </div>
11
16
  <div class="value">
@@ -1,5 +1,5 @@
1
1
  <%= alchemy_form_for resource_instance_variable, url: resource_path(resource_instance_variable) do |f| %>
2
- <% resource_handler.attributes.each do |attribute| %>
2
+ <% resource_handler.editable_attributes.each do |attribute| %>
3
3
  <% if relation = attribute[:relation] %>
4
4
  <%= f.association relation[:name].to_sym,
5
5
  label_method: relation[:attr_method],
@@ -287,6 +287,7 @@ de:
287
287
  "Warning!": "Achtung!"
288
288
  content_description_missing: "Warnung: Für diesen Content konnte die Vorlage nicht gefunden werden. Bitte überprüfen Sie die elements.yml Datei."
289
289
  element_description_missing: "Warnung! Für dieses Element konnte die Vorlage nicht gefunden werden. Bitte überprüfen Sie die elements.yml Datei."
290
+ page_layout_description_missing: "Warnung! Für diese Seite konnte die Vorlage nicht gefunden werden. Bitte überprüfen Sie die page_layouts.yml Datei."
290
291
  "Welcome to Alchemy": "Willkommen in Alchemy"
291
292
  "Who else is online": "Wer ist noch online"
292
293
  "Yes": "Ja"
@@ -286,7 +286,8 @@ en:
286
286
  "Visit page": "Visit page"
287
287
  "Warning!": "Warning!"
288
288
  content_description_missing: "Warning: Content is missing its description. Please check the elements.yml"
289
- element_description_missing: "WARNING! Missing description. Please check your elements.yml file."
289
+ element_description_missing: "WARNING! Missing element description. Please check your elements.yml file."
290
+ page_layout_description_missing: "WARNING! Missing page layout description. Please check your page_layouts.yml file."
290
291
  "Welcome to Alchemy": "Welcome to Alchemy"
291
292
  "Who else is online": "Who else is online"
292
293
  "Yes": "Yes"
@@ -4,10 +4,7 @@ Alchemy::Engine.routes.draw do
4
4
 
5
5
  get '/sitemap.xml' => 'pages#sitemap', format: 'xml'
6
6
 
7
- get '/admin' => redirect(
8
- "#{Alchemy::MountPoint.get}/admin/dashboard"
9
- )
10
-
7
+ get '/admin' => redirect('admin/dashboard')
11
8
  get '/admin/dashboard' => 'admin/dashboard#index',
12
9
  :as => :admin_dashboard
13
10
  get '/admin/dashboard/info' => 'admin/dashboard#info',
@@ -1,9 +1,8 @@
1
- # This recipe contains Capistrano recipes for handling the uploads and picture cache files while deploying your application.
2
- #
1
+ # This recipe contains Capistrano recipes for handling the uploads
2
+ # and picture cache files while deploying your application.
3
+
3
4
  require 'fileutils'
4
5
  require 'alchemy/tasks/helpers'
5
- # Loading the current Rails app's env, so we can get the Alchemy mount point.
6
- require './config/environment.rb'
7
6
  require 'alchemy/mount_point'
8
7
 
9
8
  include Alchemy::Tasks::Helpers
@@ -32,10 +32,6 @@ module Alchemy
32
32
  # Raised if calling +image_file+ on a Picture object returns nil.
33
33
  end
34
34
 
35
- class PageLayoutDefinitionError < StandardError
36
- # Raised if page_layout definition can not be found.
37
- end
38
-
39
35
  class PictureInUseError < StandardError
40
36
  # Raised if the picture is still in use and can not be deleted.
41
37
  end
@@ -1,41 +1,41 @@
1
1
  module Alchemy
2
- # Utitlities for Alchemy's mount point in the host rails app.
2
+
3
+ # Utilities for Alchemy's mount point in the host rails app.
3
4
  #
4
5
  class MountPoint
6
+ MOUNT_POINT_REGEXP = /mount\sAlchemy::Engine\s=>\s['|"](\/\w*)['|"]/
7
+
8
+ class << self
5
9
 
6
- # Returns the path of Alchemy's mount point in current rails app.
7
- #
8
- # @param [Boolean] remove_leading_slash_if_blank
9
- # Pass false to not return a leading slash on empty mount point.
10
- #
11
- def self.get(remove_leading_slash_if_blank = true)
12
- if self.mount_point == "/" && remove_leading_slash_if_blank
13
- self.mount_point.gsub(/\A\/\z/, '')
14
- else
15
- self.mount_point
10
+ # Returns the path of Alchemy's mount point in current rails app.
11
+ #
12
+ # @param [Boolean] remove_leading_slash_if_blank
13
+ # Pass false to not return a leading slash on empty mount point.
14
+ #
15
+ def get(remove_leading_slash_if_blank = true)
16
+ if path == "/" && remove_leading_slash_if_blank
17
+ path.gsub(/\A\/\z/, '')
18
+ else
19
+ path
20
+ end
16
21
  end
17
- end
18
22
 
19
- # Returns the routes object from Alchemy in the host app.
20
- #
21
- def self.routes
22
- ::Rails.application.routes.named_routes[:alchemy]
23
- end
23
+ # Returns the mount point path from the Rails app routes.
24
+ #
25
+ def path
26
+ match = File.read(routes_file_path).match(MOUNT_POINT_REGEXP)
27
+ if match.nil?
28
+ raise "Alchemy mount point not found! Please run `bin/rake alchemy:mount'"
29
+ else
30
+ match[1]
31
+ end
32
+ end
33
+
34
+ private
24
35
 
25
- # Returns the raw mount point path from the Rails app routes.
26
- #
27
- # If Alchemy is not mounted in the main app, it falls back to root path.
28
- #
29
- def self.mount_point
30
- if self.routes.nil?
31
- ::Rails.logger.warn <<-WARN
32
- Alchemy is not mounted! Falling back to root path (/).
33
- If you want to change Alchemy's mount point, please mount Alchemy::Engine in your config/routes.rb file.
34
- WARN
35
- return '/'
36
+ def routes_file_path
37
+ Rails.root.join('config/routes.rb')
36
38
  end
37
- routes.path.spec.to_s
38
39
  end
39
-
40
40
  end
41
41
  end
@@ -20,7 +20,7 @@ module Alchemy
20
20
  #
21
21
  # == Skip attributes
22
22
  #
23
- # Usually you don't want your users to edit all attributes provided by a model. Hence some default attributes,
23
+ # Usually you don't want your users to see and edit all attributes provided by a model. Hence some default attributes,
24
24
  # namely id, updated_at, created_at, creator_id and updater_id are not returned by Resource#attributes.
25
25
  #
26
26
  # If you want to skip a different set of attributes just define a +skipped_alchemy_resource_attributes+ class method in your model class
@@ -32,6 +32,17 @@ module Alchemy
32
32
  # %w(id updated_at secret_token remote_ip)
33
33
  # end
34
34
  #
35
+ # == Restrict attributes
36
+ #
37
+ # Beside skipping certain attributes you can also restrict them. Restricted attributes can not be edited by the user but still be seen in the index view.
38
+ # No attributes are restricted by default.
39
+ #
40
+ # === Example
41
+ #
42
+ # def self.restricted_alchemy_resource_attributes
43
+ # %w(synced_at remote_record_id)
44
+ # end
45
+ #
35
46
  # == Resource relations
36
47
  #
37
48
  # Alchemy::Resource can take care of ActiveRecord relations.
@@ -73,7 +84,7 @@ module Alchemy
73
84
  # resource = Resource.new('/admin/tags', {"engine_name"=>"alchemy"}, ActsAsTaggableOn::Tag)
74
85
  #
75
86
  class Resource
76
- attr_accessor :skipped_attributes, :resource_relations, :model_associations
87
+ attr_accessor :skipped_attributes, :restricted_attributes, :resource_relations, :model_associations
77
88
  attr_reader :model
78
89
 
79
90
  DEFAULT_SKIPPED_ATTRIBUTES = %w(id updated_at created_at creator_id updater_id)
@@ -84,6 +95,7 @@ module Alchemy
84
95
  @module_definition = module_definition
85
96
  @model = (custom_model or guess_model_from_controller_path)
86
97
  self.skipped_attributes = model.respond_to?(:skipped_alchemy_resource_attributes) ? model.skipped_alchemy_resource_attributes : DEFAULT_SKIPPED_ATTRIBUTES
98
+ self.restricted_attributes = model.respond_to?(:restricted_alchemy_resource_attributes) ? model.restricted_alchemy_resource_attributes : []
87
99
  if model.respond_to?(:alchemy_resource_relations)
88
100
  if not model.respond_to?(:reflect_on_all_associations)
89
101
  raise MissingActiveRecordAssociation
@@ -139,6 +151,10 @@ module Alchemy
139
151
  end.compact
140
152
  end
141
153
 
154
+ def editable_attributes
155
+ attributes.reject { |h| self.restricted_attributes.map(&:to_s).include?(h[:name].to_s) }
156
+ end
157
+
142
158
  # Returns all columns that are searchable
143
159
  #
144
160
  # For now it only uses string type columns
@@ -28,7 +28,7 @@ FactoryGirl.define do
28
28
  code 'de'
29
29
  default true
30
30
  frontpage_name 'Intro'
31
- page_layout 'intro'
31
+ page_layout { Alchemy::Config.get(:default_language)['page_layout'] }
32
32
  public true
33
33
  site { Alchemy::Site.first }
34
34
 
@@ -59,7 +59,7 @@ FactoryGirl.define do
59
59
 
60
60
  factory :language_root_page do
61
61
  name 'Startseite'
62
- page_layout 'intro'
62
+ page_layout { language.page_layout }
63
63
  language_root true
64
64
  public true
65
65
  parent_id { Alchemy::Page.root.id }
@@ -1,5 +1,5 @@
1
1
  module Alchemy
2
- VERSION = "3.1.0.beta5"
2
+ VERSION = "3.1.0.beta6"
3
3
 
4
4
  def self.version
5
5
  VERSION
@@ -5,13 +5,31 @@ module Alchemy
5
5
  class DeployScriptGenerator < ::Rails::Generators::Base
6
6
 
7
7
  desc "This generator generates a Capistrano receipt for deploying Alchemy CMS."
8
- class_option :scm, :type => :string, :desc => "Set the type of scm you use for deployment.", :default => 'git'
9
- class_option :db, :type => :string, :desc => "Set the type of database you use on your server.", :default => 'mysql'
8
+
9
+ class_option :scm,
10
+ type: 'string',
11
+ desc: "Set the type of scm you use for deployment.",
12
+ default: 'git'
13
+
14
+ class_option :db,
15
+ type: 'string',
16
+ desc: "Set the type of database you use on your server.",
17
+ default: 'mysql'
18
+
10
19
  source_root File.expand_path('templates', File.dirname(__FILE__))
11
20
 
12
21
  def copy_script
13
22
  @scm = options[:scm]
14
23
  @database_type = options[:db]
24
+ ask_questions
25
+ template "deploy.rb.tt", Rails.root.join('config', 'deploy.rb')
26
+ setup_capistrano
27
+ show_read_me
28
+ end
29
+
30
+ private
31
+
32
+ def ask_questions
15
33
  @app_name = ask('Please enter a name for your application:')
16
34
  @server = ask('Please enter server ip or domain:')
17
35
  if @store_credentials = yes?('Do want to store the ssh credentials? (PLEASE DO NOT STORE THEM IF THE REPOSITORY IS PUBLIC) (y/N)')
@@ -27,13 +45,8 @@ module Alchemy
27
45
  if @scm == "svn" && yes?('Is your repository private? (y/N)')
28
46
  ask_for_repo_credentials
29
47
  end
30
- template "deploy.rb.tt", Rails.root.join('config', 'deploy.rb')
31
- setup_capistrano
32
- show_read_me
33
48
  end
34
49
 
35
- private
36
-
37
50
  def ask_for_credentials
38
51
  @ssh_user = ask('Please enter ssh username:')
39
52
  port = ask('Please enter ssh port (22):')
@@ -59,7 +72,11 @@ module Alchemy
59
72
 
60
73
  def setup_capistrano
61
74
  puts "\nSetting up Capistrano"
62
- `capify .`
75
+ if system 'capify .'
76
+ gsub_file 'Capfile',
77
+ /\s{4}#\sload\s'deploy\/assets'/,
78
+ "load 'deploy/assets'"
79
+ end
63
80
  end
64
81
 
65
82
  def show_read_me
@@ -68,7 +85,6 @@ module Alchemy
68
85
  puts "\nIf you want to deploy Alchemy the first time type:\ncap deploy:cold"
69
86
  puts "\nAfter the first deploy you just need to type:\ncap deploy"
70
87
  end
71
-
72
88
  end
73
89
  end
74
90
  end
@@ -1,6 +1,5 @@
1
1
  require 'bundler/capistrano'
2
2
  require 'alchemy/capistrano'
3
- load 'deploy/assets'
4
3
 
5
4
  # application name
6
5
  set :application, "<%= @app_name %>"
@@ -1,19 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
- class Alchemy::Config;
4
- end
5
-
6
3
  describe Alchemy::Admin::LanguagesController do
7
4
 
8
5
  before do
9
6
  sign_in(admin_user)
10
7
  end
11
8
 
12
- describe "new" do
13
-
9
+ describe "#new" do
14
10
  context "when default_language.page_layout is set" do
15
- it "should use it as page_layout-default for the new language" do
16
- # FML :/
11
+ before do
17
12
  allow(Alchemy::Config).to receive(:get) do |arg|
18
13
  if arg == :default_language
19
14
  {'page_layout' => "new_standard"}
@@ -21,17 +16,46 @@ describe Alchemy::Admin::LanguagesController do
21
16
  Alchemy::Config.show[arg.to_s]
22
17
  end
23
18
  end
19
+ end
20
+
21
+ it "uses it as page_layout-default for the new language" do
24
22
  get :new
25
- expect(assigns(:language).page_layout).to eql("new_standard")
23
+ expect(assigns(:language).page_layout).to eq("new_standard")
26
24
  end
27
25
  end
28
26
 
29
- context "when default_language or page_layout aren't configured" do
30
- it "should fallback to one configured in config.yml" do
27
+ context "when default_language is not configured" do
28
+ before do
29
+ allow(Alchemy::Config).to receive(:get) do |arg|
30
+ if arg == :default_language
31
+ nil
32
+ else
33
+ Alchemy::Config.show[arg.to_s]
34
+ end
35
+ end
36
+ end
37
+
38
+ it "falls back to default database value." do
31
39
  get :new
32
- expect(assigns(:language).page_layout).to eql("index")
40
+ expect(assigns(:language).page_layout).to eq("intro")
33
41
  end
34
42
  end
35
43
 
44
+ context "when default language page_layout is not configured" do
45
+ before do
46
+ allow(Alchemy::Config).to receive(:get) do |arg|
47
+ if arg == :default_language
48
+ {}
49
+ else
50
+ Alchemy::Config.show[arg.to_s]
51
+ end
52
+ end
53
+ end
54
+
55
+ it "falls back to default database value." do
56
+ get :new
57
+ expect(assigns(:language).page_layout).to eq("intro")
58
+ end
59
+ end
36
60
  end
37
61
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Alchemy
4
- describe API::ContentsController do
4
+ describe Api::ContentsController do
5
5
  describe '#index' do
6
6
  let!(:page) { create(:page) }
7
7
  let!(:element) { create(:element, page: page) }
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Alchemy
4
- describe API::ElementsController do
4
+ describe Api::ElementsController do
5
5
  describe '#index' do
6
6
  let!(:page) { create(:public_page) }
7
7
  let!(:element) { create(:element, page: page) }
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Alchemy
4
- describe API::PagesController do
4
+ describe Api::PagesController do
5
5
 
6
6
  describe '#index' do
7
7
  let!(:page) { create(:public_page) }
@@ -1,4 +1,4 @@
1
- - name: intro
1
+ - name: index
2
2
  unique: true
3
3
 
4
4
  - name: standard
@@ -18,7 +18,5 @@ module Dummy
18
18
  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
19
19
  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
20
20
  # config.i18n.default_locale = :de
21
-
22
- I18n.enforce_available_locales = true
23
21
  end
24
22
  end
@@ -34,5 +34,5 @@ Dummy::Application.configure do
34
34
  # Print deprecation notices to the stderr.
35
35
  config.active_support.deprecation = :stderr
36
36
 
37
- I18n.enforce_available_locales = false
37
+ config.i18n.enforce_available_locales = false
38
38
  end
@@ -1,33 +1,34 @@
1
1
  require 'spec_helper'
2
- require 'ostruct'
3
2
 
4
3
  describe Alchemy::MountPoint do
5
-
6
4
  describe '.get' do
7
-
8
5
  it "returns the path of alchemy's mount point" do
9
- allow(Alchemy::MountPoint).to receive(:mount_point).and_return('/cms')
6
+ allow(Alchemy::MountPoint).to receive(:path).and_return('/cms')
10
7
  expect(Alchemy::MountPoint.get).to eq('/cms')
11
8
  end
12
9
 
13
10
  it "removes the leading slash if root mount point" do
14
- allow(Alchemy::MountPoint).to receive(:mount_point).and_return('/')
11
+ allow(Alchemy::MountPoint).to receive(:path).and_return('/')
15
12
  expect(Alchemy::MountPoint.get).to eq('')
16
13
  end
17
14
 
18
15
  context "with remove_leading_slash_if_blank set to false" do
19
- before {
20
- allow(Alchemy::MountPoint).to receive(:mount_point).and_return('/')
21
- }
16
+ before do
17
+ allow(Alchemy::MountPoint)
18
+ .to receive(:path)
19
+ .and_return('/')
20
+ end
22
21
 
23
22
  it "does not remove the leading white slash of path" do
24
23
  expect(Alchemy::MountPoint.get(false)).to eq('/')
25
24
  end
26
25
 
27
26
  context "and with mount point not root" do
28
- before {
29
- allow(Alchemy::MountPoint).to receive(:mount_point).and_return('/cms')
30
- }
27
+ before do
28
+ allow(Alchemy::MountPoint)
29
+ .to receive(:path)
30
+ .and_return('/cms')
31
+ end
31
32
 
32
33
  it "does not remove the leading white slash of path" do
33
34
  expect(Alchemy::MountPoint.get(false)).to eq('/cms')
@@ -36,27 +37,41 @@ describe Alchemy::MountPoint do
36
37
  end
37
38
  end
38
39
 
39
- describe '.routes' do
40
- it "returns the routes object from alchemy engine" do
41
- expect(Alchemy::MountPoint.routes).to be_instance_of(ActionDispatch::Journey::Route)
40
+ describe '.path' do
41
+ before do
42
+ allow(File)
43
+ .to receive(:read)
44
+ .and_return("mount Alchemy::Engine => '/cms'")
42
45
  end
43
- end
44
46
 
45
- describe '.mount_point' do
46
- it 'returns the raw mount point path from routes' do
47
- allow(Alchemy::MountPoint).to receive(:routes).and_return(OpenStruct.new(path: OpenStruct.new(spec: '/cms')))
48
- expect(Alchemy::MountPoint.mount_point).to eq('/cms')
47
+ it 'returns the mount point path from routes.' do
48
+ expect(Alchemy::MountPoint.path).to eq('/cms')
49
+ end
50
+
51
+ context "Alchemy mount point could not be found" do
52
+ before do
53
+ allow(File)
54
+ .to receive(:read)
55
+ .and_return("")
56
+ end
57
+
58
+ it "raises an exception" do
59
+ expect {
60
+ Alchemy::MountPoint.path
61
+ }.to raise_error
62
+ end
49
63
  end
50
64
 
51
- context "Alchemy routes could not be found" do
52
- before {
53
- allow(Alchemy::MountPoint).to receive(:routes).and_return(nil)
54
- }
65
+ context 'Mount point using double quotes string' do
66
+ before do
67
+ allow(File)
68
+ .to receive(:read)
69
+ .and_return('mount Alchemy::Engine => "/cms"')
70
+ end
55
71
 
56
- it "falls back to root path" do
57
- expect(Alchemy::MountPoint.mount_point).to eq('/')
72
+ it 'returns the mount point path from routes.' do
73
+ expect(Alchemy::MountPoint.path).to eq('/cms')
58
74
  end
59
75
  end
60
76
  end
61
-
62
77
  end
@@ -81,12 +81,18 @@ module Alchemy
81
81
 
82
82
  context "with sites layouts present" do
83
83
  let(:site) { Site.new }
84
- let(:definitions) { [{'name' => 'default_site', 'page_layouts' => %w(intro)}] }
85
- before { allow(Site).to receive(:layout_definitions).and_return(definitions) }
84
+
85
+ let(:definitions) do
86
+ [{'name' => 'default_site', 'page_layouts' => %w(index)}]
87
+ end
88
+
89
+ before do
90
+ allow(Site).to receive(:layout_definitions).and_return(definitions)
91
+ end
86
92
 
87
93
  it "should only return layouts for site" do
88
94
  expect(subject.length).to eq(1)
89
- expect(subject.first['name']).to eq('intro')
95
+ expect(subject.first['name']).to eq('index')
90
96
  end
91
97
  end
92
98
  end
@@ -63,6 +63,11 @@ module Alchemy
63
63
  expect(resource.skipped_attributes).to eq(%w(id updated_at created_at creator_id updater_id))
64
64
  end
65
65
 
66
+ it "sets the restricted attributes accessor to an empty array by default" do
67
+ resource = Resource.new("admin/parties")
68
+ expect(resource.restricted_attributes).to eq([])
69
+ end
70
+
66
71
  it "sets an instance variable that holds the controller path" do
67
72
  resource = Resource.new("admin/parties")
68
73
  expect(resource.instance_variable_get(:@controller_path)).to eq("admin/parties")
@@ -269,6 +274,25 @@ module Alchemy
269
274
  end
270
275
  end
271
276
 
277
+ describe "#editable_attributes" do
278
+ subject { resource.editable_attributes }
279
+
280
+ let(:columns) do
281
+ [
282
+ double(:column, {name: 'name', type: :string}),
283
+ double(:column, {name: 'title', type: :string}),
284
+ double(:column, {name: 'synced_at', type: :datetime}),
285
+ double(:column, {name: 'remote_record_id', type: :string})
286
+ ]
287
+ end
288
+
289
+ before { resource.restricted_attributes = [:synced_at, :remote_record_id] }
290
+
291
+ it "does not contain restricted attributes" do
292
+ is_expected.to eq([{name: "name", type: :string}, {name: "title", type: :string}])
293
+ end
294
+ end
295
+
272
296
  context "when alchemy_resource_relations defined as class method in the model" do
273
297
  let(:resource) { Resource.new("admin/events") }
274
298
 
@@ -567,22 +567,6 @@ module Alchemy
567
567
  end
568
568
  end
569
569
 
570
- describe '.layout_description' do
571
- it "should raise Exception if the page_layout could not be found in the definition file" do
572
- expect { page.layout_description }.to raise_error
573
- end
574
-
575
- context "for a language root page" do
576
- it "should return the page layout description as hash" do
577
- expect(language_root.layout_description['name']).to eq('intro')
578
- end
579
-
580
- it "should return an empty hash for root page" do
581
- expect(rootpage.layout_description).to eq({})
582
- end
583
- end
584
- end
585
-
586
570
  describe '.layoutpages' do
587
571
  it "should return 1 layoutpage" do
588
572
  FactoryGirl.create(:public_page, :layoutpage => true, :name => 'Layoutpage', :parent_id => rootpage.id, :language => language)
@@ -659,16 +643,6 @@ module Alchemy
659
643
  end
660
644
  end
661
645
 
662
- context "for page_layout not existing" do
663
- let(:page) { FactoryGirl.build_stubbed(:page, page_layout: 'not_existing_one') }
664
-
665
- it "should raise error" do
666
- expect {
667
- page.available_element_definitions
668
- }.to raise_error(Alchemy::PageLayoutDefinitionError)
669
- end
670
- end
671
-
672
646
  context 'limited amount' do
673
647
  let(:page) { FactoryGirl.build_stubbed(:page, page_layout: 'columns') }
674
648
  let(:unique_element) { FactoryGirl.build_stubbed(:unique_element, name: 'unique_headline') }
@@ -1026,6 +1000,31 @@ module Alchemy
1026
1000
  end
1027
1001
  end
1028
1002
 
1003
+ describe '#layout_description' do
1004
+ context 'if the page layout could not be found in the definition file' do
1005
+ let(:page) { build_stubbed(:page, page_layout: 'notexisting') }
1006
+
1007
+ it "it loggs a warning." do
1008
+ expect(Alchemy::Logger).to receive(:warn)
1009
+ page.layout_description
1010
+ end
1011
+
1012
+ it "it returns empty hash." do
1013
+ expect(page.layout_description).to eq({})
1014
+ end
1015
+ end
1016
+
1017
+ context "for a language root page" do
1018
+ it "it returns the page layout description as hash." do
1019
+ expect(language_root.layout_description['name']).to eq('index')
1020
+ end
1021
+
1022
+ it "it returns an empty hash for root page." do
1023
+ expect(rootpage.layout_description).to eq({})
1024
+ end
1025
+ end
1026
+ end
1027
+
1029
1028
  describe '#lock_to!' do
1030
1029
  let(:page) { create(:page) }
1031
1030
  let(:user) { mock_model('DummyUser') }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0.beta5
4
+ version: 3.1.0.beta6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2015-01-02 00:00:00.000000000 Z
15
+ date: 2015-01-08 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: actionpack-page_caching
@@ -446,6 +446,7 @@ executables:
446
446
  extensions: []
447
447
  extra_rdoc_files: []
448
448
  files:
449
+ - ".editorconfig"
449
450
  - ".gitignore"
450
451
  - ".rspec"
451
452
  - ".travis.yml"
@@ -842,14 +843,12 @@ files:
842
843
  - app/views/kaminari/alchemy/_paginator.html.erb
843
844
  - app/views/kaminari/alchemy/_prev_page.html.erb
844
845
  - app/views/layouts/alchemy/admin.html.erb
845
- - app/views/layouts/alchemy/login.html.erb
846
846
  - app/views/layouts/alchemy/sitemap.xml.erb
847
847
  - bin/alchemy
848
848
  - bin/rails
849
849
  - config/alchemy/config.yml
850
850
  - config/alchemy/modules.yml
851
851
  - config/initializers/dragonfly.rb
852
- - config/initializers/inflections.rb
853
852
  - config/initializers/simple_form.rb
854
853
  - config/locales/alchemy.de.yml
855
854
  - config/locales/alchemy.en.yml
@@ -1,2 +0,0 @@
1
- <%# This file only exist for cases where one want to override the login view %>
2
- <%= render :template => 'layouts/alchemy/admin' %>
@@ -1,3 +0,0 @@
1
- ActiveSupport::Inflector.inflections(:en) do |inflect|
2
- inflect.acronym 'API'
3
- end