udongo 5.2.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/backend/articles_controller.rb +57 -0
  3. data/app/forms/backend/article_translation_form.rb +10 -0
  4. data/app/models/article.rb +13 -0
  5. data/app/models/asset.rb +1 -1
  6. data/app/models/user.rb +2 -0
  7. data/app/views/backend/articles/_form.html.erb +33 -0
  8. data/app/views/backend/articles/_tabs.html.erb +23 -0
  9. data/app/views/backend/articles/edit.html.erb +6 -0
  10. data/app/views/backend/articles/edit_translation.html.erb +38 -0
  11. data/app/views/backend/articles/index.html.erb +55 -0
  12. data/app/views/backend/articles/new.html.erb +5 -0
  13. data/app/views/backend/assets/edit.html.erb +1 -1
  14. data/app/views/backend/redirects/index.html.erb +1 -1
  15. data/app/views/layouts/backend/_top_navigation.html.erb +1 -0
  16. data/changelog.md +7 -0
  17. data/config/locales/en_backend.yml +9 -1
  18. data/config/locales/en_forms.yml +4 -0
  19. data/config/locales/nl_backend.yml +9 -1
  20. data/config/locales/nl_forms.yml +3 -0
  21. data/config/routes.rb +5 -0
  22. data/db/migrate/20170305125627_create_articles.rb +17 -0
  23. data/lib/udongo/assets/resizer.rb +81 -0
  24. data/lib/udongo/configs/articles.rb +28 -0
  25. data/lib/udongo/image_manipulation/base.rb +14 -0
  26. data/lib/udongo/image_manipulation/resize_and_pad.rb +38 -0
  27. data/lib/udongo/image_manipulation/resize_to_fill.rb +47 -0
  28. data/lib/udongo/image_manipulation/resize_to_fit.rb +23 -0
  29. data/lib/udongo/image_manipulation/resize_to_limit.rb +24 -0
  30. data/lib/udongo/version.rb +1 -1
  31. data/readme.md +21 -0
  32. data/spec/factories/articles.rb +5 -0
  33. data/spec/support/concerns/publishable.rb +1 -1
  34. metadata +20 -3
  35. data/app/models/asset_image.rb +0 -172
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e6a22b011bc3569827aedbcdd99d353e735fae6e
4
- data.tar.gz: 3202da4e9d86005256c04c4826bc9b0b3eaede7b
3
+ metadata.gz: 9d115e3ef3cee04ca698603e4ee45311fd889657
4
+ data.tar.gz: 2cd47fc1bb20378bf303eeefd266766118cf300c
5
5
  SHA512:
6
- metadata.gz: 0a90d20c8888527092dea454829c2f04cf507f7af964d1d747b3fa10e4473b5dd1753d2051295d7a6ed727d868f5f72a1ce8571f16425b3f1ae1731aa5728792
7
- data.tar.gz: ce3492185bef64ce7b2ccc6786a41089560ac1555a0814ff7ae26c5ba4e92941d15b7f278639652c9b3a9fcc187677d3224a06796c3b8b9a5ec02a7ced5bb802
6
+ metadata.gz: deb6f201c70f981fd121a8cd062b47dd207796db046b0797a50922a1bb39a5856c34ba58fa4fbbbf8ed2a7fc9cb63a2afb91eb3f8b81f5d5a9ad065726266ab7
7
+ data.tar.gz: f9046c6519ed43548a5287de8c7ba8e690df115c34ff2635a592d5f96466885f2d4a7f608dc366f6a1353f26477c11be5e5db49225998c7d07e6f14f623a63ff
@@ -0,0 +1,57 @@
1
+ class Backend::ArticlesController < Backend::BaseController
2
+ include Concerns::Backend::TranslatableController
3
+ include Concerns::PaginationController
4
+
5
+ before_action :find_model, only: [:edit, :update, :destroy]
6
+ before_action -> { breadcrumb.add t('b.articles'), backend_articles_path }
7
+
8
+ def index
9
+ @articles = paginate Article.order('published_at DESC')
10
+ end
11
+
12
+ def new
13
+ @model = Article.new
14
+ end
15
+
16
+ def create
17
+ @model = Article.new(allowed_params)
18
+
19
+ if @model.save
20
+ redirect_to edit_translation_backend_article_path(@model, translation_locale: default_app_locale),
21
+ notice: translate_notice(:added, :article)
22
+ else
23
+ render :new
24
+ end
25
+ end
26
+
27
+ def update
28
+ if @model.update_attributes allowed_params
29
+ redirect_to backend_articles_path, notice: translate_notice(:edited, :article)
30
+ else
31
+ render :edit
32
+ end
33
+ end
34
+
35
+ def destroy
36
+ @model.destroy
37
+ redirect_to backend_articles_path, notice: translate_notice(:deleted, :article)
38
+ end
39
+
40
+ private
41
+
42
+ def find_model
43
+ @model = Article.find(params[:id])
44
+ end
45
+
46
+ def allowed_params
47
+ params[:article].permit(:user_id, :press_release, :published_at, :visible)
48
+ end
49
+
50
+ def translation_form
51
+ Backend::ArticleTranslationForm.new(
52
+ @model,
53
+ @model.translation(params[:translation_locale]),
54
+ @model.seo(params[:translation_locale])
55
+ )
56
+ end
57
+ end
@@ -0,0 +1,10 @@
1
+ class Backend::ArticleTranslationForm < Backend::TranslationWithSeoForm
2
+ attribute :title, String
3
+ attribute :summary, String
4
+
5
+ validates :title, :summary, presence: true
6
+
7
+ def self.model_name
8
+ Article.model_name
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ class Article < ApplicationRecord
2
+ include Concerns::Taggable
3
+ include Concerns::Visible
4
+ include Concerns::Seo
5
+ include Concerns::FlexibleContent
6
+ include Concerns::Searchable
7
+ include Concerns::Publishable
8
+
9
+ include Concerns::Translatable
10
+ translatable_fields :title, :summary
11
+
12
+ belongs_to :user
13
+ end
data/app/models/asset.rb CHANGED
@@ -14,7 +14,7 @@ class Asset < ApplicationRecord
14
14
  end
