You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

themes.rb 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006-2023 Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. module Redmine
  19. module Themes
  20. # Return an array of installed themes
  21. def self.themes
  22. @@installed_themes ||= scan_themes
  23. end
  24. # Rescan themes directory
  25. def self.rescan
  26. @@installed_themes = scan_themes
  27. end
  28. # Return theme for given id, or nil if it's not found
  29. def self.theme(id, options={})
  30. return nil if id.blank?
  31. found = themes.find {|t| t.id == id}
  32. if found.nil? && options[:rescan] != false
  33. rescan
  34. found = theme(id, :rescan => false)
  35. end
  36. found
  37. end
  38. # Class used to represent a theme
  39. class Theme
  40. attr_reader :path, :name, :dir
  41. def initialize(path)
  42. @path = path
  43. @dir = File.basename(path)
  44. @name = @dir.humanize
  45. @stylesheets = nil
  46. @javascripts = nil
  47. end
  48. # Directory name used as the theme id
  49. def id; dir end
  50. def ==(theme)
  51. theme.is_a?(Theme) && theme.dir == dir
  52. end
  53. def <=>(theme)
  54. return nil unless theme.is_a?(Theme)
  55. name <=> theme.name
  56. end
  57. def stylesheets
  58. @stylesheets ||= assets("stylesheets", "css")
  59. end
  60. def images
  61. @images ||= assets("images")
  62. end
  63. def javascripts
  64. @javascripts ||= assets("javascripts", "js")
  65. end
  66. def favicons
  67. @favicons ||= assets("favicon")
  68. end
  69. def favicon
  70. favicons.first
  71. end
  72. def favicon?
  73. favicon.present?
  74. end
  75. def stylesheet_path(source)
  76. "#{asset_prefix}#{source}"
  77. end
  78. def image_path(source)
  79. "#{asset_prefix}#{source}"
  80. end
  81. def javascript_path(source)
  82. "#{asset_prefix}#{source}"
  83. end
  84. def favicon_path
  85. "#{asset_prefix}#{favicon}"
  86. end
  87. def asset_prefix
  88. "themes/#{dir}/"
  89. end
  90. def asset_paths
  91. base_dir = Pathname.new(path)
  92. paths = base_dir.children.filter_map{|child| child if child.directory? &&
  93. child.basename.to_s != "src" &&
  94. !child.basename.to_s.start_with?('.') }
  95. Redmine::AssetPath.new(base_dir, paths, asset_prefix)
  96. end
  97. private
  98. def assets(dir, ext=nil)
  99. if ext
  100. Dir.glob("#{path}/#{dir}/*.#{ext}").collect {|f| File.basename(f, ".#{ext}")}
  101. else
  102. Dir.glob("#{path}/#{dir}/*").collect {|f| File.basename(f)}
  103. end
  104. end
  105. end
  106. module Helper
  107. def current_theme
  108. unless instance_variable_defined?(:@current_theme)
  109. @current_theme = Redmine::Themes.theme(Setting.ui_theme)
  110. end
  111. @current_theme
  112. end
  113. # Returns the header tags for the current theme
  114. def heads_for_theme
  115. if current_theme && current_theme.javascripts.include?('theme')
  116. javascript_include_tag current_theme.javascript_path('theme')
  117. end
  118. end
  119. end
  120. def self.scan_themes
  121. dirs = Dir.glob("#{Rails.public_path}/themes/*").select do |f|
  122. # A theme should at least override application.css
  123. File.directory?(f) && File.exist?("#{f}/stylesheets/application.css")
  124. end
  125. dirs.collect {|dir| Theme.new(dir)}.sort
  126. end
  127. private_class_method :scan_themes
  128. end
  129. end