# Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
require 'utopia/extensions'
module Utopia
class Link
XNODE_EXT = ".xnode"
def initialize(kind, path, info = nil)
path = Path.create(path)
@kind = kind
case @kind
when :file
@name = path.basename(XNODE_EXT)
@path = path
when :directory
@name = path.dirname.basename(XNODE_EXT)
@path = path
when :virtual
@name = path.to_s
@path = nil
end
@components = @name.split(".")
@locale = path.locale(XNODE_EXT)
@title = components[0]
if info
@info = info.symbolize_keys
else
@info = {}
end
end
attr :kind
attr :name
attr :path
attr :locale
attr :info
attr :components
def [] (key)
if key == :title
return @title
end
return @info[key]
end
def href
if @info[:uri]
return @info[:uri]
elsif @path
return @path.to_s
else
"\#"
end
end
def href?
return href != "\#"
end
def title
@info[:title] || @title.to_title
end
def external?
@info.key? :uri
end
def to_href(options = {})
options[:content] ||= title
options[:class] ||= "link"
if href == "\#"
"#{options[:content].to_html}"
else
"#{options[:content].to_html}"
end
end
def eql? other
if other && self.class == other.class
return kind.eql?(other.kind) &&
name.eql?(other.name) &&
path.eql?(other.path) &&
info.eql?(other.info)
else
return false
end
end
def == other
return other && kind == other.kind && name == other.name && path == other.path
end
end
module Links
XNODE_FILTER = /^(.+)\.xnode$/
INDEX_XNODE_FILTER = /^(index(\..+)*)\.xnode$/
LINKS_YAML = "links.yaml"
def self.metadata(path)
links_path = File.join(path, LINKS_YAML)
if File.exist?(links_path)
return YAML::load(File.read(links_path))
else
return {}
end
end
def self.indices(path, &block)
entries = Dir.entries(path).delete_if{|filename| !filename.match(INDEX_XNODE_FILTER)}
if block_given?
entries.each &block
else
return entries
end
end
DEFAULT_OPTIONS = {
:directories => true,
:files => true,
:virtual => true,
:indices => false,
:sort => :order,
:hidden => :hidden,
:locale => nil
}
def self.index(root, top, options = {})
options = DEFAULT_OPTIONS.merge(options)
path = File.join(root, top.components)
metadata = Links.metadata(path)
links = []
Dir.entries(path).each do |filename|
next if filename.match(/^[\._]/)
fullpath = File.join(path, filename)
if File.directory?(fullpath) && options[:directories]
name = filename
indices_metadata = Links.metadata(fullpath)
directory_metadata = metadata.delete(name) || {}
indices = 0
Links.indices(fullpath) do |index|
index_name = File.basename(index, ".xnode")
index_metadata = directory_metadata.merge(indices_metadata[index_name] || {})
directory_link = Link.new(:directory, top + [filename, index_name], index_metadata)
# Check for localized directory metadata and update the link
if directory_link.locale
localized_metadata = metadata.delete(name + "." + directory_link.locale)
if localized_metadata
directory_link.info.update(localized_metadata.symbolize_keys)
end
end
links << directory_link
indices += 1
end
if indices == 0
links << Link.new(:directory, top + [filename, ""], directory_metadata.merge(:uri => "\#"))
end
elsif filename.match(INDEX_XNODE_FILTER) && options[:indices] == false
name = $1
metadata.delete(name)
# We don't include indices in the list of pages.
next
elsif filename.match(XNODE_FILTER) && options[:files]
name = $1
links << Link.new(:file, top + name, metadata.delete(name))
end
end
if options[:virtual]
metadata.each do |name, details|
links << Link.new(:virtual, name, details)
end
end
if options[:hidden]
links = links.delete_if{|link| link[options[:hidden]]}
end
if options[:name]
case options[:name]
when Regexp
links.reject!{|link| !link.name.match(options[:name])}
when String
links.reject!{|link| link.name.index(options[:name]) != 0}
end
end
if options[:locale]
reduced = []
links.group_by(&:name).each do |name, links|
default = nil
link = links.select{|link|
link.locale == options[:locale] || link.locale == ''
}.sort_by{|link| link.locale.size}.last
if link
reduced << link
end
end
links = reduced
end
if options[:sort]
links = links.sort do |a, b|
result = nil
begin
result ||= (a[options[:sort]] <=> b[options[:sort]])
rescue
# LOG.debug("Invalid comparison between #{a.path} and #{b.path} using key #{options[:sort]}!")
end
result ||= (a.title <=> b.title)
end
end
return links
end
end
end