15
15
 
16
16
  def image
17
- AssetImage.new(self) if image?
17
+ Udongo::Assets::Resizer.new(self) if image?
18
18
  end
19
19
 
20
20
  def actual_filename
data/app/models/user.rb CHANGED
@@ -2,6 +2,8 @@ class User < ActiveRecord::Base
2
2
  include Concerns::Person
3
3
  has_secure_password
4
4
 
5
+ has_many :articles, dependent: :destroy
6
+
5
7
  validates :first_name, :last_name, presence: true
6
8
  validates :email,
7
9
  presence: true,
@@ -0,0 +1,33 @@
1
+ <%= render 'backend/general_form_error', object: @model %>
2
+
3
+ <%= simple_form_for [:backend, @model] do |f| %>
4
+ <div class="row">
5
+ <div class="col-md-8">
6
+ <div class="card">
7
+ <div class="card-header">
8
+ <%= t 'b.general' %>
9
+ </div>
10
+
11
+ <div class="card-block">
12
+ <%= f.input :user_id, collection: User.order(:first_name, :last_name), label_method: :full_name, label: t('b.author') %>
13
+ <%= f.input :published_at, as: :datetime, start_year: 1990, end_year: 2050 %>
14
+ </div>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="col-md-4">
19
+ <div class="card">
20
+ <div class="card-header">
21
+ <%= t 'b.settings' %>
22
+ </div>
23
+
24
+ <div class="card-block">
25
+ <%= f.input :press_release, as: :boolean %>
26
+ <%= f.input :visible, as: :boolean %>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </div>
31
+
32
+ <%= render 'backend/form_actions', cancel_url: backend_articles_path %>
33
+ <% end %>
@@ -0,0 +1,23 @@
1
+ <div class="row subnav">
2
+ <div class="col-md-12">
3
+ <ul class="nav nav-pills">
4
+ <li class="nav-item">
5
+ <% klass = %w(nav-link) %>
6
+ <% klass << 'active' if active == :general %>
7
+
8
+ <%= link_to t('b.general'), edit_backend_article_path(@model), class: klass %>
9
+ </li>
10
+
11
+ <% Udongo.config.i18n.app.locales.each do |locale| %>
12
+ <li class="nav-item">
13
+ <% klass = %w(nav-link) %>
14
+ <% klass << 'active' if active == locale.to_sym %>
15
+
16
+ <%= link_to locale.upcase, edit_translation_backend_article_path(@model, locale), class: klass %>
17
+ </li>
18
+ <% end %>
19
+
20
+ <!-- TODO show the images tab if Udongo.config.articles.images? -->
21
+ </ul>
22
+ </div>
23
+ </div>
@@ -0,0 +1,6 @@
1
+ <% breadcrumb.add @model.title %>
2
+ <% breadcrumb.add t('b.edit') %>
3
+ <%= render 'backend/breadcrumbs' %>
4
+
5
+ <%= render 'tabs', active: :general %>
6
+ <%= render 'form', model: @model %>
@@ -0,0 +1,38 @@
1
+ <% breadcrumb.add @model.title, edit_backend_article_path(@model) %>
2
+ <% breadcrumb.add t('b.edit') %>
3
+ <%= render 'backend/breadcrumbs' %>
4
+
5
+ <%= render 'tabs', active: params[:translation_locale].to_sym %>
6
+ <%= render 'backend/general_form_error', object: @translation %>
7
+
8
+ <%= simple_form_for([:backend, @translation], url: edit_translation_backend_article_path, html: { class: 'no-focus' }) do |f| %>
9
+
10
+ <div class="row">
11
+ <div class="col-md-12">
12
+ <!-- Content -->
13
+ <div class="card">
14
+ <div class="card-header">
15
+ <%= t 'b.content' %>
16
+ </div>
17
+
18
+ <div class="card-block">
19
+ <%= f.input :title %>
20
+
21
+ <% summary_type = Udongo.config.articles.editor_for_summary? ? :ckeditor : :text %>
22
+ <%= f.input :summary, as: summary_type %>
23
+ </div>
24
+ </div>
25
+
26
+ <!-- Flexible Content -->
27
+ <%= render 'backend/content/rows', model: @model, locale: params[:translation_locale] %>
28
+
29
+ <!-- SEO -->
30
+ <%= render 'backend/seo_form', f: f %>
31
+
32
+ <!-- Tags -->
33
+ <%= render 'backend/tags', model: @model %>
34
+ </div>
35
+ </div>
36
+
37
+ <%= render 'backend/form_actions', cancel_url: backend_articles_path %>
38
+ <% end %>
@@ -0,0 +1,55 @@
1
+ <%= render 'backend/breadcrumbs' %>
2
+
3
+ <p class="text-xs-right">
4
+ <%= link_to icon(:plus, t('b.add')), new_backend_article_path, class: 'btn btn-primary btn-sm' %>
5
+ </p>
6
+
7
+ <% if @articles.any? %>
8
+ <table class="table table-striped table-hover">
9
+ <thead class="thead-inverse">
10
+ <tr>
11
+ <th><%= t 'b.title' %></th>
12
+ <th><%= t 'b.author' %></th>
13
+ <th><%= t 'b.published_at' %></th>
14
+ <th><%= t 'b.visible' %></th>
15
+ <th><%= t 'b.press_release' %></th>
16
+ <th>&nbsp;</th>
17
+ </tr>
18
+ </thead>
19
+
20
+ <tbody>
21
+ <% @articles.each do |a| %>
22
+ <tr class="<%= 'text-muted' if a.hidden? || a.unpublished? %>">
23
+ <td>
24
+ <% if a.title.blank? %>
25
+ <%= link_to edit_translation_backend_article_path(a, default_app_locale) do %>
26
+ <%= icon(:exclamation_triangle) %>
27
+ <%= t('b.msg.no_title_set') %>
28
+ <% end %>
29
+ <% else %>
30
+ <%= a.title %>
31
+ <% end %>
32
+ </td>
33
+ <td>
34
+ <% if a.user.present? %>
35
+ <%= link_to icon(:user, a.user.full_name), edit_backend_user_path(a.user) %>
36
+ <% else %>
37
+ <%= t 'b.not_set' %>
38
+ <% end %>
39
+ </td>
40
+ <td><%= l a.published_at if a.published_at %></td>
41
+ <td><%= t a.visible?.to_s %></td>
42
+ <td><%= t a.press_release?.to_s %></td>
43
+ <td class="text-xs-right">
44
+ <%= link_to_edit [:backend, a] %>
45
+ <%= link_to_delete [:backend, a] %>
46
+ </td>
47
+ </tr>
48
+ <% end %>
49
+ </tbody>
50
+ </table>
51
+
52
+ <%= udongo_paginate @articles %>
53
+ <% else %>
54
+ <p><%= t 'b.msg.no_items' %></p>
55
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <% breadcrumb.add t('b.add') %>
2
+ <%= render 'backend/breadcrumbs' %>
3
+
4
+ <%= render 'form' %>
5
+
@@ -1,4 +1,4 @@
1
- <% breadcrumb.add truncate(@model.description, length: 20), edit_backend_snippet_path(@model) %>
1
+ <% breadcrumb.add truncate(@model.description, length: 20), edit_backend_asset_path(@model) %>
2
2
  <% breadcrumb.add t('b.edit') %>
