lib/masterview/extras/app/controllers/masterview_controller.rb in masterview-0.2.5 vs lib/masterview/extras/app/controllers/masterview_controller.rb in masterview-0.3.0

- old
+ new

@@ -1,89 +1,92 @@ # # == 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 +# +# 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; +# +# 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 +# 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 +# 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] #++ -# +# require 'masterview/extras/sample_templates' class MasterviewController < ApplicationController - include MasterView::DefaultGenerateMIOFilter + include MasterView::MIO::DefaultGenerateMIOFilter before_filter :check_authorization, :except => [ :access_not_allowed ] - ENABLE_ADMIN_LAYOUT = false #:nodoc: #UNDER CONSTRUCTION - fine idea, but doesn't work yet [DJL 03-Jun-2006] + ###layout 'masterview_admin' - # Describe the MasterView configuration option settings - def configuration - if ENABLE_ADMIN_LAYOUT - smart_render_with_layout('masterview/admin/configuration', 'masterview_admin') - else - #ugly, but keep this version - aint_got_no_layout_backstop = '<div style="padding-top: 6px;">(back to <a href=".">Admin home</a>)</div>' - config_html = MasterView::Info.to_html( :postscript => aint_got_no_layout_backstop ) - render :text => config_html - end - end - + MV_ADMIN_INSTALL_DIR = File.expand_path( File.join(File.dirname(__FILE__), '../..') ) #:nodoc: + #CONTEXT_REF_VIEW = 'app/views' + #CONTEXT_REF_LAYOUT = 'app/views/layout' + FILE_LOC_APP = 'app' #:nodoc: + FILE_LOC_MV = 'mv' #:nodoc: + def index redirect_to :action => :list end # List the templates in the application and their status. - # Provide operations to force rebuild (reparse and regenerate) + # 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' + masterview_render_with_layout( 'masterview/admin/list', 'masterview_admin' ) end + # Describe the MasterView configuration option settings + def configuration + masterview_render_with_layout( 'masterview/admin/configuration', 'masterview_admin_config' ) + end + + def directives + masterview_render_with_layout( 'masterview/admin/directives', 'masterview_admin_config' ) + end + def features - smart_render 'masterview/admin/features' + masterview_render_with_layout( 'masterview/admin/features', 'masterview_admin_config' ) end # Rebuild all templates in the application. # Invoked from the main masterview admin page. def rebuild_all @@ -101,11 +104,11 @@ end # Rebuild a specific template. # Invoked from the main masterview admin page. def rebuild - path = params[:id] + path = params[:file] template_specs, content_hash = MasterView::TemplateSpec.scan template_spec = template_specs[path] raise 'Template '+path+' not found' unless template_spec if template_spec.rebuild_template(content_hash, :write_to_file => true) flash[:notice] = 'File '+path+' updated' @@ -118,11 +121,11 @@ # Create a new, empty template. # Invoked from the main masterview admin page. def create if @request.post? action_to_create = params[:action_name] - src_file = params[:id] + src_file = params[:file] 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) @@ -134,11 +137,11 @@ 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] + @rhtml_file = params[:file] 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' @@ -147,26 +150,30 @@ # interact, test template src, view results def interact @results = [] @src = params[:src] if @src - sh_mio = MasterView::StringHashMIOTree.new({}, MasterView::LoadedConfiguration.generated_file_default_extension ) - - src_with_default_gen = add_default_gen_if_needed(@src) # apply a default generate - MasterView::Parser.parse( src_with_default_gen, { :output_mio_tree => sh_mio, :omit_comment => true, :template_pathname => 'YOUR_TEMPLATE_PATH_HERE' } ) - @results = sh_mio.string_hash.sort.collect do |file,rhtml| - os = OpenStruct.new - os.file = file - os.rhtml = rhtml - os + begin + sh_mio = MasterView::MIO::StringHashMIOTree.new({}, MasterView::ConfigSettings.generated_file_default_extension ) + + src_with_default_gen = add_default_gen_if_needed(@src) # apply a default generate + MasterView::Parser.parse( src_with_default_gen, { :output_mio_tree => sh_mio, :omit_comment => true, :template_pathname => 'YOUR_TEMPLATE_PATH_HERE', :rescue_exceptions => false } ) + @results = sh_mio.string_hash.sort.collect do |file,rhtml| + os = OpenStruct.new + os.file = file + os.rhtml = rhtml + os + end + rescue REXML::ParseException => e + @error_message = e.to_s end end smart_render 'masterview/admin/interact' 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>', + render :text => '<p>We\'re sorry, but the page you have requested is only available to authorized users.</p>', :status => 500 end protected @@ -184,58 +191,78 @@ if ! allow_access? redirect_to :action => :access_not_allowed end end - private + private # checks app path first for views and files, then falls back to files in MV def find_path(path) - local_path = File.join(RAILS_ROOT, path) - mv_path = File.join(File.dirname(__FILE__), '../..', path) - working_path = (File.exist?(local_path)) ? local_path : mv_path + app_path = File.join(RAILS_ROOT, path) + mv_path = File.join(MV_ADMIN_INSTALL_DIR, path) + working_path = (File.exist?(app_path)) ? app_path : mv_path end # render local template file if exists otherwise render file from mv def smart_render(short_path) - local_path = File.join(RAILS_ROOT, 'app/views', short_path)+'.rhtml' - if File.exist?(local_path) + template_path, template_loc = resolve_relative_path( short_path, 'app/views' ) + if template_loc == FILE_LOC_APP render :template => short_path else - mv_path = File.join(File.dirname(__FILE__), '../../app/views', short_path)+'.rhtml' - MasterView::Log.debug { 'mv_path='+mv_path } - render :file => mv_path + MasterView::Log.debug { 'render admin template from mv_path='+template_path } + render :file => template_path end end # render local template file if exists otherwise render file from mv - def smart_render_with_layout(short_path, layout) #:nodoc: #UNDER CONSTRUCTION - #DOESN'T WORK YET - we get the right path, but need a way into template rendering - # (usual path through rails bolts in assumption of app/views rel path) - # [DJL 03-Jul-2006] - local_path = File.join(RAILS_ROOT, 'app/views', short_path)+'.rhtml' - if File.exist?(local_path) - render :template => short_path, :layout => smart_layout_path(layout) + def masterview_render_with_layout(short_path, layout) #:nodoc: + template_path, template_loc = resolve_relative_path( short_path, 'app/views' ) + layout_path, layout_loc = resolve_relative_path( layout, 'app/views/layouts' ) + if (template_loc == FILE_LOC_APP) && (layout_loc == FILE_LOC_APP) + # app has customized both the admin page view and its layout + render :template => short_path, :layout => layout else - mv_path = File.join(File.dirname(__FILE__), '../../app/views', short_path)+'.rhtml' - MasterView::Log.debug { 'mv_path='+mv_path } - render :file => mv_path, :layout => smart_layout_path(layout) + # template or layout or both use std masterview + MasterView::Log.debug { 'render admin template from mv_path='+template_path } + MasterView::Log.debug { 'render admin layout from mv_path='+layout_path } if layout_loc == FILE_LOC_MV + masterview_render_file_with_layout( template_path, layout_path ) end end - def smart_layout_path(short_path) #:nodoc: #UNDER CONSTRUCTION - local_path = File.join(RAILS_ROOT, 'app/views/layouts', short_path)+'.rhtml' - if File.exist?(local_path) - short_path - else - mv_layouts_dir_path = File.expand_path( File.join(File.dirname(__FILE__), '../../app/views/layouts') ) - mv_path = File.join(mv_layouts_dir_path, short_path)+'.rhtml' - if File.exist?(mv_path) - MasterView::Log.debug { 'admin layout mv_path='+mv_path } - else - MasterView::Log.error { '***BAD ADMIN PAGE LAYOUT REF: mv_path='+mv_path } - end - mv_path + # Resolve a relative reference within the Rails application + # or to the masterview installation directory. + # Answer the full_path and a location indicator ('app' or 'mv') + def resolve_relative_path( short_path, context_ref='app/views', file_ext='.rhtml' ) + + file_ref = "#{short_path}#{file_ext}" + + # resolve to rails app/views if present to allow app customization + app_path = File.join(RAILS_ROOT, context_ref, file_ref) + return [ app_path, FILE_LOC_APP ] if File.exist?(app_path) + + # resolve to standard masterview view template if not overridden + mv_path = File.join(MV_ADMIN_INSTALL_DIR, context_ref, file_ref) + if ! File.exist?(mv_path) + MasterView::Log.error { '***NO SUCH MV ADMIN FILE: mv_path='+mv_path } end + return [ mv_path, FILE_LOC_MV ] end +end + +# patch a special version of template rendering into ActionController::Base +# standard layout rendering is bolted down to app/views/layout context +module ActionController #:nodoc: + class Base #:nodoc: + def masterview_render_file_with_layout( template_path, layout_path, local_assigns={} ) + # Implementation based on ActionController::Layout#render_with_a_layout + use_full_path = false #we already have full path, we don't want rails template_base prefixed + add_variables_to_assigns + content_for_layout = @template.render_file(template_path, use_full_path, local_assigns) + # following was needed in ActionController::Layout because of the way it does its overrides + #erase_render_results #??do we need to do this?? Is there any downside to doing so??! + #add_variables_to_assigns + @template.instance_variable_set('@content_for_layout', content_for_layout) + render_text(@template.render_file(layout_path, use_full_path, local_assigns)) + end + end end