alchemy_cms 2.4.1 → 2.5.0.b2

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 (163) hide show
  1. data/.travis.yml +1 -1
  2. data/README.md +18 -17
  3. data/alchemy_cms.gemspec +5 -10
  4. data/app/assets/images/alchemy/icons.png +0 -0
  5. data/app/assets/stylesheets/alchemy/archive.scss +27 -0
  6. data/app/assets/stylesheets/alchemy/base.scss +0 -51
  7. data/app/assets/stylesheets/alchemy/elements.scss +37 -2
  8. data/app/assets/stylesheets/alchemy/icons.scss +4 -0
  9. data/app/assets/stylesheets/alchemy/modules.scss +4 -0
  10. data/app/assets/stylesheets/alchemy/sitemap.scss +1 -1
  11. data/app/assets/stylesheets/alchemy/tables.scss +1 -1
  12. data/app/assets/stylesheets/alchemy/variables.scss +1 -0
  13. data/app/controllers/alchemy/admin/pages_controller.rb +1 -0
  14. data/app/controllers/alchemy/admin/pictures_controller.rb +22 -8
  15. data/app/controllers/alchemy/admin/resources_controller.rb +1 -1
  16. data/app/controllers/alchemy/admin/sites_controller.rb +6 -0
  17. data/app/controllers/alchemy/base_controller.rb +8 -0
  18. data/app/controllers/alchemy/elements_controller.rb +33 -3
  19. data/app/controllers/alchemy/messages_controller.rb +47 -40
  20. data/app/controllers/alchemy/pages_controller.rb +8 -31
  21. data/app/controllers/alchemy/pictures_controller.rb +64 -30
  22. data/app/helpers/alchemy/admin/base_helper.rb +7 -0
  23. data/app/helpers/alchemy/admin/pages_helper.rb +12 -0
  24. data/app/helpers/alchemy/elements_helper.rb +2 -0
  25. data/app/helpers/alchemy/pages_helper.rb +30 -10
  26. data/app/helpers/alchemy/url_helper.rb +1 -0
  27. data/app/models/alchemy/content.rb +1 -2
  28. data/app/models/alchemy/element.rb +47 -2
  29. data/app/models/alchemy/language.rb +27 -14
  30. data/app/models/alchemy/page.rb +1 -1
  31. data/app/models/alchemy/picture.rb +46 -41
  32. data/app/models/alchemy/site.rb +44 -0
  33. data/app/views/alchemy/admin/elements/_element_head.html.erb +1 -0
  34. data/app/views/alchemy/admin/languages/index.html.erb +23 -0
  35. data/app/views/alchemy/admin/pages/edit.html.erb +27 -1
  36. data/app/views/alchemy/admin/pages/fold.js.erb +1 -0
  37. data/app/views/alchemy/admin/partials/_upload_form.html.erb +2 -0
  38. data/app/views/alchemy/admin/pictures/_picture.html.erb +24 -2
  39. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +5 -4
  40. data/app/views/alchemy/admin/pictures/create.js.erb +1 -9
  41. data/app/views/alchemy/admin/pictures/info.html.erb +42 -0
  42. data/app/views/alchemy/admin/resources/_form.html.erb +0 -2
  43. data/app/views/alchemy/admin/resources/_resource.html.erb +2 -1
  44. data/app/views/alchemy/admin/resources/index.html.erb +2 -1
  45. data/app/views/alchemy/elements/show.html.erb +1 -6
  46. data/app/views/alchemy/elements/show.js.erb +4 -10
  47. data/app/views/alchemy/essences/_essence_link_view.html.erb +1 -0
  48. data/app/views/alchemy/search/_form.html.erb +9 -6
  49. data/app/views/alchemy/search/_result.html.erb +1 -1
  50. data/bin/alchemy +13 -120
  51. data/config/alchemy/config.yml +7 -11
  52. data/config/alchemy/modules.yml +24 -12
  53. data/config/authorization_rules.rb +6 -2
  54. data/config/initializers/dragonfly.rb +20 -0
  55. data/config/locales/alchemy.de.yml +57 -28
  56. data/config/locales/alchemy.en.yml +18 -4
  57. data/config/routes.rb +4 -2
  58. data/db/migrate/20121121162313_switch_from_fleximage_to_dragonfly.rb +21 -0
  59. data/db/migrate/20121205155004_create_alchemy_sites.rb +14 -0
  60. data/db/migrate/20121211163003_add_public_to_alchemy_sites.rb +6 -0
  61. data/lib/alchemy/capistrano.rb +7 -2
  62. data/lib/alchemy/ferret_search.rb +84 -0
  63. data/lib/alchemy/picture_attributes.rb +29 -0
  64. data/lib/alchemy/seeder.rb +10 -16
  65. data/lib/alchemy/upgrader.rb +59 -8
  66. data/lib/alchemy/version.rb +1 -1
  67. data/lib/alchemy_cms.rb +7 -4
  68. data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +3 -0
  69. data/lib/rails/generators/alchemy/elements/elements_generator.rb +5 -1
  70. data/lib/rails/generators/alchemy/page_layouts/page_layouts_generator.rb +1 -0
  71. data/lib/rails/generators/alchemy/scaffold/files/{pages.html.erb → application.html.erb} +0 -0
  72. data/lib/rails/generators/alchemy/scaffold/scaffold_generator.rb +11 -20
  73. data/lib/rails/templates/alchemy.rb +1 -7
  74. data/lib/tasks/{database.rake → alchemy/db.rake} +1 -1
  75. data/lib/tasks/{install.rake → alchemy/install.rake} +9 -14
  76. data/lib/tasks/{upgrade.rake → alchemy/upgrade.rake} +1 -1
  77. data/spec/controllers/elements_controller_spec.rb +24 -9
  78. data/spec/controllers/pictures_controller_spec.rb +11 -8
  79. data/{app → spec/dummy/app}/views/alchemy/elements/_article_editor.html.erb +0 -0
  80. data/{app → spec/dummy/app}/views/alchemy/elements/_article_view.html.erb +0 -0
  81. data/{app → spec/dummy/app}/views/alchemy/elements/_headline_view.html.erb +0 -0
  82. data/{app → spec/dummy/app}/views/alchemy/elements/_news_view.html.erb +0 -0
  83. data/{app → spec/dummy/app}/views/alchemy/elements/_searchresult_view.html.erb +0 -0
  84. data/{app → spec/dummy/app}/views/alchemy/page_layouts/_standard.html.erb +0 -0
  85. data/spec/dummy/config/alchemy/elements.yml +86 -0
  86. data/spec/dummy/config/alchemy/page_layouts.yml +26 -0
  87. data/spec/dummy/config/application.rb +1 -1
  88. data/spec/dummy/db/migrate/20121121162313_switch_from_fleximage_to_dragonfly.rb +21 -0
  89. data/spec/dummy/db/migrate/20121205155004_create_alchemy_sites.rb +14 -0
  90. data/spec/dummy/db/migrate/20121211163003_add_public_to_alchemy_sites.rb +6 -0
  91. data/spec/dummy/db/schema.rb +21 -6
  92. data/spec/factories.rb +6 -2
  93. data/spec/integration/translation_integration_spec.rb +4 -18
  94. data/spec/models/element_spec.rb +4 -4
  95. data/spec/models/picture_spec.rb +37 -20
  96. data/spec/models/site_spec.rb +69 -0
  97. data/spec/routing_spec.rb +115 -115
  98. data/spec/spec_helper.rb +1 -3
  99. data/spec/support/alchemy/specs_helpers.rb +4 -4
  100. data/vendor/assets/javascripts/jquery_plugins/jquery.html5uploader.js +1 -1
  101. metadata +72 -96
  102. data/app/assets/stylesheets/alchemy/standard_set.css +0 -440
  103. data/app/views/alchemy/elements/_bild_editor.html.erb +0 -1
  104. data/app/views/alchemy/elements/_bild_text_editor.html.erb +0 -7
  105. data/app/views/alchemy/elements/_bild_text_view.html.erb +0 -9
  106. data/app/views/alchemy/elements/_bild_view.html.erb +0 -9
  107. data/app/views/alchemy/elements/_claim_editor.html.erb +0 -1
  108. data/app/views/alchemy/elements/_claim_view.html.erb +0 -1
  109. data/app/views/alchemy/elements/_contactform_editor.html.erb +0 -4
  110. data/app/views/alchemy/elements/_contactform_view.html.erb +0 -78
  111. data/app/views/alchemy/elements/_download_editor.html.erb +0 -4
  112. data/app/views/alchemy/elements/_download_view.html.erb +0 -7
  113. data/app/views/alchemy/elements/_footnote_editor.html.erb +0 -1
  114. data/app/views/alchemy/elements/_footnote_view.html.erb +0 -5
  115. data/app/views/alchemy/elements/_header_editor.html.erb +0 -1
  116. data/app/views/alchemy/elements/_header_view.html.erb +0 -1
  117. data/app/views/alchemy/elements/_headline_editor.html.erb +0 -1
  118. data/app/views/alchemy/elements/_image_mosaic_editor.html.erb +0 -3
  119. data/app/views/alchemy/elements/_image_mosaic_view.html.erb +0 -14
  120. data/app/views/alchemy/elements/_intro_editor.html.erb +0 -1
  121. data/app/views/alchemy/elements/_intro_image_text_editor.html.erb +0 -3
  122. data/app/views/alchemy/elements/_intro_image_text_view.html.erb +0 -16
  123. data/app/views/alchemy/elements/_intro_view.html.erb +0 -3
  124. data/app/views/alchemy/elements/_news_editor.html.erb +0 -3
  125. data/app/views/alchemy/elements/_searchresult_editor.html.erb +0 -4
  126. data/app/views/alchemy/elements/_sitemap_editor.html.erb +0 -3
  127. data/app/views/alchemy/elements/_sitemap_view.html.erb +0 -38
  128. data/app/views/alchemy/elements/_sitename_editor.html.erb +0 -1
  129. data/app/views/alchemy/elements/_sitename_view.html.erb +0 -1
  130. data/app/views/alchemy/elements/_subheadline_editor.html.erb +0 -1
  131. data/app/views/alchemy/elements/_subheadline_view.html.erb +0 -5
  132. data/app/views/alchemy/elements/_text_editor.html.erb +0 -1
  133. data/app/views/alchemy/elements/_text_view.html.erb +0 -3
  134. data/app/views/alchemy/page_layouts/_contact.html.erb +0 -14
  135. data/app/views/alchemy/page_layouts/_external.html.erb +0 -0
  136. data/app/views/alchemy/page_layouts/_intro.html.erb +0 -14
  137. data/app/views/alchemy/page_layouts/_layout_footer.html.erb +0 -14
  138. data/app/views/alchemy/page_layouts/_layout_header.html.erb +0 -14
  139. data/app/views/alchemy/page_layouts/_news.html.erb +0 -14
  140. data/app/views/alchemy/page_layouts/_newsletter_layout.html.erb +0 -1
  141. data/app/views/alchemy/page_layouts/_search.html.erb +0 -14
  142. data/app/views/alchemy/pictures/show.gif.flexi +0 -19
  143. data/app/views/alchemy/pictures/show.jpg.flexi +0 -19
  144. data/app/views/alchemy/pictures/show.png.flexi +0 -19
  145. data/app/views/alchemy/pictures/thumbnail.png.flexi +0 -13
  146. data/app/views/alchemy/pictures/zoom.jpg.flexi +0 -3
  147. data/app/views/alchemy/pictures/zoom.png.flexi +0 -3
  148. data/app/views/layouts/alchemy/pages.html.erb +0 -51
  149. data/config/alchemy/elements.yml +0 -274
  150. data/config/alchemy/page_layouts.yml +0 -75
  151. data/config/asset_packages.yml +0 -30
  152. data/config/initializers/localeapp.rb +0 -9
  153. data/lib/rails/generators/alchemy/plugin/files/translation.pot +0 -3
  154. data/lib/rails/generators/alchemy/plugin/files/translation_de.po +0 -3
  155. data/lib/rails/generators/alchemy/plugin/files/translation_en.po +0 -3
  156. data/lib/rails/generators/alchemy/plugin/plugin_generator.rb +0 -37
  157. data/lib/rails/generators/alchemy/plugin/templates/authorization_rules.rb +0 -34
  158. data/lib/rails/generators/alchemy/plugin/templates/config.yml +0 -30
  159. data/lib/rails/generators/alchemy/plugin/templates/init.rb +0 -1
  160. data/lib/rails/generators/alchemy/plugin/templates/plugin.rb +0 -0
  161. data/lib/rails/generators/alchemy/plugin/templates/routes.rb +0 -10
  162. data/lib/tasks/fleximage.rake +0 -154
  163. data/spec/dummy/app/views/layouts/.gitkeep +0 -0