3
3
  <%= render 'backend/breadcrumbs' %>
4
4
 
@@ -13,7 +13,7 @@
13
13
  <th><%= t 'b.source' %></th>
14
14
  <th><%= t 'b.destination' %></th>
15
15
  <th><%= t 'b.status_code' %></th>
16
- <th><%= t 'b.enabled?' %></th>
16
+ <th><%= t 'b.enabled' %></th>
17
17
  <th><%= t 'b.used' %></th>
18
18
  <th>&nbsp;</th>
19
19
  </tr>
@@ -3,6 +3,7 @@
3
3
  <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= icon(:list, t('b.content')) %></a>
4
4
  <div class="dropdown-menu">
5
5
  <%= link_to t('b.pages'), backend_pages_path, class: 'dropdown-item' %>
6
+ <%= link_to t('b.articles'), backend_articles_path, class: 'dropdown-item' %>
6
7
  <%= link_to t('b.snippets'), backend_snippets_path, class: 'dropdown-item' %>
7
8
  <%= link_to t('b.files'), backend_assets_path, class: 'dropdown-item' %>
8
9
  </div>
data/changelog.md CHANGED
@@ -1,3 +1,10 @@
1
+ 5.3.0 - 2017-03-07
2
+ --
3
+ * Refactor the asset image resizer(s).
4
+ * Add a basic articles module.
5
+ * You can configure the article module with some settings. See the readme.
6
+
7
+
1
8
  5.2.0 - 2017-03-02
2
9
  --
3
10
  * Add an assets module to manage all kinds of files.
@@ -6,6 +6,9 @@ en:
6
6
  admin: Admin
7
7
  admins: Admins
8
8
  advanced: Advanced
9
+ article: Article
10
+ articles: Articles
11
+ author: Author
9
12
  cancel: Cancel
10
13
  content: Content
11
14
  current_image: Current image
@@ -21,7 +24,7 @@ en:
21
24
  emails: E-mails
22
25
  email_template: E-mail template
23
26
  email_templates: E-mail templates
24
- enabled?: Enabled?
27
+ enabled: Enabled
25
28
  extra: Extra
26
29
  file: File
27
30
  files: Files
@@ -43,11 +46,14 @@ en:
43
46
  navigation_item: Navigation item
44
47
  new: New
45
48
  none: None
