#!/usr/bin/ruby -w # sitegen.rb populates a website's pages with drop-down menus. # this script relies on these two custom Meta tags: # # # the "group" tag should be in each page # the "groupTitle" tag only needs to be in # one of each group's pages. # This scheme also relies on pages consistently having page titles, # and unique page titles within each group's pages. # set group to "NOSHOW" for pages not to appear in the menus. # also, each named group must have at least one associated page # so it can be displayed in the menus # if an argument of "-z" is provided to this script, # it will remove all its content from the # set of Web pages # WARNING: this script replaces page content en masse. # ALWAYS maintain a full backup of your page set. # Copyright (c) 2006, P. Lutus, released under the GPL. require 'find' # 1. Read options $zap = false $verbose = false while(ARGV[0] && ARGV[0].slice(0,1) == "-") case ARGV[0].slice(1,1) when 'z' then $zap = true when 'v' then $verbose = true end ARGV.shift end # 2. Set paths defaultPath="/path/to/Web/pages" sourcePath = (ARGV[0])?ARGV[0]:defaultPath $plainTextIndexPath = "/path/siteIndex.txt" $rightArrowPath = "#{sourcePath}/images/rightarrow.png" $leftArrowPath = "#{sourcePath}/images/leftarrow.png" # 3. Build list of all target pages pageList = Array.new Find.find(sourcePath) { |path| if (path =~ /\.(html?|php)$/i && !(path =~ /\/\~/)) pageList.push path end } # 4. Collect data from pages, build group tree groupTree = Hash.new # error analysis lists noGroupNameList = Array.new noPageTitleList = Array.new pageTitleNotUniqueList = Array.new noShowList = Array.new noGroupTitleList = Array.new groupTitleRedefList = Array.new $changedPages = Array.new $groupTitleString = " GroupTitle" $groupPathString = " GroupPath" $noGroupTitleString = "No Group Title!" $memberPagesString = " MemberPages" $indexListString = " IndexList" pageList.each { |path| data = File.read(path) if ( data =~ /.*/im,"\\1") if(groupPath.length > 0 && !(groupPath =~ /NOSHOW/i)) gh = groupTree gPath = groupPath.split("/") # descend into child hashes gPath.each { |node| if(!gh[node]) gh[node] = Hash.new gh[node][$groupTitleString] = $noGroupTitleString end gh = gh[node] } gh[$groupPathString] = groupPath if ( data =~ /.*/im,"\\1") if(gh[$groupTitleString] != $noGroupTitleString && gh[$groupTitleString] != groupTitle) puts "Error: group title redefinition in #{path}." groupTitleRedefList.push path else gh[$groupTitleString] = groupTitle end end gh[$memberPagesString] = Hash.new if !gh[$memberPagesString] gm = gh[$memberPagesString] pageTitle = data.sub(/.*\s*(.*?)\s*<\/title>.*/im,"\\1") if (!pageTitle || pageTitle.length == 0) puts "Error: no page title in page #{path}" noPageTitleList.push path else if (gm[pageTitle]) puts "Error: page title \"#{pageTitle}\" for #{path} is not unique." pageTitleNotUnique.push path end gm[pageTitle] = path end else noShowList.push path end else noGroupNameList.push path end } def makeRelPath(from,to) result = nil fromList = from.split("/") toList = to.split("/") # are the paths identical? if (from == to ) result = toList.pop; else # drop path elements until unequal while (fromList.first == toList.first) fromList.shift toList.shift end # create relative path from -> to result = "../" * (fromList.length-1) result += toList.join("/") end return result end def genIndexLists(hash) if(hash.class == Hash) if(hash[$memberPagesString]) # test: is there a group title? if(hash[$groupTitleString] == $noGroupTitleString) noGroupTitleList.push hash[$groupPathString] end # now create index list including primary page # key/value pair from each subgroup if(!hash[$indexListString]) indexList = Hash.new # add all existing members to list indexList.update(hash[$memberPagesString]) # now add subgroups and their # key pages to the list hash.keys.each do |key| if(!(key =~ /^ /) && hash[key].class == Hash) value = hash[key] # get primary page key from child group, # assign it to index list using group title as key indexList[value[$groupTitleString]] = genIndexLists(value) end end indexList = indexList.sort.collect # assure that the first listed item is not a group item = hash[$memberPagesString].sort.collect[0] indexList.delete(item) indexList.unshift(item) hash[$indexListString] = indexList end # primary page is first in alpha sort return hash[$indexListString][0][1] else hash.keys.each do |key| if(!(key =~ /^ /)) genIndexLists(hash[key]) end end end end end # 5. Generate index lists for use # in creating page indices genIndexLists(groupTree) $startBlockTag = "<!-- SiteIndexBegin -->" $endBlockTag = "<!-- SiteIndexEnd -->" $idTag = "<!-- Menus created by SiteGen, http://www.arachnoid.com/SiteGen -->" $scriptTag = "<script language=\"JavaScript\">\nfunction toNewPage(t) { location.href = t.options[t.selectedIndex].value; }\n</script>\n" def makeIndexBlock(hash,source,key,branch,plainIndexFile) indexBlock = $startBlockTag + "\n" + $idTag + "\n<table><tr>"; branch.each do |item| indexBlock += "<td><a href=\"" + makeRelPath(source, item[1]) + "\">" + item[0] + "</a> | </td>" end indexBlock += "<td>\n" + $scriptTag; indexBlock += "<select onChange=\"toNewPage(this)\" title=\"Open this list to choose a page\">\n"; # build drop-down list items selIndex = 0 k = 0 hash[$indexListString].each do |item| key = item[0] value = item[1] relPath = makeRelPath(source, value) sel = "" if(value == source) selIndex = k sel = " selected" end k += 1 indexBlock += "<option value=\"" + relPath + "\"" + sel + ">" + key + "\n"; end indexBlock += "</select>\n</td>" # now rescan for arrowed items k = 0 hash[$indexListString].each do |item| value = item[1] relPath = makeRelPath(source, value) if (k == selIndex - 1) # left arrow indexBlock += "<td><a href=\"" + relPath + "\" title=\"Click for prior page\">"; indexBlock += "<img src=\"" + makeRelPath(source, $leftArrowPath) + "\" border=\"0\"></a></td>" elsif (k == selIndex + 1) # right arrow indexBlock += "<td><a href=\"" + relPath + "\" title=\"Click for next page\">"; indexBlock += "<img src=\"" + makeRelPath(source, $rightArrowPath) + "\" border=\"0\"></a></td>" end k += 1 end indexBlock += "</tr></table>\n" + $endBlockTag; return indexBlock end def addToPlainIndex(branch,key,path,file) s = "" branch.each do |item| s += item[0] + "|" end s += key + "\t" + path + "\n" file.write(s) end def createPageIndexLists(hash,branch,plainIndexFile) if(hash.class == Hash) if(hash[$memberPagesString]) branch.push [hash[$groupTitleString],hash[$indexListString][0][1]] members = hash[$memberPagesString] members.keys.sort.each do |key| changed = false path = members[key] addToPlainIndex(branch,key,path,plainIndexFile) pageData = File.read(path); if($zap) # just zap existing blocks if ( pageData =~ /#{$startBlockTag}/ ) pageData.gsub!(/#{$startBlockTag}.*?#{$endBlockTag}\s*/im,"") changed = true end else topIndex = makeIndexBlock(hash,members[key],key,branch,plainIndexFile) # the bottom block doesn't need the JavaScript bottomIndex = topIndex.sub(/<script.*?<\/script>\n/im,"") origTopIndex,origBottomIndex = pageData.scan(/#{$startBlockTag}.*?#{$endBlockTag}/im) changed = (topIndex != origTopIndex || bottomIndex != origBottomIndex) if(changed) # zap existing blocks if ( pageData =~ /#{$startBlockTag}/ ) pageData.gsub!(/#{$startBlockTag}.*?#{$endBlockTag}\s*/im,"") end # put in new blocks pageData.sub!(/(<body.*?>)(\s*)/im,"\\1\n#{topIndex}\n") pageData.sub!(/(.*?)(\s*)(<\/body.*?>)/im,"\\1\n#{bottomIndex}\n\\3") end end if(changed) File.open(path,"w") { |f| f.write(pageData) } $changedPages.push path; end end hash.keys.sort.each do |key| if(!(key =~ /^ /)) createPageIndexLists(hash[key],branch,plainIndexFile) end end branch.pop else hash.keys.sort.each do |key| if(!(key =~ /^ /)) createPageIndexLists(hash[key],branch,plainIndexFile) end end end end end # 6. Write indices to pages groupPath = Array.new plainTextIndexFile = File.open($plainTextIndexPath,"w") createPageIndexLists(groupTree,groupPath,plainTextIndexFile) plainTextIndexFile.close() def showResults(tag,list,extended = true) s = sprintf("%-28s %3d",tag,list.length) puts s if($verbose && extended) puts list end end # 7. Display results showResults("Total Pages",pageList,false) showResults("Changed Pages",$changedPages) showResults("No Show",noShowList,false) showResults("No Group Name",noGroupNameList) showResults("No Group Title",noGroupTitleList) showResults("Group Title Redefinition",groupTitleRedefList) showResults("No Page Title",noPageTitleList) showResults("Page Title Not Unique",pageTitleNotUniqueList) # 8. Emit useful exit state exit( $changedPages.length != 0 )