@@ -3,8 +3,6 @@ module Alchemy
3
3
 
4
4
  include Alchemy::BaseHelper
5
5
  include Alchemy::ElementsHelper
6
- include Alchemy::ElementsBlockHelper
7
- include Alchemy::UrlHelper
8
6
 
9
7
  def render_classes(classes=[])
10
8
  s = classes.uniq.delete_if { |x| x.blank? }.join(" ")
@@ -445,19 +443,41 @@ module Alchemy
445
443
  javascript_include_tag("alchemy/preview") if @preview_mode
446
444
  end
447
445
 
448
- # Renders the search form
446
+ # Renders a search form
447
+ #
448
+ # It queries the controller and then redirects to the search result page.
449
+ #
450
+ # === Example search results page layout
451
+ #
452
+ # Only performs the search if ferret is enabled in your +config/alchemy/config.yml+ and
453
+ # a page is present that is flagged with +searchresults+ true.
454
+ #
455
+ # # config/alchemy/page_layouts.yml
456
+ # - name: search
457
+ # searchresults: true # Flag as search result page
458
+ #
459
+ # === Note
460
+ #
461
+ # The search result page will not be cached.
462
+ #
463
+ # @option options html5 [Boolean] (true) Should the search form be of type search or not?
464
+ # @option options class [String] (fulltext_search) The default css class of the form
465
+ # @option options id [String] (search) The default css id of the form
466
+ #
449
467
  def render_search_form(options={})