49
+ not_set: Not set
46
50
  not_yet_sent: Not yet sent
47
51
  page: Page
48
52
  pages: Pages
49
53
  password: Password
50
54
  plain_content: Plain content
55
+ press_release: Press release
56
+ published_at: Published at
51
57
  recipient: Recipient
52
58
  redirect: Redirect
53
59
  redirects: Redirects
@@ -77,6 +83,7 @@ en:
77
83
  users: Users
78
84
  variables: Variables
79
85
  view: View
86
+ visible: Visible
80
87
 
81
88
  msg:
82
89
  added: '%s was added.'
@@ -107,6 +114,7 @@ en:
107
114
  navigation:
108
115
  custom: Custom
109
116
  no_items: There are no items.
117
+ no_title_set: This item has not title.
110
118
  pages:
111
119
  invisible: Deze pagina is niet zichtbaar op de website.
112
120
  saved: '%s was saved.'
@@ -13,6 +13,7 @@ en:
13
13
 
14
14
  labels:
15
15
  defaults:
16
+ active: Active
16
17
  bcc: BCC
17
18
  caption: Caption
18
19
  category: Category
@@ -34,6 +35,8 @@ en:
34
35
  password_confirmation: Password confirmation
35
36
  phone: Phone
36
37
  plain_content: Plain content
38
+ press_release: Press release?
39
+ published_at: Published at
37
40
  seo_title: Page title
38
41
  seo_slug: Custom URL
39
42
  seo_description: Description
@@ -44,6 +47,7 @@ en:
44
47
  summary: Summary
45
48
  synonyms: Synonyms
46
49
  title: Title
50
+ user_id: User
47
51
  visible: Visible?
48
52
  width: Width
49
53
  url: URL
@@ -6,6 +6,9 @@ nl:
6
6
  admin: Beheerder
7
7
  admins: Beheerders
8
8
  advanced: Geavanceerd
9
+ article: Artikel
10
+ articles: Artikels
11
+ author: Auteur
9
12
  cancel: Annuleren
10
13
  content: Inhoud
11
14
  current_image: Huidige afbeelding
@@ -21,7 +24,7 @@ nl:
21
24
  emails: E-mails
22
25
  email_template: E-mail template
23
26
  email_templates: E-mail templates
24
- enabled?: Ingeschakeld?
27
+ enabled: Ingeschakeld
25
28
  extra: Extra
26
29
  file: Bestand
27
30
  files: Bestanden
@@ -43,11 +46,14 @@ nl:
43
46
  navigation_item: Navigatie-item
44
47
  new: Nieuw
45
48
  none: Geen
49
+ not_set: Niet ingesteld
46
50
  not_yet_sent: Nog niet verzonden
47
51
  page: Pagina
48
52
  pages: Pagina's
49
53
  password: Wachtwoord
50
54
  plain_content: Tekst inhoud
55
+ press_release: Persbericht
56
+ published_at: Gepubliceerd op
51
57
  recipient: Bestemmeling
52
58
  redirect: Redirect
53
59
  redirects: Redirects
@@ -77,6 +83,7 @@ nl:
77
83
  users: Gebruikers
78
84
  variables: Variabelen
79
85
  view: Bekijk
86
+ visible: Zichtbaar
80
87
 
81
88
  msg:
82
89
  added: '%{actor} werd toegevoegd.'
@@ -107,6 +114,7 @@ nl:
107
114
  navigation:
108
115
  custom: Op maat
109
116
  no_items: Er zijn geen items.
117
+ no_title_set: Dit item heeft nog geen titel.
110
118
  pages:
111
119
  invisible: Deze pagina is niet zichtbaar op de website.
112
120
  saved: '%{actor} werd bewaard.'
@@ -35,6 +35,8 @@ nl:
35
35
  password_confirmation: Wachtwoord bevestiging
36
36
  phone: Telefoonnummer
37
37
  plain_content: Tekstuele inhoud
38
+ press_release: Persbericht?
39
+ published_at: Gepubliceerd op
38
40
  seo_title: Paginatitel
39
41
  seo_slug: Aangepaste URL
40
42
  seo_description: Beschrijving
@@ -45,6 +47,7 @@ nl:
45
47
  summary: Korte inhoud
46
48
  synonyms: Synoniemen
47
49
  title: Titel
50
+ user_id: Gebruiker
48
51
  visible: Zichtbaar?
49
52
  width: Breedte
50
53
  url: URL
data/config/routes.rb CHANGED
@@ -24,11 +24,16 @@ Rails.application.routes.draw do
24
24
 
25
25
  resources :pages, except: [:show] do
26
26
  concerns :translatable
27
+
27
28
  member do
28
29
  post :tree_drag_and_drop, :toggle_visibility
29
30
  end
30
31
  end
31
32
 
33
+ resources :articles, except: [:show] do
34
+ concerns :translatable
35
+ end
36
+
32
37
  resources :navigations, only: [:index] do
33
38
  scope module: 'navigation' do
34
39
  resources :items, except: [:index, :show] do
