alchemy_cms 2.4.1 → 2.5.0.b2

Sign up to get free protection for your applications and to get access to all the features.
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