450
468
  default_options = {
451
- :html5 => false
469
+ :html5 => false,
470
+ :class => 'fulltext_search',
471
+ :id => 'search'
452
472
  }
453
- if @search_result_page.blank?
454
- warning("No search result page found")
455
- return
456
- end
457
- render :partial => 'alchemy/search/form', :locals => {:options => default_options.merge(options)}
473
+ render :partial => 'alchemy/search/form', :locals => {:options => default_options.merge(options), :search_result_page => find_search_result_page}
458
474
  end
459
475
 
460
- # Renders the search +results+ partial from +app/views/alchemy/search/+
476
+ # Renders the search results partial within +app/views/alchemy/search/_results.html+
477
+ #
478
+ # @option options show_result_count [Boolean] (true) Should the count of results be displayed or not?
479
+ # @option options show_heading [Boolean] (true) Should the heading be displayed or not?
480
+ #
461
481
  def render_search_results(options={})
462
482
  default_options = {
463
483
  :show_result_count => true,
@@ -76,6 +76,7 @@ module Alchemy
76
76
 
77
77
  # Returns the correct params-hash for passing to show_page_path
78
78
  def show_page_path_params(page, optional_params={})
79
+ raise ArgumentError, 'Page is nil' if page.nil?
79
80
  url_params = {:level1 => nil, :level2 => nil, :level3 => nil, :urlname => page.urlname}
80
81
  url_params.update(optional_params)
81
82
  url_params.update(params_for_nested_url(page)) if configuration(:url_nesting)
@@ -7,8 +7,7 @@ module Alchemy
7
7
  :essence_id,
8
8
  :essence_type,
9
9
  :ingredient,
10
- :name,
11
- :position
10
+ :name
12
11
  )