@@ -0,0 +1,17 @@
1
+ class CreateArticles < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :articles do |t|
4
+ t.references :user
5
+ t.boolean :press_release
6
+ t.datetime :published_at
7
+ t.text :locales
8
+ t.boolean :visible
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :articles, :press_release
14
+ add_index :articles, :visible
15
+ add_index :articles, :published_at
16
+ end
17
+ end
@@ -0,0 +1,81 @@
1
+ require 'fileutils'
2
+
3
+ module Udongo
4
+ module Assets
5
+ class Resizer
6
+ def initialize(asset)
7
+ @asset = asset
8
+ end
9
+
10
+ def filename(width = nil, height = nil, options = {})
11
+ action = options.key?(:action) ? options[:action] : :resize_to_limit
12
+ quality = options[:quality]
13
+ gravity = options[:gravity].to_s.underscore.split('_').map { |s| s[0,1] }.join
14
+ background = options[:background].to_s.parameterize
15
+
16
+ str = action.to_s.split('_').last
17
+ str << "-q#{quality}" if quality.present?
18
+ str << "-g#{gravity}" if gravity.present?
19
+ str << "-b#{background}" if background.present?
20
+ str << "-#{width}x#{height}-#{@asset.actual_filename}"
21
+ end
22
+
23
+ def url(width = nil, height = nil, options = {})
24
+ options[:action] = :resize_to_limit unless options.key?(:action)
25
+
26
+ if width.nil? && height.nil?
27
+ return "/uploads/assets/#{main_dir}/#{second_dir}/#{@asset.actual_filename}"
28
+ end
29
+
30
+ name = filename(width, height, options)
31
+
32
+ unless File.exists?(actual_path(name))
33
+ FileUtils.mkpath(File.dirname(actual_path(name)))
34
+
35
+ unless resize_action_allowed? options[:action]
36
+ raise "No such resize action '#{options[:action].to_s}'. Available are: resize_to_limit, resize_to_fit, resize_to_fill and resize_and_pad."
37
+ end
38
+
39
+ trigger_resize(width, height, options)
40
+ end
41
+
42
+ actual_url(name)
43
+ end
44
+
45
+ def actual_url(calculated_filename)
46
+ "/uploads/assets/_cache/#{main_dir}/#{second_dir}/#{calculated_filename}"
47
+ end
48
+
49
+ def path(width = nil, height = nil, options = {})
50
+ url(width, height, options) # Trigger the actual resize (if needed)
51
+ actual_path(filename(width, height, options))
52
+ end
53
+
54
+ def actual_path(calculated_filename)
55
+ "#{Rails.root}/public/uploads/assets/_cache/#{main_dir}/#{second_dir}/#{calculated_filename}"
56
+ end
57
+
58
+ private
59
+
60
+ def trigger_resize(width, height, options = {})
61
+ "Udongo::ImageManipulation::#{options[:action].to_s.camelcase}".constantize.new(
62
+ @asset.filename.path, width, height, options
63
+ ).resize(
64
+ actual_path(filename(width, height, options))
65
+ )
66
+ end
67
+
68
+ def resize_action_allowed?(action)
69
+ %w(resize_to_limit resize_to_fit resize_to_fill resize_and_pad).include?(action.to_s)
70
+ end
71
+
72
+ def main_dir
73
+ @main_dir ||= Digest::MD5.hexdigest(@asset.id.to_s)[0,2]
74
+ end
75
+
76
+ def second_dir
77
+ @second_dir ||= Digest::MD5.hexdigest(@asset.id.to_s)[2,2]
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,28 @@
1
+ module Udongo
2
+ module Configs
3
+ class Articles
4
+ include Virtus.model
5
+
6
+ attribute :allow_html_in_title, Axiom::Types::Boolean, default: false
7
+ attribute :allow_html_in_summary, Axiom::Types::Boolean, default: false
8
+ attribute :editor_for_summary, Axiom::Types::Boolean, default: false
9
+ attribute :images, Axiom::Types::Boolean, default: false
10
+
11
+ def allow_html_in_title?
12
+ allow_html_in_title === true
13
+ end
14
+
15
+ def allow_html_in_summary?
16
+ allow_html_in_summary === true
17
+ end
18
+
19
+ def editor_for_summary?
20
+ editor_for_summary === true
21
+ end
22
+
23
+ def images?
24
+ images === true
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ require 'mini_magick'
2
+
3
+ module Udongo
4
+ module ImageManipulation
5
+ module Base
6
+ def initialize(file, width, height, options = {})
7
+ @file = file
8
+ @width = width
9
+ @height = height
10
+ @options = options
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ require 'mini_magick'
2
+
3
+ module Udongo
4
+ module ImageManipulation
5
+ class ResizeAndPad
6
+ include Base
7
+
8
+ # Resize the image to fit within the specified dimensions while retaining
9
+ # the original aspect ratio. If necessary, will pad the remaining area
10
+ # with the given color, which defaults to transparent (for gif and png,
11
+ # white for jpeg).
12
+ #
13
+ # Possible values for options[:gravity] are:
14
+ # NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
15
+ #
16
+ def resize(path)
17
+ gravity = @options.key?(:gravity) ? @options[:gravity] : 'Center'
18
+ background = @options.key?(:background) ? @options[:background] : :transparant
19
+
20
+ img = MiniMagick::Image.open(@file)
21
+ img.combine_options do |cmd|
22
+ cmd.thumbnail "#{@width}x#{@height}>"
23
+
24
+ if background.to_sym == :transparent
25
+ cmd.background 'rgba(255, 255, 255, 0.0)'
26
+ else
27
+ cmd.background background
28
+ end
29
+
30
+ cmd.gravity gravity
31
+ cmd.extent "#{@width}x#{@height}"
32
+ end
33
+
34
+ img.write(path)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ require 'mini_magick'
2
+
3
+ module Udongo
4
+ module ImageManipulation
5
+ class ResizeToFill
6
+ include Base
7
+
8
+ # Resize the image to fit within the specified dimensions while retaining
9
+ # the aspect ratio of the original image. If necessary, crop the image in the
10
+ # larger dimension.
11
+ #
12
+ # Possible values for options[:gravity] are:
13
+ # NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
14
+ #
15
+ def resize(path)
16
+ gravity = @options.key?(:gravity) ? @options[:gravity] : 'Center'
17
+
18
+ img = MiniMagick::Image.open(@file)
19
+ cols, rows = img[:dimensions]
20
+
21
+ img.combine_options do |cmd|
22
+ if @width != cols || @height != rows
23
+ scale_x = @width/cols.to_f
24
+ scale_y = @height/rows.to_f
25
+
26
+ if scale_x >= scale_y
27
+ cols = (scale_x * (cols + 0.5)).round
28
+ rows = (scale_x * (rows + 0.5)).round
29
+ cmd.resize "#{cols}"
30
+ else
31
+ cols = (scale_y * (cols + 0.5)).round
32
+ rows = (scale_y * (rows + 0.5)).round
33
+ cmd.resize "x#{rows}"
34
+ end
35
+ end
36
+
37
+ cmd.quality @options[:quality] if @options.key?(:quality)
38
+ cmd.gravity gravity
39
+ cmd.background 'rgba(255,255,255,0.0)'
40
+ cmd.extent "#{@width}x#{@height}" if cols != @width || rows != @height
41
+ end
42
+
43
+ img.write(path)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,23 @@
1
+ require 'mini_magick'
2
+
3
+ module Udongo
4
+ module ImageManipulation
5
+ class ResizeToFit
6
+ include Base
7
+
8
+ # Resize the image to fit within the specified dimensions while retaining
9
+ # the original aspect ratio. The image may be shorter or narrower than
10
+ # specified in the smaller dimension but will not be larger than the specified values.
11
+ #
12
+ def resize(path)
13
+ img = MiniMagick::Image.open(@file)
14
+ img.combine_options do |c|
15
+ c.quality @options[:quality] if @options[:quality]
16
+ c.resize "#{@width}x#{@height}"
17
+ end
18
+
19
+ img.write(path)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ require 'mini_magick'
2
+
3
+ module Udongo
4
+ module ImageManipulation
5
+ class ResizeToLimit
6
+ include Base
7
+
8
+ # Resize the image to fit within the specified dimensions while retaining
9
+ # the original aspect ratio. Will only resize the image if it is larger than the
10
+ # specified dimensions. The resulting image may be shorter or narrower than specified
11
+ # in the smaller dimension but will not be larger than the specified values.
12
+ #
13
+ def resize(path)
14
+ img = MiniMagick::Image.open(@file)
15
+ img.combine_options do |c|
16
+ c.quality @options[:quality] if @options[:quality]
17
+ c.resize "#{@width}x#{@height}>"
18
+ end
19
+
20
+ img.write(path)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module Udongo
2
- VERSION = '5.2.0'
2
+ VERSION = '5.3.0'
3
3
  end
