lib/masterview/extras/app/controllers/masterview_controller.rb in masterview-0.1.5 vs lib/masterview/extras/app/controllers/masterview_controller.rb in masterview-0.2.0

- old
+ new

@@ -1,17 +1,77 @@ +# +# == MasterView Admin Controller +# +# The MasterviewController is a Rails controller that you can enable +# in your Rails application to provide administrative operations +# for the MasterView template engine. +# +# To enable or disable the admin controller, use the +# MasterView::Configuration setting +enable_admin_pages+ +# to enable or disable its availability in your +# config/masterview/settngs.rb or a suitable environment +# settings file, according to your intended usage: +# +# config.enable_admin_pages = true +# +# When enabled, the MasterView admin controller is accessable +# in your application at the address: +# +# http://yourapp.foo/masterview +# +# The controller places its stylesheets in a +masterview+ subdirectory +# in your rails stylesheets directory (app/stylesheets/masterview). +# +# The MasterView admin controller is primarily a developer facility; +# it is not generally appropriate for end-users of your rails application. +# If you enable this facility in your production environment, +# you should consider restricting access to authorized users. +# +#-- +# ISSUE: we need to work through scenario of how mv admin controller fits +# into overall scheme of one's rails application. How/when would developer +# integrate stylesheets and layout into their overal app look? +# How/when would access restriction actually be hooked up? +# Standard technique is to do something along the lines of using +# a before_filter which invokes things like :check_authentication +# and :check_authorization services in the app framework +# (or, say, predicate can_access? or has_permission? or...]. +# But... this whole area of user authentication and authorization +# has no specific conventions in the Rails ecosystem yet, +# so we really can't make any assumptions yet about any particular +# approach that MasterviewController itself could hook into. +# We'll have to leave this as an exercise for the user and give +# them tips on how to make it happen. +# [DJL 17-Jun-2006] +#++ +# class MasterviewController < ApplicationController + + before_filter :check_authorization, :except => [ :access_not_allowed ] + + # Describe the MasterView configuration option settings + def configuration + render :text => configuration_html, :layout => true + end def index redirect_to :action => :list end + # List the templates in the application and their status. + # Provide operations to force rebuild (reparse and regenerate) + # of specific templates or all templates and + # create utility to ...mumble... + # def list template_specs, content_hash = MasterView::TemplateSpec.scan @template_specs_sorted = template_specs.sort smart_render 'masterview/admin/list' end + # Rebuild all templates in the application. + # Invoked from the main masterview admin page. def rebuild_all files_rebuilt = [] MasterView::TemplateSpec.scan do |template_spec, content_hash| if template_spec.status == MasterView::TemplateSpec::Status::ImportsOutdated && template_spec.rebuild_template(content_hash, :write_to_file => true) @@ -22,10 +82,12 @@ flash[:notice] = files_rebuilt.join(', ')+' were updated' end redirect_to :action => :list end + # Rebuild a specific template. + # Invoked from the main masterview admin page. def rebuild path = params[:id] template_specs, content_hash = MasterView::TemplateSpec.scan template_spec = template_specs[path] raise 'Template '+path+' not found' unless template_spec @@ -35,24 +97,114 @@ flash[:notice] = 'Identical content - no update needed for '+path end redirect_to :action => :list end + # Create a new, empty template. + # Invoked from the main masterview admin page. def create if @request.post? action_to_create = params[:action_name] - short_name = File.basename(params[:id]) - src_file = File.join('app/views', MasterView::TemplateSrcRelativePath, short_name) + src_file = params[:id] empty_file_path = find_path('app/views/masterview/admin/empty.rhtml') empty_insert_erb = File.readlines(empty_file_path).join dst_file = MasterView::TemplateSpec.create_empty_shell_for_action(src_file, action_to_create, empty_insert_erb, :write_to_file => true) flash[:notice] = dst_file+' was created' redirect_to :action => :list else smart_render 'masterview/admin/create' end + end + + # View the generated rhtml + def view_rhtml + raise "View RHTML is disabled. Edit your config/masterview/settings.rb (or config/masterview/environments/xxxx.rb) file and set config.enable_view_rhtml = true. Restart application for change to take effect." unless MasterView::EnableMasterViewAdminViewRHTML + @rhtml_file = params[:id] + raise "RHTML file not specified" unless @rhtml_file + f = MasterView::IOMgr.erb.path(@rhtml_file) + raise "RHTML file ("+@rhtml_file+") not found. Maybe automatic parsing is disabled. You may invoke parsing manually by using rake mv:parse" unless f.exist? + @rhtml_content = f.read + smart_render 'masterview/admin/view_rhtml' + end + + def access_not_allowed #:nodoc: + render :text => '<p>We\'re sorry, but the page you have requested is only available to authorized users.</p>', + :status => 500 + end + + protected + + # Default implementation of authorization check + # to restrict access to administrative services + def allow_access? + # a more general solution might look something like: + # current_user && user_has_perm?('mv-admin') + # backstop: only allow for developer testing on local machine + local_request? + end + + # Check that the current user has authorization to access admin operations + def check_authorization + if ! allow_access? + redirect_to :action => :access_not_allowed + end + end + + # Answer an HTML rendering of the current MasterView configuration + def configuration_html + # This is a quick-and-dirty impl until a more elegant solution is provided + #e.g. do something more like Rails::Info + + config = MasterView::ConfigSettings + + section_general_options = 'General Options' + section_specs = [ + # [ <section-name>, [prop-name [, prop-name]... ] ] + [ 'MasterView Roots', + [ :mv_installation_dir, :rails_app?, :on_rails? ] ], + [ section_general_options, + [ :root_path, :config_dir_path, :environment ] ], #:directive_paths + [ 'Template Source Options', + [ :template_src_dir_path, :template_filename_pattern ] ], + [ 'Template Generation Options', + [ :template_dst_dir_path, :output_filename_extension, :generated_file_default_extension, :include_generated_file_comment ] ], + [ 'Template Parsing Options', + [ :default_parser_options, :namespace_prefix ] ], + [ 'Rails Application Options', + [ :parse_masterview_templates_at_startup, :reparse_changed_masterview_templates, :generate_rhtml_files ] ] + ] + + #"<h1>MasterView Configuration</h1>\n<table>\n<tbody>\n#{html}</tbody>\n</table>\n<div style=\"padding-top: 6px;\">(back to <a href=\".\">Admin home</a>)</div>" + html_config_settings = "<h1>MasterView Configuration</h1>\n<table>\n<tbody>\n%s</tbody>\n</table>\n<div style=\"padding-top: 6px;\">(back to <a href=\".\">Admin home</a>)</div>" % '%s' + + #"<tr style=\"background-color: #dcdcdc; font-weight: bold; margin-top: 6px;\"><td colspan=\"2\">#{section_name}</td></tr>" + html_subsection_entry = '<tr style="background-color: #dcdcdc; font-weight: bold; margin-top: 6px;"><td colspan="2">%s</td></tr>' #parm: section_name + + # "<tr><td><b>#{option_name}</b></td><td>#{option_value}</td></tr>" + html_option_entry = '<tr><td style="padding-left: 6px; padding-right: 6px; font-weight: bold;">%s</td><td>%s</td></tr>' #parms: option_name, option_value + + html = [] + section_specs.each { | section_name, options | + html << html_subsection_entry % section_name + options.each { | option | + option_name = CGI.escapeHTML(option.to_s) + option_value = config.send(option) + #tbd: option_value.kind_of?(Hash) then make it pretty? + option_value = option_value.nil? ? '(nil)' : CGI.escapeHTML(option_value.inspect) + html << html_option_entry % [ option_name, option_value ] + } + if section_name == section_general_options + option_name = 'logger' + option_value = MasterView::Log.class.name + html << html_option_entry % [ option_name, option_value ] + option_name = 'log_level' + option_value = MasterView.log_level + html << html_option_entry % [ option_name, option_value ] + end + } + html_config_settings % html.join("\n") end private # checks app path first for views and files, then falls back to files in MV