13
12
 
14
13
  belongs_to :essence, :polymorphic => true, :dependent => :destroy
@@ -1,7 +1,7 @@
1
1
  module Alchemy
2
2
  class Element < ActiveRecord::Base
3
3
 
4
- FORBIDDEN_DEFINITION_ATTRIBUTES = %w(contents available_contents amount picture_gallery taggable)
4
+ FORBIDDEN_DEFINITION_ATTRIBUTES = %w(contents available_contents amount picture_gallery taggable hint)
5
5
  SKIPPED_ATTRIBUTES_ON_COPY = %w(id position folded created_at updated_at creator_id updater_id cached_tag_list)
6
6
 
7
7
  acts_as_taggable
@@ -12,7 +12,6 @@ module Alchemy
12
12
  :folded,
13
13
  :name,
14
14
  :page_id,
15
- :position,
16
15
  :public,
17
16
  :tag_list,
18
17
  :unique
@@ -29,6 +28,7 @@ module Alchemy
29
28
 
30
29
  validates_uniqueness_of :position, :scope => [:page_id, :cell_id], :if => lambda { |e| e.position != nil }
31
30
  validates_presence_of :name, :on => :create
31
+ validates_format_of :name, :on => :create, :with => /\A[a-z0-9_-]+\z/
32
32
 
