root/HTTP/WikiService.lua

Revision 1548 (checked in by rsz, 5 months ago)

cleanup

Line 
1 --------------------------------------------------------------------------------
2 -- Title:               WikiService.lua
3 -- Description:         Like a square peg in a round hole
4 -- Author:              Raphaël Szwarc http://alt.textdrive.com/lua/
5 -- Creation Date:       January 30, 2007
6 -- Legal:               Copyright (C) 2007 Raphaël Szwarc
7 --                      Under the terms of the MIT License
8 --                      http://www.opensource.org/licenses/mit-license.html
9 --------------------------------------------------------------------------------
10
11 -- import dependencies
12 local HTTP = require( 'HTTP' )
13
14 local os = require( 'os' )
15 local table = require( 'table' )
16
17 local getmetatable = getmetatable
18 local next = next
19 local pairs = pairs
20 local require = require
21 local select = select
22 local setmetatable = setmetatable
23 local tonumber = tonumber
24 local tostring = tostring
25 local type = type
26
27 --------------------------------------------------------------------------------
28 -- WikiService
29 --------------------------------------------------------------------------------
30
31 module( 'WikiService' )
32 _VERSION = '1.0'
33
34 local self = setmetatable( _M, {} )
35 local meta = getmetatable( self )
36
37 --------------------------------------------------------------------------------
38 -- Utilities
39 --------------------------------------------------------------------------------
40
41 function Capitalize( aValue )
42     return ( tostring( aValue or '' ):lower():gsub( '(%l)([%w_\']*)', function( first, second ) return first:upper() .. second end ) )
43 end
44
45 function Encode( aValue )
46     local anEncoder = function( aValue )
47         return ( '&#%02d;' ):format( aValue:byte() )
48     end
49    
50     return ( tostring( aValue or '' ):gsub( '([<>&\'"])', anEncoder ) )
51 end
52
53 function Trim( aValue )
54     return ( tostring( aValue or '' ):gsub( '^[%c%s]+', '' ):gsub( '[%s%c]+$', '' ) )
55 end
56
57 --------------------------------------------------------------------------------
58 -- Name utilities
59 --------------------------------------------------------------------------------
60
61 function Names( anIterator )
62     local NaturalComparator = require( 'NaturalComparator' )
63     local WikiContent = require( 'WikiContent' )
64     local aMap = {}
65     local aList = {}
66     local anIndex = 1
67     local hasMore = false
68    
69     for aName in anIterator do
70         aMap[ aName ] = true
71    
72         anIndex = anIndex + 1
73
74         if anIndex == 1000 then
75             hasMore = true
76             break
77         end
78     end
79    
80     anIndex = 1
81    
82     for aName, aValue in pairs( aMap ) do
83         aList[ anIndex ] = aName
84         anIndex = anIndex + 1
85     end
86    
87     table.sort( aList, NaturalComparator() )
88    
89     return aList, hasMore
90 end
91
92 function NameIterator( someNames )
93     local HTTPExtra = require( 'HTTPExtra' )
94     local HTTPService = require( 'HTTPService' )
95     local WikiContent = require( 'WikiContent' )
96     local WikiContentService = require( 'WikiContentService' )
97     local aCount = #someNames
98     local anIndex = 1
99    
100     return function()
101         if anIndex <= aCount then
102             local aName = someNames[ anIndex ]
103             local aContent = WikiContent( aName )
104             local aService = WikiContentService( aContent )
105             local aURL = HTTPService[ aService ]
106            
107             anIndex = anIndex + 1
108            
109             return aContent, aURL
110         end
111     end
112 end
113
114 function ContentIterator( anIterator )
115     local someNames, hasMore = Names( anIterator )
116    
117     return NameIterator( someNames ), #someNames, hasMore
118 end
119
120 function Description( aCount, hasMore )
121     aCount = aCount or 0
122    
123     if aCount == 0 then
124         return 'no item'
125     elseif aCount == 1 then
126         return '1 item'
127     else
128         local aDescription = ( '%d items' ):format( aCount )
129        
130         if hasMore then
131             aDescription = 'over ' .. aDescription
132         end
133        
134         return aDescription
135     end
136 end
137
138 --------------------------------------------------------------------------------
139 -- Format utilities
140 --------------------------------------------------------------------------------
141
142 local suffixes = { 'st', 'nd', 'rd' }
143
144 local function Suffix( aNumber )
145     local aSuffix = 'th'
146    
147     if aNumber ~= 11 and aNumber ~= 12 and aNumber ~= 13 then
148         if aNumber > 9 then
149             aNumber = aNumber % 10   
150         end
151        
152         aSuffix = suffixes[ aNumber ] or aSuffix
153     end
154    
155     return aSuffix
156 end
157
158 local function FormatDay( aDay )
159     local aDay = tonumber( aDay )
160     local aSuffix = Suffix( aDay )
161    
162     return ' ' .. aDay .. aSuffix .. ' '
163 end
164
165 function FormatDate( aTime )
166     return os.date( '!%A, %B %d %Y', aTime ):gsub( '%s(%d%d)%s', FormatDay )
167 end
168
169 function FormatTime( aTime )
170     return os.date( '!%I:%M %p', aTime ):gsub( '^0(%d)', '%1' )
171 end
172
173 function FormatDateTime( aTime )
174     return ( '%s at %s' ):format( FormatDate( aTime), FormatTime( aTime ) )
175 end
176
177 function Today( aModification )
178     local aModification = aModification or 0
179     local aDate = os.date( '!*t' )
180     local aStartDate = { year = aDate.year, month = aDate.month, day = aDate.day, hour = 0, min = 0, sec = 0, isdst = aDate.isdst }
181     local aStartTime = os.time( aStartDate )
182     local anEndDate = { year = aDate.year, month = aDate.month, day = aDate.day, hour = 23, min = 59, sec = 59, isdst = aDate.isdst }
183     local anEndTime = os.time( anEndDate )
184     local isToday = aModification >= aStartTime and aModification <= anEndTime
185
186     return isToday, aStartTime, anEndTime
187 end
188
189 function Yesterday( aModification )
190     local aModification = aModification or 0
191     local _, aStartTime, anEndTime = Today( aModification )
192     local aStartTime = aStartTime - 86400
193     local anEndTime = anEndTime - 86400
194     local isYesterday = aModification >= aStartTime and aModification <= anEndTime
195
196     return isYesterday, aStartTime, anEndTime
197 end
198
199 function ThisWeek( aModification )
200     local aModification = aModification or 0
201     local _, aStartTime, anEndTime = Today( aModification )
202     local aStartTime = aStartTime - ( 7 * 86400 )
203     local isThisWeek = aModification >= aStartTime and aModification <= anEndTime
204    
205     return isThisWeek, aStartTime, anEndTime
206 end
207
208 --------------------------------------------------------------------------------
209 -- Content utilities
210 --------------------------------------------------------------------------------
211
212 function BaseLink()
213     return HTTP.request.url + '/'
214 end
215
216 function FeedLink( aService, aCount )
217     if aService and aCount and aCount > 0 then
218         local aQuery = HTTP.request.url.query
219         local aLink = tostring( aService.path )
220        
221         if not aLink:find( '%.xml$' ) then
222             aLink = aLink .. '.xml'
223         end
224        
225         aLink = Encode( aLink )
226        
227         if aQuery and next( aQuery ) then
228             aLink = aLink .. '?' .. aQuery
229         end
230        
231         return ( '<link rel=\'alternate\' type=\'application/atom+xml\' href=\'%s\'/>' ):format( aLink )
232     end
233 end
234
235 function IndexLink( aPrefix )
236     local HTTPExtra = require( 'HTTPExtra' )
237     local HTTPService = require( 'HTTPService' )
238     local WikiIndexService = require( 'WikiIndexService' )
239     local aFirst, aSecond = ( aPrefix or '' ):match( '^(%w)(%w?)' )
240     local aService = WikiIndexService( aFirst, aSecond )
241     local aURL = HTTPService[ aService ]
242    
243     return aURL.path
244 end
245
246 function DateLink( aYear, aMonth, aDay )
247     local HTTPExtra = require( 'HTTPExtra' )
248     local HTTPService = require( 'HTTPService' )
249     local WikiDateService = require( 'WikiDateService' )
250     local aService = WikiDateService( aYear, aMonth, aDay )
251     local aURL = HTTPService[ aService ]
252    
253     return aURL.path
254 end
255
256 function Path( aService, ... )
257     local WikiPath = require( 'WikiPath' )
258     local aPath = WikiPath()
259     local aList = {}
260    
261     aList[ #aList + 1 ] = { href = aService.path, title = tostring( aService ) }
262     aService = aService.parent
263    
264     for anIndex = 1, select( '#', ... ) do
265         local aService = select( anIndex, ... )
266        
267         if aService then
268             aList[ #aList + 1 ] = { href = aService.path, title = tostring( aService ) }
269         end
270     end
271
272     while aService do
273         aList[ #aList + 1 ] = { href = aService.path, title = tostring( aService ) }
274         aService = aService.parent
275     end
276
277     for anIndex = #aList, 1, -1 do
278         aPath[ #aPath + 1 ] = aList[ anIndex ]
279     end
280    
281     return aPath
282 end
283
284 function Render( aText )
285     if aText then
286         local markdown = require( 'markdown' )
287
288         aText = aText:gsub( '(`?)(<.->)(`?)', '`%2`' )
289         aText = markdown( aText )
290         aText = aText:gsub( '(`)(&lt;.-&gt;)(`)', '%2' )
291        
292         return aText
293     end
294 end
295
296 function HTML( aContent )
297     local Cache = require( 'Cache' )
298     local File = require( 'File' )
299     local aTime = aContent.modification
300     local aPath = aContent.directory.path
301     local aFile = File( aPath, 'data.html' )
302     local aContent = function()
303         return Render( aContent.text )
304     end
305    
306     return Cache( aFile.path, aContent, aTime )
307 end
308
309 function Text( aContent )
310     local aText = HTML( aContent )
311    
312     if aText then
313         aText = aText:gsub( '(<.->)', '' )
314     end
315    
316     return aText
317 end
318
319 function GetType( self, aType )
320     local aMethod = 'get' .. Capitalize( aType or 'html' )
321    
322     if type( self[ aMethod ] ) == 'function' then
323         return self[ aMethod ]( self )
324     end
325 end
326
327 function Tag( aModification, isSmall )
328     local aDay = os.date( '!%A', aModification )
329     local anHour = tonumber( os.date( '!%I', aModification ) )
330     local aMeridian = os.date( '!%p', aModification )
331     local aTag = nil
332     local aLabel = nil
333    
334     if Today( aModification ) then
335         aLabel = ( 'Edited today around %d %s' ):format( anHour, aMeridian )
336         aTag = 1
337     elseif Yesterday( aModification ) then
338         aLabel = ( 'Edited yesterday around %d %s' ):format( anHour, aMeridian )
339         aTag = 2
340     elseif ThisWeek( aModification ) then
341         aLabel = ( 'Edited last %s around %d %s' ):format( aDay, anHour, aMeridian )
342         aTag = 3
343     end
344    
345     if aTag and aLabel then
346         local aType = 'tag'
347        
348         if isSmall then
349             aType = 'bullet'
350         end
351    
352         return ( '<img src=\'/etc/%s%d.png\' alt=\'tag\' title=\'%s\' class=\'navigation\' style=\'vertical-align: text-bottom;\' height=\'16\' width=\'16\'/>' ):format( aType, aTag, aLabel )
353     end
354 end
355
356 local function Name( aName, anAddress )
357     local WikiContent = require( 'WikiContent' )
358     local aName = aName or ''
359    
360     if aName == 'anonymous' then
361         local IPMnemonic = require( 'IPMnemonic' )
362        
363         aName = IPMnemonic[ anAddress ] or aName
364     end
365    
366     aName = WikiContent[ aName ] or ''
367     aName = aName:gsub( '%W', ' ' )
368     aName = Capitalize( aName )
369    
370     return aName
371 end
372
373 local function Location( aContent, anAddress )
374     local aLocation = aContent.location
375
376     if not aLocation then
377         local IPLocation = require( 'IPLocation' )
378        
379         aLocation = IPLocation[ anAddress ]
380         aContent.location = aLocation
381     end
382
383     if aLocation then
384         local aBuffer = {}
385        
386         if aLocation.city and aLocation.city ~= '-' then
387             aBuffer[ #aBuffer + 1 ] = aLocation.city
388         elseif aLocation.region and aLocation.region ~= '-' then
389             aBuffer[ #aBuffer + 1 ] = aLocation.region
390         end
391        
392         if aLocation.country and aLocation.country ~= '-' then
393             aBuffer[ #aBuffer + 1 ] = aLocation.country
394         end
395        
396         aLocation = table.concat( aBuffer, ', ' )
397     end
398
399     return aLocation
400 end
401
402 function By( aContent, hasTitle )
403     local URL = require( 'URL' )
404     local HTTPService = require( 'HTTPService' )
405     local WikiContent = require( 'WikiContent' )
406     local WikiContentService = require( 'WikiContentService' )
407     local aURL = URL( aContent.by )
408     local anUser = aURL.user
409     local anAddress = aURL.password
410     local aHost = aURL.host
411     local aName = Name( anUser, anAddress )
412     local aNameContent = WikiContent( aName )
413     local aNameService = WikiContentService( aNameContent )
414     local aNameURL = HTTPService[ aNameService ]
415     local aLocation = Location( aContent, anAddress )
416     local aBuffer = {}
417    
418     if hasTitle then
419         aBuffer[ #aBuffer + 1 ] = ( '<a href=\'%s\' title=\'%s\'>%s</a>' ):format( Encode( aNameURL ), Encode( anAddress or '?' ), Encode( aName ) )
420     else
421         aBuffer[ #aBuffer + 1 ] = Encode( aName )
422     end
423    
424     if hasTitle and aLocation then
425         aBuffer[ #aBuffer + 1 ] = ( ', <span title=\'%s\'>%s</span>' ):format( Encode( aHost or '?' ), Encode( aLocation ) )
426     elseif aLocation then
427         aBuffer[ #aBuffer + 1 ] = ( ', %s' ):format( aLocation )
428     end
429    
430     return table.concat( aBuffer )
431 end
432
433 --------------------------------------------------------------------------------
434 -- Metamethods
435 --------------------------------------------------------------------------------
436
437 function meta:__concat( aValue )
438     return tostring( self ) .. tostring( aValue )
439 end
440
441 function meta:__tostring()
442     return ( '%s/%s' ):format( self._NAME, self._VERSION )
443 end
Note: See TracBrowser for help on using the browser.