class Content < Iowa::DetachedComponent # This component displays no content of its own. Rather, it retrieves content # from a content repository, called a library. The retrieved content is # treated like an IOWA template and is parsed with the TemplateParser. # The parsed template is ccahed, and is used as the template for the component. # Because the template content is parsed with the TemplateParser, that content # may also have Content tags in it. There is no limit to the depth of embedding # which will be traversed. @mutex = Iowa::Mutex.new Clibrary = 'library'.freeze unless const_defined?(:Clibrary) Cid = 'id'.freeze unless const_defined?(:Cid) Cpreview = 'preview'.freeze unless const_defined?(:Cpreview) attr_accessor :content, :predecessors def Content.default_cache_size; 100; end def Content.default_cache_ttl; nil; end class ConfObj_ < Hash def library; @library; end def library=(val); self[val] ||= {}; @library = val; end def query; self[@library][:query]; end def query=(val); self[@library][:query] = val; end def retrieve; self[@library][:retrieve]; end def retrieve=(val); self[@library][:retrieve] = val; end def cache; self[@library][:cache]; end def cache=(args) #self[@library][:cache] = Util.get_cache(args,{Cmaxsize => Content.default_cache_size, Cttl => Content.default_cache_ttl}) self[@library][:cache] = Util.get_cache(args) end end @confobj = ConfObj_.new # The Content component can serve content from multiple libraries # at the same time. To set or reference a configuration item for # a given library, pass it's name as an argument to Content.config. # i.e. Content.config('generaldb').cache = Hash.new # def Content.config(library = C_default) if block_given? @mutex.synchronize do begin oldlib = @confobj.library @confobj.library = library yield @confobj ensure @confobj.library = oldlib end end else @confobj.library = library @confobj end end # Default cache is a basic LRUCache. Content.config.cache = [:LRUCache, {:maxsize => 100}] # Default query mechanism is flatfile based, looking in # #{Iowa.app.root_path}/_content Content.config.query = Proc.new do |name, attributes| File.stat(File.join(Iowa.app.root_path,C_content,name)).mtime end Content.config.retrieve = Proc.new do |name, attributes| r = {} name.gsub!(/[\\\/]/,File::SEPARATOR) filename = File.join(Iowa.app.root_path,C_content,name) # Race condition here.... r[Cchecksum] = r[Clast_modification] = File.stat(filename).mtime r[Clabel] = name r[Ctitle],r[Cbody] =File.read(filename).split(/\n/,2) r end def initialize(*args) super(*args) @dataname = @bindings.has_key?(Cid) ? getBinding(Cid) : @name @predecessors = {@dataname => true} if @parent.respond_to?(:predecessors) @predecessors.merge! @parent.predecessors end end def setup parsed_template = nil library = @bindings.has_key?(Clibrary) ? getBinding(Clibrary) : @attributes[Clibrary] || C_default Logger['iowa_log'].info "Library: #{library}" Content.config(library) do |content_config| dn = session.context.request.params[Cpreview] ? "#{@dataname}?preview" : @dataname cache_entry = content_config.cache[dn] if !cache_entry or (cache_entry and cache_entry[0] != content_config.query.call(@dataname, @attributes, session)) or session.context.request.params.has_key?(Crefresh) or @attributes[Crefresh] =~ /(true|enabled)/ cobj = content_config.retrieve.call(@dataname, @attributes, session) my_content = generate_wrapped_content(cobj) parsed_template = TemplateParser.new(my_content,{},self.class).root content_config.cache[dn] = [cobj[Cchecksum], parsed_template] else parsed_template = cache_entry[1] end end # This prevents infinite loops. if @parent.respond_to?(:predecessors) and @parent.predecessors[@dataname] @template = TemplateParser.new(C_empty,{}).root else @template = parsed_template end end def generate_wrapped_content(content) "<div id=\"maincontent\" style=\"position: absolute; left: 140px; top: 130px\">\n#{content[Cbody]}\n</div>" end end