33
33
  attr_accessor :create_contents_after_create
34
34
 
@@ -37,6 +37,7 @@ module Alchemy
37
37
  scope :trashed, where(:position => nil).order('updated_at DESC')
38
38
  scope :not_trashed, where(Element.arel_table[:position].not_eq(nil))
39
39
  scope :published, where(:public => true)
40
+ scope :not_restricted, joins(:page).where("alchemy_pages" => {:restricted => false})
40
41
  scope :available, published.not_trashed
41
42
  scope :named, lambda { |names| where(:name => names) }
42
43
  scope :excluded, lambda { |names| where(arel_table[:name].not_in(names)) }
@@ -510,6 +511,50 @@ module Alchemy
510
511
  description['taggable'] == true
511
512
  end
512
513
 
514
+ def to_partial_path
515
+ "alchemy/elements/#{name}_view"
516
+ end
517
+
518
+ # Returns the hint for this element
519
+ #
520
+ # To add a hint to an element either pass +hint: true+ to the element definition in its element.yml
521
+ #
522
+ # Then the hint itself is placed in the locale yml files.
523
+ #
524
+ # Alternativly you can pass the hint itself to the hint key.
525
+ #
526
+ # == Locale Example:
527
+ #
528
+ # # elements.yml
529
+ # - name: headline
530
+ # hint: true
531
+ #
532
+ # # config/locales/de.yml
533
+ # de:
534
+ # element_hints:
535
+ # headline: Lorem ipsum
536
+ #
537
+ # == Hint Key Example:
538
+ #
539
+ # - name: headline
540
+ # hint: "Lorem ipsum"
541
+ #
542
+ # @return String
543
+ #
544
+ def hint
545
+ hint = definition['hint']
546
+ if hint == true
547
+ I18n.t(name, scope: :element_hints)
548
+ else
549
+ hint
550
+ end
551
+ end
552
+
553
+ # Returns true if the element has a hint
554
+ def has_hint?
555
+ hint.present?
556
+ end
557
+
513
558
  private
514
559
 
515
560
  # creates the contents for this element as described in the elements.yml
