From 701103240af17d2a1bf137b0401e2a4f1116cfbb Mon Sep 17 00:00:00 2001 From: Behrokh Satarnejad <62008897+bsatarnejad@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:25:57 +0100 Subject: [PATCH] [68323] Custom logo for mobile (#21059) * add a migration to upload custom mobile logo * add a new route for uploading nd removing mobile logo * show custom logo in header * Add a feature spec * Update custom_style.rb * Show mobile icon for desktop when there is no desktop logo * show icon logo in waffle menu modal * Show logo icon when a custom mobile logo exists or when no custom desktop logo is uploaded --- app/controllers/custom_styles_controller.rb | 10 +++ app/helpers/custom_styles_helper.rb | 28 ++++++++ app/models/custom_style.rb | 3 +- app/views/custom_styles/_branding.html.erb | 12 ++++ app/views/custom_styles/_inline_css_logo.erb | 40 +++++++---- config/locales/en.yml | 3 +- config/routes.rb | 5 ++ ...102116_add_logo_mobile_to_custom_styles.rb | 7 ++ .../global_styles/content/_custom_logo.sass | 2 + .../menu_manager/top_menu/module_menu.rb | 2 +- lib/redmine/menu_manager/top_menu_helper.rb | 18 ++++- .../custom_styles_controller_spec.rb | 67 +++++++++++++++++++ spec/factories/custom_style_factory.rb | 8 +++ 13 files changed, 186 insertions(+), 19 deletions(-) create mode 100644 db/migrate/20251117102116_add_logo_mobile_to_custom_styles.rb diff --git a/app/controllers/custom_styles_controller.rb b/app/controllers/custom_styles_controller.rb index 871171443d6..e35c1880fd0 100644 --- a/app/controllers/custom_styles_controller.rb +++ b/app/controllers/custom_styles_controller.rb @@ -36,6 +36,7 @@ class CustomStylesController < ApplicationController menu_item :custom_style UNGUARDED_ACTIONS = %i[logo_download + logo_mobile_download favicon_download touch_icon_download].freeze @@ -102,6 +103,10 @@ class CustomStylesController < ApplicationController file_download(:logo_path) end + def logo_mobile_download + file_download(:logo_mobile_path) + end + def export_logo_download file_download(:export_logo_path) end @@ -126,6 +131,10 @@ class CustomStylesController < ApplicationController file_delete(:remove_logo) end + def logo_mobile_delete + file_delete(:remove_logo_mobile) + end + def export_logo_delete file_delete(:remove_export_logo) end @@ -224,6 +233,7 @@ class CustomStylesController < ApplicationController def custom_style_params params.expect(custom_style: %i[ logo remove_logo + logo_mobile remove_logo_mobile export_logo remove_export_logo export_cover remove_export_cover export_footer remove_export_footer diff --git a/app/helpers/custom_styles_helper.rb b/app/helpers/custom_styles_helper.rb index 7c1a62294da..4c2d5350696 100644 --- a/app/helpers/custom_styles_helper.rb +++ b/app/helpers/custom_styles_helper.rb @@ -78,6 +78,34 @@ module CustomStylesHelper (CustomStyle.current.logo.present? || CustomStyle.current.theme_logo.present?) end + def desktop_logo_present? + style = CustomStyle.current + return false unless style + + style.logo.present? || style.theme_logo.present? + end + + def mobile_logo_present? + style = CustomStyle.current + return false unless style + + style.logo_mobile.present? + end + + def show_waffle_icon? + # Both logos → show icon (mobile logo will be applied by CSS) + return true if desktop_logo_present? && mobile_logo_present? + + # Only mobile → show icon + return true if mobile_logo_present? + + # Only desktop → hide icon on mobile + return false if desktop_logo_present? + + # No logos → show fallback icon + true + end + # The default favicon and touch icons are both the same for normal OP and BIM. def apply_custom_favicon? apply_custom_styles?(skip_ee_check: false) && CustomStyle.current.favicon.present? diff --git a/app/models/custom_style.rb b/app/models/custom_style.rb index 300172818fe..58f4111f9fa 100644 --- a/app/models/custom_style.rb +++ b/app/models/custom_style.rb @@ -32,6 +32,7 @@ require "ttfunk" class CustomStyle < ApplicationRecord mount_uploader :logo, OpenProject::Configuration.file_uploader + mount_uploader :logo_mobile, OpenProject::Configuration.file_uploader mount_uploader :export_logo, OpenProject::Configuration.file_uploader mount_uploader :export_cover, OpenProject::Configuration.file_uploader mount_uploader :export_footer, OpenProject::Configuration.file_uploader @@ -59,7 +60,7 @@ class CustomStyle < ApplicationRecord updated_at.to_i end - %i(favicon touch_icon export_logo export_cover export_footer logo + %i(favicon touch_icon export_logo export_cover export_footer logo logo_mobile export_font_regular export_font_bold export_font_italic export_font_bold_italic).each do |name| define_method :"#{name}_path" do attachment = send(name) diff --git a/app/views/custom_styles/_branding.html.erb b/app/views/custom_styles/_branding.html.erb index 4c38323d317..b37d8c73c85 100644 --- a/app/views/custom_styles/_branding.html.erb +++ b/app/views/custom_styles/_branding.html.erb @@ -40,6 +40,18 @@ See COPYRIGHT and LICENSE files for more details. instructions: I18n.t("text_custom_logo_instructions") } } %> +<%= render partial: "custom_styles/uploads/image", locals: { image: { + field: :logo_mobile, + label: I18n.t(:label_custom_logo_mobile), + present: @custom_style.id && @custom_style.logo_mobile.present?, + source: @custom_style.id && @custom_style.logo_mobile.present? ? + custom_style_logo_mobile_path(digest: @custom_style.digest, filename: @custom_style.logo_mobile_identifier) : + nil, + img_class: "custom-logo-mobile-preview", + accept: "image/*", + delete_path: custom_style_logo_mobile_delete_path +} } %> + <%= render partial: "custom_styles/uploads/image", locals: { image:{ field: :favicon, label: I18n.t(:label_custom_favicon), diff --git a/app/views/custom_styles/_inline_css_logo.erb b/app/views/custom_styles/_inline_css_logo.erb index a64eab4336d..b41ac8769db 100644 --- a/app/views/custom_styles/_inline_css_logo.erb +++ b/app/views/custom_styles/_inline_css_logo.erb @@ -29,36 +29,50 @@ See COPYRIGHT and LICENSE files for more details.