diff --git a/Gemfile b/Gemfile index f9c2f54e5..39dee65f8 100644 --- a/Gemfile +++ b/Gemfile @@ -65,6 +65,8 @@ gem 'browser' gem 'turnout', '~> 2.4.0' gem 'redcarpet', '~> 3.4.0' +gem 'paperclip' + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug' diff --git a/Gemfile.lock b/Gemfile.lock index 6c117ea77..67ab5a78b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -238,6 +238,7 @@ GEM mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) + mimemagic (0.3.2) mini_portile2 (2.1.0) minitest (5.10.1) multi_json (1.12.1) @@ -277,6 +278,12 @@ GEM omniauth-oauth (~> 1.1) rack orm_adapter (0.5.0) + paperclip (5.1.0) + activemodel (>= 4.2.0) + activesupport (>= 4.2.0) + cocaine (~> 0.5.5) + mime-types + mimemagic (~> 0.3.0) paranoia (2.2.1) activerecord (>= 4.0, < 5.1) parser (2.4.0.0) @@ -504,6 +511,7 @@ DEPENDENCIES omniauth-facebook (~> 4.0.0) omniauth-google-oauth2 (~> 0.4.0) omniauth-twitter + paperclip paranoia (~> 2.2.1) pg (~> 0.20.0) pg_search @@ -533,4 +541,4 @@ DEPENDENCIES whenever BUNDLED WITH - 1.13.7 + 1.14.6 diff --git a/app/controllers/admin/site_customization/images_controller.rb b/app/controllers/admin/site_customization/images_controller.rb new file mode 100644 index 000000000..c9f318f41 --- /dev/null +++ b/app/controllers/admin/site_customization/images_controller.rb @@ -0,0 +1,43 @@ +class Admin::SiteCustomization::ImagesController < Admin::SiteCustomization::BaseController + load_and_authorize_resource :image, class: "SiteCustomization::Image" + + def index + @images = SiteCustomization::Image.all_images + end + + def update + if params[:site_customization_image].nil? + redirect_to admin_site_customization_images_path + return + end + + if @image.update(image_params) + redirect_to admin_site_customization_images_path, notice: t('admin.site_customization.images.update.notice') + else + flash.now[:error] = t('admin.site_customization.images.update.error') + + @images = SiteCustomization::Image.all_images + idx = @images.index {|e| e.name == @image.name } + @images[idx] = @image + + render :index + end + end + + def destroy + @image.image = nil + if @image.save + redirect_to admin_site_customization_images_path, notice: t('admin.site_customization.images.destroy.notice') + else + redirect_to admin_site_customization_images_path, notice: t('admin.site_customization.images.destroy.error') + end + end + + private + + def image_params + params.require(:site_customization_image).permit( + :image + ) + end +end diff --git a/app/controllers/admin/site_customization/pages_controller.rb b/app/controllers/admin/site_customization/pages_controller.rb index 0b838054e..4d92a6a1e 100644 --- a/app/controllers/admin/site_customization/pages_controller.rb +++ b/app/controllers/admin/site_customization/pages_controller.rb @@ -7,7 +7,7 @@ class Admin::SiteCustomization::PagesController < Admin::SiteCustomization::Base def create if @page.save - redirect_to admin_site_customization_pages_path, notice: t('admin.site_customization.pages.create.notice', link: @page.slug.html_safe) + redirect_to admin_site_customization_pages_path, notice: t('admin.site_customization.pages.create.notice') else flash.now[:error] = t('admin.site_customization.pages.create.error') render :new @@ -16,7 +16,7 @@ class Admin::SiteCustomization::PagesController < Admin::SiteCustomization::Base def update if @page.update(page_params) - redirect_to admin_site_customization_pages_path, notice: t('admin.site_customization.pages.update.notice', link: @page.slug.html_safe) + redirect_to admin_site_customization_pages_path, notice: t('admin.site_customization.pages.update.notice') else flash.now[:error] = t('admin.site_customization.pages.update.error') render :edit diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 208ed3d3d..c449fa9f4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -47,4 +47,8 @@ module ApplicationHelper "".html_safe + t("shared.back") end end + + def image_path_for(filename) + SiteCustomization::Image.image_path_for(filename) || filename + end end diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index 8df9270a3..c61530772 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -53,6 +53,7 @@ module Abilities can [:index, :create, :edit, :update, :destroy], Geozone can :manage, SiteCustomization::Page + can :manage, SiteCustomization::Image end end end diff --git a/app/models/site_customization/image.rb b/app/models/site_customization/image.rb new file mode 100644 index 000000000..2230a96ce --- /dev/null +++ b/app/models/site_customization/image.rb @@ -0,0 +1,48 @@ +class SiteCustomization::Image < ActiveRecord::Base + VALID_IMAGES = { + "icon_home" => [330, 240], + "logo_header" => [80, 80], + "social-media-icon" => [200, 200], + "apple-touch-icon-200" => [200, 200] + } + + has_attached_file :image + + validates :name, presence: true, uniqueness: true, inclusion: { in: VALID_IMAGES.keys } + validates_attachment_content_type :image, :content_type => ["image/png"] + validate :check_image + + def self.all_images + VALID_IMAGES.keys.map do |image_name| + find_by(name: image_name) || create!(name: image_name.to_s) + end + end + + def self.image_path_for(filename) + image_name = filename.split(".").first + + if i = find_by(name: image_name) + i.image.exists? ? i.image.url : nil + end + end + + def required_width + VALID_IMAGES[name].try(:first) + end + + def required_height + VALID_IMAGES[name].try(:second) + end + + private + + def check_image + return unless image? + + dimensions = Paperclip::Geometry.from_file(image.queued_for_write[:original].path) + + errors.add(:image, :image_width, required_width: required_width) unless dimensions.width == required_width + errors.add(:image, :image_height, required_height: required_height) unless dimensions.height == required_height + end + +end diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index 448da1662..6a48b493b 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -128,5 +128,11 @@ <%= t("admin.menu.site_customization.pages") %> <% end %> + +
  • > + <%= link_to admin_site_customization_images_path do %> + <%= t("admin.menu.site_customization.images") %> + <% end %> +
  • diff --git a/app/views/admin/site_customization/images/index.html.erb b/app/views/admin/site_customization/images/index.html.erb new file mode 100644 index 000000000..b9de152d1 --- /dev/null +++ b/app/views/admin/site_customization/images/index.html.erb @@ -0,0 +1,25 @@ +

    <%= t("admin.site_customization.images.index.title") %>

    + + + + <% @images.each do |image| %> + + + + + <% end %> + +
    + <%= image.name %> (<%= image.required_width %>x<%= image.required_height %>) + + <%= form_for([:admin, image], html: { id: "edit_#{dom_id(image)}"}) do |f| %> +
    + <%= image_tag image.image.url if image.image.exists? %> + <%= f.file_field :image, label: false %> +
    +
    + <%= f.submit(t('admin.site_customization.images.index.update'), class: "button hollow") %> + <%= link_to t('admin.site_customization.images.index.delete'), admin_site_customization_image_path(image), method: :delete, class: "button hollow alert" if image.image.exists? %> +
    + <% end %> +
    diff --git a/app/views/layouts/_admin_header.html.erb b/app/views/layouts/_admin_header.html.erb index 095e351d6..72617386c 100644 --- a/app/views/layouts/_admin_header.html.erb +++ b/app/views/layouts/_admin_header.html.erb @@ -18,7 +18,7 @@
    <%= link_to admin_root_path, class: "hide-for-small-only" do %> - <%= image_tag('logo_header.png', class: 'hide-for-small-only float-left', size: '80x80', alt: t("layouts.header.logo")) %> + <%= image_tag(image_path_for('logo_header.png'), class: 'hide-for-small-only float-left', size: '80x80', alt: t("layouts.header.logo")) %> <%= setting['org_name'] %>  | <%= t("admin.dashboard.index.title") %> <% end %> @@ -34,4 +34,4 @@
    - \ No newline at end of file + diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 9a33662c6..a3162bb8c 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -22,7 +22,7 @@
    <%= link_to root_path, class: "hide-for-small-only", accesskey: "0" do %> - <%= image_tag('logo_header.png', class: 'hide-for-small-only float-left', size: '80x80', alt: t("layouts.header.logo")) %> + <%= image_tag(image_path_for('logo_header.png'), class: 'hide-for-small-only float-left', size: '80x80', alt: t("layouts.header.logo")) %> <%= setting['org_name'] %> <% end %>
    diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index ef4455320..7ae505aad 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -14,7 +14,7 @@ <%= javascript_include_tag "application", 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> <%= favicon_link_tag "favicon.ico" %> - <%= favicon_link_tag "apple-touch-icon-200.png", + <%= favicon_link_tag image_path_for("apple-touch-icon-200.png"), rel: "icon apple-touch-icon", sizes: "200x200", type: "image/png" %> diff --git a/app/views/layouts/devise.html.erb b/app/views/layouts/devise.html.erb index a0e9eaaff..50b84287d 100644 --- a/app/views/layouts/devise.html.erb +++ b/app/views/layouts/devise.html.erb @@ -17,7 +17,7 @@

    <%= link_to root_path do %> - <%= image_tag('logo_header.png', class: 'float-left', alt: t("layouts.header.logo")) %> + <%= image_tag(image_path_for('logo_header.png'), class: 'float-left', alt: t("layouts.header.logo")) %> <%= setting['org_name'] %> <% end %>

    diff --git a/app/views/layouts/management.html.erb b/app/views/layouts/management.html.erb index 8e497bf24..21367d8ab 100644 --- a/app/views/layouts/management.html.erb +++ b/app/views/layouts/management.html.erb @@ -35,7 +35,7 @@
    <%= link_to management_root_path, class: "hide-for-small-only" do %> - <%= image_tag('logo_header.png', class: 'hide-for-small-only float-left', size: '80x80', alt: t("layouts.header.logo")) %> + <%= image_tag(image_path_for('logo_header.png'), class: 'hide-for-small-only float-left', size: '80x80', alt: t("layouts.header.logo")) %> <%= setting['org_name'] %>  | <%= t("management.dashboard.index.title") %> <% end %> diff --git a/app/views/shared/_social_media_meta_tags.html.erb b/app/views/shared/_social_media_meta_tags.html.erb index 50a2efec9..c0e6a5e8a 100644 --- a/app/views/shared/_social_media_meta_tags.html.erb +++ b/app/views/shared/_social_media_meta_tags.html.erb @@ -3,7 +3,7 @@ - + <% if setting['url'] %> @@ -14,7 +14,7 @@ <% end %> - + diff --git a/app/views/welcome/index.html.erb b/app/views/welcome/index.html.erb index 32124a950..1eb6cf38c 100644 --- a/app/views/welcome/index.html.erb +++ b/app/views/welcome/index.html.erb @@ -12,7 +12,7 @@
    - <%= image_tag("icon_home.png", size: "330x240", alt:"") %> + <%= image_tag(image_path_for("icon_home.png"), size: "330x240", alt:"") %>
    diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index e8354b88e..bd18422ae 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -43,6 +43,9 @@ en: site_customization/page: one: Custom page other: Custom pages + site_customization/image: + one: Custom image + other: Custom images attributes: budget: name: "Name" @@ -151,3 +154,8 @@ en: attributes: slug: slug_format: "must be letters, numbers, _ and -" + site_customization/image: + attributes: + image: + image_width: "Width must be %{required_width}px" + image_height: "Height must be %{required_height}px" diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index 32b26223b..f3397ef19 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -43,6 +43,9 @@ es: site_customization/page: one: Página other: Páginas + site_customization/image: + one: Imagen + other: Imágenes attributes: budget: name: "Nombre" @@ -146,3 +149,8 @@ es: attributes: slug: slug_format: "deber ser letras, números, _ y -" + site_customization/image: + attributes: + image: + image_width: "Debe tener %{required_width}px de ancho" + image_height: "Debe tener %{required_height}px de alto" diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index be2f9280e..5f84f69d3 100755 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -209,6 +209,7 @@ en: signature_sheets: Signature Sheets site_customization: pages: Custom Pages + images: Custom Images moderators: index: title: Moderators @@ -478,12 +479,23 @@ en: sms_code_not_confirmed: Has not confirmed the sms code title: Incomplete verifications site_customization: + images: + index: + title: Custom images + update: Update + delete: Delete + update: + notice: Image updated successfully + error: Image couldn't be updated + destroy: + notice: Image deleted successfully + error: Image couldn't be deleted pages: create: - notice: 'Page created successfully.' + notice: Page created successfully error: Process couldn't be created update: - notice: 'Page updated successfully.' + notice: Page updated successfully error: Page couldn't be updated destroy: notice: Page deleted successfully diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 7b14bf90a..f4c834e34 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -208,7 +208,8 @@ es: stats: Estadísticas signature_sheets: Hojas de firmas site_customization: - pages: Páginas + pages: Personalizar páginas + images: Personalizar imágenes moderators: index: title: Moderadores @@ -478,12 +479,23 @@ es: sms_code_not_confirmed: No ha introducido su código de seguridad title: Verificaciones incompletas site_customization: + images: + index: + title: Personalizar imágenes + update: Actualizar + delete: Borrar + update: + notice: Imagen actualizada correctamente + error: No se ha podido actualizar la imagen + destroy: + notice: Imagen borrada correctamente + error: No se ha podido borrar la imagen pages: create: - notice: 'Página creada correctamente.' + notice: Página creada correctamente error: No se ha podido crear la página update: - notice: 'Página actualizada correctamente.s' + notice: Página actualizada correctamente error: No se ha podido actualizar la página destroy: notice: Página eliminada correctamente diff --git a/config/routes.rb b/config/routes.rb index af9ac8a08..51d2a47da 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -225,6 +225,7 @@ Rails.application.routes.draw do namespace :site_customization do resources :pages, except: [:show] + resources :images, only: [:index, :update, :destroy] end end diff --git a/db/migrate/20170322145702_create_site_customization_images.rb b/db/migrate/20170322145702_create_site_customization_images.rb new file mode 100644 index 000000000..4f980b566 --- /dev/null +++ b/db/migrate/20170322145702_create_site_customization_images.rb @@ -0,0 +1,11 @@ +class CreateSiteCustomizationImages < ActiveRecord::Migration + def change + create_table :site_customization_images do |t| + t.string :name, null: false + t.attachment :image + t.timestamps null: false + end + + add_index :site_customization_images, :name, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 0b38a4196..938fbb9ec 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170316174351) do +ActiveRecord::Schema.define(version: 20170322145702) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -439,6 +439,18 @@ ActiveRecord::Schema.define(version: 20170316174351) do t.datetime "updated_at" end + create_table "site_customization_images", force: :cascade do |t| + t.string "name", null: false + t.string "image_file_name" + t.string "image_content_type" + t.integer "image_file_size" + t.datetime "image_updated_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "site_customization_images", ["name"], name: "index_site_customization_images_on_name", unique: true, using: :btree + create_table "site_customization_pages", force: :cascade do |t| t.string "slug", null: false t.string "title", null: false diff --git a/spec/features/admin/site_customization/images_spec.rb b/spec/features/admin/site_customization/images_spec.rb new file mode 100644 index 000000000..094ac19bf --- /dev/null +++ b/spec/features/admin/site_customization/images_spec.rb @@ -0,0 +1,62 @@ +require 'rails_helper' + +feature "Admin custom images" do + + background do + admin = create(:administrator) + login_as(admin.user) + end + + scenario "Upload valid image" do + visit admin_root_path + + within("#side_menu") do + click_link "Custom Images" + end + + within("tr.logo_header") do + attach_file "site_customization_image_image", "spec/fixtures/files/logo_header.png" + click_button "Update" + end + + expect(page).to have_css("tr.logo_header img[src*='logo_header.png']") + expect(page).to have_css("img[src*='logo_header.png']", count: 2) # one in the admin form an one in the page header + end + + scenario "Upload invalid image" do + visit admin_root_path + + within("#side_menu") do + click_link "Custom Images" + end + + within("tr.icon_home") do + attach_file "site_customization_image_image", "spec/fixtures/files/logo_header.png" + click_button "Update" + end + + expect(page).to have_content("Width must be 330px") + expect(page).to have_content("Height must be 240px") + end + + scenario "Delete image" do + visit admin_root_path + + within("#side_menu") do + click_link "Custom Images" + end + + within("tr.social-media-icon") do + attach_file "site_customization_image_image", "spec/fixtures/files/social-media-icon.png" + click_button "Update" + end + + expect(page).to have_css("img[src*='social-media-icon.png']") + + within("tr.social-media-icon") do + click_link "Delete" + end + + expect(page).to_not have_css("img[src*='social-media-icon.png']") + end +end diff --git a/spec/features/admin/site_customization/pages_spec.rb b/spec/features/admin/site_customization/pages_spec.rb index 20b817f50..5d03638d8 100644 --- a/spec/features/admin/site_customization/pages_spec.rb +++ b/spec/features/admin/site_customization/pages_spec.rb @@ -39,7 +39,7 @@ feature "Admin custom pages" do context "Update" do scenario "Valid custom page" do - custom_page = create(:site_customization_page, title: "An example custom page") + create(:site_customization_page, title: "An example custom page") visit admin_root_path within("#side_menu") do @@ -58,7 +58,7 @@ feature "Admin custom pages" do end end - scenario "Index" do + scenario "Delete" do custom_page = create(:site_customization_page, title: "An example custom page") visit edit_admin_site_customization_page_path(custom_page) diff --git a/spec/fixtures/files/logo_header.png b/spec/fixtures/files/logo_header.png new file mode 100644 index 000000000..5f5355574 Binary files /dev/null and b/spec/fixtures/files/logo_header.png differ diff --git a/spec/fixtures/files/social-media-icon.png b/spec/fixtures/files/social-media-icon.png new file mode 100644 index 000000000..fcd00e212 Binary files /dev/null and b/spec/fixtures/files/social-media-icon.png differ