@@ -9,17 +9,19 @@ module Alchemy
9
9
  :public,
10
10
  :default,
11
11
  :country_code,
12
- :code
12
+ :code,
13
+ :site
13
14
  )
14
15
 
15
16
  validates_presence_of :name
16
17
  validates_presence_of :language_code
17
18
  validates_presence_of :page_layout
18
19
  validates_presence_of :frontpage_name
19
- validates_uniqueness_of :language_code, :scope => :country_code
20
+ validates_uniqueness_of :language_code, :scope => [:site_id, :country_code]
20
21
  validate :presence_of_default_language
21
22
  validate :publicity_of_default_language
22
23
  has_many :pages
24
+ belongs_to :site
23
25
  after_destroy :delete_language_root_page
24
26
  validates_format_of :language_code, :with => /^[a-z]{2}$/, :if => proc { language_code.present? }
25
27
  validates_format_of :country_code, :with => /^[a-z]{2}$/, :if => proc { country_code.present? }
@@ -30,18 +32,29 @@ module Alchemy
30
32
 
31
33
  scope :published, where(:public => true)
32
34
 
33
- def self.all_for_created_language_trees
34
- find(Page.language_roots.collect(&:language_id))
35
- end
35
+ # multi-site support
36
+ scope :on_site, lambda { |s| s.present? ? where(site_id: s) : scoped }
37
+ default_scope { on_site(Site.current) }
36
38
 
37
- def self.all_codes_for_published
38
- self.published.collect(&:code)
39
- rescue
40
- []
41
- end
39
+ class << self
40
+
41
+ # Returns all languages for which a language root page exists.
42
+ def all_for_created_language_trees
43
+ # don't use 'find' here as it would clash with our default_scopes
44
+ # in various unholy ways you don't want to find out about.
45
+ where(id: Page.language_roots.collect(&:language_id))
46
+ end
47
+
48
+ def all_codes_for_published
49
+ published.collect(&:code)
50
+ rescue
51
+ []
52
+ end
53
+
54
+ def get_default
55
+ where(default: true).first
56
+ end
42
57
 
43
- def self.get_default
44
- self.find_by_default(true)
45
58
  end
46
59
 
47
60
  def label(attrib)
@@ -54,7 +67,7 @@ module Alchemy
54
67
 
55
68
  include Code
56
69
 
57
- private
70
+ private
58
71
 
59
72
  def publicity_of_default_language
60
73
  if self.default? && !self.public?
@@ -75,7 +88,7 @@ module Alchemy
75
88
  end
76
89
 
77
90
  def remove_old_default
78
- lang = Language.get_default
91
+ lang = Language.on_site(site).get_default
79
92
  return true if lang.nil?
80
93
  lang.default = false
81
94
  lang.save(:validate => false)
@@ -28,7 +28,6 @@ module Alchemy
28
28
  :name,
29
29
  :page_layout,
30
30
  :parent_id,
31
- :position,
32
31
  :public,
33
32
  :restricted,
34
33
  :robot_index,
@@ -52,6 +51,7 @@ module Alchemy
52
51
  has_and_belongs_to_many :to_be_sweeped_elements, :class_name => 'Alchemy::Element', :uniq => true, :join_table => 'alchemy_elements_alchemy_pages'
53
52
  belongs_to :language
54
53
 
54
+ validates_presence_of :language, :on => :create, :unless => :root
55
55
  validates_presence_of :name
56
56
  validates_presence_of :page_layout, :unless => :systempage?
57
57
  validates_presence_of :parent_id, :if => proc { Page.count > 1 }
@@ -6,27 +6,28 @@ module Alchemy
6
6
  has_many :elements, :through => :contents
7
7
  has_many :pages, :through => :elements
8
8
 
