diff --git a/app/controllers/my_profile/themes_controller.rb b/app/controllers/my_profile/themes_controller.rb index 83d3f92..27c8c59 100644 --- a/app/controllers/my_profile/themes_controller.rb +++ b/app/controllers/my_profile/themes_controller.rb @@ -1,5 +1,6 @@ class ThemesController < MyProfileController + protect 'edit_appearance', :profile no_design_blocks def set @@ -12,4 +13,66 @@ class ThemesController < MyProfileController @selected_theme = profile.theme end + def new + if !request.xhr? + id = params[:name].to_slug + t = Theme.new(id, :name => params[:name], :owner => profile) + t.save + redirect_to :action => 'index' + else + render :action => 'new', :layout => false + end + end + + def edit + @theme = profile.find_theme(params[:id]) + @css_files = @theme.css_files + @image_files = @theme.image_files + end + + def add_css + @theme = profile.find_theme(params[:id]) + if request.xhr? + render :action => 'add_css', :layout => false + else + @theme.add_css(params[:css]) + redirect_to :action => 'edit', :id => @theme.id + end + end + + def css_editor + @theme = profile.find_theme(params[:id]) + @css = params[:css] + + @code = @theme.read_css(@css) + render :action => 'css_editor', :layout => false + end + + post_only :update_css + def update_css + @theme = profile.find_theme(params[:id]) + @theme.update_css(params[:css], params[:csscode]) + redirect_to :action => 'edit', :id => @theme.id + end + + def add_image + @theme = profile.find_theme(params[:id]) + if request.xhr? + render :action => 'add_image', :layout => false + else + @theme.add_image(params[:image].original_filename, params[:image].read) + redirect_to :action => 'edit', :id => @theme.id + end + end + + def start_test + session[:theme] = params[:id] + redirect_to :controller => 'content_viewer', :profile => profile.identifier, :action => 'view_page' + end + + def stop_test + session[:theme] = nil + redirect_to :action => 'index' + end + end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index fb97c43..d2efefa 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -220,24 +220,12 @@ module ApplicationHelper link_to(content_tag('span', label), url, html_options.merge(:class => the_class )) end - def button_to_function(type, label, js_code, html_options = {}) - html_options[:class] = "button with-text" unless html_options[:class] - html_options[:class] << " icon-#{type}" - link_to_function(label, js_code, html_options) - end - def button_to_function(type, label, js_code, html_options = {}, &block) html_options[:class] = "button with-text" unless html_options[:class] html_options[:class] << " icon-#{type}" link_to_function(label, js_code, html_options, &block) end - def button_to_function_without_text(type, label, js_code, html_options = {}) - html_options[:class] = "" unless html_options[:class] - html_options[:class] << " button icon-#{type}" - link_to_function(content_tag('span', label), js_code, html_options) - end - def button_to_function_without_text(type, label, js_code, html_options = {}, &block) html_options[:class] = "" unless html_options[:class] html_options[:class] << " button icon-#{type}" @@ -323,12 +311,21 @@ module ApplicationHelper def filename_for_stylesheet(name, in_theme) result = '' if in_theme - result << '/designs/themes/' + current_theme + result << theme_path end result << '/stylesheets/' << name << '.css' end + def theme_path + if session[:theme] + '/user_themes/' + current_theme + else + '/designs/themes/' + current_theme + end + end + def current_theme + return session[:theme] if (session[:theme]) p = profile if p p.theme @@ -337,6 +334,21 @@ module ApplicationHelper end end + def theme_footer + footer = ('../../public' + theme_path + '/footer.rhtml') + if File.exists?(RAILS_ROOT + '/app/views/' + footer) + render :file => footer + end + end + + def is_testing_theme + !@controller.session[:theme].nil? + end + + def theme_owner + Theme.find(current_theme).owner.identifier + end + # generates a image tag for the profile. # # If the profile has no image set yet, then a default image is used. @@ -492,8 +504,7 @@ module ApplicationHelper def theme_option(opt = nil) conf = RAILS_ROOT.to_s() + - '/public/designs/themes/' + - current_theme.to_s() + + '/public' + theme_path + '/theme.yml' if File.exists?(conf) opt ? YAML.load_file(conf)[opt.to_s()] : YAML.load_file(conf) @@ -529,7 +540,7 @@ module ApplicationHelper return if option.nil? html = [] option.each do |file| - file = '/designs/themes/'+ current_theme.to_s() + + file = theme_path + '/javascript/'+ file +'.js' if File.exists? RAILS_ROOT.to_s() +'/public'+ file html << javascript_src_tag( file, {} ) diff --git a/app/models/profile.rb b/app/models/profile.rb index 33dd4a7..74775d5 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -35,6 +35,7 @@ class Profile < ActiveRecord::Base 'validate_enterprise' => N_('Validate enterprise'), 'perform_task' => N_('Perform task'), 'moderate_comments' => N_('Moderate comments'), + 'edit_appearance' => N_('Edit appearance'), } acts_as_accessible @@ -450,4 +451,13 @@ class Profile < ActiveRecord::Base def public? public_profile end + + def themes + Theme.find_by_owner(self) + end + + def find_theme(the_id) + themes.find { |item| item.id == the_id } + end + end diff --git a/app/models/theme.rb b/app/models/theme.rb index 57503cf..34bbb2c 100644 --- a/app/models/theme.rb +++ b/app/models/theme.rb @@ -1,22 +1,155 @@ class Theme + class << self + def system_themes + Dir.glob(RAILS_ROOT + '/public/designs/themes/*').map do |item| + File.basename(item) + end.map do |item| + new(item) + end + end + + def user_themes_dir + File.join(RAILS_ROOT, 'public', 'user_themes') + end + + def create(id, attributes = {}) + if find(id) || system_themes.map(&:id).include?(id) + raise DuplicatedIdentifier + end + Theme.new(id, attributes).save + end + + def find(the_id) + if File.directory?(File.join(user_themes_dir, the_id)) + Theme.new(the_id) + else + nil + end + end + + def find_by_owner(owner) + Dir.glob(File.join(user_themes_dir, '*', 'theme.yml')).select do |desc| + config = YAML.load_file(desc) + (config['owner_type'] == owner.class.base_class.name) && (config['owner_id'] == owner.id) + end.map do |desc| + Theme.find(File.basename(File.dirname(desc))) + end + end + + end + + class DuplicatedIdentifier < Exception; end + attr_reader :id + attr_reader :config - def initialize(id) + def initialize(id, attributes = {}) @id = id + load_config + attributes.each do |k,v| + self.send("#{k}=", v) + end end def name - id + config['name'] || id end - class << self - def system_themes - Dir.glob(RAILS_ROOT + '/public/designs/themes/*').map do |item| - File.basename(item) - end.map do |item| - new(item) - end + def name=(value) + config['name'] = value + end + + def ==(other) + other.is_a?(self.class) && (other.id == self.id) + end + + def add_css(filename) + FileUtils.mkdir_p(stylesheets_directory) + FileUtils.touch(stylesheet_path(filename)) + end + + def update_css(filename, content) + add_css(filename) + File.open(stylesheet_path(filename), 'w') do |f| + f.write(content) + end + end + + def read_css(filename) + File.read(stylesheet_path(filename)) + end + + def css_files + Dir.glob(File.join(stylesheets_directory, '*.css')).map { |f| File.basename(f) } + end + + def add_image(filename, data) + FileUtils.mkdir_p(images_directory) + File.open(image_path(filename), 'w') do |f| + f.write(data) end end + + def image_files + Dir.glob(image_path('*')).map {|item| File.basename(item)} + end + + def stylesheet_path(filename) + suffix = '' + unless filename =~ /\.css$/ + suffix = '.css' + end + File.join(stylesheets_directory, filename + suffix) + end + + def stylesheets_directory + File.join(Theme.user_themes_dir, self.id, 'stylesheets') + end + + def image_path(filename) + File.join(images_directory, filename) + end + + def images_directory + File.join(self.class.user_themes_dir, id, 'images') + end + + def save + FileUtils.mkdir_p(self.class.user_themes_dir) + FileUtils.mkdir_p(File.join(self.class.user_themes_dir, id)) + %w[ common help menu article button search blocks forms login-box ].each do |item| + add_css(item) + end + write_config + self + end + + def owner + return nil unless config['owner_type'] && config['owner_id'] + @owner ||= config['owner_type'].constantize.find(config['owner_id']) + end + + def owner=(model) + config['owner_type'] = model.class.base_class.name + config['owner_id'] = model.id + @owner = model + end + + protected + + def write_config + File.open(File.join(self.class.user_themes_dir, self.id, 'theme.yml'), 'w') do |f| + f.write(config.to_yaml) + end + end + + def load_config + if File.exists?(File.join(self.class.user_themes_dir, self.id, 'theme.yml')) + @config = YAML.load_file(File.join(self.class.user_themes_dir, self.id, 'theme.yml')) + else + @config = {} + end + end + end diff --git a/app/views/layouts/application.rhtml b/app/views/layouts/application.rhtml index 6151c50..6d51b28 100644 --- a/app/views/layouts/application.rhtml +++ b/app/views/layouts/application.rhtml @@ -154,7 +154,7 @@
<%= javascript_include_tag 'better-browser-promotion' %> + <% if is_testing_theme %> + <%= render :file => 'shared/theme_test_panel' %> + <% end %> +