-------------------------------------------------------------------------------- -- Title: WikiDateService.lua -- Description: Like a square peg in a round hole -- Author: Raphaël Szwarc http://alt.textdrive.com/lua/ -- Creation Date: January 30, 2007 -- Legal: Copyright (C) 2007 Raphaël Szwarc -- Under the terms of the MIT License -- http://www.opensource.org/licenses/mit-license.html -------------------------------------------------------------------------------- -- import dependencies local HTTP = require( 'HTTP' ) local HTTPExtra = require( 'HTTPExtra' ) local Template = require( 'Template' ) local URL = require( 'URL' ) local URLPath = require( 'URLPath' ) local WikiDate = require( 'WikiDate' ) local WikiService = require( 'WikiService' ) local BaseLink = WikiService.BaseLink local DateLink = WikiService.DateLink local FeedLink = WikiService.FeedLink local IndexLink = WikiService.IndexLink local ContentIterator = WikiService.ContentIterator local Description = WikiService.Description local Encode = WikiService.Encode local FormatDate = WikiService.FormatDate local GetType = WikiService.GetType local HTML = WikiService.HTML local Path = WikiService.Path local Tag = WikiService.Tag local os = require( 'os' ) local getmetatable = getmetatable local setmetatable = setmetatable local require = require local tonumber = tonumber local tostring = tostring -------------------------------------------------------------------------------- -- WikiDateService -------------------------------------------------------------------------------- module( 'WikiDateService' ) _VERSION = '1.0' local self = setmetatable( _M, {} ) local meta = getmetatable( self ) -------------------------------------------------------------------------------- -- Utilities -------------------------------------------------------------------------------- local function Title( aYear, aMonth, aDay ) local aTime = os.time( { year = aYear or 1, month = aMonth or 1, day = aDay or 1 } ) local aFormat = nil if aDay then return FormatDate( aTime ) elseif aMonth then aFormat = '!%B %Y' else aFormat = '!%Y' end return os.date( aFormat, aTime ) end local function PathArgument( self ) if self.day then local WikiDateService = require( 'WikiDateService' ) local aMonthService = WikiDateService( self.year, self.month ) local aYearService = WikiDateService( self.year ) aMonthService.path = self.path() aYearService.path = aMonthService.path() return self, aMonthService, aYearService end if self.month then local WikiDateService = require( 'WikiDateService' ) local aYearService = WikiDateService( self.year ) aYearService.path = self.path() return self, aYearService end return self end -------------------------------------------------------------------------------- -- DAV Utilities -------------------------------------------------------------------------------- local function DAVYearIterator( aCurrentYear ) local anIterator = WikiDate() return function() local aYear, aFile = anIterator() while aCurrentYear and aCurrentYear ~= aYear and aYear do aYear, aFile = anIterator() end if aYear then local aName = ( '%04d' ):format( aYear ) local aModification = aFile.modification local aResource = { name = aName, mode = 'directory', modification = aModification, size = 0 } return aResource end end end local function DAVMonthIterator( aYear ) local aDate = { year = aYear } local anIterator = WikiDate( aDate ) return function() local aMonth, aFile = anIterator() if aMonth then local aName = ( '%02d' ):format( aMonth ) local aModification = aFile.modification local aResource = { name = aName, mode = 'directory', modification = aModification, size = 0 } return aResource end end end local function DAVDayIterator( aYear, aMonth ) local aDate = { year = aYear, month = aMonth } local anIterator = WikiDate( aDate ) return function() local aDay, aFile = anIterator() if aDay then local aName = ( '%02d' ):format( aDay ) local aModification = aFile.modification local aResource = { name = aName, mode = 'directory', modification = aModification, size = 0 } return aResource end end end local function DAVDateIterator( aYear, aMonth, aDay ) local WikiContent = require( 'WikiContent' ) local aDate = { year = aYear, month = aMonth, day = aDay } local anIterator = WikiDate[ aDate ] return function() local aName = anIterator() if aName then local aContent = WikiContent( aName ) local aModification = aContent.modification local aResource = { name = aName, mode = 'directory', modification = aModification, size = 0 } return aResource end end end local function DAVIterator( aDate ) local aType = aDate.type if aType == 'year' then return DAVMonthIterator( aDate.year ) elseif aType == 'month' then return DAVDayIterator( aDate.year, aDate.month ) elseif aType == 'day' then return DAVDateIterator( aDate.year, aDate.month, aDate.day ) end return DAVYearIterator() end local function DAVResource( aDate ) local anIterator = DAVIterator( aDate ) local aModification = WikiDate[ 'modification' ] local aResource = { iterator = anIterator, mode = 'directory', modification = aModification, size = 0 } return aResource end -------------------------------------------------------------------------------- -- Service methods -------------------------------------------------------------------------------- self.toURL = function( aService, anObject ) local aPath = URLPath() aPath.absolute = true if anObject.year then aPath[ #aPath + 1 ] = ( '%04d' ):format( anObject.year ) end if anObject.month then aPath[ #aPath + 1 ] = ( '%02d' ):format( anObject.month ) end if anObject.day then aPath[ #aPath + 1 ] = ( '%02d' ):format( anObject.day ) end return URL( aService.prefix .. aPath ) end self.toObject = function( aService, aURL ) local aPath = aURL.path local aYear = tonumber( aPath[ 2 ] ) local aMonth = tonumber( aPath[ 3 ] ) local aDay = tonumber( aPath[ 4 ] ) local WikiDateService = require( 'WikiDateService' ) local aService = WikiDateService( aYear, aMonth, aDay ) return aService end function self:getHtml() local WikiDateNavigation = require( 'WikiDateNavigation' ) local aLayoutTemplate = Template[ 'WikiLayout.txt' ] local aTemplate = Template[ 'WikiDateService.txt' ] local aNameTemplate = aTemplate[ 'names' ] local anIterator, aCount, hasMore = ContentIterator( WikiDate[ self ] ) local aTitle = Title( self.year, self.month, self.day ) aTemplate[ 'names' ] = nil aTemplate[ 'description' ] = Encode( Description( aCount, hasMore ) ) aTemplate[ 'navigation' ] = WikiDateNavigation( self ) if aCount == 0 then HTTP.response.status.code = 404 HTTP.response.status.description = 'Not Found' end for aContent, aURL in anIterator do local aNameTemplate = aTemplate[ 'names' ] aNameTemplate[ 'href' ] = Encode( aURL.path ) aNameTemplate[ 'name' ] = Encode( aContent.title ) aNameTemplate[ 'tag' ] = Tag( aContent.modification, true ) aTemplate[ 'names' ] = aNameTemplate end aTemplate[ 'title' ] = Encode( aTitle ) aLayoutTemplate[ 'baseLink' ] = Encode( BaseLink() ) aLayoutTemplate[ 'indexLink' ] = Encode( IndexLink() ) aLayoutTemplate[ 'dateLink' ] = Encode( DateLink( self.year, self.month, self.day ) ) aLayoutTemplate[ 'feedLink' ] = FeedLink( self, aCount ) aLayoutTemplate[ 'path' ] = Path( PathArgument( self ) ) aLayoutTemplate[ 'query' ] = nil aLayoutTemplate[ 'robot' ] = 'noindex' aLayoutTemplate[ 'title' ] = Encode( aTitle .. ' — Date' ) aLayoutTemplate[ 'content' ] = aTemplate return tostring( aLayoutTemplate ) end function self:getXml() local WikiFeed = require( 'WikiFeed' ) local WikiIndex = require( 'WikiIndex' ) local anIterator = ContentIterator( WikiDate[ self ] ) local aGenerator = HTML local aTitle = Title( self.year, self.month, self.day ) .. ' — Date' local aContext = { title = aTitle, link = HTTP.request.url, creation = WikiIndex[ 'creation' ] } HTTP.response.header[ 'content-type' ] = 'application/atom+xml; charset=utf-8' return tostring( WikiFeed( anIterator, aGenerator, aContext ) ) end function self:get( aType ) if self.year then return GetType( self, aType ) end return nil, HTTP.request.url.path( os.date( '!*t' ).year ) end -------------------------------------------------------------------------------- -- DAV service methods -------------------------------------------------------------------------------- function self:options() HTTP.response.header[ 'allow' ] = 'GET, HEAD, OPTIONS, PROPFIND' HTTP.response.header[ 'content-type' ] = 'text/plain' return HTTP.response.header[ 'allow' ] end function self:propfind() local WikiDAV = require( 'WikiDAV' ) local aResource = DAVResource( self ) return WikiDAV( aResource ):propfind() end -------------------------------------------------------------------------------- -- Metamethods -------------------------------------------------------------------------------- function meta:__call( aYear, aMonth, aDay ) local aService = { year = aYear, month = aMonth, day = aDay } if aYear then aService.type = 'year' end if aMonth then aService.type = 'month' end if aDay then aService.type = 'day' end if aYear then local aTime = os.time( { year = aYear, month = aMonth or 1, day = aDay or 1 } ) local aDate = os.date( '!*t', aTime ) if aYear and aYear ~= aDate.year then aService.year = aDate.year end if aMonth and aMonth ~= aDate.month then aService.month = aDate.month end if aDay and aDay ~= aDate.day then aService.day = aDate.day end end setmetatable( aService, self ) return aService end function meta:__concat( aValue ) return tostring( self ) .. tostring( aValue ) end function meta:__tostring() return ( '%s/%s' ):format( self._NAME, self._VERSION ) end function self:__index( aKey ) local aValue = getmetatable( self )[ aKey ] if not aValue and aKey:find( '^get.+' ) then local WikiContent = require( 'WikiContent' ) local aName = aKey:match( '^get(.+)$' ) local aContent = WikiContent( aName ) if aContent and aContent.exists then local WikiContentService = require( 'WikiContentService' ) local aService = WikiContentService( aContent ) return function() return aService end end end self[ aKey ] = aValue return aValue end function self:__concat( aValue ) return tostring( self ) .. tostring( aValue ) end function self:__tostring() return Title( self.year, self.month, self.day ) end