9
- acts_as_fleximage do
10
- image_directory 'uploads/pictures'
11
- image_storage_format Config.get(:image_store_format).to_sym
12
- require_image true
13
- missing_image_message I18n.t("missing_image")
14
- invalid_image_message I18n.t("not a valid image")
15
- output_image_jpg_quality Config.get(:output_image_jpg_quality) if Config.get(:image_output_format) == "jpg"
16
- unless Config.get(:preprocess_image_resize).blank?
17
- preprocess_image do |image|
18
- image.resize Config.get(:preprocess_image_resize)
19
- end
9
+ # Raise error, if picture is in use (aka. assigned to an EssencePicture)
10
+ #
11
+ # === CAUTION:
12
+ # This HAS to be placed for Dragonfly's class methods, to ensure this runs before Dragonfly's before_destroy callback.
13
+ #
14
+ before_destroy :unless => :deletable? do
15
+ raise PictureInUseError, I18n.t(:cannot_delete_picture_notice) % { :name => name }
16
+ end
17
+
18
+ image_accessor :image_file do
19
+ if Config.get(:preprocess_image_resize).present?
20
+ after_assign { |a| a.process!(:resize, Config.get(:preprocess_image_resize)) }
20
21
  end
21
22
  end
22
23
 
24
+ validates_presence_of :image_file
25
+ validates_property :format, :of => :image_file, :in => Config.get('uploader')['allowed_filetypes']['pictures'].map(&:to_sym), :message => I18n.t("not a valid image")
26
+
23
27
  acts_as_taggable
24
28
 