data/readme.md CHANGED
@@ -72,6 +72,27 @@ Udongo.config.image_white_list = %w(gif jpeg jpg png)
72
72
  Udongo.config.file_white_list = %w(doc docx pdf txt xls xlsx)
73
73
  ```
74
74
 
75
+ ## Articles
76
+ ### allow_html_in_title
77
+ ```ruby
78
+ Udongo.config.articles.allow_html_in_title = false
79
+ ```
80
+
81
+ ### allow_html_in_summary
82
+ ```ruby
83
+ Udongo.config.articles.allow_html_in_summary = false
84
+ ```
85
+
86
+ ### editor_for_summary
87
+ ```ruby
88
+ Udongo.config.articles.editor_for_summary = false
89
+ ```
90
+
91
+ ### images
92
+ ```ruby
93
+ Udongo.config.articles.images = false
94
+ ```
95
+
75
96
  # Concerns
76
97
  ## Storable concern
77
98
  ### Possible field types
@@ -0,0 +1,5 @@
1
+ FactoryGirl.define do
2
+ factory :article do
3
+ user
4
+ end
5
+ end
@@ -28,7 +28,7 @@ shared_examples_for :publishable do
28
28
  it '#unpublish!' do
29
29
  instance.published_at = Time.zone.now
30
30
  instance.unpublish!
31
- expect(instance).not_to be_unpublished
31
+ expect(instance).to be_unpublished
32
32
  end
33
33
 
34
34
  it '.respond_to?' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: udongo
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Davy Hellemans
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-03-02 00:00:00.000000000 Z
12
+ date: 2017-03-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -451,6 +451,7 @@ files:
451
451
  - app/assets/stylesheets/backend/pages/_login.scss
452
452
  - app/assets/stylesheets/backend/udongo.scss
453
453
  - app/controllers/backend/admins_controller.rb
454
+ - app/controllers/backend/articles_controller.rb
454
455
  - app/controllers/backend/assets_controller.rb
455
456
  - app/controllers/backend/base_controller.rb
456
457
  - app/controllers/backend/content/rows/columns_controller.rb
@@ -487,6 +488,7 @@ files:
487
488
  - app/decorators/pagination_decorator.rb
488
489
  - app/decorators/redirect_decorator.rb
489
490
  - app/decorators/snippet_decorator.rb
491
+ - app/forms/backend/article_translation_form.rb
490
492
  - app/forms/backend/email_template_translation_form.rb
491
493
  - app/forms/backend/navigation_item_translation_form.rb
492
494
  - app/forms/backend/page_translation_form.rb
@@ -505,8 +507,8 @@ files:
505
507
  - app/models/address.rb
506
508
  - app/models/admin.rb
507
509
  - app/models/application_record.rb
510
+ - app/models/article.rb
508
511
  - app/models/asset.rb
509
- - app/models/asset_image.rb
510
512
  - app/models/ckeditor/asset.rb
511
513
  - app/models/ckeditor/attachment_file.rb
512
514
  - app/models/ckeditor/picture.rb
@@ -579,6 +581,12 @@ files:
579
581
  - app/views/backend/admins/edit.html.erb
580
582
  - app/views/backend/admins/index.html.erb
581
583
  - app/views/backend/admins/new.html.erb
584
+ - app/views/backend/articles/_form.html.erb
585
+ - app/views/backend/articles/_tabs.html.erb
586
+ - app/views/backend/articles/edit.html.erb
587
+ - app/views/backend/articles/edit_translation.html.erb
588
+ - app/views/backend/articles/index.html.erb
589
+ - app/views/backend/articles/new.html.erb
582
590
  - app/views/backend/assets/_filter.html.erb
583
591
  - app/views/backend/assets/_form.html.erb
584
592
  - app/views/backend/assets/edit.html.erb
@@ -735,14 +743,17 @@ files:
735
743
  - db/migrate/20170215132531_add_active_to_users.rb
736
744
  - db/migrate/20170225144523_create_assets.rb
737
745
  - db/migrate/20170228183831_create_images.rb
746
+ - db/migrate/20170305125627_create_articles.rb
738
747
  - lib/tasks/task_extras.rb
739
748
  - lib/tasks/udongo_tasks.rake
740
749
  - lib/udongo.rb
741
750
  - lib/udongo/active_model_simulator.rb
742
751
  - lib/udongo/assets/loader.rb
743
752
  - lib/udongo/assets/precompiler.rb
753
+ - lib/udongo/assets/resizer.rb
744
754
  - lib/udongo/breadcrumb.rb
745
755
  - lib/udongo/config.rb
756
+ - lib/udongo/configs/articles.rb
746
757
  - lib/udongo/configs/assets.rb
747
758
  - lib/udongo/configs/base.rb
748
759
  - lib/udongo/configs/flexible_content.rb
@@ -757,6 +768,11 @@ files:
757
768
  - lib/udongo/engine.rb
758
769
  - lib/udongo/flexible_content/column_width_calculator.rb
759
770
  - lib/udongo/form.rb
771
+ - lib/udongo/image_manipulation/base.rb
772
+ - lib/udongo/image_manipulation/resize_and_pad.rb
773
+ - lib/udongo/image_manipulation/resize_to_fill.rb
774
+ - lib/udongo/image_manipulation/resize_to_fit.rb
775
+ - lib/udongo/image_manipulation/resize_to_limit.rb
760
776
  - lib/udongo/meta_info.rb
761
777
  - lib/udongo/notification.rb
762
778
  - lib/udongo/object_path.rb
@@ -775,6 +791,7 @@ files:
775
791
  - readme.md
776
792
  - spec/factories/addresses.rb
777
793
  - spec/factories/admins.rb
794
+ - spec/factories/articles.rb
778
795
  - spec/factories/assets.rb
779
796
  - spec/factories/comments.rb
780
797
  - spec/factories/content_columns.rb
@@ -1,172 +0,0 @@
1
- require 'mini_magick'
2
- require 'fileutils'
3
-
4
- class AssetImage
5
- def initialize(asset)
6
- @asset = asset
7
- end
8
-
9
- def filename(width = nil, height = nil, options = {})
10
- options[:action] = :resize_to_limit unless options.key?(:action)
11
-
12
- str = options[:action].to_s.split('_').last
13
-
14
- if options[:quality]
15
- str << '-q' + options[:quality].to_s
16
- end
17
-
18
- if options[:gravity]
19
- str << '-g' + options[:gravity].to_s.underscore.split('_').map { |s| s[0,1] }.join
20
- end
21
-
22
- if options[:background]
23
- str << '-b' + options[:background].to_s.parameterize
24
- end
25
-
26
- str << "-#{width}x#{height}-#{@asset.actual_filename}"
27
- end
28
-
29
- def url(width = nil, height = nil, options = {})
30
- options[:action] = :resize_to_limit unless options.key?(:action)
31
-
32
- if width.nil? && height.nil?
33
- return "/uploads/assets/#{main_dir}/#{second_dir}/#{@asset.actual_filename}"
34
- end
35
-
36
- name = filename(width, height, options)
37
-
38
- unless File.exists?(actual_path(name))
39
- FileUtils.mkpath(File.dirname(actual_path(name)))
40
-
41
- case options[:action].to_sym
42
- when :resize_to_limit then resize_to_limit(width, height, options)
43
- when :resize_to_fit then resize_to_fit(width, height, options)
44
- when :resize_to_fill then resize_to_fill(width, height, options)
45
- when :resize_and_pad then resize_and_pad(width, height, options)
46
- else
47
- raise "No such resize action '#{options[:action].to_s}'. Available are: resize_to_limit, resize_to_fit, resize_to_fill and resize_and_pad."
48
- end
49
- end
50
-
51
- actual_url(name)
52
- end
53
-
54
- def actual_url(calculated_filename)
55
- "/uploads/assets/_cache/#{main_dir}/#{second_dir}/#{calculated_filename}"
56
- end
57
-
58
- def path(width = nil, height = nil, options = {})
59
- url(width, height, options) # Trigger the actual resize
60
- actual_path(filename(width, height, options))
61
- end
62
-
63
- def actual_path(calculated_filename)
64
- "#{Rails.root}/public/uploads/assets/_cache/#{main_dir}/#{second_dir}/#{calculated_filename}"
65
- end
66
-
67
- private
68
-
69
- # Resize the image to fit within the specified dimensions while retaining
70
- # the original aspect ratio. Will only resize the image if it is larger than the
71
- # specified dimensions. The resulting image may be shorter or narrower than specified
72
- # in the smaller dimension but will not be larger than the specified values.
73
- #
74
- def resize_to_limit(width, height, options = {})
75
- img = MiniMagick::Image.open(@asset.filename.path)
76
- img.combine_options do |c|
77
- c.quality options[:quality] if options[:quality]
78
- c.resize "#{width}x#{height}>"
79
- end
80
-
81
- img.write(actual_path(filename(width, height, options)))
82
- end
83
-
84
- # Resize the image to fit within the specified dimensions while retaining
85
- # the original aspect ratio. The image may be shorter or narrower than
86
- # specified in the smaller dimension but will not be larger than the specified values.
87
- #
88
- def resize_to_fit(width, height, options = {})
89
- img = MiniMagick::Image.open(@asset.filename.path)
90
- img.combine_options do |c|
91
- c.quality options[:quality] if options[:quality]
92
- c.resize "#{width}x#{height}"
93
- end
94
-
95
- img.write(actual_path(filename(width, height, options)))
96
- end
97
-
98
- # Resize the image to fit within the specified dimensions while retaining
99
- # the aspect ratio of the original image. If necessary, crop the image in the
100
- # larger dimension.
101
- #
102
- # Possible values for options[:gravity] are:
103
- # NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
104
- #
105
- def resize_to_fill(width, height, options = {})
106
- gravity = options.key?(:gravity) ? options[:gravity] : 'Center'
107
-
108
- img = MiniMagick::Image.open(@asset.filename.path)
109
- cols, rows = img[:dimensions]
110
-
111
- img.combine_options do |cmd|
112
- if width != cols || height != rows
113
- scale_x = width/cols.to_f
114
- scale_y = height/rows.to_f
115
-
116
- if scale_x >= scale_y
117
- cols = (scale_x * (cols + 0.5)).round
118
- rows = (scale_x * (rows + 0.5)).round
119
- cmd.resize "#{cols}"
120
- else
121
- cols = (scale_y * (cols + 0.5)).round
122
- rows = (scale_y * (rows + 0.5)).round
123
- cmd.resize "x#{rows}"
124
- end
125
- end
126
-
127
- cmd.quality options[:quality] if options.key?(:quality)
128
- cmd.gravity gravity
129
- cmd.background 'rgba(255,255,255,0.0)'
130
- cmd.extent "#{width}x#{height}" if cols != width || rows != height
131
- end
132
-
133
- img.write(actual_path(filename(width, height, options)))
134
- end
135
-
136
- # Resize the image to fit within the specified dimensions while retaining
137
- # the original aspect ratio. If necessary, will pad the remaining area
138
- # with the given color, which defaults to transparent (for gif and png,
139
- # white for jpeg).
140
- #
141
- # Possible values for options[:gravity] are:
142
- # NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
143
- #
144
- def resize_and_pad(width, height, options = {})
145
- gravity = options.key?(:gravity) ? options[:gravity] : 'Center'
146
- background = options.key?(:background) ? options[:background] : :transparant
147
-
148
- img = MiniMagick::Image.open(@asset.filename.path)
149
- img.combine_options do |cmd|
150
- cmd.thumbnail "#{width}x#{height}>"
151
-
152
- if background.to_sym == :transparent
153
- cmd.background 'rgba(255, 255, 255, 0.0)'
154
- else
155
- cmd.background background
156
- end
157
-
158
- cmd.gravity gravity
159
- cmd.extent "#{width}x#{height}"
160
- end
161
-
162
- img.write(actual_path(filename(width, height, options)))
163
- end
164
-
165
- def main_dir
166
- @main_dir ||= Digest::MD5.hexdigest(@asset.id.to_s)[0,2]
167
- end
168
-
169
- def second_dir
170
- @second_dir ||= Digest::MD5.hexdigest(@asset.id.to_s)[2,2]
171
- end
172
- end