25
29
  attr_accessible(
26
30
  :image_file,
27
- :image_filename,
28
- :image_height,
29
- :image_width,
30
31
  :name,
31
32
  :tag_list,
32
33
  :upload_hash
@@ -35,6 +36,7 @@ module Alchemy
35
36
  stampable(:stamper_class_name => 'Alchemy::User')
36
37
 
37
38
  scope :recent, where("#{self.table_name}.created_at > ?", Time.now-24.hours).order(:created_at)
39
+ scope :deletable, where("alchemy_pictures.id NOT IN (SELECT picture_id FROM alchemy_essence_pictures)")
38
40
 
39
41
  def self.find_paginated(params, per_page)
40
42
  Picture.where("name LIKE ?", "%#{params[:query]}%").page(params[:page] || 1).per(per_page).order(:name)
@@ -60,32 +62,31 @@ module Alchemy
60
62
  end
61
63
 
62
64
  def suffix
63
- if image_filename =~ /\./
64
- image_filename.split('.').last.downcase
65
- else
66
- ""
67
- end
65
+ image_file.ext
68
66
  end
69
67
 
70
68
  def humanized_name
71
- return "" if image_filename.blank?
72
- (image_filename.downcase.gsub(/\.#{::Regexp.quote(suffix)}$/, '')).humanize
69
+ return "" if image_file_name.blank?
70
+ (image_file_name.downcase.gsub(/\.#{::Regexp.quote(suffix)}$/, '')).humanize
73
71
  end
74
72
 
75
73
  # Returning true if picture's width is greater than it's height
76
74
  def landscape_format?
77
- return (self.image_width > self.image_height) ? true : false
75
+ image_file.landscape?
78
76
  end
77
+ alias_method :landscape?, :landscape_format?
79
78
 
80
79
  # Returning true if picture's width is smaller than it's height
81
80
  def portrait_format?
82
- return (self.image_width < self.image_height) ? true : false
81
+ image_file.portrait?
83
82
  end
83
+ alias_method :portrait?, :portrait_format?
84
84
 
85
85
  # Returning true if picture's width and height is equal
86
86
  def square_format?
87
- return (self.image_width == self.image_height) ? true : false
87
+ image_file.aspect_ratio == 1.0
88
88
  end
89
+ alias_method :square?, :square_format?
89
90
 
90
91
  # Returns the default centered image mask for a given size
91
92
  def default_mask(size)
@@ -93,24 +94,24 @@ module Alchemy
93
94
  width = size.split('x')[0].to_i
94
95
  height = size.split('x')[1].to_i
95
96
  if (width > height)
96
- zoom_factor = image_width.to_f / width
97
+ zoom_factor = image_file_width.to_f / width
97
98
  mask_height = (height * zoom_factor).round
98
99
  x1 = 0
99
- x2 = image_width
100
- y1 = (image_height - mask_height) / 2
100
+ x2 = image_file_width
101
+ y1 = (image_file_height - mask_height) / 2
101
102
  y2 = y1 + mask_height
102
103
  elsif (width == 0 && height == 0)
103
104
  x1 = 0
104
- x2 = image_width
105
+ x2 = image_file_width
105
106
  y1 = 0
106
- y2 = image_height
107
+ y2 = image_file_height
107
108
  else
108
- zoom_factor = image_height.to_f / height
109
+ zoom_factor = image_file_height.to_f / height
109
110
  mask_width = (width * zoom_factor).round
110
- x1 = (image_width - mask_width) / 2
111
+ x1 = (image_file_width - mask_width) / 2
111
112
  x2 = x1 + mask_width
112
113
  y1 = 0
113
- y2 = image_height
114
+ y2 = image_file_height
114
115
  end
115
116
  {
116
117
  :x1 => x1,
@@ -141,6 +142,14 @@ module Alchemy
141
142
  pages.any? && pages.not_restricted.blank?
142
143
  end
143
144
 
145
+ def deletable?
146
+ !essence_pictures.any?
147
+ end
148
+
149
+ def image_file_dimensions
150
+ "#{image_file_width} x #{image_file_height}"
151
+ end
152
+
144
153
  # Returns a security token for signed picture rendering requests.
145
154
  #
146
155
  # Pass a params hash containing:
@@ -149,21 +158,17 @@ module Alchemy
149
158
  # crop [Boolean] (Optional)
150
159
  # crop_from [String] (Optional)
151
160
  # crop_size [String] (Optional)
161
+ # quality [Integer] (Optional)
152
162
  #
153
163
  # to sign them.
154
164
  #
155
165
  def security_token(params = {})
156
166
  @params = params.stringify_keys
157
- @params.update({'crop' => @params['crop'] ? 'crop' : nil})
158
- Digest::SHA1.hexdigest(secured_params)[0..15]
159
- end
160
-
161
- private
162
-
163
- def secured_params
164
- secret = Rails.configuration.secret_token
165
- [id, @params['size'], @params['crop'], @params['crop_from'], @params['crop_size'], secret].join('-')
167
+ @params.update({'crop' => @params['crop'] ? 'crop' : nil, 'id' => self.id})
168
+ @params.delete_if { |k,v| v.nil? }
169
+ PictureAttributes.secure(@params)
166
170
  end
167
171
 
168
172
  end
173
+ class PictureInUseError < StandardError; end
169
174
  end
@@ -0,0 +1,44 @@
1
+ module Alchemy
2
+ class Site < ActiveRecord::Base
3
+ cattr_accessor :current
4
+
5
+ attr_accessible :host, :name, :public
6
+
7
+ # validations
8
+ validates_presence_of :host
9
+ validates_uniqueness_of :host
10
+
11
+ # associations
12
+ has_many :languages
13
+
14
+ scope :published, where(public: true)
15
+
16
+ # Returns true if this site is the current site
17
+ def current?
18
+ self.class.current == self
19
+ end
20
+
21
+ class << self
22
+ def default
23
+ Site.first
24
+ end
25
+ end
26
+
27
+ before_create do
28
+ # If no languages are present, create a default language based
29
+ # on the host app's Alchemy configuration.
30
+
31
+ if languages.empty?
32
+ default_language = Alchemy::Config.get(:default_language)
33
+ languages.build(
34
+ name: default_language['name'],
35
+ language_code: default_language['code'],
36
+ frontpage_name: default_language['frontpage_name'],
37
+ page_layout: default_language['page_layout'],
38
+ public: true,
39
+ default: true
40
+ )
41
+ end
42
+ end
43
+ end
44
+ end