feed-1.0.1.0/src/0000755000000000000000000000000013354421723011533 5ustar0000000000000000feed-1.0.1.0/src/Data/0000755000000000000000000000000013354421723012404 5ustar0000000000000000feed-1.0.1.0/src/Data/Text/0000755000000000000000000000000013370672700013331 5ustar0000000000000000feed-1.0.1.0/src/Data/XML/0000755000000000000000000000000013354421723013044 5ustar0000000000000000feed-1.0.1.0/src/Text/0000755000000000000000000000000013354421723012457 5ustar0000000000000000feed-1.0.1.0/src/Text/Atom/0000755000000000000000000000000013354421723013357 5ustar0000000000000000feed-1.0.1.0/src/Text/Atom/Feed/0000755000000000000000000000000013370672700014223 5ustar0000000000000000feed-1.0.1.0/src/Text/Atom/Pub/0000755000000000000000000000000013354421723014105 5ustar0000000000000000feed-1.0.1.0/src/Text/DublinCore/0000755000000000000000000000000013354421723014505 5ustar0000000000000000feed-1.0.1.0/src/Text/Feed/0000755000000000000000000000000013370672700013323 5ustar0000000000000000feed-1.0.1.0/src/Text/RSS/0000755000000000000000000000000013370672700013127 5ustar0000000000000000feed-1.0.1.0/src/Text/RSS1/0000755000000000000000000000000013370672700013210 5ustar0000000000000000feed-1.0.1.0/tests/0000755000000000000000000000000013354421723012106 5ustar0000000000000000feed-1.0.1.0/tests/Example/0000755000000000000000000000000013354421723013501 5ustar0000000000000000feed-1.0.1.0/tests/Text/0000755000000000000000000000000013354421723013032 5ustar0000000000000000feed-1.0.1.0/tests/Text/Atom/0000755000000000000000000000000013354421723013732 5ustar0000000000000000feed-1.0.1.0/tests/Text/Feed/0000755000000000000000000000000013354421723013675 5ustar0000000000000000feed-1.0.1.0/tests/Text/Feed/Util/0000755000000000000000000000000013354421723014612 5ustar0000000000000000feed-1.0.1.0/tests/Text/RSS/0000755000000000000000000000000013354421723013501 5ustar0000000000000000feed-1.0.1.0/tests/Text/RSS/Export/0000755000000000000000000000000013354421723014762 5ustar0000000000000000feed-1.0.1.0/tests/Text/RSS/Import/0000755000000000000000000000000013354421723014753 5ustar0000000000000000feed-1.0.1.0/tests/files/0000755000000000000000000000000013354421723013210 5ustar0000000000000000feed-1.0.1.0/src/Text/Atom/Feed.hs0000644000000000000000000001442613354421723014565 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Atom.Feed -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.Atom.Feed ( URI , NCName , Date , MediaType , Attr , Feed(..) , Entry(..) , EntryContent(..) , Category(..) , Generator(..) , Link(..) , TextContent(..) , txtToString , Source(..) , Person(..) , InReplyTo(..) , InReplyTotal(..) , newCategory , nullFeed , nullEntry , nullGenerator , nullLink , nullSource , nullPerson ) where import Prelude.Compat import Data.Text (Text, unpack) import Data.XML.Compat import Data.XML.Types as XML -- *Core types -- NOTE: In the future we may want to have more structured -- types for these. type URI = Text type NCName = Text type Date = Text type MediaType = Text data Feed = Feed { feedId :: URI , feedTitle :: TextContent , feedUpdated :: Date , feedAuthors :: [Person] , feedCategories :: [Category] , feedContributors :: [Person] , feedGenerator :: Maybe Generator , feedIcon :: Maybe URI , feedLinks :: [Link] , feedLogo :: Maybe URI , feedRights :: Maybe TextContent , feedSubtitle :: Maybe TextContent , feedEntries :: [Entry] , feedAttrs :: [Attr] , feedOther :: [XML.Element] } deriving (Show) data Entry = Entry { entryId :: URI , entryTitle :: TextContent , entryUpdated :: Date , entryAuthors :: [Person] , entryCategories :: [Category] , entryContent :: Maybe EntryContent , entryContributor :: [Person] , entryLinks :: [Link] , entryPublished :: Maybe Date , entryRights :: Maybe TextContent , entrySource :: Maybe Source , entrySummary :: Maybe TextContent , entryInReplyTo :: Maybe InReplyTo , entryInReplyTotal :: Maybe InReplyTotal , entryAttrs :: [Attr] , entryOther :: [XML.Element] } deriving (Show) data EntryContent = TextContent Text | HTMLContent Text | XHTMLContent XML.Element | MixedContent (Maybe Text) [XML.Node] | ExternalContent (Maybe MediaType) URI deriving (Show) data Category = Category { catTerm :: Text -- ^ the tag\/term of the category. , catScheme :: Maybe URI -- ^ optional URL for identifying the categorization scheme. , catLabel :: Maybe Text -- ^ human-readable label of the category , catOther :: [XML.Element] -- ^ unknown elements, for extensibility. } deriving (Show) data Generator = Generator { genURI :: Maybe URI , genVersion :: Maybe Text , genText :: Text } deriving (Eq, Show) data Link = Link { linkHref :: URI -- ToDo: make the switch over to using the Atom.Feed.Link relation type. , linkRel :: Maybe (Either NCName URI) , linkType :: Maybe MediaType , linkHrefLang :: Maybe Text , linkTitle :: Maybe Text , linkLength :: Maybe Text , linkAttrs :: [Attr] , linkOther :: [XML.Element] } deriving (Show) data TextContent = TextString Text | HTMLString Text | XHTMLString XML.Element deriving (Show) txtToString :: TextContent -> String txtToString (TextString s) = unpack s txtToString (HTMLString s) = unpack s txtToString (XHTMLString x) = show x data Source = Source { sourceAuthors :: [Person] , sourceCategories :: [Category] , sourceGenerator :: Maybe Generator , sourceIcon :: Maybe URI , sourceId :: Maybe URI , sourceLinks :: [Link] , sourceLogo :: Maybe URI , sourceRights :: Maybe TextContent , sourceSubtitle :: Maybe TextContent , sourceTitle :: Maybe TextContent , sourceUpdated :: Maybe Date , sourceOther :: [XML.Element] } deriving (Show) data Person = Person { personName :: Text , personURI :: Maybe URI , personEmail :: Maybe Text , personOther :: [XML.Element] } deriving (Show) data InReplyTo = InReplyTo { replyToRef :: URI , replyToHRef :: Maybe URI , replyToType :: Maybe MediaType , replyToSource :: Maybe URI , replyToOther :: [Attr] , replyToContent :: [Node] } deriving (Show) data InReplyTotal = InReplyTotal { replyToTotal :: Integer -- non-negative :) , replyToTotalOther :: [Attr] } deriving (Show) -- *Smart Constructors newCategory :: Text -- ^catTerm -> Category newCategory t = Category {catTerm = t, catScheme = Nothing, catLabel = Just t, catOther = []} nullFeed :: URI -- ^feedId -> TextContent -- ^feedTitle -> Date -- ^feedUpdated -> Feed nullFeed i t u = Feed { feedId = i , feedTitle = t , feedUpdated = u , feedAuthors = [] , feedCategories = [] , feedContributors = [] , feedGenerator = Nothing , feedIcon = Nothing , feedLinks = [] , feedLogo = Nothing , feedRights = Nothing , feedSubtitle = Nothing , feedEntries = [] , feedAttrs = [] , feedOther = [] } nullEntry :: URI -- ^entryId -> TextContent -- ^entryTitle -> Date -- ^entryUpdated -> Entry nullEntry i t u = Entry { entryId = i , entryTitle = t , entryUpdated = u , entryAuthors = [] , entryCategories = [] , entryContent = Nothing , entryContributor = [] , entryLinks = [] , entryPublished = Nothing , entryRights = Nothing , entrySource = Nothing , entrySummary = Nothing , entryInReplyTo = Nothing , entryInReplyTotal = Nothing , entryAttrs = [] , entryOther = [] } nullGenerator :: Text -- ^genText -> Generator nullGenerator t = Generator {genURI = Nothing, genVersion = Nothing, genText = t} nullLink :: URI -- ^linkHref -> Link nullLink uri = Link { linkHref = uri , linkRel = Nothing , linkType = Nothing , linkHrefLang = Nothing , linkTitle = Nothing , linkLength = Nothing , linkAttrs = [] , linkOther = [] } nullSource :: Source nullSource = Source { sourceAuthors = [] , sourceCategories = [] , sourceGenerator = Nothing , sourceIcon = Nothing , sourceId = Nothing , sourceLinks = [] , sourceLogo = Nothing , sourceRights = Nothing , sourceSubtitle = Nothing , sourceTitle = Nothing , sourceUpdated = Nothing , sourceOther = [] } nullPerson :: Person nullPerson = Person {personName = "", personURI = Nothing, personEmail = Nothing, personOther = []} feed-1.0.1.0/src/Text/Atom/Feed/Export.hs0000644000000000000000000002102413370672700016037 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Atom.Feed.Export -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability:: portable -- Description: Convert from Atom to XML -- -- Convert from Atom to XML -- -------------------------------------------------------------------- module Text.Atom.Feed.Export ( atom_prefix , atom_thr_prefix , atomNS , atomThreadNS , xmlns_atom , xmlns_atom_thread , atomName , atomAttr , atomNode , atomLeaf , atomThreadName , atomThreadAttr , atomThreadNode , atomThreadLeaf , xmlFeed , textFeed , xmlEntry , xmlContent , xmlCategory , xmlLink , xmlSource , xmlGenerator , xmlAuthor , xmlContributor , xmlPerson , xmlInReplyTo , xmlInReplyTotal , xmlId , xmlIcon , xmlLogo , xmlUpdated , xmlPublished , xmlRights , xmlTitle , xmlSubtitle , xmlSummary , xmlTextContent , mb ) where import Prelude.Compat import Data.Text (Text, pack) import Data.XML.Types as XML import Text.Atom.Feed import qualified Data.Text.Lazy as TL import qualified Data.Text.Util as U atom_prefix :: Maybe Text atom_prefix = Nothing -- Just "atom" atom_thr_prefix :: Maybe Text atom_thr_prefix = Just "thr" atomNS :: Text atomNS = "http://www.w3.org/2005/Atom" atomThreadNS :: Text atomThreadNS = "http://purl.org/syndication/thread/1.0" blank_element :: Name -> [Node] -> XML.Element blank_element name = XML.Element name [] xmlns_atom :: Attr xmlns_atom = (qn, [ContentText atomNS]) where qn = case atom_prefix of Nothing -> Name {nameLocalName = "xmlns", nameNamespace = Nothing, namePrefix = Nothing} Just s -> Name { nameLocalName = s , nameNamespace = Nothing -- XXX: is this ok? , namePrefix = Just "xmlns" } xmlns_atom_thread :: Attr xmlns_atom_thread = (qn, [ContentText atomThreadNS]) where qn = case atom_prefix of Nothing -> Name {nameLocalName = "xmlns", nameNamespace = Nothing, namePrefix = Nothing} Just s -> Name { nameLocalName = s , nameNamespace = Nothing -- XXX: is this ok? , namePrefix = Just "xmlns" } atomName :: Text -> Name atomName nc = Name {nameLocalName = nc, nameNamespace = Just atomNS, namePrefix = atom_prefix} atomAttr :: Text -> Text -> Attr atomAttr x y = (atomName x, [ContentText y]) atomNode :: Text -> [Node] -> XML.Element atomNode x = blank_element (atomName x) atomLeaf :: Text -> Text -> XML.Element atomLeaf tag txt = blank_element (atomName tag) [NodeContent $ ContentText txt] atomThreadName :: Text -> Name atomThreadName nc = Name {nameLocalName = nc, nameNamespace = Just atomThreadNS, namePrefix = atom_thr_prefix} atomThreadAttr :: Text -> Text -> Attr atomThreadAttr x y = (atomThreadName x, [ContentText y]) atomThreadNode :: Text -> [Node] -> XML.Element atomThreadNode x = blank_element (atomThreadName x) atomThreadLeaf :: Text -> Text -> XML.Element atomThreadLeaf tag txt = blank_element (atomThreadName tag) [NodeContent $ ContentText txt] -------------------------------------------------------------------------------- xmlFeed :: Feed -> XML.Element xmlFeed f = (atomNode "feed" $ map NodeElement $ [xmlTitle (feedTitle f)] ++ [xmlId (feedId f)] ++ [xmlUpdated (feedUpdated f)] ++ map xmlLink (feedLinks f) ++ map xmlAuthor (feedAuthors f) ++ map xmlCategory (feedCategories f) ++ map xmlContributor (feedContributors f) ++ mb xmlGenerator (feedGenerator f) ++ mb xmlIcon (feedIcon f) ++ mb xmlLogo (feedLogo f) ++ mb xmlRights (feedRights f) ++ mb xmlSubtitle (feedSubtitle f) ++ map xmlEntry (feedEntries f) ++ feedOther f) {elementAttributes = [xmlns_atom]} textFeed :: Feed -> Maybe TL.Text textFeed = U.renderFeed xmlFeed xmlEntry :: Entry -> XML.Element xmlEntry e = (atomNode "entry" $ map NodeElement $ [xmlId (entryId e)] ++ [xmlTitle (entryTitle e)] ++ [xmlUpdated (entryUpdated e)] ++ map xmlAuthor (entryAuthors e) ++ map xmlCategory (entryCategories e) ++ mb xmlContent (entryContent e) ++ map xmlContributor (entryContributor e) ++ map xmlLink (entryLinks e) ++ mb xmlPublished (entryPublished e) ++ mb xmlRights (entryRights e) ++ mb xmlSource (entrySource e) ++ mb xmlSummary (entrySummary e) ++ mb xmlInReplyTo (entryInReplyTo e) ++ mb xmlInReplyTotal (entryInReplyTotal e) ++ entryOther e) {elementAttributes = entryAttrs e} xmlContent :: EntryContent -> XML.Element xmlContent cont = case cont of TextContent t -> (atomLeaf "content" t) {elementAttributes = [atomAttr "type" "text"]} HTMLContent t -> (atomLeaf "content" t) {elementAttributes = [atomAttr "type" "html"]} XHTMLContent x -> (atomNode "content" [NodeElement x]) {elementAttributes = [atomAttr "type" "xhtml"]} MixedContent mbTy cs -> (atomNode "content" cs) {elementAttributes = mb (atomAttr "type") mbTy} ExternalContent mbTy src -> (atomNode "content" []) {elementAttributes = atomAttr "src" src : mb (atomAttr "type") mbTy} xmlCategory :: Category -> XML.Element xmlCategory c = (atomNode "category" (map NodeElement (catOther c))) { elementAttributes = [atomAttr "term" (catTerm c)] ++ mb (atomAttr "scheme") (catScheme c) ++ mb (atomAttr "label") (catLabel c) } xmlLink :: Link -> XML.Element xmlLink l = (atomNode "link" (map NodeElement (linkOther l))) { elementAttributes = [atomAttr "href" (linkHref l)] ++ mb (atomAttr "rel" . either id id) (linkRel l) ++ mb (atomAttr "type") (linkType l) ++ mb (atomAttr "hreflang") (linkHrefLang l) ++ mb (atomAttr "title") (linkTitle l) ++ mb (atomAttr "length") (linkLength l) ++ linkAttrs l } xmlSource :: Source -> Element xmlSource s = atomNode "source" $ map NodeElement $ sourceOther s ++ map xmlAuthor (sourceAuthors s) ++ map xmlCategory (sourceCategories s) ++ mb xmlGenerator (sourceGenerator s) ++ mb xmlIcon (sourceIcon s) ++ mb xmlId (sourceId s) ++ map xmlLink (sourceLinks s) ++ mb xmlLogo (sourceLogo s) ++ mb xmlRights (sourceRights s) ++ mb xmlSubtitle (sourceSubtitle s) ++ mb xmlTitle (sourceTitle s) ++ mb xmlUpdated (sourceUpdated s) xmlGenerator :: Generator -> Element xmlGenerator g = (atomLeaf "generator" (genText g)) {elementAttributes = mb (atomAttr "uri") (genURI g) ++ mb (atomAttr "version") (genVersion g)} xmlAuthor :: Person -> XML.Element xmlAuthor p = atomNode "author" (xmlPerson p) xmlContributor :: Person -> XML.Element xmlContributor c = atomNode "contributor" (xmlPerson c) xmlPerson :: Person -> [XML.Node] xmlPerson p = map NodeElement $ [atomLeaf "name" (personName p)] ++ mb (atomLeaf "uri") (personURI p) ++ mb (atomLeaf "email") (personEmail p) ++ personOther p xmlInReplyTo :: InReplyTo -> XML.Element xmlInReplyTo irt = (atomThreadNode "in-reply-to" (replyToContent irt)) { elementAttributes = mb (atomThreadAttr "ref") (Just $ replyToRef irt) ++ mb (atomThreadAttr "href") (replyToHRef irt) ++ mb (atomThreadAttr "type") (replyToType irt) ++ mb (atomThreadAttr "source") (replyToSource irt) ++ replyToOther irt } xmlInReplyTotal :: InReplyTotal -> XML.Element xmlInReplyTotal irt = (atomThreadLeaf "total" (pack $ show $ replyToTotal irt)) {elementAttributes = replyToTotalOther irt} xmlId :: Text -> XML.Element xmlId = atomLeaf "id" xmlIcon :: URI -> XML.Element xmlIcon = atomLeaf "icon" xmlLogo :: URI -> XML.Element xmlLogo = atomLeaf "logo" xmlUpdated :: Date -> XML.Element xmlUpdated = atomLeaf "updated" xmlPublished :: Date -> XML.Element xmlPublished = atomLeaf "published" xmlRights :: TextContent -> XML.Element xmlRights = xmlTextContent "rights" xmlTitle :: TextContent -> XML.Element xmlTitle = xmlTextContent "title" xmlSubtitle :: TextContent -> XML.Element xmlSubtitle = xmlTextContent "subtitle" xmlSummary :: TextContent -> XML.Element xmlSummary = xmlTextContent "summary" xmlTextContent :: Text -> TextContent -> XML.Element xmlTextContent tg t = case t of TextString s -> (atomLeaf tg s) {elementAttributes = [atomAttr "type" "text"]} HTMLString s -> (atomLeaf tg s) {elementAttributes = [atomAttr "type" "html"]} XHTMLString e -> (atomNode tg [XML.NodeElement e]) {elementAttributes = [atomAttr "type" "xhtml"]} -------------------------------------------------------------------------------- mb :: (a -> b) -> Maybe a -> [b] mb _ Nothing = [] mb f (Just x) = [f x] feed-1.0.1.0/src/Text/Atom/Feed/Import.hs0000644000000000000000000002105313354421723016031 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Atom.Feed.Import -- Copyright : (c) Galois, Inc. 2007-2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability:: portable -- Description: Convert from XML to Atom -- -- Convert from XML to Atom -- -------------------------------------------------------------------- module Text.Atom.Feed.Import ( pNodes , pQNodes , pNode , pQNode , pLeaf , pQLeaf , pAttr , pAttrs , pQAttr , pMany , children , elementFeed , pTextContent , pPerson , pCategory , pGenerator , pSource , pLink , pEntry , pContent , pInReplyTotal , pInReplyTo ) where import Prelude.Compat import Control.Monad.Compat (guard, mplus) import Data.List.Compat (find) import Data.Maybe (isNothing, listToMaybe, mapMaybe) import Data.Text (Text) import Data.Text.Read import Data.XML.Types as XML import Text.Atom.Feed import Text.Atom.Feed.Export (atomName, atomThreadName) import qualified Data.Text as T pNodes :: Text -> [XML.Element] -> [XML.Element] pNodes x = filter ((atomName x ==) . elementName) pQNodes :: Name -> [XML.Element] -> [XML.Element] pQNodes x = filter ((x ==) . elementName) pNode :: Text -> [XML.Element] -> Maybe XML.Element pNode x es = listToMaybe (pNodes x es) pQNode :: Name -> [XML.Element] -> Maybe XML.Element pQNode x es = listToMaybe (pQNodes x es) pLeaf :: Text -> [XML.Element] -> Maybe Text pLeaf x es = (T.concat . elementText) `fmap` pNode x es pQLeaf :: Name -> [XML.Element] -> Maybe Text pQLeaf x es = (T.concat . elementText) `fmap` pQNode x es pAttr :: Text -> XML.Element -> Maybe Text pAttr x e = (`attributeText` e) =<< fst <$> find sameAttr (elementAttributes e) where ax = atomName x sameAttr (k, _) = k == ax || (isNothing (nameNamespace k) && nameLocalName k == x) pAttrs :: Text -> XML.Element -> [Text] pAttrs x e = [t | ContentText t <- cnts] where cnts = concat [v | (k, v) <- elementAttributes e, k == atomName x] pQAttr :: Name -> XML.Element -> Maybe Text pQAttr = attributeText pMany :: Text -> (XML.Element -> Maybe a) -> [XML.Element] -> [a] pMany p f es = mapMaybe f (pNodes p es) children :: XML.Element -> [XML.Element] children = elementChildren elementTexts :: Element -> Text elementTexts = T.concat . elementText elementFeed :: XML.Element -> Maybe Feed elementFeed e = do guard (elementName e == atomName "feed") let es = children e i <- pLeaf "id" es t <- pTextContent "title" es `mplus` return (TextString "") u <- pLeaf "updated" es return Feed { feedId = i , feedTitle = t , feedSubtitle = pTextContent "subtitle" es , feedUpdated = u , feedAuthors = pMany "author" pPerson es , feedContributors = pMany "contributor" pPerson es , feedCategories = pMany "category" pCategory es , feedGenerator = pGenerator `fmap` pNode "generator" es , feedIcon = pLeaf "icon" es , feedLogo = pLeaf "logo" es , feedRights = pTextContent "rights" es , feedLinks = pMany "link" pLink es , feedEntries = pMany "entry" pEntry es , feedOther = other_es es , feedAttrs = other_as (elementAttributes e) } where other_es = filter ((`notElem` known_elts) . elementName) other_as = filter ((`notElem` known_attrs) . fst) -- let's have them all (including xml:base and xml:lang + xmlns: stuff) known_attrs = [] known_elts = map atomName [ "author" , "category" , "contributor" , "generator" , "icon" , "id" , "link" , "logo" , "rights" , "subtitle" , "title" , "updated" , "entry" ] pTextContent :: Text -> [XML.Element] -> Maybe TextContent pTextContent tag es = do e <- pNode tag es case pAttr "type" e of Nothing -> return (TextString (elementTexts e)) Just "text" -> return (TextString (elementTexts e)) Just "html" -> return (HTMLString (elementTexts e)) Just "xhtml" -> case children e -- hmm... of [c] -> return (XHTMLString c) _ -> Nothing -- Multiple XHTML children. _ -> Nothing -- Unknown text content type. pPerson :: XML.Element -> Maybe Person pPerson e = do let es = children e name <- pLeaf "name" es -- or missing "name" return Person { personName = name , personURI = pLeaf "uri" es , personEmail = pLeaf "email" es , personOther = [] -- XXX? } pCategory :: XML.Element -> Maybe Category pCategory e = do term <- pAttr "term" e -- or missing "term" attribute return Category { catTerm = term , catScheme = pAttr "scheme" e , catLabel = pAttr "label" e , catOther = [] -- XXX? } pGenerator :: XML.Element -> Generator pGenerator e = Generator {genURI = pAttr "href" e, genVersion = pAttr "version" e, genText = elementTexts e} pSource :: XML.Element -> Source pSource e = let es = children e in Source { sourceAuthors = pMany "author" pPerson es , sourceCategories = pMany "category" pCategory es , sourceGenerator = pGenerator `fmap` pNode "generator" es , sourceIcon = pLeaf "icon" es , sourceId = pLeaf "id" es , sourceLinks = pMany "link" pLink es , sourceLogo = pLeaf "logo" es , sourceRights = pTextContent "rights" es , sourceSubtitle = pTextContent "subtitle" es , sourceTitle = pTextContent "title" es , sourceUpdated = pLeaf "updated" es , sourceOther = [] -- XXX ? } pLink :: XML.Element -> Maybe Link pLink e = do uri <- pAttr "href" e return Link { linkHref = uri , linkRel = Right `fmap` pAttr "rel" e , linkType = pAttr "type" e , linkHrefLang = pAttr "hreflang" e , linkTitle = pAttr "title" e , linkLength = pAttr "length" e , linkAttrs = other_as (elementAttributes e) , linkOther = [] } where other_as = filter ((`notElem` known_attrs) . fst) known_attrs = map atomName ["href", "rel", "type", "hreflang", "title", "length"] pEntry :: XML.Element -> Maybe Entry pEntry e = do let es = children e i <- pLeaf "id" es t <- pTextContent "title" es u <- pLeaf "updated" es `mplus` pLeaf "published" es return Entry { entryId = i , entryTitle = t , entryUpdated = u , entryAuthors = pMany "author" pPerson es , entryContributor = pMany "contributor" pPerson es , entryCategories = pMany "category" pCategory es , entryContent = pContent =<< pNode "content" es , entryLinks = pMany "link" pLink es , entryPublished = pLeaf "published" es , entryRights = pTextContent "rights" es , entrySource = pSource `fmap` pNode "source" es , entrySummary = pTextContent "summary" es , entryInReplyTo = pInReplyTo es , entryInReplyTotal = pInReplyTotal es , entryAttrs = other_as (elementAttributes e) , entryOther = [] -- ? } where other_as = filter ((`notElem` known_attrs) . fst) -- let's have them all (including xml:base and xml:lang + xmlns: stuff) known_attrs = [] pContent :: XML.Element -> Maybe EntryContent pContent e = case pAttr "type" e of Nothing -> return (TextContent (elementTexts e)) Just "text" -> return (TextContent (elementTexts e)) Just "html" -> return (HTMLContent (elementTexts e)) Just "xhtml" -> case children e of [] -> return (TextContent "") [c] -> return (XHTMLContent c) _ -> Nothing Just ty -> case pAttr "src" e of Nothing -> return (MixedContent (Just ty) (elementNodes e)) Just uri -> return (ExternalContent (Just ty) uri) pInReplyTotal :: [XML.Element] -> Maybe InReplyTotal pInReplyTotal es = do t <- pQLeaf (atomThreadName "total") es case decimal t of Right (x, _) -> do n <- pQNode (atomThreadName "total") es return InReplyTotal {replyToTotal = x, replyToTotalOther = elementAttributes n} _ -> fail "no parse" pInReplyTo :: [XML.Element] -> Maybe InReplyTo pInReplyTo es = do t <- pQNode (atomThreadName "reply-to") es case pQAttr (atomThreadName "ref") t of Just ref -> return InReplyTo { replyToRef = ref , replyToHRef = pQAttr (atomThreadName "href") t , replyToType = pQAttr (atomThreadName "type") t , replyToSource = pQAttr (atomThreadName "source") t , replyToOther = elementAttributes t -- ToDo: snip out matched ones. , replyToContent = elementNodes t } _ -> fail "no parse" feed-1.0.1.0/src/Text/Atom/Feed/Link.hs0000644000000000000000000000566613354421723015470 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Atom.Feed.Link -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.Atom.Feed.Link ( LinkRelation(..) , showLinkRelation , showLinkAttr ) where import Prelude.Compat -- | Atom feeds uses typed IRI links to represent -- information \/ metadata that is of interest to the -- consumers (software, in the main) of feeds. For instance, -- the edit link relation attached to an atom:entry element -- points to the IRI to use to update\/edit it. -- -- The Atom standard encourages that such typed links to -- be registered with IANA if they have wider applicability, -- and the 'LinkRelation' data type encodes the currently -- registered link types (derived from: -- http:\/\/www.iana.org\/assignments\/link-relations.html -- on 2007-10-28] -- data LinkRelation -- relevant RFC: = LinkAlternate -- http://www.rfc-editor.org/rfc/rfc4287.txt | LinkCurrent -- http://www.rfc-editor.org/rfc/rfc5005.txt | LinkEnclosure -- http://www.rfc-editor.org/rfc/rfc4287.txt | LinkEdit -- http://www.rfc-editor.org/rfc/rfc5023.txt | LinkEditMedia -- http://www.rfc-editor.org/rfc/rfc5023.txt | LinkFirst -- http://www.iana.org/assignments/link-relations/first | LinkLast -- http://www.iana.org/assignments/link-relations/last | LinkLicense -- http://www.rfc-editor.org/rfc/rfc4946.txt | LinkNext -- http://www.rfc-editor.org/rfc/rfc5005.txt | LinkNextArchive -- http://www.rfc-editor.org/rfc/rfc5005.txt | LinkPayment -- http://www.iana.org/assignments/link-relations/payment | LinkPrevArchive -- http://www.rfc-editor.org/rfc/rfc5005.txt | LinkPrevious -- http://www.rfc-editor.org/rfc/rfc5005.txt | LinkRelated -- http://www.rfc-editor.org/rfc/rfc4287.txt | LinkReplies -- http://www.rfc-editor.org/rfc/rfc4685.txt | LinkSelf -- http://www.rfc-editor.org/rfc/rfc4287.txt | LinkVia -- http://www.rfc-editor.org/rfc/rfc4287.txt | LinkOther String deriving (Eq, Show) showLinkRelation :: LinkRelation -> String showLinkRelation lr = case lr of LinkAlternate -> "alternate" LinkCurrent -> "current" LinkEnclosure -> "enclosure" LinkEdit -> "edit" LinkEditMedia -> "edit-media" LinkFirst -> "first" LinkLast -> "last" LinkLicense -> "license" LinkNext -> "next" LinkNextArchive -> "next-archive" LinkPayment -> "payment" LinkPrevArchive -> "prev-archive" LinkPrevious -> "previous" LinkRelated -> "related" LinkReplies -> "replies" LinkSelf -> "self" LinkVia -> "via" LinkOther s -> s showLinkAttr :: LinkRelation -> String -> String {-URI-} showLinkAttr lr s = showLinkRelation lr ++ '=' : '"' : concatMap escQ s ++ "\"" where escQ '"' = "&dquot;" escQ x = [x] feed-1.0.1.0/src/Text/Atom/Feed/Validate.hs0000644000000000000000000002374613370672700016324 0ustar0000000000000000{-# LANGUAGE TupleSections #-} -------------------------------------------------------------------- -- | -- Module : Text.Atom.Feed.Validate -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.Atom.Feed.Validate ( VTree(..) , ValidatorResult , advice , demand , valid , mkTree , flattenT , validateEntry , checkEntryAuthor , checkCats , checkContents , checkContributor , checkContentLink , checkLinks , checkId , checkPublished , checkRights , checkSource , checkSummary , checkTitle , checkUpdated , checkCat , checkContent , checkTerm , checkAuthor , checkPerson , checkName , checkEmail , checkUri ) where import Prelude.Compat import Data.XML.Types import Text.Atom.Feed.Import import Data.List.Compat import Data.Maybe data VTree a = VNode [a] [VTree a] | VLeaf [a] deriving (Eq, Show) type ValidatorResult = VTree (Bool, String) advice :: String -> ValidatorResult advice s = VLeaf [(False, s)] demand :: String -> ValidatorResult demand s = VLeaf [(True, s)] valid :: ValidatorResult valid = VLeaf [] mkTree :: [(Bool, String)] -> [ValidatorResult] -> ValidatorResult mkTree = VNode flattenT :: VTree a -> [a] flattenT (VLeaf xs) = xs flattenT (VNode as bs) = as ++ concatMap flattenT bs validateEntry :: Element -> ValidatorResult validateEntry e = mkTree [] [ checkEntryAuthor e , checkCats e , checkContents e , checkContributor e , checkId e , checkContentLink e , checkLinks e , checkPublished e , checkRights e , checkSource e , checkSummary e , checkTitle e , checkUpdated e ] -- Sec 4.1.2, check #1 checkEntryAuthor :: Element -> ValidatorResult checkEntryAuthor e = case pNodes "author" (elementChildren e) of [] -- required -> case pNode "summary" (elementChildren e) of Nothing -> demand "Required 'author' element missing (no 'summary' either)" Just e1 -> case pNode "author" (elementChildren e1) of Just a -> checkAuthor a _ -> demand "Required 'author' element missing" xs -> mkTree [] $ map checkAuthor xs -- Sec 4.1.2, check #2 checkCats :: Element -> ValidatorResult checkCats e = mkTree [] $ map checkCat (pNodes "category" (elementChildren e)) checkContents :: Element -> ValidatorResult checkContents e = case pNodes "content" (elementChildren e) of [] -> valid [c] -> mkTree [] [checkContent c] cs -> mkTree (flattenT (demand ("at most one 'content' element expected inside 'entry', found: " ++ show (length cs)))) (map checkContent cs) checkContributor :: Element -> ValidatorResult checkContributor _e = valid checkContentLink :: Element -> ValidatorResult checkContentLink e = case pNodes "content" (elementChildren e) of [] -> case pNodes "link" (elementChildren e) of [] -> demand "An 'entry' element with no 'content' element must have at least one 'link-rel' element" xs -> case filter (== "alternate") $ mapMaybe (pAttr "rel") xs of [] -> demand "An 'entry' element with no 'content' element must have at least one 'link-rel' element" _ -> valid _ -> valid checkLinks :: Element -> ValidatorResult checkLinks e = case pNodes "link" (elementChildren e) of xs -> case map fst $ filter (\(_, n) -> n == "alternate") $ mapMaybe (\ex -> (ex,) <$> pAttr "rel" ex) xs of xs1 -> let jmb (Just x) (Just y) = Just (x, y) jmb _ _ = Nothing in case mapMaybe (\ex -> pAttr "type" ex `jmb` pAttr "hreflang" ex) xs1 of xs2 -> if any (\x -> length x > 1) (group xs2) then demand "An 'entry' element cannot have duplicate 'link-rel-alternate-type-hreflang' elements" else valid checkId :: Element -> ValidatorResult checkId e = case pNodes "id" (elementChildren e) of [] -> demand "required field 'id' missing from 'entry' element" [_] -> valid xs -> demand ("only one 'id' field expected in 'entry' element, found: " ++ show (length xs)) checkPublished :: Element -> ValidatorResult checkPublished e = case pNodes "published" (elementChildren e) of [] -> valid [_] -> valid xs -> demand ("expected at most one 'published' field in 'entry' element, found: " ++ show (length xs)) checkRights :: Element -> ValidatorResult checkRights e = case pNodes "rights" (elementChildren e) of [] -> valid [_] -> valid xs -> demand ("expected at most one 'rights' field in 'entry' element, found: " ++ show (length xs)) checkSource :: Element -> ValidatorResult checkSource e = case pNodes "source" (elementChildren e) of [] -> valid [_] -> valid xs -> demand ("expected at most one 'source' field in 'entry' element, found: " ++ show (length xs)) checkSummary :: Element -> ValidatorResult checkSummary e = case pNodes "summary" (elementChildren e) of [] -> valid [_] -> valid xs -> demand ("expected at most one 'summary' field in 'entry' element, found: " ++ show (length xs)) checkTitle :: Element -> ValidatorResult checkTitle e = case pNodes "title" (elementChildren e) of [] -> demand "required field 'title' missing from 'entry' element" [_] -> valid xs -> demand ("only one 'title' field expected in 'entry' element, found: " ++ show (length xs)) checkUpdated :: Element -> ValidatorResult checkUpdated e = case pNodes "updated" (elementChildren e) of [] -> demand "required field 'updated' missing from 'entry' element" [_] -> valid xs -> demand ("only one 'updated' field expected in 'entry' element, found: " ++ show (length xs)) checkCat :: Element -> ValidatorResult checkCat e = mkTree [] [checkTerm e, checkScheme e, checkLabel e] where checkScheme e' = case pAttrs "scheme" e' of [] -> valid (_:xs) | null xs -> valid | otherwise -> demand ("Expected at most one 'scheme' attribute, found: " ++ show (1 + length xs)) checkLabel e' = case pAttrs "label" e' of [] -> valid (_:xs) | null xs -> valid | otherwise -> demand ("Expected at most one 'label' attribute, found: " ++ show (1 + length xs)) checkContent :: Element -> ValidatorResult checkContent e = mkTree (flattenT (mkTree [] [type_valid, src_valid])) [ case ty of "text" -> case elementChildren e of [] -> valid _ -> demand "content with type 'text' cannot have child elements, text only." "html" -> case elementChildren e of [] -> valid _ -> demand "content with type 'html' cannot have child elements, text only." "xhtml" -> case elementChildren e of [] -> valid [_] -> valid -- ToDo: check that it is a 'div'. _ds -> demand "content with type 'xhtml' should only contain one 'div' child." _ -> valid ] where types = pAttrs "type" e (ty, type_valid) = case types of [] -> ("text", valid) [t] -> checkTypeA t (t:ts) -> (t, demand ("Expected at most one 'type' attribute, found: " ++ show (1 + length ts))) src_valid = case pAttrs "src" e of [] -> valid [_] -> case types of [] -> advice "It is advisable to provide a 'type' along with a 'src' attribute" (_:_) -> valid ss -> demand ("Expected at most one 'src' attribute, found: " ++ show (length ss)) checkTypeA v | v `elem` std_types = (v, valid) | otherwise = (v, valid) where std_types = ["text", "xhtml", "html"] {- case parseMIMEType ty of Nothing -> valid Just mt | isXmlType mt -> valid | otherwise -> case onlyElems (elContent e) of [] -> valid -- check _ -> demand ("content with MIME type '" ++ ty ++ "' must only contain base64 data")] -} {- case parseMIMEType t of Just{} -> valid _ -> demand "The 'type' attribute must be a valid MIME type" -} {- case parseMIMEType v of Nothing -> ("text", demand ("Invalid/unknown type value " ++ v)) Just mt -> case mimeType mt of Multipart{} -> ("text", demand "Multipart MIME types not a legal 'type'") _ -> (v, valid) -} checkTerm :: Element -> ValidatorResult checkTerm e = case pNodes "term" (elementChildren e) of [] -> demand "required field 'term' missing from 'category' element" [_] -> valid xs -> demand ("only one 'term' field expected in 'category' element, found: " ++ show (length xs)) checkAuthor :: Element -> ValidatorResult checkAuthor = checkPerson checkPerson :: Element -> ValidatorResult checkPerson e = mkTree (flattenT $ checkName e) [checkEmail e, checkUri e] checkName :: Element -> ValidatorResult checkName e = case pNodes "name" (elementChildren e) of [] -> demand "required field 'name' missing from 'author' element" [_] -> valid xs -> demand ("only one 'name' expected in 'author' element, found: " ++ show (length xs)) checkEmail :: Element -> ValidatorResult checkEmail e = case pNodes "email" (elementChildren e) of [] -> valid (_:xs) | null xs -> valid | otherwise -> demand ("at most one 'email' expected in 'author' element, found: " ++ show (1 + length xs)) checkUri :: Element -> ValidatorResult checkUri e = case pNodes "email" (elementChildren e) of [] -> valid (_:xs) | null xs -> valid | otherwise -> demand ("at most one 'uri' expected in 'author' element, found: " ++ show (1 + length xs)) feed-1.0.1.0/src/Text/Atom/Pub.hs0000644000000000000000000000242513354421723014444 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Atom.Pub -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -- Types for the Atom Publishing Protocol (APP) -- -------------------------------------------------------------------- module Text.Atom.Pub ( Service(..) , Workspace(..) , Collection(..) , Categories(..) , Accept(..) ) where import Prelude.Compat import Data.Text (Text) import Data.XML.Types as XML import Text.Atom.Feed (Category, TextContent, URI) data Service = Service { serviceWorkspaces :: [Workspace] , serviceOther :: [XML.Element] } data Workspace = Workspace { workspaceTitle :: TextContent , workspaceCols :: [Collection] , workspaceOther :: [XML.Element] } data Collection = Collection { collectionURI :: URI , collectionTitle :: TextContent , collectionAccept :: [Accept] , collectionCats :: [Categories] , collectionOther :: [XML.Element] } data Categories = CategoriesExternal URI | Categories (Maybe Bool) (Maybe URI) [Category] deriving (Show) newtype Accept = Accept { acceptType :: Text } feed-1.0.1.0/src/Text/Atom/Pub/Export.hs0000644000000000000000000000505513354421723015727 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Atom.Pub.Export -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability:: portable -- Description: Serializing APP types (as XML.) -- -- Serializing Atom Publishing Protocol types as XML. -- -------------------------------------------------------------------- module Text.Atom.Pub.Export ( mkQName , mkElem , mkLeaf , mkAttr , xmlns_app , appNS , xmlService , xmlWorkspace , xmlCollection , xmlCategories , xmlAccept ) where import Prelude.Compat import Data.Text (Text) import Data.XML.Compat import Data.XML.Types import Text.Atom.Feed.Export (mb, xmlCategory, xmlTitle, xmlns_atom) import Text.Atom.Pub -- ToDo: old crud; inline away. mkQName :: Maybe Text -> Text -> Name mkQName a b = Name b a Nothing mkElem :: Name -> [Attr] -> [Element] -> Element mkElem a b c = Element a b $ map NodeElement c mkLeaf :: Name -> [Attr] -> Text -> Element mkLeaf a b c = Element a b [NodeContent $ ContentText c] xmlns_app :: Attr xmlns_app = (mkQName (Just "xmlns") "app", [ContentText appNS]) appNS :: Text appNS = "http://purl.org/atom/app#" appName :: Text -> Name appName nc = (mkQName (Just "app") nc) {nameNamespace = Just appNS} xmlService :: Service -> Element xmlService s = mkElem (appName "service") [xmlns_app, xmlns_atom] (map xmlWorkspace (serviceWorkspaces s) ++ serviceOther s) xmlWorkspace :: Workspace -> Element xmlWorkspace w = mkElem (appName "workspace") [mkAttr "xml:lang" "en"] (concat [[xmlTitle (workspaceTitle w)], map xmlCollection (workspaceCols w), workspaceOther w]) xmlCollection :: Collection -> Element xmlCollection c = mkElem (appName "collection") [mkAttr "href" (collectionURI c)] (concat [ [xmlTitle (collectionTitle c)] , map xmlAccept (collectionAccept c) , map xmlCategories (collectionCats c) , collectionOther c ]) xmlCategories :: Categories -> Element xmlCategories (CategoriesExternal u) = mkElem (appName "categories") [mkAttr "href" u] [] xmlCategories (Categories mbFixed mbScheme cs) = mkElem (appName "categories") (mb (\f -> mkAttr "fixed" (if f then "yes" else "no")) mbFixed ++ mb (mkAttr "scheme") mbScheme) (map xmlCategory cs) xmlAccept :: Accept -> Element xmlAccept a = mkLeaf (appName "accept") [] (acceptType a) feed-1.0.1.0/src/Text/DublinCore/Types.hs0000644000000000000000000000543513354421723016154 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.DublinCore.Types -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- -- Representing the DublinCore metadata elements in Haskell. -- For information on the Dublin Core Metadata Element Set, -- see: -- module Text.DublinCore.Types ( DCItem(..) , DCInfo(..) , infoToTag , dc_element_names ) where import Prelude.Compat import Data.Text -- | A DCItem pairs a specific element with its (string) value. data DCItem = DCItem { dcElt :: DCInfo , dcText :: Text } deriving (Eq, Show) -- | The Dublin Core Metadata Element Set, all 15 of them (plus an extension constructor.) data DCInfo = DC_Title -- ^ A name given to the resource. | DC_Creator -- ^ An entity primarily responsible for making the content of the resource. | DC_Subject -- ^ The topic of the content of the resource. | DC_Description -- ^ An account of the content of the resource. | DC_Publisher -- ^ An entity responsible for making the resource available | DC_Contributor -- ^ An entity responsible for making contributions to the content of the resource. | DC_Date -- ^ A date associated with an event in the life cycle of the resource (YYYY-MM-DD) | DC_Type -- ^ The nature or genre of the content of the resource. | DC_Format -- ^ The physical or digital manifestation of the resource. | DC_Identifier -- ^ An unambiguous reference to the resource within a given context. | DC_Source -- ^ A Reference to a resource from which the present resource is derived. | DC_Language -- ^ A language of the intellectual content of the resource. | DC_Relation -- ^ A reference to a related resource. | DC_Coverage -- ^ The extent or scope of the content of the resource. | DC_Rights -- ^ Information about rights held in and over the resource. | DC_Other Text -- ^ Other; data type extension mechanism. deriving (Eq, Show) infoToTag :: DCInfo -> Text infoToTag i = case i of DC_Title -> "title" DC_Creator -> "creator" DC_Subject -> "subject" DC_Description -> "description" DC_Publisher -> "publisher" DC_Contributor -> "contributor" DC_Date -> "date" DC_Type -> "type" DC_Format -> "format" DC_Identifier -> "identifier" DC_Source -> "source" DC_Language -> "language" DC_Relation -> "relation" DC_Coverage -> "coverage" DC_Rights -> "rights" DC_Other o -> o dc_element_names :: [Text] dc_element_names = [ "title" , "creator" , "subject" , "description" , "publisher" , "contributor" , "date" , "type" , "format" , "identifier" , "source" , "language" , "relation" , "coverage" , "rights" ] feed-1.0.1.0/src/Text/Feed/Constructor.hs0000644000000000000000000007247213354421723016217 0ustar0000000000000000{-# OPTIONS -fno-warn-incomplete-patterns #-} -------------------------------------------------------------------- -- | -- Module : Text.Feed.Constructor -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Description: Module for an abstraction layer between different kinds of feeds. -- -------------------------------------------------------------------- module Text.Feed.Constructor ( FeedKind(..) , newFeed -- :: FeedKind -> Feed , feedFromRSS -- :: RSS -> Feed , feedFromAtom -- :: Atom.Feed -> Feed , feedFromRDF -- :: RSS1.Feed -> Feed , feedFromXML -- :: Element -> Feed , getFeedKind -- :: Feed -> FeedKind , FeedSetter -- type _ a = a -> Feed -> Feed , addItem -- :: FeedSetter Item , withFeedTitle -- :: FeedSetter Text , withFeedHome -- :: FeedSetter URLString , withFeedHTML -- :: FeedSetter URLString , withFeedDescription -- :: FeedSetter Text , withFeedPubDate -- :: FeedSetter DateString , withFeedLastUpdate -- :: FeedSetter DateString , withFeedDate -- :: FeedSetter DateString , withFeedLogoLink -- :: FeedSetter URLString , withFeedLanguage -- :: FeedSetter Text , withFeedCategories -- :: FeedSetter [(Text, Maybe Text)] , withFeedGenerator -- :: FeedSetter Text , withFeedItems -- :: FeedSetter [Item] , newItem -- :: FeedKind -> Item , getItemKind -- :: Item -> FeedKind , atomEntryToItem -- :: Atom.Entry -> Item , rssItemToItem -- :: RSS.Item -> Item , rdfItemToItem -- :: RSS1.Item -> Item , ItemSetter -- type _ a = a -> Item -> Item , withItemTitle -- :: ItemSetter Text , withItemLink -- :: ItemSetter URLString , withItemPubDate -- :: ItemSetter DateString , withItemDate -- :: ItemSetter DateString , withItemAuthor -- :: ItemSetter Text , withItemCommentLink -- :: ItemSetter Text , withItemEnclosure -- :: Text -> Maybe Text -> ItemSetter Integer , withItemFeedLink -- :: Text -> ItemSetter Text , withItemId -- :: Bool -> ItemSetter Text , withItemCategories -- :: ItemSetter [(Text, Maybe Text)] , withItemDescription -- :: ItemSetter Text , withItemRights -- :: ItemSetter Text ) where import Prelude.Compat import Text.Feed.Types as Feed.Types import Text.Atom.Feed as Atom import Text.DublinCore.Types import Text.RSS.Syntax as RSS import Text.RSS1.Syntax as RSS1 import Data.XML.Compat import Data.XML.Types as XML import Data.Char (toLower) import Data.Maybe (fromMaybe, mapMaybe) import Data.Text (Text, pack) -- ToDo: -- -- - complete set of constructors over feeds -- - provide a unified treatment of date string reps. -- (i.e., I know they differ across formats, but ignorant what -- the constraints are at the moment.) -- | Construct an empty feed document, intending to output it in -- the 'fk' feed format. newFeed :: FeedKind -> Feed.Types.Feed newFeed fk = case fk of AtomKind -> AtomFeed (Atom.nullFeed "feed-id-not-filled-in" (TextString "dummy-title") "dummy-and-bogus-update-date") RSSKind mbV -> let def = RSS.nullRSS "dummy-title" "default-channel-url" in RSSFeed $ maybe def (\v -> def {RSS.rssVersion = v}) mbV RDFKind mbV -> let def = RSS1.nullFeed "default-channel-url" "dummy-title" in RSS1Feed $ maybe def (\v -> def {RSS1.feedVersion = v}) mbV feedFromRSS :: RSS.RSS -> Feed.Types.Feed feedFromRSS = RSSFeed feedFromAtom :: Atom.Feed -> Feed.Types.Feed feedFromAtom = AtomFeed feedFromRDF :: RSS1.Feed -> Feed.Types.Feed feedFromRDF = RSS1Feed feedFromXML :: XML.Element -> Feed.Types.Feed feedFromXML = XMLFeed getFeedKind :: Feed.Types.Feed -> FeedKind getFeedKind f = case f of Feed.Types.AtomFeed {} -> AtomKind Feed.Types.RSSFeed r -> RSSKind (case RSS.rssVersion r of "2.0" -> Nothing v -> Just v) Feed.Types.RSS1Feed r -> RDFKind (case RSS1.feedVersion r of "1.0" -> Nothing v -> Just v) Feed.Types.XMLFeed {} -> RSSKind (Just "2.0") -- for now, just a hunch.. addItem :: Feed.Types.Item -> Feed.Types.Feed -> Feed.Types.Feed addItem it f = case (it, f) of (Feed.Types.AtomItem e, Feed.Types.AtomFeed fe) -> Feed.Types.AtomFeed fe {Atom.feedEntries = e : Atom.feedEntries fe} (Feed.Types.RSSItem e, Feed.Types.RSSFeed r) -> Feed.Types.RSSFeed r {RSS.rssChannel = (RSS.rssChannel r) {RSS.rssItems = e : RSS.rssItems (RSS.rssChannel r)}} (Feed.Types.RSS1Item e, Feed.Types.RSS1Feed r) -- note: do not update the channel item URIs at this point; -- will delay doing so until serialization. -> Feed.Types.RSS1Feed r {RSS1.feedItems = e : RSS1.feedItems r} _ -> error "addItem: currently unable to automatically convert items from one feed type to another" withFeedItems :: FeedSetter [Feed.Types.Item] withFeedItems is fe = foldr addItem (case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f {Atom.feedEntries = []} Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f {rssChannel = (rssChannel f) {rssItems = []}} Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed f {feedItems = []}) is newItem :: FeedKind -> Feed.Types.Item newItem fk = case fk of AtomKind -> Feed.Types.AtomItem $ Atom.nullEntry "entry-id-not-filled-in" (TextString "dummy-entry-title") "dummy-and-bogus-entry-update-date" RSSKind {} -> Feed.Types.RSSItem $ RSS.nullItem "dummy-rss-item-title" RDFKind {} -> Feed.Types.RSS1Item $ RSS1.nullItem "dummy-item-uri" "dummy-item-title" "dummy-item-link" getItemKind :: Feed.Types.Item -> FeedKind getItemKind f = case f of Feed.Types.AtomItem {} -> AtomKind Feed.Types.RSSItem {} -> RSSKind (Just "2.0") -- good guess.. Feed.Types.RSS1Item {} -> RDFKind (Just "1.0") Feed.Types.XMLItem {} -> RSSKind (Just "2.0") type FeedSetter a = a -> Feed.Types.Feed -> Feed.Types.Feed withFeedTitle :: FeedSetter Text withFeedTitle tit fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f {feedTitle = TextString tit} Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f {rssChannel = (rssChannel f) {rssTitle = tit}} Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed f {feedChannel = (feedChannel f) {channelTitle = tit}} Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (mapMaybeChildren (\e2 -> if elementName e2 == "title" then Just (unode "title" tit) else Nothing) e) else Nothing) f withFeedHome :: FeedSetter URLString withFeedHome url fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f {feedLinks = newSelf : Atom.feedLinks f} -- ToDo: fix, the element is for the HTML home of the channel, not the -- location of the feed itself. Struggling to find if there is a common way -- to represent this outside of RSS 2.0 standard elements.. Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f {rssChannel = (rssChannel f) {rssLink = url}} Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed f {feedChannel = (feedChannel f) {channelURI = url}} Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (mapMaybeChildren (\e2 -> if elementName e2 == "link" then Just (unode "link" url) else Nothing) e) else Nothing) f where newSelf = (nullLink url) {linkRel = Just (Left "self"), linkType = Just "application/atom+xml"} -- | 'withFeedHTML' sets the URL where an HTML version of the -- feed is published. withFeedHTML :: FeedSetter URLString withFeedHTML url fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f {feedLinks = newAlt : Atom.feedLinks f} Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f {rssChannel = (rssChannel f) {rssLink = url}} Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed f {feedChannel = (feedChannel f) {channelLink = url}} Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (mapMaybeChildren (\e2 -> if elementName e2 == "link" then Just (unode "link" url) else Nothing) e) else Nothing) f where newAlt = (nullLink url) {linkRel = Just (Left "alternate"), linkType = Just "text/html"} -- | 'withFeedHTML' sets the URL where an HTML version of the -- feed is published. withFeedDescription :: FeedSetter Text withFeedDescription desc fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f {feedSubtitle = Just (TextString desc)} Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f {rssChannel = (rssChannel f) {rssDescription = desc}} Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed f {feedChannel = (feedChannel f) {channelDesc = desc}} Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (mapMaybeChildren (\e2 -> if elementName e2 == "description" then Just (unode "description" desc) else Nothing) e) else Nothing) f withFeedPubDate :: FeedSetter Text withFeedPubDate dateStr fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f {feedUpdated = dateStr} Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f {rssChannel = (rssChannel f) {rssPubDate = Just dateStr}} Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed $ case break isDate $ RSS1.channelDC (RSS1.feedChannel f) of (as, dci:bs) -> f { RSS1.feedChannel = (RSS1.feedChannel f) {RSS1.channelDC = as ++ dci {dcText = dateStr} : bs} } (_, []) -> f { RSS1.feedChannel = (RSS1.feedChannel f) { RSS1.channelDC = DCItem {dcElt = DC_Date, dcText = dateStr} : RSS1.channelDC (RSS1.feedChannel f) } } Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (mapMaybeChildren (\e2 -> if elementName e2 == "pubDate" then Just (unode "pubDate" dateStr) else Nothing) e) else Nothing) f where isDate dc = dcElt dc == DC_Date withFeedLastUpdate :: FeedSetter DateString withFeedLastUpdate dateStr fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f {feedUpdated = dateStr} Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f {rssChannel = (rssChannel f) {rssLastUpdate = Just dateStr}} Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed $ case break isDate $ RSS1.channelDC (RSS1.feedChannel f) of (as, dci:bs) -> f { RSS1.feedChannel = (RSS1.feedChannel f) {RSS1.channelDC = as ++ dci {dcText = dateStr} : bs} } (_, []) -> f { RSS1.feedChannel = (RSS1.feedChannel f) { RSS1.channelDC = DCItem {dcElt = DC_Date, dcText = dateStr} : RSS1.channelDC (RSS1.feedChannel f) } } Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (mapMaybeChildren (\e2 -> if elementName e2 == "lastUpdate" then Just (unode "lastUpdate" dateStr) else Nothing) e) else Nothing) f where isDate dc = dcElt dc == DC_Date -- | 'withFeedDate dt' is the composition of 'withFeedPubDate' -- and 'withFeedLastUpdate', setting both publication date and -- last update date to 'dt'. Notice that RSS2.0 is the only format -- supporting both pub and last-update. withFeedDate :: FeedSetter DateString withFeedDate dt f = withFeedPubDate dt (withFeedLastUpdate dt f) withFeedLogoLink :: URLString -> FeedSetter URLString withFeedLogoLink imgURL lnk fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f {feedLogo = Just imgURL, feedLinks = newSelf : Atom.feedLinks f} Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f { rssChannel = (rssChannel f) {rssImage = Just $ RSS.nullImage imgURL (rssTitle (rssChannel f)) lnk} } Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed $ f { feedImage = Just $ RSS1.nullImage imgURL (RSS1.channelTitle (RSS1.feedChannel f)) lnk , feedChannel = (feedChannel f) {channelImageURI = Just imgURL} } Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (mapMaybeChildren (\e2 -> if elementName e2 == "image" then Just (unode "image" [unode "url" imgURL, unode "title" title, unode "link" lnk]) else Nothing) e) else Nothing) f where title = case fmap (findChild "title") (findChild "channel" f) of Just (Just e1) -> strContent e1 _ -> "feed_title" -- shouldn't happen.. where newSelf = (nullLink lnk) {linkRel = Just (Left "self"), linkType = Just "application/atom+xml"} withFeedLanguage :: FeedSetter Text withFeedLanguage lang fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f {Atom.feedAttrs = langAttr : Atom.feedAttrs f} where langAttr = (name, [ContentText lang]) name = Name {nameLocalName = "lang", nameNamespace = Nothing, namePrefix = Just "xml"} Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f {rssChannel = (rssChannel f) {rssLanguage = Just lang}} Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed $ case break isLang $ RSS1.channelDC (RSS1.feedChannel f) of (as, dci:bs) -> f { RSS1.feedChannel = (RSS1.feedChannel f) {RSS1.channelDC = as ++ dci {dcText = lang} : bs} } (_, []) -> f { RSS1.feedChannel = (RSS1.feedChannel f) { RSS1.channelDC = DCItem {dcElt = DC_Language, dcText = lang} : RSS1.channelDC (RSS1.feedChannel f) } } Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (mapMaybeChildren (\e2 -> if elementName e2 == "language" then Just (unode "language" lang) else Nothing) e) else Nothing) f where isLang dc = dcElt dc == DC_Language withFeedCategories :: FeedSetter [(Text, Maybe Text)] withFeedCategories cats fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed f { Atom.feedCategories = map (\(t, mb) -> (Atom.newCategory t) {Atom.catScheme = mb}) cats ++ feedCategories f } Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f { rssChannel = (rssChannel f) { RSS.rssCategories = map (\(t, mb) -> (RSS.newCategory t) {RSS.rssCategoryDomain = mb}) cats ++ RSS.rssCategories (rssChannel f) } } Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed f { feedChannel = (feedChannel f) { RSS1.channelDC = map (\(t, _) -> DCItem {dcElt = DC_Subject, dcText = t}) cats ++ RSS1.channelDC (feedChannel f) } } Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (foldr (\(t, mb) acc -> addChild (unode "category" (maybe (: []) (\v x -> [mkAttr "domain" v, x]) mb (mkAttr "term" t))) acc) e cats) else Nothing) f withFeedGenerator :: FeedSetter (Text, Maybe URLString) withFeedGenerator (gen, mbURI) fe = case fe of Feed.Types.AtomFeed f -> Feed.Types.AtomFeed $ f {Atom.feedGenerator = Just ((Atom.nullGenerator gen) {Atom.genURI = mbURI})} Feed.Types.RSSFeed f -> Feed.Types.RSSFeed f {rssChannel = (rssChannel f) {rssGenerator = Just gen}} Feed.Types.RSS1Feed f -> Feed.Types.RSS1Feed $ case break isSource $ RSS1.channelDC (RSS1.feedChannel f) of (as, dci:bs) -> f { RSS1.feedChannel = (RSS1.feedChannel f) {RSS1.channelDC = as ++ dci {dcText = gen} : bs} } (_, []) -> f { RSS1.feedChannel = (RSS1.feedChannel f) { RSS1.channelDC = DCItem {dcElt = DC_Source, dcText = gen} : RSS1.channelDC (RSS1.feedChannel f) } } Feed.Types.XMLFeed f -> Feed.Types.XMLFeed $ mapMaybeChildren (\e -> if elementName e == "channel" then Just (mapMaybeChildren (\e2 -> if elementName e2 == "generator" then Just (unode "generator" gen) else Nothing) e) else Nothing) f where isSource dc = dcElt dc == DC_Source -- Item constructors (all the way to the end): atomEntryToItem :: Atom.Entry -> Feed.Types.Item atomEntryToItem = Feed.Types.AtomItem rssItemToItem :: RSS.RSSItem -> Feed.Types.Item rssItemToItem = Feed.Types.RSSItem rdfItemToItem :: RSS1.Item -> Feed.Types.Item rdfItemToItem = Feed.Types.RSS1Item type ItemSetter a = a -> Feed.Types.Item -> Feed.Types.Item -- | 'withItemPubDate dt' associates the creation\/ publication date 'dt' -- with a feed item. withItemPubDate :: ItemSetter DateString withItemPubDate dt fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e {Atom.entryUpdated = dt} Feed.Types.RSSItem i -> Feed.Types.RSSItem i {RSS.rssItemPubDate = Just dt} Feed.Types.RSS1Item i -> case break isDate $ RSS1.itemDC i of (as, dci:bs) -> Feed.Types.RSS1Item i {RSS1.itemDC = as ++ dci {dcText = dt} : bs} (_, []) -> Feed.Types.RSS1Item i {RSS1.itemDC = DCItem {dcElt = DC_Date, dcText = dt} : RSS1.itemDC i} Feed.Types.XMLItem i -> Feed.Types.XMLItem $ addChild (unode "pubDate" dt) $ filterChildren (\e -> elementName e /= "pubDate") i where isDate dc = dcElt dc == DC_Date -- | 'withItemDate' is a synonym for 'withItemPubDate'. withItemDate :: ItemSetter DateString withItemDate = withItemPubDate -- | 'withItemTitle myTitle' associates a new title, 'myTitle', -- with a feed item. withItemTitle :: ItemSetter Text withItemTitle tit fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e {Atom.entryTitle = TextString tit} Feed.Types.RSSItem i -> Feed.Types.RSSItem i {RSS.rssItemTitle = Just tit} Feed.Types.RSS1Item i -> Feed.Types.RSS1Item i {RSS1.itemTitle = tit} Feed.Types.XMLItem i -> Feed.Types.XMLItem $ addChild (unode "title" tit) $ filterChildren (\e -> elementName e /= "title") i -- | 'withItemAuthor auStr' associates new author info -- with a feed item. withItemAuthor :: ItemSetter Text withItemAuthor au fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e {Atom.entryAuthors = [nullPerson {personName = au, personURI = Just au}]} Feed.Types.RSSItem i -> Feed.Types.RSSItem i {RSS.rssItemAuthor = Just au} Feed.Types.RSS1Item i -> case break isAuthor $ RSS1.itemDC i of (as, dci:bs) -> Feed.Types.RSS1Item i {RSS1.itemDC = as ++ dci {dcText = au} : bs} (_, []) -> Feed.Types.RSS1Item i {RSS1.itemDC = DCItem {dcElt = DC_Creator, dcText = au} : RSS1.itemDC i} Feed.Types.XMLItem i -> Feed.Types.XMLItem $ addChild (unode "author" au) $ filterChildren (\e -> elementName e /= "author") i where isAuthor dc = dcElt dc == DC_Creator -- | 'withItemFeedLink name myFeed' associates the parent feed URL 'myFeed' -- with a feed item. It is labelled as 'name'. withItemFeedLink :: Text -> ItemSetter Text withItemFeedLink tit url fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e { Atom.entrySource = Just Atom.nullSource {sourceId = Just url, sourceTitle = Just (TextString tit)} } Feed.Types.RSSItem i -> Feed.Types.RSSItem i {RSS.rssItemSource = Just (RSS.nullSource url tit)} Feed.Types.RSS1Item i -> Feed.Types.RSS1Item i {RSS1.itemTitle = tit} Feed.Types.XMLItem i -> Feed.Types.XMLItem $ addChild (unode "source" ([mkAttr "url" url], tit)) $ filterChildren (\e -> elementName e /= "source") i -- | 'withItemCommentLink url' sets the URL reference to the comment page to 'url'. withItemCommentLink :: ItemSetter Text withItemCommentLink url fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e {Atom.entryLinks = ((nullLink url) {linkRel = Just (Left "replies")}) : Atom.entryLinks e} Feed.Types.RSSItem i -> Feed.Types.RSSItem i {RSS.rssItemComments = Just url} Feed.Types.RSS1Item i -> case break isRel $ RSS1.itemDC i of (as, dci:bs) -> Feed.Types.RSS1Item i {RSS1.itemDC = as ++ dci {dcText = url} : bs} (_, []) -> Feed.Types.RSS1Item i {RSS1.itemDC = DCItem {dcElt = DC_Relation, dcText = url} : RSS1.itemDC i} Feed.Types.XMLItem i -> Feed.Types.XMLItem $ addChild (unode "comments" url) $ filterChildren (\e -> elementName e /= "comments") i where isRel dc = dcElt dc == DC_Relation -- | 'withItemEnclosure url mbTy len' sets the URL reference to the comment page to 'url'. withItemEnclosure :: Text -> Maybe Text -> ItemSetter (Maybe Integer) withItemEnclosure url ty mb_len fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e { Atom.entryLinks = ((nullLink url) { linkRel = Just (Left "enclosure") , linkType = ty , linkLength = fmap (pack . show) mb_len }) : Atom.entryLinks e } Feed.Types.RSSItem i -> Feed.Types.RSSItem i {RSS.rssItemEnclosure = Just (nullEnclosure url mb_len (fromMaybe "text/html" ty))} Feed.Types.RSS1Item i -> Feed.Types.RSS1Item i { RSS1.itemContent = nullContentInfo {contentURI = Just url, contentFormat = ty} : RSS1.itemContent i } Feed.Types.XMLItem i -> Feed.Types.XMLItem $ addChild ((unode "enclosure" url) {elementAttributes = [mkAttr "length" "0", mkAttr "type" (fromMaybe "text/html" ty)]}) $ filterChildren (\e -> elementName e /= "enclosure") i -- | 'withItemId isURL id' associates new unique identifier with a feed item. -- If 'isURL' is 'True', then the id is assumed to point to a valid web resource. withItemId :: Bool -> ItemSetter Text withItemId isURL idS fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e {Atom.entryId = idS} Feed.Types.RSSItem i -> Feed.Types.RSSItem i {RSS.rssItemGuid = Just (nullGuid idS) {rssGuidPermanentURL = Just isURL}} Feed.Types.RSS1Item i -> case break isId $ RSS1.itemDC i of (as, dci:bs) -> Feed.Types.RSS1Item i {RSS1.itemDC = as ++ dci {dcText = idS} : bs} (_, []) -> Feed.Types.RSS1Item i {RSS1.itemDC = DCItem {dcElt = DC_Identifier, dcText = idS} : RSS1.itemDC i} Feed.Types.XMLItem i -> Feed.Types.XMLItem $ addChild (unode "guid" ([mkAttr "isPermaLink" (showBool isURL)], idS)) $ filterChildren (\e -> elementName e /= "guid") i where showBool x = pack $ map toLower (show x) isId dc = dcElt dc == DC_Identifier -- | 'withItemDescription desc' associates a new descriptive string (aka summary) -- with a feed item. withItemDescription :: ItemSetter Text withItemDescription desc fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e {Atom.entrySummary = Just (TextString desc)} Feed.Types.RSSItem i -> Feed.Types.RSSItem i {RSS.rssItemDescription = Just desc} Feed.Types.RSS1Item i -> Feed.Types.RSS1Item i {RSS1.itemDesc = Just desc} Feed.Types.XMLItem i -> Feed.Types.XMLItem $ addChild (unode "description" desc) $ filterChildren (\e -> elementName e /= "description") i -- | 'withItemRights rightStr' associates the rights information 'rightStr' -- with a feed item. withItemRights :: ItemSetter Text withItemRights desc fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e {Atom.entryRights = Just (TextString desc)} -- Note: per-item copyright information isn't supported by RSS2.0 (and earlier editions), -- you can only attach this at the feed/channel level. So, there's not much we can do -- except dropping the information on the floor here. (Rolling our own attribute or -- extension element is an option, but would prefer if someone else had started that -- effort already. Feed.Types.RSSItem {} -> fi Feed.Types.RSS1Item i -> case break ((== DC_Rights) . dcElt) $ RSS1.itemDC i of (as, dci:bs) -> Feed.Types.RSS1Item i {RSS1.itemDC = as ++ dci {dcText = desc} : bs} (_, []) -> Feed.Types.RSS1Item i {RSS1.itemDC = DCItem {dcElt = DC_Rights, dcText = desc} : RSS1.itemDC i} -- Since we're so far assuming that a shallow XML rep. of an item -- is of RSS2.0 ilk, pinning on the rights info is hard (see above.) Feed.Types.XMLItem {} -> fi -- | 'withItemTitle myLink' associates a new URL, 'myLink', -- with a feed item. withItemLink :: ItemSetter URLString withItemLink url fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e {Atom.entryLinks = replaceAlternate url (Atom.entryLinks e)} Feed.Types.RSSItem i -> Feed.Types.RSSItem i {RSS.rssItemLink = Just url} Feed.Types.RSS1Item i -> Feed.Types.RSS1Item i {RSS1.itemLink = url} Feed.Types.XMLItem i -> Feed.Types.XMLItem $ addChild (unode "link" url) $ filterChildren (\e -> elementName e /= "link") i where replaceAlternate _ [] = [] replaceAlternate x (lr:xs) | toStr (Atom.linkRel lr) == "alternate" = lr {Atom.linkHref = x} : xs | otherwise = lr : replaceAlternate x xs toStr Nothing = "" toStr (Just (Left x)) = x toStr (Just (Right x)) = x withItemCategories :: ItemSetter [(Text, Maybe Text)] withItemCategories cats fi = case fi of Feed.Types.AtomItem e -> Feed.Types.AtomItem e { Atom.entryCategories = map (\(t, mb) -> (Atom.newCategory t) {Atom.catScheme = mb}) cats ++ entryCategories e } Feed.Types.RSSItem i -> Feed.Types.RSSItem i { RSS.rssItemCategories = map (\(t, mb) -> (RSS.newCategory t) {RSS.rssCategoryDomain = mb}) cats ++ rssItemCategories i } Feed.Types.RSS1Item i -> Feed.Types.RSS1Item i { RSS1.itemDC = map (\(t, _) -> DCItem {dcElt = DC_Subject, dcText = t}) cats ++ RSS1.itemDC i } Feed.Types.XMLItem i -> Feed.Types.XMLItem $ foldr (\(t, mb) acc -> addChild (unode "category" (maybe (: []) (\v x -> [mkAttr "domain" v, x]) mb (mkAttr "term" t))) acc) i cats -- helpers.. filterChildren :: (XML.Element -> Bool) -> XML.Element -> XML.Element filterChildren pre e = case elementNodes e of [] -> e cs -> e {elementNodes = mapMaybe filterElt cs} where filterElt xe@(XML.NodeElement el) | pre el = Just xe | otherwise = Nothing filterElt xe = Just xe addChild :: XML.Element -> XML.Element -> XML.Element addChild a b = b {elementNodes = XML.NodeElement a : elementNodes b} mapMaybeChildren :: (XML.Element -> Maybe XML.Element) -> XML.Element -> XML.Element mapMaybeChildren f e = case elementNodes e of [] -> e cs -> e {elementNodes = map procElt cs} where procElt xe@(XML.NodeElement el) = case f el of Nothing -> xe Just el1 -> XML.NodeElement el1 procElt xe = xe feed-1.0.1.0/src/Text/Feed/Export.hs0000644000000000000000000000216713370672700015146 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Feed.Export -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- -- Convert from Feeds to XML. -- -------------------------------------------------------------------- module Text.Feed.Export ( Text.Feed.Export.xmlFeed -- :: Feed -> XML.Element , Text.Feed.Export.textFeed -- :: Feed -> TL.Text ) where import Prelude.Compat import Text.Feed.Types import Text.Atom.Feed.Export as Atom import Text.RSS.Export as RSS import Text.RSS1.Export as RSS1 import qualified Data.Text.Util as U import Data.XML.Types as XML import qualified Data.Text.Lazy as TL -- | 'xmlFeed f' serializes a @Feed@ document into a conforming -- XML toplevel element. xmlFeed :: Feed -> XML.Element xmlFeed fe = case fe of AtomFeed f -> Atom.xmlFeed f RSSFeed f -> RSS.xmlRSS f RSS1Feed f -> RSS1.xmlFeed f XMLFeed e -> e -- that was easy! textFeed :: Feed -> Maybe TL.Text textFeed = U.renderFeed Text.Feed.Export.xmlFeed feed-1.0.1.0/src/Text/Feed/Import.hs0000644000000000000000000000715713354421723015142 0ustar0000000000000000{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE CPP #-} -------------------------------------------------------------------- -- | -- Module : Text.Feed.Import -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- -- Convert from XML to Feeds. -- -------------------------------------------------------------------- module Text.Feed.Import ( parseFeedFromFile -- :: FilePath -> IO Feed , parseFeedString -- :: String -> Maybe Feed , parseFeedSource -- :: FeedSource s => s -> Maybe Feed , FeedSource -- if you know your format, use these directly: , readRSS2 -- :: XML.Element -> Maybe Feed , readRSS1 -- :: XML.Element -> Maybe Feed , readAtom -- :: XML.Element -> Maybe Feed ) where import Prelude.Compat import Control.Exception import Data.ByteString.Lazy (ByteString) import Data.Text.Lazy (Text, pack) import Data.XML.Types as XML import Text.Atom.Feed.Import as Atom import Text.Feed.Types import Text.RSS.Import as RSS import Text.RSS1.Import as RSS1 import Control.Monad.Compat import qualified Text.XML as C #if MIN_VERSION_utf8_string(1,0,0) import Codec.Binary.UTF8.String (decodeString) import System.IO (IOMode(..), hGetContents, openBinaryFile) utf8readFile :: FilePath -> IO String utf8readFile fp = fmap decodeString (hGetContents =<< openBinaryFile fp ReadMode) #else import System.IO.UTF8 as UTF8 (readFile) utf8readFile :: FilePath -> IO String utf8readFile = UTF8.readFile #endif class FeedSource s where parseFeedSourceXML :: s -> Either SomeException C.Document instance FeedSource ByteString where parseFeedSourceXML = C.parseLBS C.def instance FeedSource Text where parseFeedSourceXML = C.parseText C.def instance FeedSource String where parseFeedSourceXML = parseFeedSourceXML . pack -- | 'parseFeedFromFile fp' reads in the contents of the file at @fp@; -- the assumed encoding is UTF-8. parseFeedFromFile :: FilePath -> IO Feed parseFeedFromFile fp = do ls <- utf8readFile fp case parseFeedString ls of Nothing -> fail ("parseFeedFromFile: not a well-formed XML content in: " ++ fp) Just f -> return f -- | 'parseFeedWithParser tries to parse the string @str@ -- as one of the feed formats. First as Atom, then RSS2 before -- giving RSS1 a try. @Nothing@ is, rather unhelpfully, returned -- as an indication of error. parseFeedWithParser :: FeedSource s => (s -> Either e C.Document) -> s -> Maybe Feed parseFeedWithParser parser str = case parser str of Left _ -> Nothing Right d -> readAtom e `mplus` readRSS2 e `mplus` readRSS1 e `mplus` Just (XMLFeed e) where e = C.toXMLElement $ C.documentRoot d parseFeedString :: String -> Maybe Feed parseFeedString = parseFeedSource -- | 'parseFeedSource s' tries to parse the source @s@ as -- one of the feed formats. First as Atom, then RSS2 before giving -- RSS1 a try. @Nothing@ is, rather unhelpfully, returned as an -- indication of error. parseFeedSource :: FeedSource s => s -> Maybe Feed parseFeedSource = parseFeedWithParser parseFeedSourceXML -- | 'readRSS2 elt' tries to derive an RSS2.x, RSS-0.9x feed document -- from the XML element @e@. readRSS2 :: XML.Element -> Maybe Feed readRSS2 e = RSSFeed <$> RSS.elementToRSS e -- | 'readRSS1 elt' tries to derive an RSS1.0 feed document -- from the XML element @e@. readRSS1 :: XML.Element -> Maybe Feed readRSS1 e = RSS1Feed <$> RSS1.elementToFeed e -- | 'readAtom elt' tries to derive an Atom feed document -- from the XML element @e@. readAtom :: XML.Element -> Maybe Feed readAtom e = AtomFeed <$> Atom.elementFeed e feed-1.0.1.0/src/Text/Feed/Query.hs0000644000000000000000000004052213354421723014766 0ustar0000000000000000{-# LANGUAGE CPP #-} -------------------------------------------------------------------- -- | -- Module : Text.Feed.Query -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.Feed.Query ( Text.Feed.Query.feedItems -- :: Feed.Feed -> [Feed.Item] , FeedGetter -- type _ a = Feed -> a , getFeedTitle -- :: FeedGetter Text , getFeedAuthor -- :: FeedGetter Text , getFeedHome -- :: FeedGetter URLString , getFeedHTML -- :: FeedGetter URLString , getFeedDescription -- :: FeedGetter Text , getFeedPubDate -- :: FeedGetter DateString , getFeedLastUpdate -- :: FeedGetter Text , getFeedDate -- :: FeedGetter DateString , getFeedLogoLink -- :: FeedGetter URLString , getFeedLanguage -- :: FeedGetter Text , getFeedCategories -- :: FeedGetter [(Text, Maybe Text)] , getFeedGenerator -- :: FeedGetter Text , getFeedItems -- :: FeedGetter [Item] , ItemGetter -- type _ a = Item -> Maybe a , getItemTitle -- :: ItemGetter Text , getItemLink -- :: ItemGetter Text , getItemPublishDate -- :: Data.Time.ParseTime t => ItemGetter (Maybe t) , getItemPublishDateString -- :: ItemGetter (DateString) , getItemDate -- :: ItemGetter (DateString) , getItemAuthor -- :: ItemGetter Text , getItemCommentLink -- :: ItemGetter (URLString) , getItemEnclosure -- :: ItemGetter (URI, Maybe Text, Integer) , getItemFeedLink -- :: ItemGetter (URLString) , getItemId -- :: ItemGetter (Bool, Text) , getItemCategories -- :: ItemGetter [Text] , getItemRights -- :: ItemGetter Text , getItemSummary -- :: ItemGetter Text , getItemDescription -- :: ItemGetter Text (synonym of previous.) ) where import Prelude.Compat import Text.Feed.Types as Feed import Data.XML.Types as XML import Text.Atom.Feed as Atom import Text.Atom.Feed.Export (atomName) import Text.RSS.Syntax as RSS import Text.RSS1.Syntax as RSS1 import Data.XML.Compat import Text.DublinCore.Types import Control.Applicative ((<|>)) import Control.Arrow ((&&&)) import Control.Monad.Compat (mplus) import Data.Maybe import Data.Text (Text) import qualified Data.Text as T import Data.Text.Read import Data.Time.Format (ParseTime) import qualified Data.Time.Format as F -- for getItemPublishDate rfc822 date parsing. import Data.Time.Locale.Compat (defaultTimeLocale, iso8601DateFormat, rfc822DateFormat) feedItems :: Feed.Feed -> [Feed.Item] feedItems fe = case fe of AtomFeed f -> map Feed.AtomItem (Atom.feedEntries f) RSSFeed f -> map Feed.RSSItem (RSS.rssItems $ RSS.rssChannel f) RSS1Feed f -> map Feed.RSS1Item (RSS1.feedItems f) XMLFeed f -> case findElements "item" f of [] -> map Feed.XMLItem $ findElements (atomName "entry") f l -> map Feed.XMLItem l getFeedItems :: Feed.Feed -> [Feed.Item] getFeedItems = Text.Feed.Query.feedItems type FeedGetter a = Feed.Feed -> Maybe a getFeedAuthor :: FeedGetter Text getFeedAuthor ft = case ft of Feed.AtomFeed f -> fmap Atom.personName $ listToMaybe $ Atom.feedAuthors f Feed.RSSFeed f -> RSS.rssEditor (RSS.rssChannel f) Feed.RSS1Feed f -> fmap dcText $ listToMaybe $ filter isAuthor $ RSS1.channelDC (RSS1.feedChannel f) Feed.XMLFeed f -> case findElement "channel" f of Just e1 -> strContent <$> findElement "editor" e1 Nothing -> fmap strContent $ findElement (atomName "name") =<< findChild (atomName "author") f where isAuthor dc = dcElt dc == DC_Creator getFeedTitle :: Feed.Feed -> Text getFeedTitle ft = case ft of Feed.AtomFeed f -> contentToStr $ Atom.feedTitle f Feed.RSSFeed f -> RSS.rssTitle (RSS.rssChannel f) Feed.RSS1Feed f -> RSS1.channelTitle (RSS1.feedChannel f) Feed.XMLFeed f -> case findElement "channel" f of Just e1 -> maybe "" strContent (findElement "title" e1) Nothing -> maybe "" strContent (findChild (atomName "title") f) getFeedHome :: FeedGetter URLString getFeedHome ft = case ft of Feed.AtomFeed f -> fmap Atom.linkHref $ listToMaybe $ filter isSelf (Atom.feedLinks f) Feed.RSSFeed f -> Just (RSS.rssLink (RSS.rssChannel f)) Feed.RSS1Feed f -> Just (RSS1.channelURI (RSS1.feedChannel f)) Feed.XMLFeed f -> case findElement "channel" f of Just e1 -> strContent <$> findElement "link" e1 Nothing -> attributeText "href" =<< findChild (atomName "link") f where isSelf lr = toStr (Atom.linkRel lr) == "self" getFeedHTML :: FeedGetter URLString getFeedHTML ft = case ft of Feed.AtomFeed f -> fmap Atom.linkHref $ listToMaybe $ filter isSelf (Atom.feedLinks f) Feed.RSSFeed f -> Just (RSS.rssLink (RSS.rssChannel f)) Feed.RSS1Feed f -> Just (RSS1.channelURI (RSS1.feedChannel f)) Feed.XMLFeed f -> case findElement "channel" f of Just e1 -> strContent <$> findElement "link" e1 Nothing -> Nothing -- ToDo parse atom like tags where isSelf lr = let rel = Atom.linkRel lr in (isNothing rel || toStr rel == "alternate") && isHTMLType (linkType lr) isHTMLType (Just str) = "html" `T.isSuffixOf` str isHTMLType _ = True -- if none given, assume html. getFeedDescription :: FeedGetter Text getFeedDescription ft = case ft of Feed.AtomFeed f -> fmap contentToStr (Atom.feedSubtitle f) Feed.RSSFeed f -> Just $ RSS.rssDescription (RSS.rssChannel f) Feed.RSS1Feed f -> Just (RSS1.channelDesc (RSS1.feedChannel f)) Feed.XMLFeed f -> case findElement "channel" f of Just e1 -> strContent <$> findElement "description" e1 Nothing -> strContent <$> findChild (atomName "subtitle") f getFeedPubDate :: FeedGetter DateString getFeedPubDate ft = case ft of Feed.AtomFeed f -> Just $ Atom.feedUpdated f Feed.RSSFeed f -> RSS.rssPubDate (RSS.rssChannel f) Feed.RSS1Feed f -> fmap dcText $ listToMaybe $ filter isDate (RSS1.channelDC $ RSS1.feedChannel f) Feed.XMLFeed f -> case findElement "channel" f of Just e1 -> strContent <$> findElement "pubDate" e1 Nothing -> strContent <$> findChild (atomName "published") f where isDate dc = dcElt dc == DC_Date getFeedLastUpdate :: FeedGetter Text getFeedLastUpdate ft = case ft of Feed.AtomFeed f -> Just $ Atom.feedUpdated f Feed.RSSFeed f -> RSS.rssPubDate (RSS.rssChannel f) Feed.RSS1Feed f -> fmap dcText $ listToMaybe $ filter isDate (RSS1.channelDC $ RSS1.feedChannel f) Feed.XMLFeed f -> case findElement "channel" f of Just e1 -> strContent <$> findElement "pubDate" e1 Nothing -> strContent <$> findChild (atomName "updated") f where isDate dc = dcElt dc == DC_Date getFeedDate :: FeedGetter DateString getFeedDate = getFeedPubDate getFeedLogoLink :: FeedGetter URLString getFeedLogoLink ft = case ft of Feed.AtomFeed f -> Atom.feedLogo f Feed.RSSFeed f -> fmap RSS.rssImageURL (RSS.rssImage $ RSS.rssChannel f) Feed.RSS1Feed f -> RSS1.imageURI <$> RSS1.feedImage f Feed.XMLFeed f -> case findElement "channel" f of Just ch -> do e1 <- findElement "image" ch v <- findElement "url" e1 return (strContent v) Nothing -> strContent <$> findChild (atomName "logo") f getFeedLanguage :: FeedGetter Text getFeedLanguage ft = case ft of Feed.AtomFeed f -> attributeText "lang" $ unode "" (Atom.feedAttrs f) Feed.RSSFeed f -> RSS.rssLanguage (RSS.rssChannel f) Feed.RSS1Feed f -> fmap dcText $ listToMaybe $ filter isLang (RSS1.channelDC $ RSS1.feedChannel f) Feed.XMLFeed f -> do ch <- findElement "channel" f e1 <- findElement "language" ch return (strContent e1) -- ToDo parse atom like tags too where isLang dc = dcElt dc == DC_Language getFeedCategories :: Feed.Feed -> [(Text, Maybe Text)] getFeedCategories ft = case ft of Feed.AtomFeed f -> map (Atom.catTerm &&& Atom.catScheme) (Atom.feedCategories f) Feed.RSSFeed f -> map (RSS.rssCategoryValue &&& RSS.rssCategoryDomain) (RSS.rssCategories (RSS.rssChannel f)) Feed.RSS1Feed f -> case filter isCat (RSS1.channelDC $ RSS1.feedChannel f) of ls -> map (\l -> (dcText l, Nothing)) ls Feed.XMLFeed f -> case maybe [] (findElements "category") (findElement "channel" f) of ls -> map (\l -> (maybe "" strContent (findElement "term" l), attributeText "domain" l)) ls -- ToDo parse atom like tags too where isCat dc = dcElt dc == DC_Subject getFeedGenerator :: FeedGetter Text getFeedGenerator ft = case ft of Feed.AtomFeed f -> do gen <- Atom.feedGenerator f Atom.genURI gen Feed.RSSFeed f -> RSS.rssGenerator (RSS.rssChannel f) Feed.RSS1Feed f -> fmap dcText $ listToMaybe $ filter isSource (RSS1.channelDC (RSS1.feedChannel f)) Feed.XMLFeed f -> case findElement "channel" f of Just e1 -> strContent <$> findElement "generator" e1 Nothing -> attributeText "uri" =<< findChild (atomName "generator") f where isSource dc = dcElt dc == DC_Source type ItemGetter a = Feed.Item -> Maybe a getItemTitle :: ItemGetter Text getItemTitle it = case it of Feed.AtomItem i -> Just (contentToStr $ Atom.entryTitle i) Feed.RSSItem i -> RSS.rssItemTitle i Feed.RSS1Item i -> Just (RSS1.itemTitle i) Feed.XMLItem e -> fmap strContent $ findElement "title" e <|> findChild (atomName "title") e getItemLink :: ItemGetter Text getItemLink it = case it -- look up the 'alternate' HTML link relation on the entry, or one -- without link relation since that is equivalent to 'alternate': of Feed.AtomItem i -> fmap Atom.linkHref $ listToMaybe $ filter isSelf $ Atom.entryLinks i Feed.RSSItem i -> RSS.rssItemLink i Feed.RSS1Item i -> Just (RSS1.itemLink i) Feed.XMLItem i -> fmap strContent (findElement "link" i) <|> (findChild (atomName "link") i >>= attributeText "href") where isSelf lr = let rel = Atom.linkRel lr in (isNothing rel || toStr rel == "alternate") && isHTMLType (linkType lr) isHTMLType (Just str) = "html" `T.isSuffixOf` str isHTMLType _ = True -- if none given, assume html. -- | 'getItemPublishDate item' returns the publication date of the item, -- but first parsed per the supported RFC 822 and RFC 3339 formats. -- -- If the date string cannot be parsed as such, Just Nothing is -- returned. The caller must then instead fall back to processing the -- date string from 'getItemPublishDateString'. -- -- The parsed date representation is one of the ParseTime instances; -- see 'Data.Time.Format'. getItemPublishDate :: ParseTime t => ItemGetter (Maybe t) getItemPublishDate it = do ds <- getItemPublishDateString it let rfc3339DateFormat1 = iso8601DateFormat (Just "%H:%M:%S%Z") rfc3339DateFormat2 = iso8601DateFormat (Just "%H:%M:%S%Q%Z") formats = [rfc3339DateFormat1, rfc3339DateFormat2, rfc822DateFormat] date = foldl1 mplus (map (\fmt -> parseTime defaultTimeLocale fmt $ T.unpack ds) formats) return date where #if MIN_VERSION_time(1,5,0) parseTime = F.parseTimeM True #else parseTime = F.parseTime #endif getItemPublishDateString :: ItemGetter DateString getItemPublishDateString it = case it of Feed.AtomItem i -> Just $ Atom.entryUpdated i Feed.RSSItem i -> RSS.rssItemPubDate i Feed.RSS1Item i -> fmap dcText $ listToMaybe $ filter isDate $ RSS1.itemDC i Feed.XMLItem e -> fmap strContent $ findElement "pubDate" e <|> findElement (atomName "published") e where isDate dc = dcElt dc == DC_Date getItemDate :: ItemGetter DateString getItemDate = getItemPublishDateString -- | 'getItemAuthor f' returns the optional author of the item. getItemAuthor :: ItemGetter Text getItemAuthor it = case it of Feed.AtomItem i -> fmap Atom.personName $ listToMaybe $ Atom.entryAuthors i Feed.RSSItem i -> RSS.rssItemAuthor i Feed.RSS1Item i -> fmap dcText $ listToMaybe $ filter isAuthor $ RSS1.itemDC i Feed.XMLItem e -> fmap strContent $ findElement "author" e <|> (findElement (atomName "author") e >>= findElement (atomName "name")) where isAuthor dc = dcElt dc == DC_Creator getItemCommentLink :: ItemGetter URLString getItemCommentLink it = case it -- look up the 'replies' HTML link relation on the entry: of Feed.AtomItem e -> fmap Atom.linkHref $ listToMaybe $ filter isReplies $ Atom.entryLinks e Feed.RSSItem i -> RSS.rssItemComments i Feed.RSS1Item i -> fmap dcText $ listToMaybe $ filter isRel $ RSS1.itemDC i Feed.XMLItem i -> fmap strContent (findElement "comments" i) <|> (findElement (atomName "link") i >>= attributeText "href") where isReplies lr = toStr (Atom.linkRel lr) == "replies" isRel dc = dcElt dc == DC_Relation getItemEnclosure :: ItemGetter (URI, Maybe Text, Maybe Integer) getItemEnclosure it = case it of Feed.AtomItem e -> case filter isEnc $ Atom.entryLinks e of (l:_) -> Just (Atom.linkHref l, Atom.linkType l, readLength (Atom.linkLength l)) _ -> Nothing Feed.RSSItem i -> fmap (\e -> (RSS.rssEnclosureURL e, Just (RSS.rssEnclosureType e), RSS.rssEnclosureLength e)) (RSS.rssItemEnclosure i) Feed.RSS1Item i -> case RSS1.itemContent i of [] -> Nothing (c:_) -> Just (fromMaybe "" (RSS1.contentURI c), RSS1.contentFormat c, Nothing) Feed.XMLItem e -> fmap xmlToEnclosure $ findElement "enclosure" e <|> findElement (atomName "enclosure") e where isEnc lr = toStr (Atom.linkRel lr) == "enclosure" readLength Nothing = Nothing readLength (Just str) = case decimal str of Right (v, _) -> Just v _ -> Nothing xmlToEnclosure e = ( fromMaybe "" (attributeText "url" e) , attributeText "type" e , readLength $ attributeText "length" e) getItemFeedLink :: ItemGetter URLString getItemFeedLink it = case it of Feed.AtomItem e -> case Atom.entrySource e of Nothing -> Nothing Just s -> Atom.sourceId s Feed.RSSItem i -> case RSS.rssItemSource i of Nothing -> Nothing Just s -> Just (RSS.rssSourceURL s) Feed.RSS1Item _ -> Nothing Feed.XMLItem e -> case findElement "source" e of Nothing -> Nothing Just s -> fmap strContent (findElement "url" s) -- ToDo parse atom like tags too getItemId :: ItemGetter (Bool, Text) getItemId it = case it of Feed.AtomItem e -> Just (True, Atom.entryId e) Feed.RSSItem i -> case RSS.rssItemGuid i of Nothing -> Nothing Just ig -> Just (fromMaybe True (RSS.rssGuidPermanentURL ig), RSS.rssGuidValue ig) Feed.RSS1Item i -> case filter isId (RSS1.itemDC i) of (l:_) -> Just (True, dcText l) _ -> Nothing Feed.XMLItem e -> fmap (\e1 -> (True, strContent e1)) $ findElement "guid" e <|> findElement (atomName "id") e where isId dc = dcElt dc == DC_Identifier getItemCategories :: Feed.Item -> [Text] getItemCategories it = case it of Feed.AtomItem i -> map Atom.catTerm $ Atom.entryCategories i Feed.RSSItem i -> map RSS.rssCategoryValue $ RSS.rssItemCategories i Feed.RSS1Item i -> concat $ getCats1 i -- ToDo parse atom like tags too Feed.XMLItem i -> map strContent $ findElements "category" i -- get RSS1 categories; either via DublinCore's subject (or taxonomy topics...not yet.) where getCats1 i1 = map (T.words . dcText) $ filter (\dc -> dcElt dc == DC_Subject) $ RSS1.itemDC i1 getItemRights :: ItemGetter Text getItemRights it = case it of Feed.AtomItem e -> contentToStr <$> Atom.entryRights e Feed.RSSItem _ -> Nothing Feed.RSS1Item i -> fmap dcText $ listToMaybe $ filter isRights (RSS1.itemDC i) Feed.XMLItem i -> strContent <$> findElement (atomName "rights") i where isRights dc = dcElt dc == DC_Rights getItemSummary :: ItemGetter Text getItemSummary = getItemDescription getItemDescription :: ItemGetter Text getItemDescription it = case it of Feed.AtomItem e -> contentToStr <$> Atom.entrySummary e Feed.RSSItem e -> RSS.rssItemDescription e Feed.RSS1Item i -> itemDesc i Feed.XMLItem i -> strContent <$> findElement (atomName "summary") i -- strip away toStr :: Maybe (Either Text Text) -> Text toStr Nothing = "" toStr (Just (Left x)) = x toStr (Just (Right x)) = x contentToStr :: TextContent -> Text contentToStr x = case x of Atom.TextString s -> s Atom.HTMLString s -> s Atom.XHTMLString s -> strContent s feed-1.0.1.0/src/Text/Feed/Translate.hs0000644000000000000000000001071413354421723015616 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Feed.Translate -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -- Translating between RSS formats; work in progress. -- module Text.Feed.Translate ( translateItemTo -- :: FeedKind -> Item -> Item , withAtomEntry -- :: (Atom.Entry -> Atom.Entry) -> Item -> Item , withRSSItem -- :: (RSS.RSSItem -> RSS.RSSItem) -> Item -> Item , withRSS1Item -- :: (RSS1.Item -> RSS1.Item) -> Item -> Item ) where import Prelude.Compat import Control.Arrow ((&&&)) import Text.Atom.Feed as Atom import Text.Feed.Constructor import Text.Feed.Types as Feed import Text.RSS.Syntax as RSS import qualified Text.RSS1.Syntax as RSS1 import Data.Maybe (fromMaybe) import Data.Text (Text) -- functions for performing format-specific transformations. -- If the item isn't in the of-interest format, no transformation -- is performed (i.e., no on-the-fly translation into the requested -- format is performed; the caller is responsible -- withAtomEntry :: (Atom.Entry -> Atom.Entry) -> Item -> Item withAtomEntry f it = case it of Feed.AtomItem e -> Feed.AtomItem (f e) _ -> it withRSSItem :: (RSS.RSSItem -> RSS.RSSItem) -> Item -> Item withRSSItem f it = case it of Feed.RSSItem e -> Feed.RSSItem (f e) _ -> it withRSS1Item :: (RSS1.Item -> RSS1.Item) -> Item -> Item withRSS1Item f it = case it of Feed.RSS1Item e -> Feed.RSS1Item (f e) _ -> it translateItemTo :: FeedKind -> Item -> Item translateItemTo fk it = case fk of AtomKind -> toAtomItem it RSSKind v -> toRSSItem v it RDFKind v -> toRDFItem v it toRSSItem :: Maybe Text -> Item -> Item toRSSItem = error "toRSSItem: unimplemented" toRDFItem :: Maybe Text -> Item -> Item toRDFItem = error "toRDFItem: unimplemented" toAtomItem :: Item -> Item toAtomItem it = case it of AtomItem {} -> it RSS1Item {} -> error "toAtomItem: unimplemented (from RSS1 item rep.)" XMLItem {} -> error "toAtomItem: unimplemented (from shallow XML rep.)" Feed.RSSItem ri -> foldl (\oi f -> f oi) outIt pipeline_rss_atom where outIt = withAtomEntry (\e -> e {Atom.entryOther = RSS.rssItemOther ri, Atom.entryAttrs = RSS.rssItemAttrs ri}) (newItem AtomKind) pipeline_rss_atom = [ mb withItemTitle (rssItemTitle ri) , mb withItemLink (rssItemLink ri) , mb withItemDescription (rssItemDescription ri) , mb withItemAuthor (rssItemAuthor ri) , ls withItemCategories (rssItemCategories ri) , mb withItemId' (rssItemGuid ri) , mb withItemCommentLink (rssItemComments ri) , mb withItemEnclosure' (rssItemEnclosure ri) , mb withItemPubDate (rssItemPubDate ri) ] withItemEnclosure' e = withItemEnclosure (rssEnclosureURL e) (Just $ rssEnclosureType e) (rssEnclosureLength e) withItemId' g = withItemId (fromMaybe True (rssGuidPermanentURL g)) (rssGuidValue g) mb _ Nothing = id mb f (Just v) = f v ls _ [] = id -- hack, only used for cats, so specialize: ls f xs = f (map (rssCategoryValue &&& rssCategoryDomain) xs) {- pipeline_rss_atom = [ withItemTitle (rssItemTitle ri) , withItemLink (rssLink ri) , withDescription (rssDescription ri) , \ inp -> mb (\ la -> inp{feedLanguage=...}) (rssLanguage ri) , \ inp -> mb (\ ed -> inp{feedAuthors=[nullPerson{personName=ed}]}) (rssEditor ri) , \ inp -> mb (\ ed -> inp{feedAuthors=[nullPerson{personName=ed}]}) (rssWebMaster ri) , \ inp -> mb (\ pu -> withPubDate) (rssPubDate ri) , \ inp -> mb withLastUpdate (rssLastUpdate ri) , \ inp -> withCategories (map (\c -> (RSS.rssCategoryValue c, RSS.rssCategoryDomain c)) (rssCategories ri)) inp , \ inp -> mb withGenerator (rssGenerator ri) , rssDocs , rssCloud , rssTTL , rssImage , rssRating , rssTextInput , rssSkipHours , rssSkipDays } in -} feed-1.0.1.0/src/Text/Feed/Types.hs0000644000000000000000000000302713354421723014764 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Feed.Types -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.Feed.Types ( Feed(..) , Item(..) , FeedKind(..) ) where import Prelude.Compat import Data.Text import qualified Data.XML.Types as XML import qualified Text.Atom.Feed as Atom import qualified Text.RSS.Syntax as RSS import qualified Text.RSS1.Syntax as RSS1 -- | The abstract type of feed documents. The internal representation -- is as whatever feed variant type the document was either imported or -- has now been translated to. data Feed = AtomFeed Atom.Feed | RSSFeed RSS.RSS | RSS1Feed RSS1.Feed -- if we're unable to correctly the well-formed XML as a feed, -- keep it as an untyped document. | XMLFeed XML.Element deriving (Show) -- | The abstract type of feed items. Like the 'Text.Feed.Types.Feed' type, the -- representation of a value is as one of the different RSS item\/entry -- variants. data Item = AtomItem Atom.Entry | RSSItem RSS.RSSItem | RSS1Item RSS1.Item | XMLItem XML.Element deriving (Show) -- | The kinds of feed documents supported. data FeedKind = AtomKind | RSSKind (Maybe Text) -- Nothing => default version (2.0) | RDFKind (Maybe Text) -- Nothing => default version (1.0) deriving (Eq, Show) feed-1.0.1.0/src/Text/Feed/Util.hs0000644000000000000000000000302013354421723014566 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.Feed.Util -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.Feed.Util ( toFeedDateString , toFeedDateStringUTC ) where import Prelude.Compat import Data.Time (UTCTime, formatTime) import qualified Data.Time.Locale.Compat import qualified System.Locale import System.Time (ClockTime, formatCalendarTime, toUTCTime) import Text.Feed.Types -- | 'toFeedDateString' translates a calendar time into -- the format expected by the feed kind. toFeedDateString :: FeedKind -> ClockTime -> String {-Date-} toFeedDateString fk ct = formatCalendarTime System.Locale.defaultTimeLocale fmt ut where fmt = feedKindTimeFormat fk ut = toUTCTime ct -- | 'toFeedDateStringUTC' translates a UTC time into -- the format expected by the feed kind. toFeedDateStringUTC :: FeedKind -> UTCTime -> String {-Date-} toFeedDateStringUTC fk = formatTime Data.Time.Locale.Compat.defaultTimeLocale fmt where fmt = feedKindTimeFormat fk -- | Time format expected by the feed kind. feedKindTimeFormat :: FeedKind -> String feedKindTimeFormat fk = case fk of AtomKind {} -> atomRdfTimeFormat RSSKind {} -> "%a, %d %b %Y %H:%M:%S GMT" RDFKind {} -> atomRdfTimeFormat where atomRdfTimeFormat = "%Y-%m-%dT%H:%M:%SZ" feed-1.0.1.0/src/Text/RSS/Export.hs0000644000000000000000000001320113370672700014741 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.RSS.Export -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Description: Convert from RSS to XML -- -------------------------------------------------------------------- module Text.RSS.Export ( qualNode , qualName , xmlRSS , textRSS , xmlChannel , xmlItem , xmlSource , xmlEnclosure , xmlCategory , xmlGuid , xmlImage , xmlCloud , xmlTextInput , xmlSkipHours , xmlSkipDays , xmlAttr , xmlLeaf , mb ) where import Prelude.Compat import Data.XML.Compat import Data.XML.Types as XML import Text.RSS.Syntax import qualified Data.Text.Util as U import Data.Text (Text, pack) import qualified Data.Text.Lazy as TL qualName :: Text -> XML.Name qualName n = Name n Nothing Nothing qualNode :: Text -> [XML.Node] -> XML.Element qualNode n = Element (Name n Nothing Nothing) [] --- xmlRSS :: RSS -> XML.Element xmlRSS r = (qualNode "rss" $ map NodeElement (xmlChannel (rssChannel r) : rssOther r)) {elementAttributes = mkAttr "version" (rssVersion r) : rssAttrs r} textRSS :: RSS -> Maybe TL.Text textRSS = U.renderFeed xmlRSS xmlChannel :: RSSChannel -> XML.Element xmlChannel ch = qualNode "channel" $ map NodeElement ([ xmlLeaf "title" (rssTitle ch) , xmlLeaf "link" (rssLink ch) , xmlLeaf "description" (rssDescription ch) ] ++ map xmlItem (rssItems ch) ++ mb (xmlLeaf "language") (rssLanguage ch) ++ mb (xmlLeaf "copyright") (rssCopyright ch) ++ mb (xmlLeaf "managingEditor") (rssEditor ch) ++ mb (xmlLeaf "webMaster") (rssWebMaster ch) ++ mb (xmlLeaf "pubDate") (rssPubDate ch) ++ mb (xmlLeaf "lastBuildDate") (rssLastUpdate ch) ++ map xmlCategory (rssCategories ch) ++ mb (xmlLeaf "generator") (rssGenerator ch) ++ mb (xmlLeaf "docs") (rssDocs ch) ++ mb xmlCloud (rssCloud ch) ++ mb (xmlLeaf "ttl" . pack . show) (rssTTL ch) ++ mb xmlImage (rssImage ch) ++ mb (xmlLeaf "rating") (rssRating ch) ++ mb xmlTextInput (rssTextInput ch) ++ mb xmlSkipHours (rssSkipHours ch) ++ mb xmlSkipDays (rssSkipDays ch) ++ rssChannelOther ch) xmlItem :: RSSItem -> XML.Element xmlItem it = (qualNode "item" $ map NodeElement (mb (xmlLeaf "title") (rssItemTitle it) ++ mb (xmlLeaf "link") (rssItemLink it) ++ mb (xmlLeaf "description") (rssItemDescription it) ++ mb (xmlLeaf "author") (rssItemAuthor it) ++ map xmlCategory (rssItemCategories it) ++ mb (xmlLeaf "comments") (rssItemComments it) ++ mb xmlEnclosure (rssItemEnclosure it) ++ mb xmlGuid (rssItemGuid it) ++ mb (xmlLeaf "pubDate") (rssItemPubDate it) ++ mb xmlSource (rssItemSource it) ++ rssItemOther it)) {elementAttributes = rssItemAttrs it} xmlSource :: RSSSource -> XML.Element xmlSource s = (xmlLeaf "source" (rssSourceTitle s)) {elementAttributes = mkAttr "url" (rssSourceURL s) : rssSourceAttrs s} xmlEnclosure :: RSSEnclosure -> XML.Element xmlEnclosure e = (xmlLeaf "enclosure" "") { elementAttributes = mkAttr "url" (rssEnclosureURL e) : mkAttr "type" (rssEnclosureType e) : mb (mkAttr "length" . pack . show) (rssEnclosureLength e) ++ rssEnclosureAttrs e } xmlCategory :: RSSCategory -> XML.Element xmlCategory c = (xmlLeaf "category" (rssCategoryValue c)) { elementAttributes = maybe id (\n -> (mkAttr "domain" n :)) (rssCategoryDomain c) (rssCategoryAttrs c) } xmlGuid :: RSSGuid -> XML.Element xmlGuid g = (xmlLeaf "guid" (rssGuidValue g)) { elementAttributes = maybe id (\n -> (mkAttr "isPermaLink" (toBool n) :)) (rssGuidPermanentURL g) (rssGuidAttrs g) } where toBool False = "false" toBool _ = "true" xmlImage :: RSSImage -> XML.Element xmlImage im = qualNode "image" $ map NodeElement ([ xmlLeaf "url" (rssImageURL im) , xmlLeaf "title" (rssImageTitle im) , xmlLeaf "link" (rssImageLink im) ] ++ mb (xmlLeaf "width" . pack . show) (rssImageWidth im) ++ mb (xmlLeaf "height" . pack . show) (rssImageHeight im) ++ mb (xmlLeaf "description") (rssImageDesc im) ++ rssImageOther im) xmlCloud :: RSSCloud -> XML.Element xmlCloud cl = (xmlLeaf "cloud" "") { elementAttributes = mb (mkAttr "domain") (rssCloudDomain cl) ++ mb (mkAttr "port") (rssCloudPort cl) ++ mb (mkAttr "path") (rssCloudPath cl) ++ mb (mkAttr "registerProcedure") (rssCloudRegisterProcedure cl) ++ mb (mkAttr "protocol") (rssCloudProtocol cl) ++ rssCloudAttrs cl } xmlTextInput :: RSSTextInput -> XML.Element xmlTextInput ti = (qualNode "textInput" $ map NodeElement ([ xmlLeaf "title" (rssTextInputTitle ti) , xmlLeaf "description" (rssTextInputDesc ti) , xmlLeaf "name" (rssTextInputName ti) , xmlLeaf "link" (rssTextInputLink ti) ] ++ rssTextInputOther ti)) {elementAttributes = rssTextInputAttrs ti} xmlSkipHours :: [Integer] -> XML.Element xmlSkipHours hs = qualNode "skipHours" $ map (NodeElement . (\n -> xmlLeaf "hour" (pack $ show n))) hs xmlSkipDays :: [Text] -> XML.Element xmlSkipDays hs = qualNode "skipDays" $ map (NodeElement . xmlLeaf "day") hs xmlAttr :: Text -> Text -> Attr xmlAttr k = mkNAttr (qualName k) xmlLeaf :: Text -> Text -> XML.Element xmlLeaf tg txt = Element { elementAttributes = [] , elementName = Name tg Nothing Nothing , elementNodes = [NodeContent (ContentText txt)] } --- mb :: (a -> b) -> Maybe a -> [b] mb _ Nothing = [] mb f (Just x) = [f x] feed-1.0.1.0/src/Text/RSS/Import.hs0000644000000000000000000002327313354421723014743 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.RSS.Import -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- -- Converting from XML to RSS -- -------------------------------------------------------------------- module Text.RSS.Import ( pNodes , pQNodes , pNode , pQNode , pLeaf , pQLeaf , pAttr , pMany , children , qualName , dcName , elementToRSS , elementToChannel , elementToImage , elementToCategory , elementToCloud , elementToItem , elementToSource , elementToEnclosure , elementToGuid , elementToTextInput , elementToSkipHours , elementToSkipDays , readInt , readBool ) where import Prelude.Compat import Data.XML.Compat import Data.XML.Types as XML import Text.RSS.Syntax import Text.RSS1.Utils (dcNS, dcPrefix) import Control.Monad.Compat (guard, mplus) import Data.Char (isSpace) import Data.Maybe (fromMaybe, listToMaybe, mapMaybe) import Data.Text (Text, dropWhile) import Data.Text.Util pNodes :: Text -> [XML.Element] -> [XML.Element] pNodes x = filter ((qualName x ==) . elementName) pQNodes :: Name -> [XML.Element] -> [XML.Element] pQNodes x = filter ((x ==) . elementName) pNode :: Text -> [XML.Element] -> Maybe XML.Element pNode x es = listToMaybe (pNodes x es) pQNode :: Name -> [XML.Element] -> Maybe XML.Element pQNode x es = listToMaybe (pQNodes x es) pLeaf :: Text -> [XML.Element] -> Maybe Text pLeaf x es = strContent `fmap` pNode x es pQLeaf :: Name -> [XML.Element] -> Maybe Text pQLeaf x es = strContent `fmap` pQNode x es pAttr :: Text -> XML.Element -> Maybe Text pAttr x = attributeText (qualName x) pMany :: Text -> (XML.Element -> Maybe a) -> [XML.Element] -> [a] pMany p f es = mapMaybe f (pNodes p es) children :: XML.Element -> [XML.Element] children = elementChildren qualName :: Text -> Name qualName x = Name x Nothing Nothing dcName :: Text -> Name dcName x = Name x (Just dcNS) (Just dcPrefix) elementToRSS :: XML.Element -> Maybe RSS elementToRSS e = do guard (elementName e == qualName "rss") let es = children e let as = elementAttributes e v <- pAttr "version" e ch <- pNode "channel" es >>= elementToChannel return RSS { rssVersion = v , rssAttrs = filter ((`notElem` known_attrs) . fst) as , rssChannel = ch , rssOther = filter (\e1 -> elementName e1 /= qualName "channel") es } where known_attrs = ["version"] elementToChannel :: XML.Element -> Maybe RSSChannel elementToChannel e = do guard (elementName e == qualName "channel") let es = children e title <- pLeaf "title" es link <- pLeaf "link" es return RSSChannel { rssTitle = title , rssLink = link -- being liberal, is a required channel element. , rssDescription = fromMaybe title (pLeaf "description" es) , rssItems = pMany "item" elementToItem es , rssLanguage = pLeaf "language" es `mplus` pQLeaf (dcName "lang") es , rssCopyright = pLeaf "copyright" es , rssEditor = pLeaf "managingEditor" es `mplus` pQLeaf (dcName "creator") es , rssWebMaster = pLeaf "webMaster" es , rssPubDate = pLeaf "pubDate" es `mplus` pQLeaf (dcName "date") es , rssLastUpdate = pLeaf "lastBuildDate" es `mplus` pQLeaf (dcName "date") es , rssCategories = pMany "category" elementToCategory es , rssGenerator = pLeaf "generator" es `mplus` pQLeaf (dcName "source") es , rssDocs = pLeaf "docs" es , rssCloud = pNode "cloud" es >>= elementToCloud , rssTTL = pLeaf "ttl" es >>= readInt , rssImage = pNode "image" es >>= elementToImage , rssRating = pLeaf "rating" es , rssTextInput = pNode "textInput" es >>= elementToTextInput , rssSkipHours = pNode "skipHours" es >>= elementToSkipHours , rssSkipDays = pNode "skipDays" es >>= elementToSkipDays , rssChannelOther = filter ((`notElem` known_channel_elts) . elementName) es } where known_channel_elts = map qualName [ "title" , "link" , "description" , "item" , "language" , "copyright" , "managingEditor" , "webMaster" , "pubDate" , "lastBuildDate" , "category" , "generator" , "docs" , "cloud" , "ttl" , "image" , "rating" , "textInput" , "skipHours" , "skipDays" ] elementToImage :: XML.Element -> Maybe RSSImage elementToImage e = do guard (elementName e == qualName "image") let es = children e url <- pLeaf "url" es title <- pLeaf "title" es link <- pLeaf "link" es return RSSImage { rssImageURL = url , rssImageTitle = title , rssImageLink = link , rssImageWidth = pLeaf "width" es >>= readInt , rssImageHeight = pLeaf "height" es >>= readInt , rssImageDesc = pLeaf "description" es , rssImageOther = filter ((`notElem` known_image_elts) . elementName) es } where known_image_elts = map qualName ["url", "title", "link", "width", "height", "description"] elementToCategory :: XML.Element -> Maybe RSSCategory elementToCategory e = do guard (elementName e == qualName "category") let as = elementAttributes e return RSSCategory { rssCategoryDomain = pAttr "domain" e , rssCategoryAttrs = filter ((`notElem` known_attrs) . nameLocalName . attrKey) as , rssCategoryValue = strContent e } where known_attrs = ["domain"] elementToCloud :: XML.Element -> Maybe RSSCloud elementToCloud e = do guard (elementName e == qualName "cloud") let as = elementAttributes e return RSSCloud { rssCloudDomain = pAttr "domain" e , rssCloudPort = pAttr "port" e , rssCloudPath = pAttr "path" e , rssCloudRegisterProcedure = pAttr "registerProcedure" e , rssCloudProtocol = pAttr "protocol" e , rssCloudAttrs = filter ((`notElem` known_attrs) . nameLocalName . attrKey) as } where known_attrs = ["domain", "port", "path", "registerProcedure", "protocol"] elementToItem :: XML.Element -> Maybe RSSItem elementToItem e = do guard (elementName e == qualName "item") let es = children e return RSSItem { rssItemTitle = pLeaf "title" es , rssItemLink = pLeaf "link" es , rssItemDescription = pLeaf "description" es , rssItemAuthor = pLeaf "author" es `mplus` pQLeaf (dcName "creator") es , rssItemCategories = pMany "category" elementToCategory es , rssItemComments = pLeaf "comments" es , rssItemEnclosure = pNode "enclosure" es >>= elementToEnclosure , rssItemGuid = pNode "guid" es >>= elementToGuid , rssItemPubDate = pLeaf "pubDate" es `mplus` pQLeaf (dcName "date") es , rssItemSource = pNode "source" es >>= elementToSource , rssItemAttrs = elementAttributes e , rssItemOther = filter ((`notElem` known_item_elts) . elementName) es } where known_item_elts = map qualName [ "title" , "link" , "description" , "author" , "category" , "comments" , "enclosure" , "guid" , "pubDate" , "source" ] elementToSource :: XML.Element -> Maybe RSSSource elementToSource e = do guard (elementName e == qualName "source") let as = elementAttributes e url <- pAttr "url" e return RSSSource { rssSourceURL = url , rssSourceAttrs = filter ((`notElem` known_attrs) . nameLocalName . attrKey) as , rssSourceTitle = strContent e } where known_attrs = ["url"] elementToEnclosure :: XML.Element -> Maybe RSSEnclosure elementToEnclosure e = do guard (elementName e == qualName "enclosure") let as = elementAttributes e url <- pAttr "url" e ty <- pAttr "type" e return RSSEnclosure { rssEnclosureURL = url , rssEnclosureType = ty , rssEnclosureLength = pAttr "length" e >>= readInt , rssEnclosureAttrs = filter ((`notElem` known_attrs) . nameLocalName . attrKey) as } where known_attrs = ["url", "type", "length"] elementToGuid :: XML.Element -> Maybe RSSGuid elementToGuid e = do guard (elementName e == qualName "guid") let as = elementAttributes e return RSSGuid { rssGuidPermanentURL = pAttr "isPermaLink" e >>= readBool , rssGuidAttrs = filter ((`notElem` known_attrs) . nameLocalName . attrKey) as , rssGuidValue = strContent e } where known_attrs = ["isPermaLink"] elementToTextInput :: XML.Element -> Maybe RSSTextInput elementToTextInput e = do guard (elementName e == qualName "textInput") let es = children e title <- pLeaf "title" es desc <- pLeaf "description" es name <- pLeaf "name" es link <- pLeaf "link" es return RSSTextInput { rssTextInputTitle = title , rssTextInputDesc = desc , rssTextInputName = name , rssTextInputLink = link , rssTextInputAttrs = elementAttributes e , rssTextInputOther = filter ((`notElem` known_ti_elts) . elementName) es } where known_ti_elts = map qualName ["title", "description", "name", "link"] elementToSkipHours :: XML.Element -> Maybe [Integer] elementToSkipHours e = do guard (elementName e == qualName "skipHours") -- don't bother checking that this is below limit ( <= 24) return (pMany "hour" (readInt . strContent) (children e)) elementToSkipDays :: XML.Element -> Maybe [Text] elementToSkipDays e = do guard (elementName e == qualName "skipDays") -- don't bother checking that this is below limit ( <= 7) return (pMany "day" (return . strContent) (children e)) ---- readBool :: Text -> Maybe Bool readBool s = case Data.Text.dropWhile isSpace s of "true" -> Just True "false" -> Just False _ -> Nothing feed-1.0.1.0/src/Text/RSS/Syntax.hs0000644000000000000000000001627513354421723014763 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.RSS.Syntax -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- -- The basic syntax for putting together feeds. -- -- For instance, to create a feed with a single item item: -- (nullRSS \"rss title\" \"link\") {rssChannel=(nullChannel \"channel title\" \"link\") {rssItems=[(nullItem \"item title\")]}} -------------------------------------------------------------------- module Text.RSS.Syntax ( RSS(..) , URLString , DateString , RSSChannel(..) , RSSItem(..) , RSSSource(..) , RSSEnclosure(..) , RSSCategory(..) , RSSGuid(..) , RSSImage(..) , RSSCloud(..) , RSSTextInput(..) , nullRSS , nullChannel , nullItem , nullSource , nullEnclosure , newCategory , nullGuid , nullPermaGuid , nullImage , nullCloud , nullTextInput ) where import Prelude.Compat import Data.Text (Text) import Data.XML.Compat import Data.XML.Types as XML -- * Core Types -- ^The Radio Userland version of RSS documents\/feeds. -- (versions 0.9x, 2.x) data RSS = RSS { rssVersion :: Text , rssAttrs :: [Attr] , rssChannel :: RSSChannel , rssOther :: [XML.Element] } deriving (Show) type URLString = Text -- | RFC 822 conforming. type DateString = Text data RSSChannel = RSSChannel { rssTitle :: Text , rssLink :: URLString , rssDescription :: Text , rssItems :: [RSSItem] , rssLanguage :: Maybe Text , rssCopyright :: Maybe Text , rssEditor :: Maybe Text , rssWebMaster :: Maybe Text , rssPubDate :: Maybe DateString -- ^ rfc 822 conforming. , rssLastUpdate :: Maybe DateString -- ^ rfc 822 conforming. , rssCategories :: [RSSCategory] , rssGenerator :: Maybe Text , rssDocs :: Maybe URLString , rssCloud :: Maybe RSSCloud , rssTTL :: Maybe Integer , rssImage :: Maybe RSSImage , rssRating :: Maybe Text , rssTextInput :: Maybe RSSTextInput , rssSkipHours :: Maybe [Integer] , rssSkipDays :: Maybe [Text] , rssChannelOther :: [XML.Element] } deriving (Show) data RSSItem = RSSItem { rssItemTitle :: Maybe Text , rssItemLink :: Maybe URLString , rssItemDescription :: Maybe Text -- ^if not present, the title is. (per spec, at least.) , rssItemAuthor :: Maybe Text , rssItemCategories :: [RSSCategory] , rssItemComments :: Maybe URLString , rssItemEnclosure :: Maybe RSSEnclosure , rssItemGuid :: Maybe RSSGuid , rssItemPubDate :: Maybe DateString , rssItemSource :: Maybe RSSSource , rssItemAttrs :: [Attr] , rssItemOther :: [XML.Element] } deriving (Show) data RSSSource = RSSSource { rssSourceURL :: URLString , rssSourceAttrs :: [Attr] , rssSourceTitle :: Text } deriving (Show) data RSSEnclosure = RSSEnclosure { rssEnclosureURL :: URLString , rssEnclosureLength :: Maybe Integer , rssEnclosureType :: Text , rssEnclosureAttrs :: [Attr] } deriving (Show) data RSSCategory = RSSCategory { rssCategoryDomain :: Maybe Text , rssCategoryAttrs :: [Attr] , rssCategoryValue :: Text } deriving (Show) data RSSGuid = RSSGuid { rssGuidPermanentURL :: Maybe Bool , rssGuidAttrs :: [Attr] , rssGuidValue :: Text } deriving (Show) data RSSImage = RSSImage { rssImageURL :: URLString -- the URL to the image resource. , rssImageTitle :: Text , rssImageLink :: URLString -- URL that the image resource should be an href to. , rssImageWidth :: Maybe Integer , rssImageHeight :: Maybe Integer , rssImageDesc :: Maybe Text , rssImageOther :: [XML.Element] } deriving (Show) data RSSCloud = RSSCloud { rssCloudDomain :: Maybe Text , rssCloudPort :: Maybe Text -- on purpose (i.e., not an int) , rssCloudPath :: Maybe Text , rssCloudRegisterProcedure :: Maybe Text , rssCloudProtocol :: Maybe Text , rssCloudAttrs :: [Attr] } deriving (Show) data RSSTextInput = RSSTextInput { rssTextInputTitle :: Text , rssTextInputDesc :: Text , rssTextInputName :: Text , rssTextInputLink :: URLString , rssTextInputAttrs :: [Attr] , rssTextInputOther :: [XML.Element] } deriving (Show) -- * Default Constructors: nullRSS :: Text -- ^channel title -> URLString -- ^channel link -> RSS nullRSS title link = RSS {rssVersion = "2.0", rssAttrs = [], rssChannel = nullChannel title link, rssOther = []} nullChannel :: Text -- ^rssTitle -> URLString -- ^rssLink -> RSSChannel nullChannel title link = RSSChannel { rssTitle = title , rssLink = link , rssDescription = title , rssItems = [] , rssLanguage = Nothing , rssCopyright = Nothing , rssEditor = Nothing , rssWebMaster = Nothing , rssPubDate = Nothing , rssLastUpdate = Nothing , rssCategories = [] , rssGenerator = Nothing , rssDocs = Nothing , rssCloud = Nothing , rssTTL = Nothing , rssImage = Nothing , rssRating = Nothing , rssTextInput = Nothing , rssSkipHours = Nothing , rssSkipDays = Nothing , rssChannelOther = [] } nullItem :: Text -- ^title -> RSSItem nullItem title = RSSItem { rssItemTitle = Just title , rssItemLink = Nothing , rssItemDescription = Nothing , rssItemAuthor = Nothing , rssItemCategories = [] , rssItemComments = Nothing , rssItemEnclosure = Nothing , rssItemGuid = Nothing , rssItemPubDate = Nothing , rssItemSource = Nothing , rssItemAttrs = [] , rssItemOther = [] } nullSource :: URLString -- ^source URL -> Text -- ^title -> RSSSource nullSource url title = RSSSource {rssSourceURL = url, rssSourceAttrs = [], rssSourceTitle = title} nullEnclosure :: URLString -- ^enclosure URL -> Maybe Integer -- ^enclosure length -> Text -- ^enclosure type -> RSSEnclosure nullEnclosure url mb_len ty = RSSEnclosure { rssEnclosureURL = url , rssEnclosureLength = mb_len , rssEnclosureType = ty , rssEnclosureAttrs = [] } newCategory :: Text -- ^category Value -> RSSCategory newCategory nm = RSSCategory {rssCategoryDomain = Nothing, rssCategoryAttrs = [], rssCategoryValue = nm} nullGuid :: Text -- ^guid value -> RSSGuid nullGuid v = RSSGuid {rssGuidPermanentURL = Nothing, rssGuidAttrs = [], rssGuidValue = v} nullPermaGuid :: Text -- ^guid value -> RSSGuid nullPermaGuid v = (nullGuid v) {rssGuidPermanentURL = Just True} nullImage :: URLString -- ^imageURL -> Text -- ^imageTitle -> URLString -- ^imageLink -> RSSImage nullImage url title link = RSSImage { rssImageURL = url , rssImageTitle = title , rssImageLink = link , rssImageWidth = Nothing , rssImageHeight = Nothing , rssImageDesc = Nothing , rssImageOther = [] } nullCloud :: RSSCloud nullCloud = RSSCloud { rssCloudDomain = Nothing , rssCloudPort = Nothing , rssCloudPath = Nothing , rssCloudRegisterProcedure = Nothing , rssCloudProtocol = Nothing , rssCloudAttrs = [] } nullTextInput :: Text -- ^inputTitle -> Text -- ^inputName -> URLString -- ^inputLink -> RSSTextInput nullTextInput title nm link = RSSTextInput { rssTextInputTitle = title , rssTextInputDesc = title , rssTextInputName = nm , rssTextInputLink = link , rssTextInputAttrs = [] , rssTextInputOther = [] } feed-1.0.1.0/src/Text/RSS1/Export.hs0000644000000000000000000001733613370672700015037 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.RSS1.Export -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.RSS1.Export ( xmlFeed , textFeed ) where import Prelude.Compat import Text.DublinCore.Types import Text.RSS1.Syntax import Text.RSS1.Utils import qualified Data.Text.Util as U import Data.List.Compat import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.XML.Compat import Data.XML.Types as XML qualNode :: (Maybe Text, Maybe Text) -> Text -> [XML.Node] -> XML.Element qualNode ns n cs = Element {elementName = qualName ns n, elementAttributes = [], elementNodes = cs} qualNode' :: (Text, Text) -> Text -> [XML.Node] -> XML.Element qualNode' (uri, pre) = qualNode (Just uri, Just pre) --- xmlFeed :: Feed -> XML.Element xmlFeed f = (qualNode' (rdfNS, rdfPrefix) "RDF" $ map NodeElement (concat [ [xmlChannel (feedChannel f)] , mb xmlImage (feedImage f) , map xmlItem (feedItems f) , mb xmlTextInput (feedTextInput f) , map xmlTopic (feedTopics f) , feedOther f ])) -- should we expect these to be derived by the XML pretty printer..? { elementAttributes = nub $ mkNAttr (qualName (Nothing, Nothing) "xmlns") rss10NS : mkNAttr (qualName (Nothing, Just "xmlns") rdfPrefix) rdfNS : mkNAttr (qualName (Nothing, Just "xmlns") synPrefix) synNS : mkNAttr (qualName (Nothing, Just "xmlns") taxPrefix) taxNS : mkNAttr (qualName (Nothing, Just "xmlns") conPrefix) conNS : mkNAttr (qualName (Nothing, Just "xmlns") dcPrefix) dcNS : feedAttrs f } textFeed :: Feed -> Maybe TL.Text textFeed = U.renderFeed xmlFeed xmlChannel :: Channel -> XML.Element xmlChannel ch = (qualNode (Just rss10NS, Nothing) "channel" $ map NodeElement ([ xmlLeaf (rss10NS, Nothing) "title" (channelTitle ch) , xmlLeaf (rss10NS, Nothing) "link" (channelLink ch) , xmlLeaf (rss10NS, Nothing) "description" (channelDesc ch) ] ++ mb xmlTextInputURI (channelTextInputURI ch) ++ mb xmlImageURI (channelImageURI ch) ++ xmlItemURIs (channelItemURIs ch) ++ map xmlDC (channelDC ch) ++ concat [ mb xmlUpdatePeriod (channelUpdatePeriod ch) , mb xmlUpdateFreq (channelUpdateFreq ch) , mb (xmlLeaf (synNS, Just synPrefix) "updateBase") (channelUpdateBase ch) ] ++ xmlContentItems (channelContent ch) ++ xmlTopics (channelTopics ch) ++ channelOther ch)) { elementAttributes = mkNAttr (qualName' (rdfNS, rdfPrefix) "about") (channelURI ch) : channelAttrs ch } xmlImageURI :: URIString -> XML.Element xmlImageURI u = xmlEmpty (rss10NS, Nothing) "image" [mkNAttr (rdfName "resource") u] xmlImage :: Image -> XML.Element xmlImage i = (qualNode (Just rss10NS, Nothing) "image" $ map NodeElement ([ xmlLeaf (rss10NS, Nothing) "title" (imageTitle i) , xmlLeaf (rss10NS, Nothing) "url" (imageURL i) , xmlLeaf (rss10NS, Nothing) "link" (imageLink i) ] ++ map xmlDC (imageDC i) ++ imageOther i)) {elementAttributes = mkNAttr (qualName' (rdfNS, rdfPrefix) "about") (imageURI i) : imageAttrs i} xmlItemURIs :: [URIString] -> [XML.Element] xmlItemURIs [] = [] xmlItemURIs xs = [ qualNode (Just rss10NS, Nothing) "items" [NodeElement (qualNode' (rdfNS, rdfPrefix) "Seq" (map toRes xs))] ] where toRes u = NodeElement (xmlEmpty (rdfNS, Just rdfPrefix) "li" [mkNAttr (rdfName "resource") u]) xmlTextInputURI :: URIString -> XML.Element xmlTextInputURI u = xmlEmpty (rss10NS, Nothing) "textinput" [mkNAttr (rdfName "resource") u] xmlTextInput :: TextInputInfo -> XML.Element xmlTextInput ti = (qualNode (Just rss10NS, Nothing) "textinput" $ map NodeElement $ [ xmlLeaf (rss10NS, Nothing) "title" (textInputTitle ti) , xmlLeaf (rss10NS, Nothing) "description" (textInputDesc ti) , xmlLeaf (rss10NS, Nothing) "name" (textInputName ti) , xmlLeaf (rss10NS, Nothing) "link" (textInputLink ti) ] ++ map xmlDC (textInputDC ti) ++ textInputOther ti) {elementAttributes = mkNAttr (rdfName "about") (textInputURI ti) : textInputAttrs ti} xmlDC :: DCItem -> XML.Element xmlDC dc = xmlLeaf (dcNS, Just dcPrefix) (infoToTag (dcElt dc)) (dcText dc) xmlUpdatePeriod :: UpdatePeriod -> XML.Element xmlUpdatePeriod u = xmlLeaf (synNS, Just synPrefix) "updatePeriod" (toStr u) where toStr ux = case ux of Update_Hourly -> "hourly" Update_Daily -> "daily" Update_Weekly -> "weekly" Update_Monthly -> "monthly" Update_Yearly -> "yearly" xmlUpdateFreq :: Integer -> XML.Element xmlUpdateFreq f = xmlLeaf (synNS, Just synPrefix) "updateFrequency" (T.pack $ show f) xmlContentItems :: [ContentInfo] -> [XML.Element] xmlContentItems [] = [] xmlContentItems xs = [ qualNode' (conNS, conPrefix) "items" [ NodeElement $ qualNode' (rdfNS, rdfPrefix) "Bag" (map (\e -> NodeElement (qualNode' (rdfNS, rdfPrefix) "li" [NodeElement (xmlContentInfo e)])) xs) ] ] xmlContentInfo :: ContentInfo -> XML.Element xmlContentInfo ci = (qualNode' (conNS, conPrefix) "item" $ map NodeElement (concat [ mb (rdfResource (conNS, conPrefix) "format") (contentFormat ci) , mb (rdfResource (conNS, conPrefix) "encoding") (contentEncoding ci) , mb (rdfValue []) (contentValue ci) ])) {elementAttributes = mb (mkNAttr (rdfName "about")) (contentURI ci)} rdfResource :: (Text, Text) -> Text -> Text -> XML.Element rdfResource (uri, pre) t v = xmlEmpty (uri, Just pre) t [mkNAttr (rdfName "resource") v] rdfValue :: [Attr] -> Text -> XML.Element rdfValue as s = (xmlLeaf (rdfNS, Just rdfPrefix) "value" s) {elementAttributes = as} xmlTopics :: [URIString] -> [XML.Element] xmlTopics [] = [] xmlTopics xs = [ qualNode' (taxNS, taxPrefix) "topics" [ NodeElement (qualNode' (rdfNS, rdfPrefix) "Bag" (map (NodeElement . rdfResource (rdfNS, rdfPrefix) "li") xs)) ] ] xmlTopic :: TaxonomyTopic -> XML.Element xmlTopic tt = (qualNode' (taxNS, taxPrefix) "topic" $ map NodeElement (xmlLeaf (rss10NS, Nothing) "link" (taxonomyLink tt) : mb (xmlLeaf (rss10NS, Nothing) "title") (taxonomyTitle tt) ++ mb (xmlLeaf (rss10NS, Nothing) "description") (taxonomyDesc tt) ++ xmlTopics (taxonomyTopics tt) ++ map xmlDC (taxonomyDC tt) ++ taxonomyOther tt)) {elementAttributes = [mkNAttr (rdfName "about") (taxonomyURI tt)]} xmlItem :: Item -> XML.Element xmlItem i = (qualNode (Just rss10NS, Nothing) "item" $ map NodeElement ([ xmlLeaf (rss10NS, Nothing) "title" (itemTitle i) , xmlLeaf (rss10NS, Nothing) "link" (itemLink i) ] ++ mb (xmlLeaf (rss10NS, Nothing) "description") (itemDesc i) ++ map xmlDC (itemDC i) ++ xmlTopics (itemTopics i) ++ map xmlContentInfo (itemContent i) ++ itemOther i)) {elementAttributes = mkNAttr (qualName' (rdfNS, rdfPrefix) "about") (itemURI i) : itemAttrs i} xmlLeaf :: (Text, Maybe Text) -> Text -> Text -> XML.Element xmlLeaf (ns, pre) tg txt = Element { elementName = qualName (Just ns, pre) tg , elementAttributes = [] , elementNodes = [NodeContent $ ContentText txt] } xmlEmpty :: (Text, Maybe Text) -> Text -> [Attr] -> XML.Element xmlEmpty (uri, pre) t as = (qualNode (Just uri, pre) t []) {elementAttributes = as} --- mb :: (a -> b) -> Maybe a -> [b] mb _ Nothing = [] mb f (Just x) = [f x] feed-1.0.1.0/src/Text/RSS1/Import.hs0000644000000000000000000001720213354421723015017 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.RSS1.Import -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.RSS1.Import ( elementToFeed ) where import Prelude.Compat import Data.XML.Compat import Data.XML.Types as XML import Text.DublinCore.Types import Text.RSS1.Syntax import Text.RSS1.Utils import Control.Monad.Compat (guard, mplus) import Data.Maybe (mapMaybe) import Data.Text.Util --- elementToFeed :: XML.Element -> Maybe Feed elementToFeed e = do guard (elementName e == rdfName "RDF") ver <- pAttr (Nothing, Nothing) "xmlns" e `mplus` Just rss10NS ch <- pNode "channel" e >>= elementToChannel let mbImg = pNode "image" e >>= elementToImage let is = maybe [] elementToItems $ pNode "items" e let mbTI = pNode "textinput" e >>= elementToTextInput let ch1 = ch {channelItemURIs = is} let its = pMany (Just rss10NS, Nothing) "item" elementToItem e let es_rest = removeKnownElts e let as_rest = removeKnownAttrs e return Feed { feedVersion = ver , feedChannel = ch1 , feedImage = mbImg , feedItems = its , feedTextInput = mbTI , feedTopics = mapMaybe elementToTaxonomyTopic $ pQNodes (qualName' (taxNS, taxPrefix) "topic") e , feedOther = es_rest , feedAttrs = as_rest } elementToItems :: XML.Element -> [URIString] elementToItems = seqLeaves elementToTextInput :: XML.Element -> Maybe TextInputInfo elementToTextInput e = do let es = children e uri <- pAttr' (rdfNS, rdfPrefix) "about" e ti <- pQLeaf (rss10NS, Nothing) "title" e desc <- pQLeaf (rss10NS, Nothing) "description" e na <- pQLeaf (rss10NS, Nothing) "name" e li <- pQLeaf (rss10NS, Nothing) "link" e let dcs = mapMaybe elementToDC es return TextInputInfo { textInputURI = uri , textInputTitle = ti , textInputDesc = desc , textInputName = na , textInputLink = li , textInputDC = dcs , textInputOther = es , textInputAttrs = elementAttributes e } elementToItem :: XML.Element -> Maybe Item elementToItem e = do guard (elementName e == qualName (Just rss10NS, Nothing) "item") let es = children e uri <- pAttr' (rdfNS, rdfPrefix) "about" e ti <- pQLeaf (rss10NS, Nothing) "title" e li <- pQLeaf (rss10NS, Nothing) "link" e let desc = pQLeaf (rss10NS, Nothing) "description" e let dcs = mapMaybe elementToDC es let tos = maybe [] bagLeaves $ pQNode (qualName' (taxNS, taxPrefix) "topics") e let cs = mapMaybe elementToContent es let es_other = removeKnownElts e let as_other = removeKnownAttrs e return Item { itemURI = uri , itemTitle = ti , itemLink = li , itemDesc = desc , itemDC = dcs , itemTopics = tos , itemContent = cs , itemOther = es_other , itemAttrs = as_other } elementToImage :: XML.Element -> Maybe Image elementToImage e = do let es = children e let as = elementAttributes e uri <- pAttr' (rdfNS, rdfPrefix) "about" e ti <- pLeaf "title" e ur <- pLeaf "url" e li <- pLeaf "link" e let dcs = mapMaybe elementToDC es return Image { imageURI = uri , imageTitle = ti , imageURL = ur , imageLink = li , imageDC = dcs , imageOther = es , imageAttrs = as } elementToChannel :: XML.Element -> Maybe Channel elementToChannel e = do let es = children e uri <- pAttr' (rdfNS, rdfPrefix) "about" e ti <- pLeaf "title" e li <- pLeaf "link" e de <- pLeaf "description" e let mbImg = pLeaf "image" e let is = maybe [] seqLeaves $ pNode "items" e let tinp = pLeaf "textinput" e let dcs = mapMaybe elementToDC es let tos = maybe [] bagLeaves $ pQNode (qualName' (taxNS, taxPrefix) "topics") e let cs = mapMaybe elementToContent es let es_other = removeKnownElts e let as_other = removeKnownAttrs e let def_chan = Channel { channelURI = uri , channelTitle = ti , channelLink = li , channelDesc = de , channelImageURI = mbImg , channelItemURIs = is , channelTextInputURI = tinp , channelDC = dcs , channelUpdatePeriod = Nothing , channelUpdateFreq = Nothing , channelUpdateBase = Nothing , channelContent = cs , channelTopics = tos , channelOther = es_other , channelAttrs = as_other } return (addSyndication e def_chan) addSyndication :: XML.Element -> Channel -> Channel addSyndication e ch = ch { channelUpdatePeriod = toUpdatePeriod <$> pQLeaf' (synNS, synPrefix) "updatePeriod" e , channelUpdateFreq = readInt =<< pQLeaf' (synNS, synPrefix) "updateFrequency" e , channelUpdateBase = pQLeaf' (synNS, synPrefix) "updateBase" e } where toUpdatePeriod x = case x of "hourly" -> Update_Hourly "daily" -> Update_Daily "weekly" -> Update_Weekly "monthly" -> Update_Monthly "yearly" -> Update_Yearly _ -> Update_Hourly -- ToDo: whine elementToDC :: XML.Element -> Maybe DCItem elementToDC e = do guard (nameNamespace (elementName e) == Just dcNS) let dcItem x = DCItem {dcElt = x, dcText = strContent e} return $ dcItem $ case nameLocalName $ elementName e of "title" -> DC_Title "creator" -> DC_Creator "subject" -> DC_Subject "description" -> DC_Description "publisher" -> DC_Publisher "contributor" -> DC_Contributor "date" -> DC_Date "type" -> DC_Type "format" -> DC_Format "identifier" -> DC_Identifier "source" -> DC_Source "language" -> DC_Language "relation" -> DC_Relation "coverage" -> DC_Coverage "rights" -> DC_Rights oth -> DC_Other oth elementToTaxonomyTopic :: XML.Element -> Maybe TaxonomyTopic elementToTaxonomyTopic e = do guard (elementName e == qualName' (taxNS, taxPrefix) "topic") let es = children e uri <- pAttr' (rdfNS, rdfPrefix) "about" e li <- pQLeaf' (taxNS, taxPrefix) "link" e return TaxonomyTopic { taxonomyURI = uri , taxonomyLink = li , taxonomyTitle = pLeaf "title" e , taxonomyDesc = pLeaf "description" e , taxonomyTopics = maybe [] bagLeaves $ pQNode (qualName' (taxNS, taxPrefix) "topics") e , taxonomyDC = mapMaybe elementToDC es , taxonomyOther = es } elementToContent :: XML.Element -> Maybe ContentInfo elementToContent e = do guard (elementName e == qualName' (conNS, conPrefix) "items") return ContentInfo { contentURI = pAttr' (rdfNS, rdfPrefix) "about" e , contentFormat = pQLeaf' (conNS, conPrefix) "format" e , contentEncoding = pQLeaf' (conNS, conPrefix) "encoding" e , contentValue = pQLeaf' (rdfNS, rdfPrefix) "value" e } bagLeaves :: XML.Element -> [URIString] bagLeaves be = mapMaybe (\e -> do guard (elementName e == qualName' (rdfNS, rdfPrefix) "li") pAttr' (rdfNS, rdfPrefix) "resource" e `mplus` fmap strContent (pQNode (qualName' (rdfNS, rdfPrefix) "li") e)) (maybe [] children $ pQNode (qualName' (rdfNS, rdfPrefix) "Bag") be) {- bagElements :: XML.Element -> [XML.Element] bagElements be = mapMaybe (\ e -> do guard (elementName e == rdfName "li") return e) (fromMaybe [] $ fmap children $ pQNode (rdfName "Bag") be) -} seqLeaves :: XML.Element -> [URIString] seqLeaves se = mapMaybe (\e -> do guard (elementName e == rdfName "li") return (strContent e)) (maybe [] children $ pQNode (rdfName "Seq") se) feed-1.0.1.0/src/Text/RSS1/Syntax.hs0000644000000000000000000001314313354421723015033 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.RSS1.Syntax -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.RSS1.Syntax ( URIString , TitleString , TimeString , TextString , Feed(..) , Channel(..) , Image(..) , Item(..) , TextInputInfo(..) , TaxonomyTopic(..) , UpdatePeriod(..) , ContentInfo(..) , nullFeed , nullChannel , nullImage , nullItem , nullTextInputInfo , nullTaxonomyTopic , nullContentInfo ) where import Prelude.Compat import Data.Text import Data.XML.Compat import Data.XML.Types as XML import Text.DublinCore.Types type URIString = Text type TitleString = Text type TimeString = Text type TextString = Text data Feed = Feed { feedVersion :: Text , feedChannel :: Channel , feedImage :: Maybe Image , feedItems :: [Item] , feedTextInput :: Maybe TextInputInfo , feedTopics :: [TaxonomyTopic] , feedOther :: [XML.Element] , feedAttrs :: [Attr] } deriving (Show) data Channel = Channel { channelURI :: URIString , channelTitle :: TitleString , channelLink :: URIString , channelDesc :: TextString -- these are indirect RDF associations to elements declared -- outside the channel element in the RDF \/ feed document. , channelImageURI :: Maybe URIString , channelItemURIs :: [URIString] , channelTextInputURI :: Maybe URIString , channelDC :: [DCItem] , channelUpdatePeriod :: Maybe UpdatePeriod , channelUpdateFreq :: Maybe Integer , channelUpdateBase :: Maybe TimeString -- format is yyyy-mm-ddThh:mm , channelContent :: [ContentInfo] , channelTopics :: [URIString] , channelOther :: [XML.Element] , channelAttrs :: [Attr] } deriving (Show) data Image = Image { imageURI :: URIString -- the image resource, most likely. , imageTitle :: TextString -- the "alt"ernative text. , imageURL :: URIString , imageLink :: URIString -- the href of the rendered img resource. , imageDC :: [DCItem] , imageOther :: [XML.Element] , imageAttrs :: [Attr] } deriving (Show) data Item = Item { itemURI :: URIString , itemTitle :: TextString , itemLink :: URIString , itemDesc :: Maybe TextString , itemDC :: [DCItem] , itemTopics :: [URIString] , itemContent :: [ContentInfo] , itemOther :: [XML.Element] , itemAttrs :: [Attr] } deriving (Show) data TextInputInfo = TextInputInfo { textInputURI :: URIString , textInputTitle :: TextString , textInputDesc :: TextString , textInputName :: TextString , textInputLink :: URIString , textInputDC :: [DCItem] , textInputOther :: [XML.Element] , textInputAttrs :: [Attr] } deriving (Show) data TaxonomyTopic = TaxonomyTopic { taxonomyURI :: URIString , taxonomyLink :: URIString , taxonomyTitle :: Maybe Text , taxonomyDesc :: Maybe Text , taxonomyTopics :: [URIString] , taxonomyDC :: [DCItem] , taxonomyOther :: [XML.Element] } deriving (Show) data UpdatePeriod = Update_Hourly | Update_Daily | Update_Weekly | Update_Monthly | Update_Yearly deriving (Eq, Show) data ContentInfo = ContentInfo { contentURI :: Maybe URIString , contentFormat :: Maybe URIString , contentEncoding :: Maybe URIString , contentValue :: Maybe Text -- should be: RDFValue } deriving (Eq, Show) --default constructors: nullFeed :: URIString -> TitleString -> Feed nullFeed uri title = Feed { feedVersion = "1.0" , feedChannel = nullChannel uri title , feedImage = Nothing , feedItems = [] , feedTextInput = Nothing , feedTopics = [] , feedOther = [] , feedAttrs = [] } nullChannel :: URIString -> TitleString -> Channel nullChannel uri title = Channel { channelURI = uri , channelTitle = title , channelLink = uri , channelDesc = title , channelImageURI = Nothing , channelItemURIs = [] , channelTextInputURI = Nothing , channelDC = [] , channelUpdatePeriod = Nothing , channelUpdateFreq = Nothing , channelUpdateBase = Nothing , channelContent = [] , channelTopics = [] , channelOther = [] , channelAttrs = [] } nullImage :: URIString -> Text -> URIString -> Image nullImage imguri title link = Image { imageURI = imguri , imageTitle = title , imageURL = imguri , imageLink = link , imageDC = [] , imageOther = [] , imageAttrs = [] } nullItem :: URIString -> TextString -> URIString -> Item nullItem uri title link = Item { itemURI = uri , itemTitle = title , itemLink = link , itemDesc = Nothing , itemDC = [] , itemTopics = [] , itemContent = [] , itemOther = [] , itemAttrs = [] } nullTextInputInfo :: URIString -> TextString -> TextString -> URIString -> TextInputInfo nullTextInputInfo uri title nm link = TextInputInfo { textInputURI = uri , textInputTitle = title , textInputDesc = title , textInputName = nm , textInputLink = link , textInputDC = [] , textInputOther = [] , textInputAttrs = [] } nullTaxonomyTopic :: URIString -> URIString -> TaxonomyTopic nullTaxonomyTopic uri link = TaxonomyTopic { taxonomyURI = uri , taxonomyLink = link , taxonomyTitle = Nothing , taxonomyDesc = Nothing , taxonomyTopics = [] , taxonomyDC = [] , taxonomyOther = [] } nullContentInfo :: ContentInfo nullContentInfo = ContentInfo { contentURI = Nothing , contentFormat = Nothing , contentEncoding = Nothing , contentValue = Nothing } feed-1.0.1.0/src/Text/RSS1/Utils.hs0000644000000000000000000000762613354421723014656 0ustar0000000000000000-------------------------------------------------------------------- -- | -- Module : Text.RSS1.Utils -- Copyright : (c) Galois, Inc. 2008, -- (c) Sigbjorn Finne 2009- -- License : BSD3 -- -- Maintainer: Sigbjorn Finne -- Stability : provisional -- Portability: portable -- -------------------------------------------------------------------- module Text.RSS1.Utils ( pQNodes , pNode , pQNode , pLeaf , pQLeaf , pQLeaf' , pAttr , pAttr' , pMany , children , qualName , qualName' , rss10NS , rdfPrefix , rdfNS , synPrefix , synNS , taxPrefix , taxNS , conPrefix , conNS , dcPrefix , dcNS , rdfName , rssName , synName , known_rss_elts , known_syn_elts , known_dc_elts , known_tax_elts , known_con_elts , removeKnownElts , removeKnownAttrs ) where import Prelude.Compat import Data.XML.Compat import Data.XML.Types as XML import Text.DublinCore.Types import Data.Maybe (listToMaybe, mapMaybe) import Data.Text (Text) pQNodes :: Name -> XML.Element -> [XML.Element] pQNodes = findChildren pNode :: Text -> XML.Element -> Maybe XML.Element pNode x e = listToMaybe (pQNodes (qualName (Just rss10NS, Nothing) x) e) pQNode :: Name -> XML.Element -> Maybe XML.Element pQNode x e = listToMaybe (pQNodes x e) pLeaf :: Text -> XML.Element -> Maybe Text pLeaf x e = strContent `fmap` pQNode (qualName (Just rss10NS, Nothing) x) e pQLeaf' :: (Text, Text) -> Text -> XML.Element -> Maybe Text pQLeaf' (ns, pre) = pQLeaf (ns, Just pre) pQLeaf :: (Text, Maybe Text) -> Text -> XML.Element -> Maybe Text pQLeaf (ns, pre) x e = strContent `fmap` pQNode (qualName (Just ns, pre) x) e pAttr :: (Maybe Text, Maybe Text) -> Text -> XML.Element -> Maybe Text pAttr ns x = attributeText (qualName ns x) pAttr' :: (Text, Text) -> Text -> XML.Element -> Maybe Text pAttr' (ns, pre) = pAttr (Just ns, Just pre) pMany :: (Maybe Text, Maybe Text) -> Text -> (XML.Element -> Maybe a) -> XML.Element -> [a] pMany ns p f e = mapMaybe f (pQNodes (qualName ns p) e) children :: XML.Element -> [XML.Element] children = elementChildren qualName :: (Maybe Text, Maybe Text) -> Text -> Name qualName (ns, pre) x = Name x ns pre qualName' :: (Text, Text) -> Text -> Name qualName' (ns, pre) x = Name x (Just ns) (Just pre) rss10NS :: Text rss10NS = "http://purl.org/rss/1.0/" rdfPrefix, rdfNS :: Text rdfNS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdfPrefix = "rdf" synPrefix, synNS :: Text synNS = "http://purl.org/rss/1.0/modules/syndication/" synPrefix = "sy" taxPrefix, taxNS :: Text taxNS = "http://purl.org/rss/1.0/modules/taxonomy/" taxPrefix = "taxo" conPrefix, conNS :: Text conNS = "http://purl.org/rss/1.0/modules/content/" conPrefix = "content" dcPrefix, dcNS :: Text dcNS = "http://purl.org/dc/elements/1.1/" dcPrefix = "dc" rdfName :: Text -> Name rdfName x = Name x (Just rdfNS) (Just rdfPrefix) rssName :: Text -> Name rssName x = Name x (Just rss10NS) Nothing synName :: Text -> Name synName x = Name x (Just synNS) (Just synPrefix) known_rss_elts :: [Name] known_rss_elts = map rssName ["channel", "item", "image", "textinput"] known_syn_elts :: [Name] known_syn_elts = map synName ["updateBase", "updateFrequency", "updatePeriod"] known_dc_elts :: [Name] known_dc_elts = map (qualName' (dcNS, dcPrefix)) dc_element_names known_tax_elts :: [Name] known_tax_elts = map (qualName' (taxNS, taxPrefix)) ["topic", "topics"] known_con_elts :: [Name] known_con_elts = map (qualName' (conNS, conPrefix)) ["items", "item", "format", "encoding"] removeKnownElts :: XML.Element -> [XML.Element] removeKnownElts e = filter (\e1 -> elementName e1 `notElem` known_elts) (elementChildren e) where known_elts = concat [known_rss_elts, known_syn_elts, known_dc_elts, known_con_elts, known_tax_elts] removeKnownAttrs :: XML.Element -> [Attr] removeKnownAttrs e = filter ((`notElem` known_attrs) . fst) (elementAttributes e) where known_attrs = map rdfName ["about"] feed-1.0.1.0/src/Data/Text/Util.hs0000644000000000000000000000141013370672700014576 0ustar0000000000000000module Data.Text.Util ( readInt , renderFeed ) where import Prelude.Compat import Data.Text import Data.Text.Read import qualified Data.XML.Types as XT -- from xml-types import qualified Text.XML as XC -- from xml-conduit import qualified Data.Text.Lazy as TL readInt :: Text -> Maybe Integer readInt s = case decimal s of Right (x, _) -> Just x _ -> Nothing renderFeed :: (a -> XT.Element) -> a -> Maybe TL.Text renderFeed cf f = let e = cf f d = elToDoc e in XC.renderText XC.def <$> d -- Ancillaries -- elToDoc :: XT.Element -> Maybe XC.Document elToDoc el = let txd = XT.Document (XC.Prologue [] Nothing []) el [] cxd = XC.fromXMLDocument txd in either (const Nothing) Just cxd feed-1.0.1.0/src/Data/XML/Compat.hs0000644000000000000000000000246513354421723014632 0ustar0000000000000000{-# LANGUAGE FlexibleInstances #-} -- | Compatibility interface between `xml` and `xml-types`. module Data.XML.Compat where import Prelude.Compat import Data.Text (Text) import qualified Data.Text as T import Data.XML.Types import Safe type Attr = (Name, [Content]) mkAttr :: Text -> Text -> Attr mkAttr k = mkNAttr (Name k Nothing Nothing) mkNAttr :: Name -> Text -> Attr mkNAttr k v = (k, [ContentText v]) attrKey :: Attr -> Name attrKey = fst strContent :: Element -> Text strContent = T.concat . elementText class ToNode t where unode :: Name -> t -> Element instance ToNode [Attr] where unode n as = Element n as [] instance ToNode [Element] where unode n = Element n [] . map NodeElement instance ToNode ([Attr], Text) where unode n (as, t) = Element n as [NodeContent $ ContentText t] instance ToNode Text where unode n t = unode n ([] :: [Attr], t) findChildren :: Name -> Element -> [Element] findChildren n el = filter ((n ==) . elementName) $ elementChildren el findChild :: Name -> Element -> Maybe Element findChild = (headMay .) <$> findChildren findElements :: Name -> Element -> [Element] findElements n e | n == elementName e = [e] | otherwise = concatMap (findElements n) $ elementChildren e findElement :: Name -> Element -> Maybe Element findElement = (headMay .) <$> findElements feed-1.0.1.0/README.lhs0000644000000000000000000001163613370672700012421 0ustar0000000000000000# Feed [![feed](https://img.shields.io/hackage/v/feed.svg)](http://hackage.haskell.org/package/feed) [![Build Status](https://travis-ci.org/bergmark/feed.svg?branch=master)](https://travis-ci.org/bergmark/feed) ## Goal Interfacing with *RSS* (v 0.9x, 2.x, 1.0) + *Atom* feeds. - Parsers - Pretty Printers - Querying To help working with the multiple feed formats we've ended up with this set of modules providing parsers, pretty printers and some utility code for querying and just generally working with a concrete representation of feeds in Haskell. For basic reading and editing of feeds, consult the documentation of the Text.Feed.* hierarchy. ## Usage Building an Atom feed is similar to building an RSS feed, but we'll arbitrarily pick Atom here: We'd like to generate the XML for a minimal working example. Constructing our base `Feed` can use the smart constructor called `nullFeed`: *This is a pattern the library maintains for smart constructors. If you want the minimum viable 'X', use the 'nullX' constructor.* ```haskell {-# LANGUAGE OverloadedStrings #-} module Main where import Prelude.Compat hiding (take) import Data.Text import Data.XML.Types as XML import qualified Data.Text.Lazy as Lazy import qualified Text.Atom.Feed as Atom import qualified Text.Atom.Feed.Export as Export (textFeed) myFeed :: Atom.Feed myFeed = Atom.nullFeed "http://example.com/atom.xml" -- ^ id (Atom.TextString "Example Website") -- ^ title "2017-08-01" -- ^ last updated ``` Now we can export the feed to `Text`. ```haskell renderFeed :: Atom.Feed -> Maybe Lazy.Text renderFeed = Export.textFeed ``` ``` > renderFeed myFeed Example Website http://example.com/atom.xml 2017-08-01 ``` The `TextContent` sum type allows us to specify which type of text we're providing. ```haskell data TextContent = TextString Text | HTMLString Text | XHTMLString XML.Element deriving (Show) ``` A feed isn't very useful without some content though, so we'll need to build up an `Entry`. ```haskell data Post = Post { _postedOn :: Text , _url :: Text , _content :: Text } examplePosts :: [Post] examplePosts = [ Post "2000-02-02T18:30:00Z" "http://example.com/2" "Bar." , Post "2000-01-01T18:30:00Z" "http://example.com/1" "Foo." ] ``` Our `Post` data type will need to be converted into an `Entry` in order to use it in the top level `Feed`. The required fields for an entry are an url "id" from which an entry's presence can be validated, a title for the entry, and a posting date. In this example we'll also add authors, link, and the entries actual content, since we have all of this available in the `Post` provided. ```haskell toEntry :: Post -> Atom.Entry toEntry (Post date url content) = (Atom.nullEntry url -- The ID field. Must be a link to validate. (Atom.TextString (take 20 content)) -- Title date) { Atom.entryAuthors = [Atom.nullPerson {Atom.personName = "J. Smith"}] , Atom.entryLinks = [Atom.nullLink url] , Atom.entryContent = Just (Atom.HTMLContent content) } ``` From the base feed we created earlier, we can add further details (`Link` and `Entry` content) as well as map our `toEntry` function over the posts we'd like to include in the feed. ```haskell feed :: Atom.Feed feed = myFeed { Atom.feedEntries = fmap toEntry examplePosts , Atom.feedLinks = [Atom.nullLink "http://example.com/"] } ``` ``` > renderFeed feed Example Website http://example.com/atom.xml 2017-08-01 http://example.com/2 Bar. 2000-02-02T18:30:00Z J. Smith Bar. http://example.com/1 Foo. 2000-01-01T18:30:00Z J. Smith Foo. ``` See [here](https://github.com/bergmark/feed/blob/master/tests/Example/CreateAtom.hs) for this content as an uninterrupted running example. ```haskell -- Dummy main needed to compile this file with markdown-unlit main :: IO () main = return () ``` feed-1.0.1.0/tests/Main.hs0000644000000000000000000000047613354421723013335 0ustar0000000000000000module Main ( main ) where import Prelude.Compat import Example (exampleTests) import Test.Framework (defaultMain) import Text.Atom.Tests (atomTests) import Text.Feed.Util.Tests (feedUtilTests) import Text.RSS.Tests (rssTests) main :: IO () main = defaultMain [rssTests, atomTests, feedUtilTests, exampleTests] feed-1.0.1.0/tests/Example.hs0000644000000000000000000000110113354421723014026 0ustar0000000000000000module Example ( exampleTests ) where import Prelude.Compat import Example.CreateAtom (atomFeed) import Test.Framework (Test, testGroup) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit (Assertion) exampleTests :: Test exampleTests = testGroup "Examples" [testCase "example code to create an atom feed typechecks" typeCheckAtom] typeCheckAtom :: Assertion typeCheckAtom = case atomFeed of Just "" -> error "createAtom returned an empty Text" Just _ -> return () Nothing -> error "createAtom returned document with unresolved entities" feed-1.0.1.0/tests/Example/CreateAtom.hs0000644000000000000000000000265413354421723016070 0ustar0000000000000000module Example.CreateAtom ( atomFeed ) where import Prelude.Compat hiding (take) import qualified Text.Atom.Feed as Atom import qualified Text.Atom.Feed.Export as Export import Text.RSS.Utils import Data.Text import Data.Text.Lazy (toStrict) import Text.XML atomFeed :: Maybe Text atomFeed = renderFeed examplePosts data Post = Post { _postedOn :: Text , _url :: Text , _content :: Text } examplePosts :: [Post] examplePosts = [ Post "2000-02-02T18:30:00Z" "http://example.com/2" "Bar." , Post "2000-01-01T18:30:00Z" "http://example.com/1" "Foo." ] toEntry :: Post -> Atom.Entry toEntry (Post date url content) = (Atom.nullEntry url -- The ID field. Must be a link to validate. (Atom.TextString (take 20 content)) -- Title date) { Atom.entryAuthors = [Atom.nullPerson {Atom.personName = "J. Smith"}] , Atom.entryLinks = [Atom.nullLink url] , Atom.entryContent = Just (Atom.HTMLContent content) } feed :: [Post] -> Atom.Feed feed posts = Atom.nullFeed "http://example.com/atom.xml" -- ID (Atom.TextString "Example Website") -- Title (case posts -- Updated of Post latestPostDate _ _:_ -> latestPostDate _ -> "") renderFeed :: [Post] -> Maybe Text renderFeed posts = fmap (toStrict . renderText def) $ elementToDoc $ Export.xmlFeed $ (feed posts) {Atom.feedEntries = fmap toEntry posts, Atom.feedLinks = [Atom.nullLink "http://example.com/"]} feed-1.0.1.0/tests/Text/Atom/Tests.hs0000644000000000000000000000250513354421723015372 0ustar0000000000000000module Text.Atom.Tests ( atomTests ) where import Prelude.Compat import Data.Maybe (isJust) import Test.Framework (Test, mutuallyExclusive, testGroup) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit (Assertion, assertBool) import Text.XML import Text.Atom.Feed (TextContent(TextString), entryLinks, nullEntry, nullLink) import Text.Feed.Export (xmlFeed) import Text.Feed.Import (parseFeedFromFile) import Text.Feed.Query import Text.Feed.Types import Text.RSS.Utils import Paths_feed atomTests :: Test atomTests = testGroup "Text.Atom" [mutuallyExclusive $ testGroup "Atom" [testFullAtomParse, testAtomAlternate]] testFullAtomParse :: Test testFullAtomParse = testCase "parse a complete atom file" testAtom where testAtom :: Assertion testAtom = do contents <- parseFeedFromFile =<< getDataFileName "tests/files/atom.xml" let res = fmap (renderText def) . elementToDoc . xmlFeed $ contents assertBool "Atom Parsing" $ isJust res testAtomAlternate :: Test testAtomAlternate = testCase "*unspecified* link relation means 'alternate'" testAlt where testAlt :: Assertion testAlt = let nullent = nullEntry "" (TextString "") "" item = AtomItem nullent {entryLinks = [nullLink ""]} in assertBool "unspecified means alternate" $ isJust $ getItemLink item feed-1.0.1.0/tests/Text/Feed/Util/Tests.hs0000644000000000000000000000241313354421723016250 0ustar0000000000000000module Text.Feed.Util.Tests ( feedUtilTests ) where import Prelude.Compat import Data.Time import Data.Time.Clock.POSIX import System.Time import Test.Framework (Test, testGroup) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit (Assertion, assertEqual) import Text.Feed.Types import Text.Feed.Util feedUtilTests :: Test feedUtilTests = testGroup "Text.Feed.Util" [ testGroup "toFeedDateString[UTC]" [ testToFeedDateString AtomKind (read "2016-01-07 15:33:29 UTC") "2016-01-07T15:33:29Z" , testToFeedDateString (RSSKind Nothing) (read "2016-01-07 15:33:29 UTC") "Thu, 07 Jan 2016 15:33:29 GMT" , testToFeedDateString (RDFKind Nothing) (read "2016-01-07 15:33:29 UTC") "2016-01-07T15:33:29Z" ] ] testToFeedDateString :: FeedKind -> UTCTime -> String -> Test testToFeedDateString kind utct expectResult = testCase (show kind) testToDateString where testToDateString :: Assertion testToDateString = do assertEqual "toFeedDateStringUTC" expectResult (toFeedDateStringUTC kind utct) assertEqual "toFeedDateString" expectResult (toFeedDateString kind clockTime) clockTime = TOD (truncate $ utcTimeToPOSIXSeconds utct) 0 feed-1.0.1.0/tests/Text/RSS/Equals.hs0000644000000000000000000000123413354421723015267 0ustar0000000000000000{-# OPTIONS_GHC -fno-warn-orphans #-} module Text.RSS.Equals where import Prelude.Compat import Text.RSS.Syntax (RSSCloud(..)) instance Eq RSSCloud where (RSSCloud rssCloudDomain1 rssCloudPort1 rssCloudPath1 rssCloudRegisterProcedure1 rssCloudProtocol1 rssCloudAttrs1) == (RSSCloud rssCloudDomain2 rssCloudPort2 rssCloudPath2 rssCloudRegisterProcedure2 rssCloudProtocol2 rssCloudAttrs2) = (rssCloudDomain1 == rssCloudDomain2) && (rssCloudPort1 == rssCloudPort2) && (rssCloudPath1 == rssCloudPath2) && (rssCloudRegisterProcedure1 == rssCloudRegisterProcedure2) && (rssCloudProtocol1 == rssCloudProtocol2) && (rssCloudAttrs1 == rssCloudAttrs2) feed-1.0.1.0/tests/Text/RSS/Export/Tests.hs0000644000000000000000000002523313354421723016425 0ustar0000000000000000module Text.RSS.Export.Tests ( rssExportTests ) where import Prelude.Compat import Data.Text (pack) import Data.XML.Types as XML import Test.Framework (Test, mutuallyExclusive, testGroup) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit (Assertion, assertEqual) import Text.RSS.Equals () import Text.RSS.Export import Text.RSS.Syntax import Text.RSS.Utils rssExportTests :: Test rssExportTests = testGroup "Text.RSS.Export" [ mutuallyExclusive $ testGroup "RSS export" [ testCreateXMLImage , testCreateXMLCloud , testCreateXMLTextInput , testCreateEmptyXMLSkipHours , testCreateXMLSkipHours , testCreateEmptyXMLSkipDays , testCreateXMLSkipDays , testCreateXMLAttr , testCreateXMLLeaf ] ] testCreateXMLImage :: Test testCreateXMLImage = testCase "should create image as xml" testImage where testImage :: Assertion testImage = do let other = XML.Element { elementName = createQName "other" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "image other"] } let image = RSSImage { rssImageURL = "image url" , rssImageTitle = "image title" , rssImageLink = "image link" , rssImageWidth = Just 100 , rssImageHeight = Just 200 , rssImageDesc = Just "image desc" , rssImageOther = [other] } let expected = XML.Element { elementName = createQName "image" , elementAttributes = [] :: [Attr] , elementNodes = [ NodeElement XML.Element { elementName = createQName "url" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "image url"] } , NodeElement XML.Element { elementName = createQName "title" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "image title"] } , NodeElement XML.Element { elementName = createQName "link" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "image link"] } , NodeElement XML.Element { elementName = createQName "width" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "100"] } , NodeElement XML.Element { elementName = createQName "height" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "200"] } , NodeElement XML.Element { elementName = createQName "description" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "image desc"] } , NodeElement XML.Element { elementName = createQName "other" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "image other"] } ] } assertEqual "image" expected (xmlImage image) testCreateXMLCloud :: Test testCreateXMLCloud = testCase "should create cloud as xml" testCloud where testCloud :: Assertion testCloud = do let attr = mkNAttr (createQName "attr") "text for attr" let cloud = RSSCloud { rssCloudDomain = Just "domain cloud" , rssCloudPort = Just "port cloud" , rssCloudPath = Just "path cloud" , rssCloudRegisterProcedure = Just "register cloud" , rssCloudProtocol = Just "protocol cloud" , rssCloudAttrs = [attr] } let expected = XML.Element { elementName = createQName "cloud" , elementAttributes = [ mkNAttr (createQName "domain") "domain cloud" , mkNAttr (createQName "port") "port cloud" , mkNAttr (createQName "path") "path cloud" , mkNAttr (createQName "registerProcedure") "register cloud" , mkNAttr (createQName "protocol") "protocol cloud" , attr ] :: [Attr] , elementNodes = [createContent ""] } assertEqual "cloud" expected (xmlCloud cloud) testCreateXMLTextInput :: Test testCreateXMLTextInput = testCase "should create text input as xml" textInput where textInput :: Assertion textInput = do let attr = mkNAttr (createQName "attr") "text for attr" let other = XML.Element { elementName = createQName "leaf" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "text for leaf"] } let input = RSSTextInput { rssTextInputTitle = "title" , rssTextInputDesc = "desc" , rssTextInputName = "name" , rssTextInputLink = "http://url.com" , rssTextInputAttrs = [attr] , rssTextInputOther = [other] } let expected = XML.Element { elementName = createQName "textInput" , elementAttributes = [attr] :: [Attr] , elementNodes = [ NodeElement XML.Element { elementName = createQName "title" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "title"] } , NodeElement XML.Element { elementName = createQName "description" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "desc"] } , NodeElement XML.Element { elementName = createQName "name" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "name"] } , NodeElement XML.Element { elementName = createQName "link" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "http://url.com"] } , NodeElement XML.Element { elementName = createQName "leaf" , elementAttributes = [] :: [Attr] , elementNodes = [createContent "text for leaf"] } ] } assertEqual "text input" expected (xmlTextInput input) testCreateEmptyXMLSkipHours :: Test testCreateEmptyXMLSkipHours = testCase "should create an empty list of skip hours as xml" emptySkipHours where emptySkipHours :: Assertion emptySkipHours = do let hoursToSkip = [] let expected = XML.Element { elementName = createQName "skipHours" , elementAttributes = [] :: [Attr] , elementNodes = [] } assertEqual "empty skip hours" expected (xmlSkipHours hoursToSkip) testCreateXMLSkipHours :: Test testCreateXMLSkipHours = testCase "should create skip hours as xml" skipHours where skipHours :: Assertion skipHours = do let hoursToSkip = [1, 2, 3] let expected = XML.Element { elementName = createQName "skipHours" , elementAttributes = [] :: [Attr] , elementNodes = [hourElem 0, hourElem 1, hourElem 2] } where hourElem ind = NodeElement XML.Element { elementName = createQName "hour" , elementAttributes = [] :: [Attr] , elementNodes = [createContent $ pack $ show $ hoursToSkip !! ind] } assertEqual "skip hours" expected (xmlSkipHours hoursToSkip) testCreateEmptyXMLSkipDays :: Test testCreateEmptyXMLSkipDays = testCase "should create an empty list of skip days as xml" emptySkipDays where emptySkipDays :: Assertion emptySkipDays = do let daysToSkip = [] let expected = XML.Element { elementName = createQName "skipDays" , elementAttributes = [] :: [Attr] , elementNodes = [] } assertEqual "empty skip days" expected (xmlSkipDays daysToSkip) testCreateXMLSkipDays :: Test testCreateXMLSkipDays = testCase "should create skip days as xml" skipDays where skipDays :: Assertion skipDays = do let daysToSkip = ["first day", "second day", "third day"] let expected = XML.Element { elementName = createQName "skipDays" , elementAttributes = [] :: [Attr] , elementNodes = [dayElem 0, dayElem 1, dayElem 2] } where dayElem ind = NodeElement XML.Element { elementName = createQName "day" , elementAttributes = [] :: [Attr] , elementNodes = [createContent $ daysToSkip !! ind] } assertEqual "skip days" expected (xmlSkipDays daysToSkip) testCreateXMLAttr :: Test testCreateXMLAttr = testCase "should create attr as xml" createXMLAttr where createXMLAttr :: Assertion createXMLAttr = do let tg = "attr" let txt = "example of attr value" let expected = mkNAttr (createQName tg) txt assertEqual "create a leaf" expected (xmlAttr tg txt) testCreateXMLLeaf :: Test testCreateXMLLeaf = testCase "should create leaf as xml" createXMLLeaf where createXMLLeaf :: Assertion createXMLLeaf = do let tg = "leaf" let txt = "example of leaf text" let expected = XML.Element { elementName = createQName tg , elementAttributes = [] :: [Attr] , elementNodes = [createContent txt] } assertEqual "create a leaf" expected (xmlLeaf tg txt) feed-1.0.1.0/tests/Text/RSS/Import/Tests.hs0000644000000000000000000000441513354421723016415 0ustar0000000000000000module Text.RSS.Import.Tests ( rssImportTests ) where import Prelude.Compat import Data.XML.Types as XML import Test.Framework (Test, mutuallyExclusive, testGroup) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit (Assertion, assertEqual) import Text.RSS.Equals () import Text.RSS.Import import Text.RSS.Syntax import Text.RSS.Utils rssImportTests :: Test rssImportTests = testGroup "Text.RSS.Import" [ mutuallyExclusive $ testGroup "RSS import" [testElementToCloudIsNotCreated, testElementToCloud] ] testElementToCloudIsNotCreated :: Test testElementToCloudIsNotCreated = testCase "should not create rss cloud" notCreateRSSCloud where notCreateRSSCloud :: Assertion notCreateRSSCloud = do let notXmlCloudElement = XML.Element {elementName = createQName "notCloud", elementAttributes = [], elementNodes = []} let expected = Nothing assertEqual "not create rss cloud" expected (elementToCloud notXmlCloudElement) testElementToCloud :: Test testElementToCloud = testCase "should create rss cloud" createRSSCloud where createRSSCloud :: Assertion createRSSCloud = do let attr = mkNAttr (createQName "attr") "text for attr" let xmlCloudElement = XML.Element { elementName = createQName "cloud" , elementAttributes = [ mkNAttr (createQName "domain") "domain cloud" , mkNAttr (createQName "port") "port cloud" , mkNAttr (createQName "path") "path cloud" , mkNAttr (createQName "registerProcedure") "register cloud" , mkNAttr (createQName "protocol") "protocol cloud" , attr ] :: [Attr] , elementNodes = [createContent ""] } let expected = Just RSSCloud { rssCloudDomain = Just "domain cloud" , rssCloudPort = Just "port cloud" , rssCloudPath = Just "path cloud" , rssCloudRegisterProcedure = Just "register cloud" , rssCloudProtocol = Just "protocol cloud" , rssCloudAttrs = [attr] } assertEqual "create rss cloud" expected (elementToCloud xmlCloudElement) feed-1.0.1.0/tests/Text/RSS/Tests.hs0000644000000000000000000000166713354421723015151 0ustar0000000000000000module Text.RSS.Tests ( rssTests ) where import Prelude.Compat import Data.Maybe (isJust) import Test.Framework (Test, mutuallyExclusive, testGroup) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit (Assertion, assertBool) import Text.RSS.Export.Tests (rssExportTests) import Text.RSS.Import.Tests (rssImportTests) import Text.XML import Text.Feed.Export import Text.Feed.Import import Text.RSS.Utils import Paths_feed rssTests :: Test rssTests = testGroup "Text.RSS" [mutuallyExclusive $ testGroup "RSS" [rssExportTests, rssImportTests, testFullRss20Parse]] testFullRss20Parse :: Test testFullRss20Parse = testCase "parse a complete rss 2.0 file" testRss20 where testRss20 :: Assertion testRss20 = do contents <- parseFeedFromFile =<< getDataFileName "tests/files/rss20.xml" let res = fmap (renderText def) . elementToDoc . xmlFeed $ contents assertBool "RSS 2.0 Parsing" $ isJust res feed-1.0.1.0/tests/Text/RSS/Utils.hs0000644000000000000000000000111113354421723015127 0ustar0000000000000000module Text.RSS.Utils where import Prelude.Compat import Data.Text import Data.XML.Types as XML import Text.XML as C createContent :: Text -> XML.Node createContent = XML.NodeContent . ContentText createQName :: Text -> Name createQName txt = XML.Name {nameLocalName = txt, nameNamespace = Nothing, namePrefix = Nothing} type Attr = (Name, [Content]) mkNAttr :: Name -> Text -> Attr mkNAttr k v = (k, [ContentText v]) elementToDoc :: XML.Element -> Maybe C.Document elementToDoc el = either (const Nothing) Just $ fromXMLDocument $ XML.Document (Prologue [] Nothing []) el [] feed-1.0.1.0/tests/files/rss20.xml0000644000000000000000000047153613354421723014723 0ustar0000000000000000 Planet Haskell http://planet.haskell.org/ en Planet Haskell - http://planet.haskell.org/ Brent Yorgey: Brent http://byorgey.wordpress.com/?p=63 http://byorgey.wordpress.com/2008/04/17/collecting-unstructured-information-with-the-monoid-of-partial-knowledge/ <div class="snap_preview"><br /><p>In <a href="http://byorgey.wordpress.com/2008/04/17/an-interesting-monoid/">my last post</a>, I described what I&#8217;m calling the &#8220;monoid of partial knowledge&#8221;, a way of creating a monoid over sets of elements from a preorder, which is a generalization of the familiar monoid <img src="http://l.wordpress.com/latex.php?latex=%28S%2C%5Cmax%29&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="(S,\max)" title="(S,\max)" class="latex" /> over a set with a total order and a smallest element.</p> <p>There&#8217;s actually one situation where a special case of this monoid is commonly used in Haskell. Suppose you have a record type which contains several fields, and you would like to parse some input to create a value of this type. The problem is that the input is not very nice: the bits of input which designate values for various fields are not in any particular order; some occur more than once; some might even be missing. How to deal with this?</p> <p>One common solution is to wrap the type of each field in the record with Maybe, and create a Monoid instance which allows you to combine two partial records into one (hopefully less partial) record. Using this framework, you can just parse each bit of input into a mostly-blank record, with one (or more) fields filled in, then combine all the records (with mconcat) into one summary record. For example:</p> <pre> import Data.Monoid import Control.Monad -- for the MonadPlus instance of Maybe data Record = R { name :: Maybe String, age :: Maybe Int } deriving (Eq, Show) instance Monoid Record where mempty = R Nothing Nothing mappend r1 r2 = R { name = name r1 `mplus` name r2 , age = age r1 `mplus` age r2 } </pre> <p>The mplus function, from the MonadPlus instance for Maybe, simply takes the first Just value that it finds. This is nice and simple, and works just great:</p> <pre> &gt; mempty :: Record R {name = Nothing, age = Nothing} &gt; mconcat [mempty { name = Just "Brent" }] R {name = Just &#8220;Brent&#8221;, age = Nothing} &gt; mconcat [mempty { name = Just "Brent" }, mempty { age = Just 26 }] R {name = Just &#8220;Brent&#8221;, age = Just 26} &gt; mconcat [mempty { name = Just "Brent" }, mempty { age = Just 26 }, mempty { age = Just 23 }] R {name = Just &#8220;Brent&#8221;, age = Just 26} </pre> <p>Note how the appending is left-biased, because mplus is left-biased: after seeing the first age, all subsequent ages are ignored.</p> <p>Now, really what we&#8217;re doing here is using the monoid (as in my previous post) induced by the preorder which says that any name is <img src="http://l.wordpress.com/latex.php?latex=%5Clesssim&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\lesssim" title="\lesssim" class="latex" /> any other name, and any age is <img src="http://l.wordpress.com/latex.php?latex=%5Clesssim&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\lesssim" title="\lesssim" class="latex" /> any other age, and names and ages can&#8217;t be compared! </p> <p>Some code is in order. First, we create a class for preorders, and a newtype to contain sets of elements (there&#8217;s already a Monoid instance for Set, so we need a newtype to give a different semantics). Then we translate the specification from my previous post directly into a Monoid instance.</p> <pre> import qualified Data.Set as S -- laws: -- if x == y, then x &lt;~ y -- if x &lt;~ y and y &lt;~ z, then x &lt;~ z class (Eq p) =&gt; Preorder p where (&lt;~) :: p -&gt; p -&gt; Bool newtype PStar p = PStar { unPStar :: (S.Set p) } deriving Show instance (Preorder p, Ord p) =&gt; Monoid (PStar p) where mempty = PStar S.empty mappend (PStar ss) (PStar ts) = PStar $ S.filter (\s -&gt; all (\t -&gt; s == t || t &lt;~ s || not (s &lt;~ t)) $ S.toList ts) ss `S.union` S.filter (\t -&gt; all (\s -&gt; s == t || not (t &lt;~ s)) $ S.toList ss) ts </pre> <p>Pretty straightforward! Of course, there are probably more efficient ways to do this, but I don&#8217;t care about that at the moment: let&#8217;s get some working proof-of-concept code, and worry about efficiency later. Now, one thing you may notice is that our Monoid instance requires an Ord instance for p! &#8220;But I thought we only needed a preorder?&#8221; I hear you cry. Well, the Ord is just there so that we can use an efficient implementation of Set. In particular, note that the Ord instance need have nothing at all to do with the Preorder instance, and won&#8217;t affect the semantics of this Monoid instance in any way. If we wanted, we could do away with the Ord entirely and use lists of unique elements (or something like that) instead of Sets.</p> <p>So, how do we translate our record example from above into this new framework? Easy, we just create a data type to represent the different kinds of facts we want to represent, along with a convenience method or two, and make it an instance of Preorder:</p> <pre> data Fact = Name String | Age Int deriving (Eq,Show,Ord) fact :: Fact -&gt; PStar Fact fact f = PStar (S.singleton f) instance Preorder Fact where (Name _) &lt;~ (Name _) = True (Age _) &lt;~ (Age _) = True _ &lt;~ _ = False </pre> <p>Let&#8217;s try it!</p> <pre> &gt; mempty :: PStar Fact PStar {unPStar = fromList []} &gt; mconcat . map fact $ [Name "Brent"] PStar {unPStar = fromList [Name "Brent"]} &gt; mconcat . map fact $ [Name "Brent", Age 26] PStar {unPStar = fromList [Name "Brent",Age 26]} &gt; mconcat . map fact $ [Name "Brent", Age 26, Age 23] PStar {unPStar = fromList [Name "Brent",Age 26]} </pre> <p>Neato! But the really cool thing is all the extra power we get now: we can easily tweak the semantics of the Monoid instance by altering the Preorder instance. For example, suppose we want the first name that we see, but the <i>oldest</i> age. Easy peasy:</p> <pre> instance Preorder Fact where (Name _) &lt;~ (Name _) = True (Age m) &lt;~ (Age n) = m &lt;= n _ &lt;~ _ = False &gt; mconcat . map fact $ [Age 23, Name "Brent"] PStar {unPStar = fromList [Name "Brent",Age 23]} &gt; mconcat . map fact $ [Age 23, Name "Brent", Age 24] PStar {unPStar = fromList [Name "Brent",Age 24]} &gt; mconcat . map fact $ [Age 23, Name "Brent", Age 24, Name "Joe", Age 26] PStar {unPStar = fromList [Name "Brent",Age 26]} </pre> <p>Of course, we could have done this with the Record model, but it wouldn&#8217;t be terribly elegant. But we&#8217;re not done: let&#8217;s say we want to remember the oldest age we find, and the first name, unless the age is over 50, in which case we don&#8217;t want to remember the name (I admit this is a bit contrived)? That&#8217;s not too hard either:</p> <pre> instance Preorder Fact where (Name _) &lt;~ (Name _) = True (Age m) &lt;~ (Age n) = m &lt;= n (Name _) &lt;~ (Age n) = n &gt; 50 _ &lt;~ _ = False &gt; mconcat . map fact $ [Name "Brent", Age 26] PStar {unPStar = fromList [Name "Brent",Age 26]} &gt; mconcat . map fact $ [Name "Brent", Age 26, Age 45] PStar {unPStar = fromList [Name "Brent",Age 45]} &gt; mconcat . map fact $ [Name "Brent", Age 26, Age 45, Age 53] PStar {unPStar = fromList [Age 53]} </pre> <p>This would have been a huge pain to do with the Record model! Now, this isn&#8217;t an unqualified improvement; there are several things we can&#8217;t do here. One is if we want to be able to combine facts into larger compound facts: we can do that fairly straightforwardly with the Record-of-Maybes model, but not with the preorder-monoid model. We also can&#8217;t easily choose to have some fields be left-biased and some right-biased (the Monoid instance for PStar has left-bias built in!). But it&#8217;s certainly an interesting approach.</p> <p>Now, one thing we do have to be careful of is that our Preorder instances really do define a preorder! For example, at first I tried using <img src="http://l.wordpress.com/latex.php?latex=n+%3C+18&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="n &lt; 18" title="n &lt; 18" class="latex" /> in the above Preorder instance instead of <img src="http://l.wordpress.com/latex.php?latex=n+%3E+50&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="n &gt; 50" title="n &gt; 50" class="latex" />, and was confused by the weird results I got. But such a Preorder instance violates transitivity, so no wonder I was getting weird semantics. =) It would be interesting to reformulate this in a dependently typed language like <a href="http://appserv.cs.chalmers.se/users/ulfn/wiki/agda.php">Agda</a>, where creating a Preorder could actually require a proof that it satisfied the preorder axioms.</p> <p>Thanks to <a href="http://conal.net/">Conal Elliott</a> for some suggestions on making the formulation in the previous post more elegant &#8212; we&#8217;ll see what comes of it!</p> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/byorgey.wordpress.com/63/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/byorgey.wordpress.com/63/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/byorgey.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/byorgey.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/byorgey.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/byorgey.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/byorgey.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/byorgey.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/byorgey.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/byorgey.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/byorgey.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/byorgey.wordpress.com/63/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=byorgey.wordpress.com&amp;blog=1152889&amp;post=63&amp;subd=byorgey&amp;ref=&amp;feed=1" /></div> Thu, 17 Apr 2008 21:32:00 +0000 Brent Brent Yorgey: Brent http://byorgey.wordpress.com/?p=62 http://byorgey.wordpress.com/2008/04/17/an-interesting-monoid/ <div class="snap_preview"><br /><p>The other day I was just sort of letting my mind wander, and I came up with an interesting monoid, which I&#8217;m calling the &#8220;monoid of partial knowledge&#8221;. So I thought I&#8217;d write about it here, partly just because it&#8217;s interesting, and partly to see whether anyone has any pointers to any literature (I&#8217;m sure I&#8217;m not the first to come up with it).</p> <p>Recall that a <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Monoid"><i>monoid</i></a> is a set with an associative binary operation and a distinguished element which is the identity for the operation.</p> <p>Now, given a <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Total_order"><i>total order</i></a> on a set <img src="http://l.wordpress.com/latex.php?latex=S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S" title="S" class="latex" /> with a smallest element <img src="http://l.wordpress.com/latex.php?latex=e&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="e" title="e" class="latex" />, we get a monoid <img src="http://l.wordpress.com/latex.php?latex=%28S%2C+%5Cmax%29&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="(S, \max)" title="(S, \max)" class="latex" />, where <img src="http://l.wordpress.com/latex.php?latex=%5Cmax&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\max" title="\max" class="latex" /> denotes the function which determines the larger of two elements, according to the total order on <img src="http://l.wordpress.com/latex.php?latex=S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S" title="S" class="latex" />. <img src="http://l.wordpress.com/latex.php?latex=%5Cmax&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\max" title="\max" class="latex" /> is clearly associative, and has identity <img src="http://l.wordpress.com/latex.php?latex=e&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="e" title="e" class="latex" />. Taking a list of elements of <img src="http://l.wordpress.com/latex.php?latex=S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S" title="S" class="latex" /> and summarizing it via this monoid corresponds to finding the maximum element in the list. If you think of receiving the elements of the list one by one, and applying <img src="http://l.wordpress.com/latex.php?latex=%5Cmax&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\max" title="\max" class="latex" /> to each new incoming value and the value of an accumulator (storing the result back into the accumulator, which should obviously be initialized to <img src="http://l.wordpress.com/latex.php?latex=e&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="e" title="e" class="latex" />), at any given time the value of the accumulator represents the &#8216;current best&#8217;, i.e. the largest element among those received so far.</p> <p>The idea I had was to generalize this from a total order to a preorder. Recall that a <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Preorder"><i>preorder</i></a> is a set <img src="http://l.wordpress.com/latex.php?latex=S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S" title="S" class="latex" /> equipped with a reflexive, transitive binary relation, often denoted <img src="http://l.wordpress.com/latex.php?latex=%5Clesssim&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\lesssim" title="\lesssim" class="latex" />. That is, for any <img src="http://l.wordpress.com/latex.php?latex=x%2Cy%2Cz+%5Cin+S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x,y,z \in S" title="x,y,z \in S" class="latex" />, we have <img src="http://l.wordpress.com/latex.php?latex=x+%5Clesssim+x&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \lesssim x" title="x \lesssim x" class="latex" />; and <img src="http://l.wordpress.com/latex.php?latex=x+%5Clesssim+y+%5Cland+y+%5Clesssim+z&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \lesssim y \land y \lesssim z" title="x \lesssim y \land y \lesssim z" class="latex" /> implies <img src="http://l.wordpress.com/latex.php?latex=x+%5Clesssim+z&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \lesssim z" title="x \lesssim z" class="latex" />. If <img src="http://l.wordpress.com/latex.php?latex=%5Clesssim&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\lesssim" title="\lesssim" class="latex" /> is also antisymmetric, that is, <img src="http://l.wordpress.com/latex.php?latex=x+%5Clesssim+y+%5Cland+y+%5Clesssim+x&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \lesssim y \land y \lesssim x" title="x \lesssim y \land y \lesssim x" class="latex" /> implies <img src="http://l.wordpress.com/latex.php?latex=x+%3D+y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x = y" title="x = y" class="latex" />, it is called a <i>partial order</i>, or <i>poset</i>. Then if <img src="http://l.wordpress.com/latex.php?latex=x+%5Clesssim+y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \lesssim y" title="x \lesssim y" class="latex" /> or <img src="http://l.wordpress.com/latex.php?latex=y+%5Clesssim+x&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="y \lesssim x" title="y \lesssim x" class="latex" /> for any two elements <img src="http://l.wordpress.com/latex.php?latex=x&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x" title="x" class="latex" /> and <img src="http://l.wordpress.com/latex.php?latex=y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="y" title="y" class="latex" />, we get a total order, but for a general preorder some pairs of elements may not be comparable &#8212; that is, there may be elements <img src="http://l.wordpress.com/latex.php?latex=x&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x" title="x" class="latex" /> and <img src="http://l.wordpress.com/latex.php?latex=y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="y" title="y" class="latex" /> for which neither <img src="http://l.wordpress.com/latex.php?latex=x+%5Clesssim+y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \lesssim y" title="x \lesssim y" class="latex" /> nor <img src="http://l.wordpress.com/latex.php?latex=y+%5Clesssim+x&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="y \lesssim x" title="y \lesssim x" class="latex" /> holds.</p> <p>Let&#8217;s think about this. Suppose we are given a preorder <img src="http://l.wordpress.com/latex.php?latex=%28P%2C%5Clesssim%29&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="(P,\lesssim)" title="(P,\lesssim)" class="latex" /> with an <i>initial object</i> <img src="http://l.wordpress.com/latex.php?latex=e&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="e" title="e" class="latex" /> (an initial object in this context is an element which is <img src="http://l.wordpress.com/latex.php?latex=%5Clesssim&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\lesssim" title="\lesssim" class="latex" /> all other elements). We&#8217;ll initialize an accumulator to <img src="http://l.wordpress.com/latex.php?latex=e&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="e" title="e" class="latex" />, and imagine receiving elements of <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" /> one at a time. For a concrete example, suppose we are dealing with the preorder (actually also a poset) of positive integers under the divisibility relation, so our accumulator is initialized to 1. Let&#8217;s say we receive the integer 4. Clearly, 1 divides 4, so we should replace the 1 in our accumulator with 4. But now suppose we next receive the integer 5. 4 does not divide 5 or vice versa, so what should we do? We would be justified in neither throwing the 5 away nor replacing the 4, since 4 and 5 are not related to each other under the divisibility relation. Somehow we need to keep the 4 <i>and</i> the 5 around.</p> <p>The solution is that instead of creating a monoid over <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" /> itself, as we can for sets with a total order, we create a monoid over <i>subsets</i> of <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" />. In particular, consider the set <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" /> of subsets of <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" /> which do not contain two distinct elements <img src="http://l.wordpress.com/latex.php?latex=x%2Cy&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x,y" title="x,y" class="latex" /> for which <img src="http://l.wordpress.com/latex.php?latex=x+%5Clesssim+y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \lesssim y" title="x \lesssim y" class="latex" />. Since we are dealing with subsets of <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" />, we can actually drop the restriction that <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" /> contain an initial object; the empty set will serve as the identity for the monoid.</p> <p>We then define the monoid operation <img src="http://l.wordpress.com/latex.php?latex=%5Coplus&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\oplus" title="\oplus" class="latex" /> on two such subsets as</p> <p><img src="http://l.wordpress.com/latex.php?latex=S+%5Coplus+T+%3D+%28S+%5Ctriangleleft+T%29+%5Ccup+%28S+%5Ctriangleright+T%29&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \oplus T = (S \triangleleft T) \cup (S \triangleright T)" title="S \oplus T = (S \triangleleft T) \cup (S \triangleright T)" class="latex" /></p> <p>where</p> <p><img src="http://l.wordpress.com/latex.php?latex=S+%5Ctriangleleft+T+%3D+%5C%7B+s+%5Cin+S+%5Cmid+%5Cforall+t+%5Cin+T%2C+s+%3D+t+%5Cmbox%7B+or+%7D+t+%5Clesssim+s+%5Cmbox%7B+or+%7D+s+%5Cnot+%5Clesssim+t+%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \triangleleft T = \{ s \in S \mid \forall t \in T, s = t \mbox{ or } t \lesssim s \mbox{ or } s \not \lesssim t \}" title="S \triangleleft T = \{ s \in S \mid \forall t \in T, s = t \mbox{ or } t \lesssim s \mbox{ or } s \not \lesssim t \}" class="latex" /></p> <p>and</p> <p><img src="http://l.wordpress.com/latex.php?latex=S+%5Ctriangleright+T+%3D+%5C%7B+t+%5Cin+T+%5Cmid+%5Cforall+s+%5Cin+S%2C+s+%3D+t+%5Cmbox%7B+or+%7D+t+%5Cnot+%5Clesssim+s+%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \triangleright T = \{ t \in T \mid \forall s \in S, s = t \mbox{ or } t \not \lesssim s \}" title="S \triangleright T = \{ t \in T \mid \forall s \in S, s = t \mbox{ or } t \not \lesssim s \}" class="latex" />.</p> <p>In words, we combine subsets <img src="http://l.wordpress.com/latex.php?latex=S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S" title="S" class="latex" /> and <img src="http://l.wordpress.com/latex.php?latex=T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="T" title="T" class="latex" /> by forming the set of objects from <img src="http://l.wordpress.com/latex.php?latex=S+%5Ccup+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \cup T" title="S \cup T" class="latex" /> which are not <img src="http://l.wordpress.com/latex.php?latex=%5Clesssim&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\lesssim" title="\lesssim" class="latex" /> any others, with the exception of objects <img src="http://l.wordpress.com/latex.php?latex=s+%5Cin+S%2C+t+%5Cin+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="s \in S, t \in T" title="s \in S, t \in T" class="latex" /> where both <img src="http://l.wordpress.com/latex.php?latex=s+%5Clesssim+t&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="s \lesssim t" title="s \lesssim t" class="latex" /> and <img src="http://l.wordpress.com/latex.php?latex=t+%5Clesssim+s&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="t \lesssim s" title="t \lesssim s" class="latex" />; in this case we keep <img src="http://l.wordpress.com/latex.php?latex=s&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="s" title="s" class="latex" /> but not <img src="http://l.wordpress.com/latex.php?latex=t&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="t" title="t" class="latex" />. This introduces a &#8220;left bias&#8221; to <img src="http://l.wordpress.com/latex.php?latex=%5Coplus&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\oplus" title="\oplus" class="latex" />; there is also an equally valid version with right bias (in particular, <img src="http://l.wordpress.com/latex.php?latex=S+%5Coplus%27+T+%3D+%28T+%5Ctriangleleft+S%29+%5Ccup+%28T+%5Ctriangleright+S%29&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \oplus&#039; T = (T \triangleleft S) \cup (T \triangleright S)" title="S \oplus&#039; T = (T \triangleleft S) \cup (T \triangleright S)" class="latex" />).</p> <p>Now, let&#8217;s show that this really does define a valid monoid. First, we need to show that <img src="http://l.wordpress.com/latex.php?latex=%5Coplus&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\oplus" title="\oplus" class="latex" /> is closed over <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" />. Suppose <img src="http://l.wordpress.com/latex.php?latex=S%2C+T+%5Cin+P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S, T \in P_*" title="S, T \in P_*" class="latex" />. Suppose also that <img src="http://l.wordpress.com/latex.php?latex=x%2Cy+%5Cin+S+%5Coplus+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x,y \in S \oplus T" title="x,y \in S \oplus T" class="latex" /> are distinct elements of <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" /> with <img src="http://l.wordpress.com/latex.php?latex=x+%5Clesssim+y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \lesssim y" title="x \lesssim y" class="latex" />; we&#8217;ll derive a contradiction. First, we cannot have <img src="http://l.wordpress.com/latex.php?latex=x%2Cy+%5Cin+S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x,y \in S" title="x,y \in S" class="latex" /> or <img src="http://l.wordpress.com/latex.php?latex=x%2Cy+%5Cin+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x,y \in T" title="x,y \in T" class="latex" /> by definition of <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" />. Now suppose <img src="http://l.wordpress.com/latex.php?latex=x+%5Cin+T%2C+y+%5Cin+S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \in T, y \in S" title="x \in T, y \in S" class="latex" />. The fact that <img src="http://l.wordpress.com/latex.php?latex=x+%5Cin+S+%5Coplus+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \in S \oplus T" title="x \in S \oplus T" class="latex" /> together with the definition of <img src="http://l.wordpress.com/latex.php?latex=S+%5Ctriangleright+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \triangleright T" title="S \triangleright T" class="latex" /> imply that we must have <img src="http://l.wordpress.com/latex.php?latex=x+%5Cnot+%5Clesssim+y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \not \lesssim y" title="x \not \lesssim y" class="latex" />, a contradiction. Finally, suppose <img src="http://l.wordpress.com/latex.php?latex=x+%5Cin+S%2C+y+%5Cin+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \in S, y \in T" title="x \in S, y \in T" class="latex" />. Again, by the definition of <img src="http://l.wordpress.com/latex.php?latex=S+%5Ctriangleright+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \triangleright T" title="S \triangleright T" class="latex" /> we must have <img src="http://l.wordpress.com/latex.php?latex=y+%5Cnot+%5Clesssim+x&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="y \not \lesssim x" title="y \not \lesssim x" class="latex" />. But then the fact that <img src="http://l.wordpress.com/latex.php?latex=x+%5Cin+S+%5Coplus+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \in S \oplus T" title="x \in S \oplus T" class="latex" />, together with the definition of <img src="http://l.wordpress.com/latex.php?latex=S+%5Ctriangleleft+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \triangleleft T" title="S \triangleleft T" class="latex" /> and the facts that <img src="http://l.wordpress.com/latex.php?latex=x+%5Cneq+y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \neq y" title="x \neq y" class="latex" /> and <img src="http://l.wordpress.com/latex.php?latex=y+%5Cnot+%5Clesssim+x&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="y \not \lesssim x" title="y \not \lesssim x" class="latex" /> imply that <img src="http://l.wordpress.com/latex.php?latex=x+%5Cnot+%5Clesssim+y&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="x \not \lesssim y" title="x \not \lesssim y" class="latex" />, a contradiction again. Hence <img src="http://l.wordpress.com/latex.php?latex=S+%5Coplus+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \oplus T" title="S \oplus T" class="latex" /> contains no such pair of elements.</p> <p>The fact that the empty set <img src="http://l.wordpress.com/latex.php?latex=%5Cvarnothing&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\varnothing" title="\varnothing" class="latex" /> is the identity for <img src="http://l.wordpress.com/latex.php?latex=%5Coplus&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\oplus" title="\oplus" class="latex" /> is clear. (Incidentally, this is why we require that none of the sets in <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" /> contain two distinct elements with one <img src="http://l.wordpress.com/latex.php?latex=%5Clesssim&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\lesssim" title="\lesssim" class="latex" /> the other: if <img src="http://l.wordpress.com/latex.php?latex=S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S" title="S" class="latex" /> were such a set, we would have <img src="http://l.wordpress.com/latex.php?latex=%5Cvarnothing+%5Coplus+S+%5Cneq+S&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\varnothing \oplus S \neq S" title="\varnothing \oplus S \neq S" class="latex" />.) I leave the associativity of <img src="http://l.wordpress.com/latex.php?latex=%5Coplus&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\oplus" title="\oplus" class="latex" /> as an exercise for the reader (translation: this post is already getting long, the associativity of <img src="http://l.wordpress.com/latex.php?latex=%5Coplus&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\oplus" title="\oplus" class="latex" /> seems intuitively obvious to me, and I don&#8217;t feel like formalizing it at the moment &#8212; perhaps I&#8217;ll try writing it up later). I also leave as an interesting exercise the following theorem: if <img src="http://l.wordpress.com/latex.php?latex=S%2C+T+%5Cin+P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S, T \in P_*" title="S, T \in P_*" class="latex" /> are both finite and nonempty, then <img src="http://l.wordpress.com/latex.php?latex=S+%5Coplus+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \oplus T" title="S \oplus T" class="latex" /> is also finite and nonempty.</p> <p>In our example from before, we could now begin with <img src="http://l.wordpress.com/latex.php?latex=%5C%7B1%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{1\}" title="\{1\}" class="latex" /> in our accumulator. After receiving the singleton set <img src="http://l.wordpress.com/latex.php?latex=%5C%7B4%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{4\}" title="\{4\}" class="latex" />, our accumulator would have that singleton set as its new value. Upon receiving <img src="http://l.wordpress.com/latex.php?latex=%5C%7B5%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{5\}" title="\{5\}" class="latex" />, our accumulator would become <img src="http://l.wordpress.com/latex.php?latex=%5C%7B4%2C5%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{4,5\}" title="\{4,5\}" class="latex" />. Receiving <img src="http://l.wordpress.com/latex.php?latex=%5C%7B10%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{10\}" title="\{10\}" class="latex" /> would result in <img src="http://l.wordpress.com/latex.php?latex=%5C%7B4%2C10%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{4,10\}" title="\{4,10\}" class="latex" /> (5 divides 10, so the 5 is discarded); if we later received <img src="http://l.wordpress.com/latex.php?latex=%5C%7B20%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{20\}" title="\{20\}" class="latex" />, we would simply have <img src="http://l.wordpress.com/latex.php?latex=%5C%7B20%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{20\}" title="\{20\}" class="latex" /> in our accumulator (both 4 and 10 divide 20).</p> <p>I like to think of this as the monoid of <i>partial knowledge</i>. If we consider <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" /> to be a set of facts or beliefs, some better (more reliable, useful, correct, complete, etc.) than others, then elements of <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" /> correspond to possible sets of beliefs. <img src="http://l.wordpress.com/latex.php?latex=%5Coplus&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\oplus" title="\oplus" class="latex" /> describes how a set of beliefs changes upon encountering a new set of facts; some of the new facts may supersede and replace old ones, some may not impart any new information, and some may be completely new facts that aren&#8217;t related to any currently known.</p> <p>Now, why can this be thought of as a generalization of the monoid <img src="http://l.wordpress.com/latex.php?latex=%28P%2C+%5Cmax%29&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="(P, \max)" title="(P, \max)" class="latex" /> on a totally ordered set? Well, look what happens when we replace <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" /> in the definitions above with a totally ordered set with relation <img src="http://l.wordpress.com/latex.php?latex=%5Cleq&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\leq" title="\leq" class="latex" />: first of all, the restriction on <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" /> (no two elements of a set in <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" /> should be related by <img src="http://l.wordpress.com/latex.php?latex=%5Cleq&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\leq" title="\leq" class="latex" />) means that <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" /> contains only the empty set and singleton sets, so (ignoring the empty set) <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" /> is isomorphic to <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" />. Now look at the definition of <img src="http://l.wordpress.com/latex.php?latex=S+%5Ctriangleleft+T&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \triangleleft T" title="S \triangleleft T" class="latex" />, with <img src="http://l.wordpress.com/latex.php?latex=%5Clesssim&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\lesssim" title="\lesssim" class="latex" /> replaced by <img src="http://l.wordpress.com/latex.php?latex=%5Cleq&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\leq" title="\leq" class="latex" /> (and <img src="http://l.wordpress.com/latex.php?latex=%5Cnot+%5Clesssim&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\not \lesssim" title="\not \lesssim" class="latex" /> replaced by <img src="http://l.wordpress.com/latex.php?latex=%3E&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="&gt;" title="&gt;" class="latex" />):</p> <p><img src="http://l.wordpress.com/latex.php?latex=S+%5Ctriangleleft+T+%3D+%5C%7B+s+%5Cin+S+%5Cmid+%5Cforall+t+%5Cin+T%2C+s+%3D+t+%5Cmbox%7B+or+%7D+t+%5Cleq+s+%5Cmbox%7B+or+%7D+s+%3E+t+%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="S \triangleleft T = \{ s \in S \mid \forall t \in T, s = t \mbox{ or } t \leq s \mbox{ or } s &gt; t \}" title="S \triangleleft T = \{ s \in S \mid \forall t \in T, s = t \mbox{ or } t \leq s \mbox{ or } s &gt; t \}" class="latex" /></p> <p>But <img src="http://l.wordpress.com/latex.php?latex=s+%3D+t&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="s = t" title="s = t" class="latex" /> and <img src="http://l.wordpress.com/latex.php?latex=s+%3E+t&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="s &gt; t" title="s &gt; t" class="latex" /> are both subsumed by <img src="http://l.wordpress.com/latex.php?latex=t+%5Cleq+s&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="t \leq s" title="t \leq s" class="latex" />, so we can rewrite this as</p> <p><img src="http://l.wordpress.com/latex.php?latex=%5C%7Bs%5C%7D+%5Ctriangleleft+%5C%7Bt%5C%7D+%3D+%5C%7Bs%5C%7D+%5Cmbox%7B+if+%7D+s+%5Cgeq+t%2C+%5Cmbox%7B+or+%7D+%5Cvarnothing+%5Cmbox%7B+otherwise+%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{s\} \triangleleft \{t\} = \{s\} \mbox{ if } s \geq t, \mbox{ or } \varnothing \mbox{ otherwise }" title="\{s\} \triangleleft \{t\} = \{s\} \mbox{ if } s \geq t, \mbox{ or } \varnothing \mbox{ otherwise }" class="latex" />.</p> <p>An analysis of <img src="http://l.wordpress.com/latex.php?latex=%5Ctriangleright&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\triangleright" title="\triangleright" class="latex" /> is similar, and it is clear that <img src="http://l.wordpress.com/latex.php?latex=%5C%7Bs%5C%7D+%5Coplus+%5C%7Bt%5C%7D+%3D+%5C%7B%5Cmax%28s%2Ct%29%5C%7D&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\{s\} \oplus \{t\} = \{\max(s,t)\}" title="\{s\} \oplus \{t\} = \{\max(s,t)\}" class="latex" />.</p> <p>I note in passing that although it might appear shady that I swept that &#8220;ignoring the empty set&#8221; bit under the rug, everything really does check out: technically, to see a direct generalization of <img src="http://l.wordpress.com/latex.php?latex=%28P%2C%5Cmax%29&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="(P,\max)" title="(P,\max)" class="latex" /> to <img src="http://l.wordpress.com/latex.php?latex=%28P_%2A%2C+%5Coplus%29&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="(P_*, \oplus)" title="(P_*, \oplus)" class="latex" />, we can require that <img src="http://l.wordpress.com/latex.php?latex=P&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P" title="P" class="latex" /> have an initial object and that <img src="http://l.wordpress.com/latex.php?latex=P_%2A&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="P_*" title="P_*" class="latex" /> contains only finite, nonempty sets. Then it requires a bit more work to prove that <img src="http://l.wordpress.com/latex.php?latex=%5Coplus&amp;bg=ffffff&amp;fg=333333&amp;s=0" alt="\oplus" title="\oplus" class="latex" /> is closed, but it still goes through. I used the formulation I did since it seems more general and requires less proving.</p> <p>Anyway, this ended up being longer than I originally anticipated (why does that always happen!? =), so I&#8217;ll stop here for now, but next time I&#8217;ll give some actual Haskell code (which I think ends up being pretty neat!), and talk about one relatively common design pattern which is actually a special case of this monoid.</p> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/byorgey.wordpress.com/62/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/byorgey.wordpress.com/62/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/byorgey.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/byorgey.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/byorgey.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/byorgey.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/byorgey.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/byorgey.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/byorgey.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/byorgey.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/byorgey.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/byorgey.wordpress.com/62/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=byorgey.wordpress.com&amp;blog=1152889&amp;post=62&amp;subd=byorgey&amp;ref=&amp;feed=1" /></div> Thu, 17 Apr 2008 19:37:24 +0000 Brent Joachim Breitner: Announcing DarcsWatch http://www.joachim-breitner.de/blog/archives/290-guid.html http://www.joachim-breitner.de/blog/archives/290-Announcing-DarcsWatch.html <p>A lot of haskell-related projects use the <a href="http://darcs.net/">darcs</a> version control system. Darcs has the nice feature that you can easily submit a patch by e-mail, usually sent to the project maintainer or a mailing list. What has bothered me in the past was that I had to manually check whether my patch was applied yet.</p><p>So this week, I wrote <a href="http://darcs.nomeata.de/">DarcsWatch</a>. DarcsWatch receives submitted patches and compares them to the repositories it knows about, whether the patch is missing there or not. You either send your patches to DarcsWeb directly when submitting them, using CC, or DarcsWatch is subscribed to the project mailing list itself, which is the case for the <a href="http://lists.osuosl.org/mailman/listinfo/darcs-devel">darcs-devel</a> and <a href="http://www.haskell.org/mailman/listinfo/xmonad">xmonad</a> lists. Other than that, not much is to do, and you’ll find a nice overview of your patches, like this <a href="http://darcswatch.nomeata.de/user_mail@joachim-breitner.de.html">overview of my patches</a>, with diffs and download links.</p><p>If you want to use DarcsWatch for your contributions to other projects, or for your own projects, have a look the <a href="http://darcs.nomeata.de/darcswatch/documentation.html">documentation</a>. There are probably still bugs around, and the problem of marking patches as obsolete is not really solved yet, so if you have ideas about it, mail me, comment here, or come to #darcs on freenode. Patches are always welcome, you can get the source from <a href="http://darcs.nomeata.de/darcswatch/">DarcsWatch’s repository</a>.</p> Thu, 17 Apr 2008 17:25:15 +0000 mail@joachim-breitner.de (nomeata) Mark Jason Dominus: Is blood a transitive relation? tag:blog.plover.com,2008:/oops/blood-relatives http://blog.plover.com/oops/blood-relatives.html When you're first teaching high school students about the idea of a relation, you give examples of the important properties of relations. Relations can be some, none, or all of reflexive, symmetric, antisymmetric, or transitive. You start with examples that everyone is already familiar with, such as the identity relation, which is reflexive, symmetric, and transitive, and the &le; relation, which is antisymmetric and transitive. Other good examples include familial relations: "sister-in-law of" is symmetric on the set of women, but not on the larger set of people; "ancestor of" is transitive but not symmetric.<p> It might seem at first glance that "is related to" is transitive, but, at least under conventional definitions, it isn't, because my wife is not related to my cousins.<p> (I was once invited to speak at Haverford College, and, since I have no obvious qualifications in <a href="http://perl.plover.com/yak/cftalk/">the topic on which I was speaking</a>, I was asked how I had come to be there. I explained that it was because <a href="http://www.haverford.edu/math/lbutler.html">my wife's mother's younger brother's daughter's husband's older brother's wife was the chair of the mathematics department</a>. Remember, it's not what you know, it's who you know.)<p> I think I had sometimes tried to turn "related to" into a transitive relation by restricting it to "is related to by blood". This rules out the example above of my wife being unrelated to my cousins, because my relationship with my wife is not one of blood. I don't quite remember using "related by blood" as an example of a transitive relation, but I think I might have, because I was quite surprised when I realized that it didn't work. I spent a lot of time that morning going over my counterexample in detail, writing it up in my head, as it were. I was waiting around in Trevose for the mechanic to finish examining my car, and had nothing better to do that morning. If I had had a blog then, I would probably have posted it. But it is a good thing that I didn't, because in spite of all my thought about it, I missed something important.<p> The example is as follows. <i>A</i> and <i>B</i> have a child, <i>X</i>. (You may suppose that they marry beforehand, and divorce afterward, if your morality requires it.) Similarly, <i>C</i> and <i>D</i> have a child, <i>Z</i>. Then <i>B</i> and <i>C</i> have a child <i>Y</i>. <i>Y</i> is now the half-sibling of both <i>X</i> and <i>Z</i>, and so is unquestionably a blood relative of both, but <i>X</i> and <i>Z</i> are entirely unrelated. They are not even step-siblings.<p> Well, this is all very well, but the reason I have filed it under <a href="http://blog.plover.com/oops/">oops/</a>, and the reason it's a good thing I didn't post it on my (then nonexistent) blog is that this elaborate counterexample contains a much simpler one: <i>X</i> is the child and hence the blood relative of both <i>A</i> and <i>B</i>, who are not in general related to each other. <i>C</i>, <i>D</i>, <i>Y</i>, and <i>Z</i> are wholly unnecessary.<p> I wish I had some nice conclusion to draw here, but if there's something I could learn from it I can't think would it might be.<p> <!-- That is all I meant to write about, but I am in the waiting room at the Parking Authority, waiting to protest a ticket, so I have lots of time, and I may as well go on for a while.<p> At my last regular job, one of my co-workers observed that everyone on the corridor was part-Asian. Her mother was Japanese; another colleague's was Thai. One colleague was Bengali. "I am on this corridor," I said, "and I am not Asian." "You have blood relatives who are Asian," she pointed out, which I had not really though of before. Iris had recently been born, and she is half-Korean.<p> When Iris was still <i>in utero</i>, Lorrie and I visited a genetic counselor whose job was to judge the chance that Iris might come out with some hereditary disorder. She asked me many questions about my forebears, which I answered in my characteristically guarded way: "Is there any history of Tay-Sachs disease in your family?" "Not so far as I know." "I am not aware of any." "I believe not." And so on.<p> But then she got to a more general question: "Is there any possibility that you and Lorrie might be related?" And I laughed, and said heartily, "None whatsoever!". <p> --></p></p></p></p></p></p></p> Thu, 17 Apr 2008 17:05:00 +0000 Mark Dominus Conrad Parker: :rikaichan for Vimperator tag:blogger.com,1999:blog-9101292118679422945.post-8373941523429907668 http://blog.kfish.org/2008/04/rikaichan-for-vimperator.html <p> Some of my favourite Firefox plugins are: <ul> <li><a href="http://www.polarcloud.com/rikaichan/">Rikaichan</a>, a Japanese dictionary, which adds instant translation popups when you mouse over a word;</li> <li><a href="http://vimperator.mozdev.org/">Vimperator</a>, which provides <tt>vi</tt>-like user interface;</li> <li>and <a href="https://addons.mozilla.org/en-US/firefox/addon/1337">Hide Tab Bar</a>, because Vimperator's buffer list is more useful.</li> </ul> <p> Vimperator hides the menu bar by default. <b>Tools->Toggle Rikaichan</b> has no default keybinding, and the keybindings to navigate the menubar are not available if the menubar is not visible, so Rikaichan can no longer be activated. </p> <p> The following adds a vimperator command <tt>:rikaichan</tt>; save it to <tt>.vimperator/plugin/toggleRikaichan.js</tt>: <blockquote><pre> (function(){ vimperator.commands.add(new vimperator.Command( ['rikaichan', 'rikai'], function(){ rcxMain.inlineToggle(); } )) }) (); </pre></blockquote> </p> <p> It is aliased to <tt>:rikai</tt> for short, but unfortunately vimperator won't recognize <tt>:理解</tt>. Thanks to ktsukagoshi for the explanation of how to write a vimperator plugin (<a href="http://d.hatena.ne.jp/ktsukagoshi/20080305/1204730962">vimperatorのプラグインの作成</a>). </p> <p> <i>Remember, <a href="http://www.vergenet.net/~conrad/syre/">the interface is inside your mind</a>.</i> </p></p> Thu, 17 Apr 2008 16:01:22 +0000 Conrad Parker Philip Wadler: Fun with correlation and citations tag:blogger.com,1999:blog-9757377.post-9088874984566084902 http://wadler.blogspot.com/2008/04/fun-with-correllation-and-citations.html I'm sure I'll want to cite this someday, possibly in class but perhaps in the pub. What I'll be citing it as an example of, I'm not yet sure. Thanks to Leonid Libkin for spotting this. Thu, 17 Apr 2008 10:00:50 +0000 Philip Wadler Kenn Knowles: Drawing fractals in Haskell with a cursor graphics DSEL and a cute list representation http://www.kennknowles.com/blog/2008/04/16/drawing-fractals-in-haskell-with-a-cursor-graphics-dsel-and-a-cute-list-representation/ http://www.kennknowles.com/blog/2008/04/16/drawing-fractals-in-haskell-with-a-cursor-graphics-dsel-and-a-cute-list-representation/ <p>I'm reading the very fun <em>Measure, Topology, and Fractal Geometry</em> by GA Edgar, and thought I'd hack up some of the examples in Haskell. So this post implements cursor graphics in OpenGL in (I think) DSEL style, demonstrating the <code>StateT</code> and <code>Writer</code> monad gadgets from the standard library and a cool "novel representation of lists" due to R Hughes. On the fractal side, the examples will hopefully convince you that fractals are not just cute pictures, but extremely important illustrations that the real numbers are weird. As usual, you can save this post to <code>Fractals.lhs</code> and compile it with <code>ghc --make Fractals</code> <a href="http://www.kennknowles.com/blog/2008/04/16/drawing-fractals-in-haskell-with-a-cursor-graphics-dsel-and-a-cute-list-representation/#more-42" class="more-link">(more&#8230;)</a></p> Wed, 16 Apr 2008 18:02:52 +0000 Kenn Luke Palmer: Symphonic Poem no. 1 http://luqui.org/blog?p=517 http://luqui.org/blog/archives/2008/04/16/symphonic-poem-no-1/ <p>I just finished, in some sense, <a href="http://luqui.org/music/progress/symphonic_poems/01.mp3">the first</a> of a series (hopefully) of symphonic poems. I took a long break in the middle of writing this, so I could be mistaken, but I think it took about 20 hours total. It still needs a little tweaking; in particular some of the harmonies on the climaxes are drowned out by the percussion and poor instrument balancing, and I&#8217;m not sure if I&#8217;m happy with the ending (it&#8217;s meant to be a little unsettling&#8230;). But the idea is there.</p> Wed, 16 Apr 2008 11:21:47 +0000 Luke John Goerzen (CosmicRay): datapacker http://changelog.complete.org/posts/707-guid.html http://changelog.complete.org/posts/707-datapacker.html Every so often, I come across some utility that need. I think it <b>must</b> have been written before, but I can't find it.<br /> <br /> Today I needed a tool to take a set of files and split them up into directories in a size that will fit on DVDs. I wanted a tool that could either produce the minimum number of DVDs, or keep the files in order. I couldn't find one. So I wrote <a href="http://software.complete.org/datapacker">datapacker</a>.<br /> <br /> datapacker is a tool to group files by size. It is perhaps most often used to fit a set of files onto the minimum number of CDs or DVDs.<br /> <br /> datapacker is designed to group files such that they fill fixed-size containers (called "bins") using the minimum number of containers. This is useful, for instance, if you want to archive a number of files to CD or DVD, and want to organize them such that you use the minimum possible number of CDs or DVDs.<br /> <br /> In many cases, datapacker executes almost instantaneously. Of particular note, the hardlink action can be used to effectively copy data into bins without having to actually copy the data at all.<br /> <br /> datapacker is a tool in the traditional Unix style; it can be used in pipes and call other tools.<br /> <br /> I have, of course, uploaded it to sid. But while it sits in NEW, you can download the source tarball (with debian/ directory) from the project homepage at <a href="http://software.complete.org/datapacker">http://software.complete.org/datapacker</a>. I've also got an <a href="http://software.complete.org/datapacker/static/doc/datapacker.html">HTML version of the manpage</a> online, so you can see all the cool features of datapacker. It works nicely with find, xargs, mkisofs, and any other Unixy pipe-friendly program.<br /> <br /> Those of you that know me will not be surprised that I wrote datapacker in Haskell. For this project, I added a <a href="http://en.wikipedia.org/wiki/Bin_packing_problem">bin-packing</a> module and support for parsing inputs like <tt>1.5g</tt> to <a href="http://software.complete.org/missingh">MissingH</a>. So everyone else that needs to do that sort of thing can now use library functions for it.<br /> <br /> <b>Update...</b> I should have mentioned the <b>really cool thing</b> about this. After datapacker compiled and ran, I had only <b>one mistake</b> that was not caught by the Haskell compiler: I said &lt; where I should have said &lt;= one place. This is one of the very nice things about Haskell: the language lends itself to compilers that can catch so much. It's not that I'm a perfect programmer, just that my compiler is pretty crafty. Wed, 16 Apr 2008 04:09:53 +0000 nospam@example.com (John Goerzen) Nick Mudge: Inelegance of Combining HTML and Code http://nickmudge.info?post=92 http://nickmudge.info?post=92 <p> I thought I’d shoot off an idea about programming webpages. </p> <p> To me writing a script to generate HTML has always been a problem. It’s a problem because you have to mix a bunch of code with HTML. No matter how you mix more than a little bit of code and HTML the result is inelegance, ugly code, hard to read code. It’s not very good because you’re mixing two different syntaxes together in a jumbled fashion. Code that is just code without HTML in it so much nicer and easy to follow and read. And gosh, I’m always perturbed about how to indent code and HTML together so that structure and things can be made clearer. The elegance, clarity and structure provided by indentation is reduced a lot by the combination of programming logic and HTML intermingled, at least from my experience. </p> <p> There’s been various ways to help make this better. For instance in PHP you can move in and out of HTML and PHP modes which makes the transitions nicer sometimes. The <a href="http://www.smarty.net/">Smarty templating system</a> is largely a system to make this easier. The Smarty language syntax and tools integrate nicer with HTML and help keep programming away from HTML. This also has a nice benefit of being nice to designers that need to work with HTML and CSS but don’t want to hassle with programming. </p> <p> Smarty is a nice improvement and the best solution I’ve used so far but the problem is still there. Sometimes you want to add quite a bit of logic to your Smarty templates, adding loops, and inner loops, adding lots of if conditions, making function calls etc. You might want to do this because the display of the webpage you are making is complex and requires lots of if conditions and loops or because you want to reuse the same templates for different webpages (this cuts down the amount of work you need to do and manage – reuse is good) and so need to add additional logic that runs depending on which page the template is being used to generate. Too much logic in Smarty templates quickly gets inelegant and hard. </p> <p> Things get inelegant and harder to follow when you start adding a bunch of logic to HTML pages. It’s like HTML wasn’t meant to have logic in it. </p> <p> A related problem that is also a big problem with HTML is that it does not scale. The bigger an HTML file gets the harder it is to follow and work with. With a zillion div tags and table tags it gets harder and harder to know what exactly is happening anywhere in a document. It just doesn’t scale. This combined with logic makes for general ickyness. </p> <p> I wonder if there is another way. </p> <p> I’ve looked into Lisp a little in the past and what I’ve seen is that in Common Lisp the combination of programming and HTML are handled in a completely different way. I wonder if something here could be a real solution to this inelegance pest, and actually really bring about an elegance and smoothness in the combination of programming logic and HTML. </p> <p> From what I've seen in Common Lisp when you program webpages you don’t really mix HTML and programming. Instead you make it all programming. I’m thinking it might be something along these lines: You have a library of functions and/or macors. Each function or macro or arguement corresponds to an HTML tag. For instance you have the div function that is used to generate the HTML for a div tag etc. You just use all these functions that generate all the HTML for you. And it’s easy and natural to add logic because it’s all consistent programming code. You keep the consistency, elegance and scalability of a programming language by only programming in the programming language. </p> <p> The main problem I see with this approach of using functions to generate HTML is that HTML is highly flexible -- some tags can have certain attributes and can be nestled within some tags but not others and there’s a ton of variance and things you can do in HTML. In lots of programming languages I think functions are probably not flexible enough to cope with all the possibilities. I think these functions would limit what you could do with HTML and cut your flexibility of how you want the HTML to be. It really might not be very workable. I certainly don’t like to code with constraints on me. </p> <p> But maybe it’s possible with something like Lisp. You’d need finely grained functions/macros that had the same flexibility and limitations as the HTML tags. And I think that’s something Lisp might be able to provide. In Lisp you have finely grained control on what your functions are and how they work – more than I’ve seen anywhere. In addition Lisp has powerful macro abilities that enable you to mold the syntax and semantics of the language to your will. This fine grainess of control on the syntax and semantics of the language might enable you to write a library of HTML functions that you could use to write pure elegant code that generates all the HTML you need and with as much flexibility as you need. Perhaps this already exists. </p> <p> I think something like this would make it much easier and faster to work with webpages because pure well written code is much easier to write and read than a mess of HTML and code together. With code you have one consistent syntax and it scales better than HTML does. In addition if your functions are perfect (have no bugs in their implementation), then any HTML you generate with them should automatically be perfect too – that’s real scalability power when talking about HTML correctness. </p> <p> Of course this pure programming way of handling HTML might not be so good for designers. So I was thinking that there could be some kind of separate interface for designers – like CSS is a separate interface into the display of HTML and a webpage. One idea is that a designer could design stuff in pure HTML and CSS and then feed it into a program that analyses it and if it is correct HTML and CSS generates or modifies the programming code to generate that HTML and CSS and if it is wrong spits the HTML and CSS back to the designer to fix. </p> <hr /> <p> From Chris Minnick: </p> <p> Good thoughts, Nick. </p> <p> I see what you’re getting at, but I’m skeptical of solutions to make code or HTML more 'pure'. It seems like no matter what you do, someone is going to start slipping some HTML in where it doesn’t belong, or is going to put something that should be in the CSS into the HTML, or is going to use tables for layout, or what-have-you. Usually, people resist doing things the 'right' way because the right way is more difficult or doesn't work right. </p> <p> CSS positioning is a perfect example: purists insist that all Web page layout should be done with divs and CSS, and that tables should only be used for tabular data. This makes perfect sense, of course, but the reality is that you just can’t do some of the things you can do with tables using divs, or it’s very difficult—especially considering browser differences. </p> <p> So, what usually ends up happening as a result of this is that the people who were insisting on purity start backing off … so, now RSS 2.0 is popular, while the strict RDF version (RSS 1.1) was a big flop, and HTML 5 is going to be the successor to XHTML because people continued to use the much more flexible HTML 4.01 years and years after the stricter XHTML 1.0 came out. </p> <p> My question is: would the benefits of writing HTML using functions outweigh the added complexity and inconvenience to people who design webpages? </p> <hr /> <p> From Nick Mudge: </p> <p> Hey Chris, </p> <p> I totally agree and excellent point. Your email made me realize that ideally the functions should be flexible enough to allow people to be as non-standard and/or hacky as they want. I think people should have the freedom to do things how they want. I think the functions should prevent obvious errors like a &lt;div&gt; without a &lt;/div&gt; and things that will obviously break webpages in web browsers. It would be nice if it could also help people be as standard as they want by letting them set some global setting or something so that functions enforce more strictness to a standard or whatever they want. </p> <p> I think the goal of a library like this would be to enable programmers to combine programming and HTML in way that is syntactically consistent, easier to read, write and maintain without loosing the flexibility that they want. Or to achieve a scalable elegance with the combination of logic and HTML. Perhaps a secondary goal would be to help people create HTML as correct or as standard/unstandard as they want. </p> <p> Any system around the functions/macro library such as a program that translates HTML into code that generates the same HTML should be as flexible to the user as the functions. </p> <p> I think a designer who does HTML and CSS work would use some program that does an HTML to code translation and the designer should have a way to specify how loose the translator is in accepting HTML and CSS. And I think programmers would just work in the code. </p> Tue, 15 Apr 2008 00:00:00 +0000 Luke Palmer: SNW Backlogging http://luqui.org/blog?p=516 http://luqui.org/blog/archives/2008/04/14/snw-backlogging/ <p>SNW is dead <tt>:-(</tt>. But I still have some recordings lying around that I&#8217;m gradually editing. Here&#8217;s the first batch, from March 20:</p> <ul> <li><a href="http://luqui.org/music/improv/2008-03-20_01.mp3">01</a></li> <li><a href="http://luqui.org/music/improv/2008-03-20_02.mp3">02</a> - a minimalist (in the classical sense) exercise</li> <li><a href="http://luqui.org/music/improv/2008-03-20_03.mp3">03</a></li> <li><a href="http://luqui.org/music/improv/2008-03-20_04.mp3">04</a> - fun and funky</li> <li><a href="http://luqui.org/music/improv/2008-03-20_05.mp3">05</a> - a long piece with many sections, pretty upbeat</li> </ul> <p>#01 and #03 are included for completeness; they suck <tt>:-p</tt>. The rest are pretty good.</p> Mon, 14 Apr 2008 21:28:13 +0000 Luke Conrad Parker: Continuation Fest 2008: Continuations for video decoding and scrubbing tag:blogger.com,1999:blog-9101292118679422945.post-8478267578838678327 http://blog.kfish.org/2008/04/continuation-fest-2008-continuations.html <p> Yesterday was <a href="http://logic.cs.tsukuba.ac.jp/Continuation/">Continuation Fest 2008</a>, at the University of Tokyo's campus in Akihabara (a very nice venue!). It was very well attended; latecomers overflowed to a second room and participated by video conference. It was a little strange to see so many people interested in such an <strike>obscure, troublesome and malignant</strike> expressively powerful programming construct; the breadth of talks made for a very inspiring and practical introduction to the theory, applications and implementation of continuations in many different languages. </p> <p> I recommend reading <a href="http://pllab.is.ocha.ac.jp/~asai/">Kenichi Asai</a>'s introduction to delimited continuations (<a href="http://pllab.is.ocha.ac.jp/~asai/papers/contfest08slide.pdf">slides</a> [PDF]). He introduced the <tt>shift</tt> and <tt>reset</tt> operators through the problem of expressing exceptional control flow, and then explained how to use these to type (ie. determine a concrete type for) <tt>printf</tt>. The main point was that <tt>shift/reset</tt> provide a high-level abstraction over control flow, with minimal impact on the implementation of your existing functions. </p> <p> <a href="http://okmij.org/ftp/">Oleg Kiselyov</a> demonstrated some new code for transactional web applications, using delimited continuations for explicit state sharing between parallel connections. The result is that the user has a consistent view across multiple tabs are open on the same site, and the state is transactional so that there is no need for warnings like "Do not press the BUY button more than once!". He said that everyone already understands delimited continuations, they just don't realize it. </p> <p> The topic of my presentation at Continuation Fest was <b>Continuations for video decoding and scrubbing</b>: </p> <blockquote><p> Playback of encoded video involves scheduling the decoding of audio and video frames and synchronizing their playback. "Scrubbing" is the ability to quickly seek to and display an arbitrary frame, and is a common user interface requirement for a video editor. The implementation of playback and scrubbing is complicated by data dependencies in compressed video formats, which require setup and manipulation of decoder state. </p><p> We present the preliminary design of a continuation-based system for video decoding, reified as a cursor into a stream of decoded video frames. Frames are decoded lazily, and decoder states are recreated or restored when seeking. To reduce space requirements, a sequence of decoded frames can be replaced after use by the continuation which created them. <strike>We outline implementations in Haskell and C.</strike> </p></blockquote> <p> <ul> <li><a href="http://seq.kfish.org/~conrad/static/continuation-fest-2008/continuations-for-video.pdf">Slides</a> [383KB PDF]</li> <li><a href="http://seq.kfish.org/~conrad/static/continuation-fest-2008/continuations-for-video.article.pdf">Article</a> [215KB PDF]</li> </ul> </p> <p>I'll be introducing the code for this over the next few months. Whereas in my presentation about <a href="http://blog.kfish.org/2008/03/bossa-2008-video-player-internals.html">video player internals</a> at BOSSA I outlined the problem space in designing a multimedia architecture, at Continuation Fest I tried to break it down into subproblems and considered useful data structures and programming techniques for dealing with them. </p> <p> I got a lot of great feedback, and I think I succeeded in my mission to introduce this problem space to some really smart people. Thanks particularly to <a href="http://www.cs.rutgers.edu/~ccshan">Chung-chieh Shan</a> for some insightful ideas about how to deal with existing stateful codec implementations. It was also very interesting to talk with <a href="http://www.ie.u-ryukyu.ac.jp/~kono/index-e.html">Shinji Kono</a> about <a href="http://sourceforge.jp/projects/cbc/">Continuation-based C (cBc)</a> (<a href="http://www.ie.u-ryukyu.ac.jp/~kono/tmp/cf08-kono.tgz">slides</a> [HTML tarball]), a C-like language capable of expressing continuations, non-local jumps, multiple function entry-points, and assorted other ways to shoot yourself in the foot. He suggested that it was designed for exactly the kind of thing I'm doing, and I'll be interested to try it out. It is implemented in a modifed GCC 4.x as an RTL code generator, so should now be (fairly) architecture-independent. </p> <p> Thanks to the organizers of Continuation Fest 2008 for putting together such a useful and interesting event. I look forward to implementing just some of the things I learned :-) </p> Mon, 14 Apr 2008 18:10:56 +0000 Conrad Parker John Goerzen (CosmicRay): Backup Software http://changelog.complete.org/posts/706-guid.html http://changelog.complete.org/posts/706-Backup-Software.html <p>I think most people reading my blog would agree that backups are extremely important. So much important data is on computers these days: family photos, emails, financial records. So I take backups seriously. </p><br /> <p> A little while back, I purchased two identical 400GB external hard disks. One is kept at home, and the other at a safe deposit box in a bank in a different town. Every week or two, I swap drives, so that neither one ever becomes too dated. This process is relatively inexpensive (safe deposit boxes big enough to hold the drive go for $25/year), and works well. </p><br /> <p> I have been using <a href="http://www.nongnu.org/rdiff-backup/">rdiff-backup</a> to make these backups for several years now. (Since at least 2004, when I submitted a patch to make it record all metadata on MacOS X). rdiff-backup is quite nice. It is designed for storage to a hard disk. It stores on the disk a current filesystem mirror along with some metadata files that include permissions information. History is achieved by storing compressed rdiff (rsync) deltas going backwards in time. So restoring "most recent" files is a simple copy plus application of metadata, and restoring older files means reversing history. rdiff-backup does both automatically. </p><br /> <p> This is a nice system and has served me well for quite some time. But it has its drawbacks. One is that you always have to have the current image, uncompressed, which uses up lots of space. Another is that you can't encrypt these backups with something like gpg for storage on a potentially untrusted hosting service (say, rsync.net). Also, when your backup disk fills up, it takes forever to figure out what to delete, since rdiff-backup --list-increment-sizes must stat tens of thousands of files. So I went looking for alternatives. </p><br /> <p> The author of rdiff-backup actually wrote one, called <a href="http://duplicity.nongnu.org/">duplicity</a>. Duplicity works by, essentially, storing a tarball full backup with its rdiff signature, then storing tarballs of rdiff deltas going forward in time. The reason rdiff-backup must have the full mirror is that it must generate rdiff deltas "backwards", which requires the full prior file available. Duplicity works around this. </p><br /> <p> However, the problem with duplicity is that if the full backup gets lost or corrupted, nothing newer than it can be restored. You must make new full backups periodically so that you can remove the old history. The other big problem with duplicity is that it doesn't grok hard links at all. That makes it unsuitable for backing up /sbin, /bin, /usr, and my /home, in which I frequently use hard links for preparing CD images, linking DVCS branches, etc. </p><br /> <p> So I went off searching out other projects and thinking about the problem myself. </p><br /> <p> One potential solution is to simply store tarballs and rdiff deltas going forward. That would require performing an entire full backup every day, which probably isn't a problem for me now, but I worry about the load that will place on my hard disks and the additional power it would consume to process all that data. </p><br /> <p> So what other projects are out there? Two caught my attention. The first is <a href="http://www.boxbackup.org/">Box Backup</a>. It is similar in concept to rdiff-backup. It has its own archive format, and otherwise operates on a similar principle to rdiff-backup. It stores the most recent data in its archive format, compressed, along with the signatures for it. Then it generates reverse deltas similar to rdiff-backup. It supports encryption out of the box, too. It sounded like a perfect solution. Then I realized it <a href="http://lists.warhead.org.uk/pipermail/boxbackup/2008-April/004338.html">doesn't store hard links</a>, device entries, etc., and has a design flaw that causes it to miss some changes to config files in /etc on Gentoo. That's a real bummer, because it sounded so nice otherwise. But I just can't trust my system to a program where I have to be careful not to use certain OS features because they won't be backed up right. </p><br /> <p> The other interesting one is <a href="http://dar.linux.free.fr/">dar</a>, the Disk ARchive tool, described by its author as the great grandson of tar -- and a pretty legitimate claim at that. Traditionally, if you are going to back up a Unix box, you have to choose between two not-quite-perfect options. You could use something like tar, which backs up all your permissions, special files, hard links, etc, but doesn't support random access. So to extract just one file, tar will read through the 5GB before it in the archive. Or you could use zip, which doesn't handle all the special stuff, but does support random access. Over the years, many backup systems have improved upon this in various ways. Bacula, for instance, is incredibly fast for tapes as it creates new tape "files" every so often and stores the precise tape location of each file in its database. </p><br /> <p> But none seem quite as nice as dar for disk backups. In addition to supporting all the special stuff out there, dar sports built-in compression and encryption. Unlike tar, compression is applied per-file, and encryption is applied per 10K block, which is really slick. This allows you to extract one file without having to decrypt and decompress the entire archive. dar also maintains a catalog which permits random access, has built-in support for splitting archives across removable media like CD-Rs, has a nice incremental backup feature, and sports a host of tools for tweaking archives -- removing files from them, changing compression schemes, etc. </p><br /> <p> But dar does not use binary deltas. I thought this would be quite space-inefficient, so I decided I would put it to the test, against a real-world scenario that would probably be pretty much a worst case scenario for it and a best case for rdiff-backup. </p><br /> <p> I track Debian sid and haven't updated my home box in quite some time. I have over 1GB of .debs downloaded which represent updates. Many of these updates are going to touch tons of files in /usr, though often making small changes, or even none at all. Sounds like rdiff-backup heaven, right? </p><br /> <p> I ran rdiff-backup to a clean area before applying any updates, and used dar to create a full backup file of the same data. Then I ran apt-get upgrade, and made incrementals with both rdiff-backup and dar. Finally I ran apt-get dist-upgrade, and did the same thing. So I have three backups with each system. </p><br /> <p> Let's look at how rdiff-backup did first. </p><br /> <p> According to rdiff-backup --list-increment-sizes, my /usr backup looks like this: </p><br /> <pre> Time Size Cumulative size ----------------------------------------------------------------------------- Sun Apr 13 18:37:56 2008 5.15 GB 5.15 GB (current mirror) Sun Apr 13 08:51:30 2008 405 MB 5.54 GB Sun Apr 13 03:08:07 2008 471 MB 6.00 GB </pre><br /> <p> So what we see here is that we're using 5.15GB for the mirror of the current state of /usr. The delta between the old state of /usr and the state after apt-get upgrade was 471MB, and the delta representing dist-upgrade was 405MB, for total disk consumption of 6GB. </p><br /> <p> But if I run du -s over the /usr storage area in rdiff, it says that 7.0GB was used. du -s --apparent-size shows 6.1GB. The difference is that all the tens of thousands of files each waste some space at the end of their blocks, and that adds up to an entire gigabyte. rdiff-backup effectively consumed 7.0GB of space. </p><br /> <p> Now, for dar: </p><br /> <pre> -rw-r--r-- 1 root root 2.3G Apr 12 22:47 usr-l00.1.dar -rw-r--r-- 1 root root 826M Apr 13 11:34 usr-l01.1.dar -rw-r--r-- 1 root root 411M Apr 13 19:05 usr-l02.1.dar </pre><br /> <p> This was using bzip2 compression, and backed up the exact same files and data that rdiff-backup did. The initial mirror was 2.3GB, much smaller than the 5.1GB that rdiff-backup consumes. The apt-get upgrade differential was 826MB compared to the 471MB in rdiff-backup -- not really a surprise. But the dist-upgrade differential -- still a pathologically bad case for dar, but less so -- was only 6MB larger than the 405MB rdiff-backup case. And the total actual disk consumption of dar was only 3.5GB -- half the 7.0GB rdiff-backup claimed! </p><br /> <p> I still expect that, over an extended time, rdiff-backup could chip away at dar's lead... or maybe not, if lots of small files change. </p><br /> <p> But this was a completely unexpected result. I am definitely going to give dar a closer look. </p><br /> <p> Also, before I started all this, I converted my external hard disk from ext3 to XFS because of ext3's terrible performance with rdiff-backup. </p> Mon, 14 Apr 2008 00:35:00 +0000 nospam@example.com (John Goerzen) "FP Lunch": The shortest beta-normalizer http://sneezy.cs.nott.ac.uk/fplunch/weblog/?p=95 http://sneezy.cs.nott.ac.uk/fplunch/weblog/?p=95 <p>I presented the shortest implementation of a normalizer for beta equality for untyped lambda calculus which is based on Normalisation by Evaluation. </p> <p>We start with the definition of lambda terms using de Bruijn levels:<br /> <img src="http://sneezy.cs.nott.ac.uk/fplunch/weblog/latexrender/pictures/6b2c3e05c957460fa08ae20bdfeb1753.png" title="&#10;\begin{code}&#10;data T = Var Int | Lam T | App T T deriving Show&#10;\end{code}&#10;" alt="&#10;\begin{code}&#10;data T = Var Int | Lam T | App T T deriving Show&#10;\end{code}&#10;" /></p> <p>The reason for using de Bruijn levels is that weakening comes for free. The other side of the coin is that one has to reindex bound variables when substituting. However, this is not an issue because we never do that.</p> <p>We define values by solving a negative domain equation:<br /> <img src="http://sneezy.cs.nott.ac.uk/fplunch/weblog/latexrender/pictures/8025c23de1c08f7c265a350cb637a688.png" title="&#10;\begin{code}&#10;data V = Fun (V -&gt; V)&#10;\end{code}&#10;" alt="&#10;\begin{code}&#10;data V = Fun (V -&gt; V)&#10;\end{code}&#10;" /></p> <p>It is easy to define application:<br /> <img src="http://sneezy.cs.nott.ac.uk/fplunch/weblog/latexrender/pictures/4c3e28203bdc418b9bb3483ee2e3f79d.png" title="&#10;\begin{code}&#10;app :: V -&gt; V -&gt; V&#10;app (Fun f) v = f v&#10;\end{code}&#10;" alt="&#10;\begin{code}&#10;app :: V -&gt; V -&gt; V&#10;app (Fun f) v = f v&#10;\end{code}&#10;" /></p> <p>We can evaluate lambda terms in the Value domain using a list of values as the environment:<br /> <img src="http://sneezy.cs.nott.ac.uk/fplunch/weblog/latexrender/pictures/2e59405e96890b4c97292547d66c775d.png" title="&#10;\begin{code}&#10;eval :: T -&gt; [V] -&gt; V&#10;eval (Var x) e = e !! x&#10;eval (Lam t) e = Fun (\ v -&gt; eval t (e++[v]))&#10;eval (App t u) e = app (eval t e) (eval u e)&#10;\end{code}&#10;" alt="&#10;\begin{code}&#10;eval :: T -&gt; [V] -&gt; V&#10;eval (Var x) e = e !! x&#10;eval (Lam t) e = Fun (\ v -&gt; eval t (e++[v]))&#10;eval (App t u) e = app (eval t e) (eval u e)&#10;\end{code}&#10;" /></p> <p>All we have to do now is to invert evaluation, i.e. to define a function<br /> <img src="http://sneezy.cs.nott.ac.uk/fplunch/weblog/latexrender/pictures/5742dab51f5ea1b7a4673a4a7ef21e5e.png" title="&#10;\begin{code}&#10;quote :: V -&gt; T&#10;\end{code}&#10;" alt="&#10;\begin{code}&#10;quote :: V -&gt; T&#10;\end{code}&#10;" /></p> <p>To be able to do this we have first to extend values with variables. Then to be able to extend app, we also have to represent stuck applications, or neutral values:<br /> <img src="http://sneezy.cs.nott.ac.uk/fplunch/weblog/latexrender/pictures/232e1b86c01bdb2e1330d22055bbace9.png" title="&#10;\begin{code}&#10;data V = Fun (V -&gt; V) | NVar Int | NApp V V&#10;&#10;app :: V -&gt; V -&gt; V&#10;app (Fun f) v = f v&#10;app n v = NApp n v&#10;\end{code}&#10;" alt="&#10;\begin{code}&#10;data V = Fun (V -&gt; V) | NVar Int | NApp V V&#10;&#10;app :: V -&gt; V -&gt; V&#10;app (Fun f) v = f v&#10;app n v = NApp n v&#10;\end{code}&#10;" /><br /> The code for eval remains unchanged. Now we can implement quote, though we need an extra parameter to keep track of the free variables:<br /> <img src="http://sneezy.cs.nott.ac.uk/fplunch/weblog/latexrender/pictures/4d9eaf056742b7d605ac1ad380045eee.png" title="&#10;\begin{code}&#10;quote :: Int -&gt; V -&gt; T&#10;quote x (Fun f) = Lam (quote (x+1) (f (NVar x)))&#10;quote x (NVar y) = Var y&#10;quote x (NApp n v) = App (quote x n) (quote x v)&#10;\end{code}&#10;" alt="&#10;\begin{code}&#10;quote :: Int -&gt; V -&gt; T&#10;quote x (Fun f) = Lam (quote (x+1) (f (NVar x)))&#10;quote x (NVar y) = Var y&#10;quote x (NApp n v) = App (quote x n) (quote x v)&#10;\end{code}&#10;" /><br /> Now nf just evaluates and quotes the result:<br /> <img src="http://sneezy.cs.nott.ac.uk/fplunch/weblog/latexrender/pictures/36c4c25141994c1cf3af73da66418a49.png" title="&#10;\begin{code}&#10;nf :: T -&gt; T&#10;nf t = quote 0 (eval t [])&#10;\end{code}&#10;" alt="&#10;\begin{code}&#10;nf :: T -&gt; T&#10;nf t = quote 0 (eval t [])&#10;\end{code}&#10;" /></p> <p>We discussed how to verify such a normalisation function - I suggested to use the partiality monad.</p> Sun, 13 Apr 2008 08:16:37 +0000 Thorsten Ulisses Costa: Lexical Analysis - Wikipedia http://caos.di.uminho.pt/~ulisses/blog/2008/04/13/lexical-analysis-wikipedia/ http://caos.di.uminho.pt/~ulisses/blog/2008/04/13/lexical-analysis-wikipedia/ <h2 id="58_intro_1">Intro</h2> <p>This year I started to learn processing languages. I started by regular expressions and past few days I began to study the <a href="http://flex.sourceforge.net/">Flex</a>, as with regular expressions we can&#8217;t create text filters.<br /> The first job I did was a kind of a dictionary. Getting a source of words and a faithful translation, add all the information in one document.</p> <p>The problem was finding a good document with many Portuguese words and their translations into English.</p> <p>With this post I want to teach what I learned from this work.</p> <h2 id="58_wikipedia-xml-struct_1">Wikipedia XML structure</h2> <p>I started by picking up the last dump of Wikipedia-PT and &#8220;decipher&#8221; the XML where it is stored. The structure is something like that:</p> <div class="wp_syntax"><div class="code"><pre class="xml"><span><span>&lt;page<span>&gt;</span></span></span> ... <span><span>&lt;/page<span>&gt;</span></span></span> <span><span>&lt;page<span>&gt;</span></span></span> ... <span><span>&lt;/page<span>&gt;</span></span></span></pre></div></div> <p>And each <strong>page</strong> tag, expanded, have this structure:</p> <div class="wp_syntax"><div class="code"><pre class="xml"><span><span>&lt;page<span>&gt;</span></span></span> <span><span>&lt;title<span>&gt;</span></span></span>TITLE<span><span>&lt;/title<span>&gt;</span></span></span> <span><span>&lt;id<span>&gt;</span></span></span>PAGE_ID_NUMBER<span><span>&lt;/id<span>&gt;</span></span></span> <span><span>&lt;revision<span>&gt;</span></span></span> <span><span>&lt;id<span>&gt;</span></span></span>ID_REVISION_NUMBER<span><span>&lt;/id<span>&gt;</span></span></span> <span><span>&lt;timestamp<span>&gt;</span></span></span>TIMESTAMP<span><span>&lt;/timestamp<span>&gt;</span></span></span> <span><span>&lt;contributor<span>&gt;</span></span></span> <span><span>&lt;username<span>&gt;</span></span></span>USERNAME<span><span>&lt;/username<span>&gt;</span></span></span> <span><span>&lt;id<span>&gt;</span></span></span>ID_CONTRIBUTOR<span><span>&lt;/id<span>&gt;</span></span></span> <span><span>&lt;/contributor<span>&gt;</span></span></span> <span><span>&lt;comment<span>&gt;</span></span></span>COMMENT<span><span>&lt;/comment<span>&gt;</span></span></span> <span><span>&lt;text</span> <span>xml:space</span>=<span>&quot;preserve&quot;</span><span>&gt;</span></span>WIKIPEDIA_ENTRY<span><span>&lt;/text<span>&gt;</span></span></span> <span><span>&lt;/revision<span>&gt;</span></span></span> <span><span>&lt;/page<span>&gt;</span></span></span></pre></div></div> <p>So, here we have the variables: TITLE, PAGE_ID_NUMBER, ID_REVISION_NUMBER, TIMESTAMP, USERNAME, ID_CONTRIBUTOR, COMMENT and WIKIPEDIA_ENTRY.</p> <p>TITLE is the Portuguese word, because the Wikipedia I&#8217;ve downloaded is in Portuguese.<br /> But so far, no English word.</p> <p>Lets expand WIKIPEDIA_ENTRY:</p> <div class="wp_syntax"><div class="code"><pre class="xml"><span><span>&lt;text</span> <span>xml:space</span>=<span>&quot;preserve&quot;</span><span>&gt;</span></span> ... [[categoria:CATEGORY]] ... [[en:ENGLISH]] ... <span><span>&lt;/text<span>&gt;</span></span></span></pre></div></div> <p>Here we have the ENGLISH variable that is the corresponding word in English to TITLE. I also want the CATEGORY variable that indicates from what category this entry belong.</p> <p>As some entries in the Wikipedia have multiple categories lines I am also interested in keep them all.<br /> I want that the output of my program became something like that:</p> <pre> PT - TITLE1 EN - ENGLISH1 Categoria - CATEGORY11 CATEGORY12 ... CATEGORY1j ... PT - TITLEi EN - ENGLISHi Categoria - CATEGORYi1 CATEGORYi2 ... CATEGORYij ...</pre> <p>Some entries in the Portuguese Wikipedia do not have the English correspondent version so I will not want those entries.</p> <h2 id="58_lexing_1"><em>Lexing</em></h2> <p>A Lex file have this aspect:</p> <pre> definitions %% rules %% user code </pre> <p>Let&#8217;s focus in <strong>rules</strong> part:</p> <p>Rules have the following structure;</p> <pre> %% REGEX code REGEX code %% </pre> <p>I know Regular Expressions (REGEX) now, so let&#8217;s start to build that thing!</p> <p>I got me to realize that the part of the TITLE may have not only the entries name, also has the Wikipedia contributors pages, among other things that do not interest me to save.<br /> I start to do a list of all those pages:<br /> {Wikipedia,Usuário,Discussão,Ajuda,Anexo,MediaWiki,Categoria}</p> <p>So, I have to, somehow, throw away all the pages with the following structure:</p> <div class="wp_syntax"><div class="code"><pre class="xml"> <span><span>&lt;page<span>&gt;</span></span></span> <span><span>&lt;title<span>&gt;</span></span></span>[&quot;Wikipedia&quot;&quot;Usuario&quot;&quot;Discussao&quot;&quot;Ajuda&quot;&quot;Anexo&quot;&quot;MediaWiki&quot;&quot;Categoria&quot;]:TITLE<span><span>&lt;/title<span>&gt;</span></span></span> <span><span>&lt;id<span>&gt;</span></span></span>ID_PAGE<span><span>&lt;/id<span>&gt;</span></span></span> <span><span>&lt;revision<span>&gt;</span></span></span> <span><span>&lt;id<span>&gt;</span></span></span>ID_REVISION<span><span>&lt;/id<span>&gt;</span></span></span> <span><span>&lt;timestamp<span>&gt;</span></span></span>TIMESTAMP<span><span>&lt;/timestamp<span>&gt;</span></span></span> <span><span>&lt;contributor<span>&gt;</span></span></span> <span><span>&lt;username<span>&gt;</span></span></span>USERNAME<span><span>&lt;/username<span>&gt;</span></span></span> <span><span>&lt;id<span>&gt;</span></span></span>ID_CONTRIBUTOR<span><span>&lt;/id<span>&gt;</span></span></span> <span><span>&lt;/contributor<span>&gt;</span></span></span> <span><span>&lt;comment<span>&gt;</span></span></span>COMMENT<span><span>&lt;/comment<span>&gt;</span></span></span> <span><span>&lt;text</span> <span>xml:space</span>=<span>&quot;preserve&quot;</span><span>&gt;</span></span>ENTRY<span><span>&lt;/text<span>&gt;</span></span></span> <span><span>&lt;/revision<span>&gt;</span></span></span> <span><span>&lt;/page<span>&gt;</span></span></span></pre></div></div> <p>After a dozen of lines I start to understand that I have to some how &#8220;explain&#8221; Lex the structure of the Wikipedia XML file. That way will be easier.</p> <p>I start to read the <a href="http://flex.sourceforge.net/manual/">Flex manual</a> and I find the <a href="http://flex.sourceforge.net/manual/Start-Conditions.html#Start-Conditions">Start Conditions</a>, a very clever way to treat a block of information.</p> <p>Languages like C, HTML and XML are structured by blocks, so Start Conditions may be the way to easily get the information from those.</p> <h2 id="58_start-conditions_1">Start Conditions</h2> <p>If you have a block of code that have this aspect:</p> <div class="wp_syntax"><div class="code"><pre class="xml"><span><span>&lt;title<span>&gt;</span></span></span>TITLE<span><span>&lt;/title<span>&gt;</span></span></span></pre></div></div> <p>Our block start with &#8220;&#8221; string.</p> <p>So in Lex, we must use Start Conditions and produce the following code:</p> <div class="wp_syntax"><div class="code"><pre class="c">%x title_sc anything .|<span>&#91;</span>\n\t\r<span>&#93;</span> %% <span>&quot;&lt;title&gt;&quot;</span> BEGIN<span>&#40;</span>title_sc<span>&#41;</span>; &lt;title_sc&gt;<span>&#91;</span>^&gt;<span>&#93;</span>+ <span>&#123;</span><span>printf</span><span>&#40;</span><span>&quot;title=%s<span>\n</span>&quot;</span>,yytext<span>&#41;</span>;<span>&#125;</span> &lt;title_sc&gt;<span>&quot;&lt;/title&gt;&quot;</span> BEGIN<span>&#40;</span>INITIAL<span>&#41;</span>; <span>&#123;</span>anything<span>&#125;</span> <span>&#123;</span>;<span>&#125;</span> <span>/* do nothing*/</span> %% main<span>&#40;</span><span>&#41;</span> <span>&#123;</span> yylex<span>&#40;</span><span>&#41;</span>; <span>&#125;</span></pre></div></div> <p>The <code>%x title_sc</code> declaration is to declare a state exclusive, that means; while we are inside <code>code_sc</code> Flex won&#8217;t look rules outside of that, until <code>BEGIN(INITAIL)</code>.</p> <p>In <strong>definitions</strong> part we can declare variables <code>anything .|[\n\t\r]</code> to use in <strong>rules</strong> part as <code>{anything}</code>.</p> <p>The <code>BEGIN(title_sc)</code> statement makes Lex jump to the first line of rule and it start to match the rules that finds there.</p> <p>We can rewrite the above code like that:</p> <div class="wp_syntax"><div class="code"><pre class="c">%x title_sc anything .|<span>&#91;</span>\n\t\r<span>&#93;</span> %% <span>&quot;&lt;title&gt;&quot;</span> BEGIN<span>&#40;</span>title_sc<span>&#41;</span>; &lt;title_sc&gt;<span>&#123;</span> <span>&#91;</span>^&gt;<span>&#93;</span>+ <span>&#123;</span><span>printf</span><span>&#40;</span><span>&quot;title=%s<span>\n</span>&quot;</span>,yytext<span>&#41;</span>;<span>&#125;</span> <span>&quot;&lt;/title&gt;&quot;</span> BEGIN<span>&#40;</span>INITIAL<span>&#41;</span>; <span>&#125;</span> <span>&#123;</span>anything<span>&#125;</span> <span>&#123;</span>;<span>&#125;</span> <span>/* do nothing*/</span> %% main<span>&#40;</span><span>&#41;</span> <span>&#123;</span> yylex<span>&#40;</span><span>&#41;</span>; <span>&#125;</span></pre></div></div> <p>When Lex find <code>BEGIN(INITIAL)</code> statement it jumps to the first <code>BEGIN(XXX)</code>, so we can never be able to use block&#8217;s inside other block&#8217;s (like XML does).</p> <p>Of course that&#8217;s not true.</p> <h2 id="58_start-conditions-ins_1">Start Conditions inside Start Conditions</h2> <p>Lex have a brilliant way to deal with that. It uses Stack&#8217;s to store the state were we are!</p> <p>The idea is something like that, imagine a mathematical expression:</p> <p><img src="http://caos.di.uminho.pt/~ulisses/blog/wp-content/latex/87e/87e6b04da406d2885e69188f49514a2b-FFFFFF000000.png" alt="(1+2)-(3*(4/5))" title="(1+2)-(3*(4/5))" class="latex" /></p> <p>I can say:</p> <ul> <li>2 or [1,2]</li> <li>3 or [2,1]</li> <li>5 or [2,2,2]</li> <li>and so on&#8230;</li> </ul> <p>That&#8217;s all about keeping the path, our actual position in the tree.</p> <p>So, now we replace the <code>BEGIN(state)</code> to <code>yy_push_state(state)</code>, and to go to the previously block I say <code>yy_pop_state()</code>.</p> <p>With that now I can read structures like that one:</p> <div class="wp_syntax"><div class="code"><pre class="xml"> <span><span>&lt;page<span>&gt;</span></span></span> <span><span>&lt;title<span>&gt;</span></span></span>TITLE<span><span>&lt;/title<span>&gt;</span></span></span> ... <span><span>&lt;text</span> <span>xml:space</span>=<span>&quot;preserve&quot;</span><span>&gt;</span></span> ... [[Categoria:CATEGORY]] ... [[en:ENGLISH]] ... <span><span>&lt;/text<span>&gt;</span></span></span> <span><span>&lt;/page<span>&gt;</span></span></span></pre></div></div> <p>And to do so, I write that Lex code:</p> <div class="wp_syntax"><div class="code"><pre class="c">%x PAGE TITLE TEXT CATEGORIA EN %option stack anything .|<span>&#91;</span>\n\t\r<span>&#93;</span> notPage <span>&#91;</span><span>&quot;Wikipedia&quot;</span><span>&quot;Usuário&quot;</span><span>&quot;Discussão&quot;</span><span>&quot;Ajuda&quot;</span><span>&quot;Anexo&quot;</span><span>&quot;MediaWiki&quot;</span><span>&quot;Categoria&quot;</span><span>&#93;</span> %% <span>&quot;&lt;page&gt;&quot;</span> yy_push_state<span>&#40;</span>PAGE<span>&#41;</span>; &lt;PAGE&gt;<span>&#123;</span> <span>&quot;&lt;title&gt;&quot;</span><span>&#123;</span>notPagina<span>&#125;</span> yy_pop_state<span>&#40;</span><span>&#41;</span>; <span>// not a valid page</span> <span>&quot;&lt;title&gt;&quot;</span> yy_push_state<span>&#40;</span>TITLE<span>&#41;</span>; <span>&quot;&lt;text&quot;</span><span>&#91;</span>^&gt;<span>&#93;</span>+<span>&quot;&gt;&quot;</span> yy_push_state<span>&#40;</span>TEXT<span>&#41;</span>; <span>&quot;&lt;/page&gt;&quot;</span> yy_pop_state<span>&#40;</span><span>&#41;</span>; <span>&#123;</span>anything<span>&#125;</span> <span>/* do nothing */</span> <span>&#125;</span> &nbsp; &lt;TEXT&gt;<span>&#123;</span> <span>&quot;[[&quot;</span><span>&#91;</span>cC<span>&#93;</span><span>&quot;ategoria:&quot;</span> yy_push_state<span>&#40;</span>CATEGORIA<span>&#41;</span>; <span>&quot;[[en:&quot;</span> yy_push_state<span>&#40;</span>EN<span>&#41;</span>; <span>&quot;&lt;/text&gt;&quot;</span> yy_pop_state<span>&#40;</span><span>&#41;</span>; <span>&#123;</span>anything<span>&#125;</span> <span>/* do nothing */</span> <span>&#125;</span> &nbsp; &lt;TITLE&gt;<span>&#123;</span> <span>&#91;</span>^&lt;<span>&#93;</span>+ <span>&#123;</span> i=<span>0</span>; imprime<span>&#40;</span>cat, pt, en<span>&#41;</span>; limpa<span>&#40;</span>cat<span>&#41;</span>; pt=<span>NULL</span>; en=<span>NULL</span>; pt=strdup<span>&#40;</span>yytext<span>&#41;</span>; <span>&#125;</span> <span>&quot;&lt;/title&gt;&quot;</span> yy_pop_state<span>&#40;</span><span>&#41;</span>; <span>&#123;</span>anything<span>&#125;</span> <span>/* do nothing */</span> <span>&#125;</span> &nbsp; &lt;EN&gt;<span>&#123;</span> <span>&#91;</span>^\<span>&#93;</span><span>&#93;</span>+ en=strdup<span>&#40;</span>yytext<span>&#41;</span>; <span>&#91;</span>\<span>&#93;</span><span>&#93;</span>+ yy_pop_state<span>&#40;</span><span>&#41;</span>; <span>&quot;]&quot;</span>\n<span>&#123;</span>anything<span>&#125;</span> <span>/* do nothing */</span> <span>&#125;</span> &nbsp; &lt;CATEGORIA&gt;<span>&#123;</span> <span>&#91;</span> \<span>#\!\*\|]+ yy_pop_state();</span> <span>&#91;</span>^\<span>&#93;</span>\|\n<span>&#93;</span>+ <span>&#123;</span> cat<span>&#91;</span>i<span>&#93;</span>=strdup<span>&#40;</span>yytext<span>&#41;</span>; i++; <span>&#125;</span> <span>&#91;</span>\<span>&#93;</span><span>&#93;</span>+ yy_pop_state<span>&#40;</span><span>&#41;</span>; <span>&quot;]&quot;</span>\n<span>&#123;</span>anything<span>&#125;</span> <span>/* do nothing */</span> <span>&#125;</span> <span>&#123;</span>anything<span>&#125;</span> <span>/* do nothing */</span> %% <span>int</span> main<span>&#40;</span><span>&#41;</span> <span>&#123;</span> yylex<span>&#40;</span><span>&#41;</span>; <span>return</span> <span>0</span>; <span>&#125;</span></pre></div></div> <p>As you can see we are all the time matching a rule that makes lex jump to another state until one terminal rule (in this case variables affectations) or until pop.</p> <p>To see all the code <a href="http://caos.di.uminho.pt/~ulisses/code/pl/trab1/trab.lex">go here</a>(.lex).</p> <p>If you understand Portuguese and feel interested in more information you can <a href="http://caos.di.uminho.pt/~ulisses/code/pl/trab1/rel/online/index.html">go here</a>(.html).</p> <h3 id="58_references_1">References</h3> <p><a href="http://flex.sourceforge.net/">flex: The Fast Lexical Analyzer</a></p> <span class="post2pdf_span"><a href="http://caos.di.uminho.pt/~ulisses/blog/wp-content/plugins/post2pdf/generate.php?post=58" rel="nofollow"><img src="http://caos.di.uminho.pt/~ulisses/blog/wp-content/plugins/post2pdf/icon/pdf.png" width="16px" height="16px" />convert this post to pdf.</a></span> Sun, 13 Apr 2008 06:50:50 +0000 ulisses David R. MacIver: Blog move tag:blogger.com,1999:blog-2789077809400086800.post-2114636340936909669 http://unenterprise.blogspot.com/2008/04/blog-move.html <p>Well, I've been meaning to set up a site of my own for ages. I finally got around to it. I can now be found at <a href="http://www.drmaciver.com/">http://www.drmaciver.com</a>/ </p> <p>This blog will be moving to the "programming" category there. I'll update the various aggregator sites that have it on to point there instead.</p> Sun, 13 Apr 2008 06:50:09 +0000 David R. MacIver Nick Mudge: A New Operating System http://nickmudge.info?post=91 http://nickmudge.info?post=91 <p> I've decided to attempt to write an operating system. </p> <p> Basically I want to be able to understand a whole system as much as I can and I want to make it as easy as possible for others to understand it as well. This is the main reason I want to write an operating system. </p> <p> The reason I'm not just studying an existing operating system like <a href="http://www.kernel.org/">Linux</a> or <a href="http://www.minix3.org/">Minix</a> or <a href="http://mikeos.berlios.de/">MikeOS</a> is because I think I will understand a system better if I write it myself and it seems more fun to me. I am of course studying those operating systems to learn to write my own. </p> <p> <strong>Goals and Guides</strong> </p> <p> The design of the system is pretty loose so far but I do have some general guides and goals for the system. </p> <ol> <li> Designed to explicitly take advantage of 64 bit processors. </li> <li> Designed to explicitly take advantage of multi-core or multi-cpu processors. </li> <li> Designed to take advantage of and provide virtualization i.e. operating system virtualization etc. </li> <li> It's a server operating system. Not designed to be a desktop operating system or anything else. </li> <li> It needs to be designed so that the system can be understood as easily as possible. It must be simple. The code base must be as small as possible. </li> <li> It must be good. </li> </ol> <p> I'm not sure if the kernel will be a microkernel or monolithic or something else. </p> Sun, 13 Apr 2008 00:00:00 +0000 Dan Piponi (sigfpe): Negative Probabilities tag:blogger.com,1999:blog-11295132.post-5498552306554187806 http://sigfpe.blogspot.com/2008/04/negative-probabilities.html I'm always interested in new ways to present the ideas of quantum mechanics to a wider audience. Unfortunately the conventional description of quantum mechanics requires knowledge of complex numbers and vector spaces making it difficult to avoid falling back on misleading analogies and metaphors. But there's another approach to quantum mechanics, due to Feynman, that I have never seen mentioned in the popular science literature. It assumes only basic knowledge of <a href="http://simple.wikipedia.org/wiki/Probability">probability theory</a> and <a href="http://simple.wikipedia.org/wiki/Negative_number">negative numbers</a> to get a foot on the ladder.<br /><br />We start with tweaking probability theory a bit. One of the axioms of probability theory says that all probabilities must lie in the range zero to one. However, we could imagine relaxing this rule even though on the face of it it seems meaningless. For example, suppose we have a coin that has a 3/2 chance of landing heads and a -1/2 chance of landing tails. We can still reason that the chance of getting two heads in a row is 3/2&times;3/2=9/4 by the usual multiplication rule. But obviously no situation like this could ever arise in the real world because after tossing such a coin 10 times we'd expect to see -5 tails on average.<br /><br />But what if we could contrive a system with some kind of internal state governed by negative probabilities even though we couldn't observe it directly? So consider this case: a machine produces boxes with (ordererd) pairs of bits in them, each bit viewable through its own door. Let's suppose the probability of each possible combination of two bits is given by the following table:<br /><table><br /><tr><td>First bit</td><td>Second bit</td><td>Probability</td></tr><br /><tr><td>0</td><td>0</td><td>-1/2</td></tr><br /><tr><td>0</td><td>1</td><td>1/2</td></tr><br /><tr><td>1</td><td>0</td><td>1/2</td></tr><br /><tr><td>1</td><td>1</td><td>1/2</td></tr><br /></table><br />Obviously if we were able to look through both doors we'd end up with the meaningless prediction that we'd expect to see 00 a negative number of times. But suppose that things are arranged so that we can only look through one door. Maybe the boxes self-destruct if one or other door is opened but you still get enough time to see what was behind the door. Now what happens?<br /><br />If you look through the first door the probability of seeing 1 is P(10)+P(11)=1. We get the same result if we look through the second door. We only get probabilities in the range zero to one. As long as we're restricted to one door we get meaningful results.<br /><br />If we were to perform this experiment repeatedly with different runs of the machine, each time picking a random door to look through, we'd eventually become very confident that every box contained 11. After all, if we freely choose which door to look through, and we always see 1, there's no place 0's could be 'hiding'.<br /><br />But now suppose a new feature is added to the box that allows us to compare the two bits to see if they are equal. It reveals nothing about what the bits are, just their state of equality. And of course, after telling us, it self-destructs. We now find that the probability of the two bits being different is P(01)+P(10)=1. So if we randomly chose one of the three possible observations each time the machine produced the box we'd quickly run into the strange situation that the two bits both appear to be 1, and yet are different. But note that although the situation is weird, it's not meaningless. As long as we never get to see both bits at the same time we never directly observe a paradox. If we met such boxes in the real world we'd be forced to conclude that maybe the boxes knew which bit you were going to look at and changed value as a result, or that maybe you didn't have the free will to choose door that you thought you had, or maybe, even more radically, you'd conclude that the bits generated by the machine were described by negative probabilities.<br /><br />That's all very well, but obviously the world doesn't really work like this and we never see boxes like this. Except that actually it does! The <a href="http://en.wikipedia.org/wiki/EPR_Paradox">EPR</a> experiment has many similarities to the scenario I described above. The numbers aren't quite the same, and we're not talking about bits in boxes, but we do end up with a scenario involving observations of bits that simply don't add up. If we do try to explain what's going on using probability theory, we either conclude there's something weird about our assumptions of locality or causality or we end up assigning negative probabilities to the internal states of our systems. In fact, you can read the details in an article by <a href="http://www.drchinese.com/David/Bell_Theorem_Negative_Probabilities.htm">David Schneider</a>. Being forced to conclude that we have negative probabilities in a physical system is usually taken as a sign that we have a contradiction. In the case of <a href="http://en.wikipedia.org/wiki/Bell's_theorem">Bell's theorem</a> it shows that we can't interpret what we see in terms of probability theory and hence that the weirdness of quantum mechanics can't be explained in terms of some hidden random variable that we can't see. QM simply doesn't obey the rules you'd expect of hidden variables we can't see.<br /><br />But in a paper called <a href="http://en.wikipedia.org/wiki/Negative_probability">Negative Probability</a>, Feynman tried taking the idea of negative probabilities seriously. He showed that you could reformulate quantum mechanics completely in terms of them so that you no longer needed to think in terms of the complex number valued 'amplitudes' that physicists normally use. This means the above isn't just an analogy, it's actually a formal system within which you can do QM, although I haven't touched on the bit that refers to the dynamics of quantum systems. So if you can get your head around the ideas I've talked about above you're well on your way to understanding some reasons why quantum mechanics seems so paradoxical.<br /><br />At this point you may be wondering how nature contrives to hide these negative probabilities from direct observation. Her trick is that making one kind of observation disturbs up the state of what you've observed so that you can't make the other kind of observation on a pristine state. You have to pick one kind of observation or the other. Electrons and photons really are a lot like the boxes I just described.<br /><br />So why don't physicists use this formulation? Despite the fact that negative numbers seem simpler to most people than imaginary numbers, the negative number formulation of QM is much more complicated. What's more, because it makes exactly the same predictions as regular QM there's no compelling reason to switch to it. And anyway, it's not as if directly observing negative probabilities is any more intuitive or meaningful than imaginary ones. Once you've introduced negative ones, you might as well go all the way!<br /><br />This all ties in with what I said a <a href="http://sigfpe.blogspot.com/2006/05/quantum-mechanics-and-probability.html">while back</a>. The important thing about QM is that having two ways to do something can make it <em>less</em> likely to happen, not more.<br /><br />For a different perspective <a href="http://www.lns.cornell.edu/spr/2002-03/msg0040195.html">this</a> is an interesting comment.<br /><br />Footnote: We can embed QM in negative probability theory. But can we do the converse? Can every negative probability distribution be physically realised in a quantum system? I've a hunch the answer is obvious but I'm too stupid to see it. Sat, 12 Apr 2008 17:02:00 +0000 sigfpe Shin-Cheng Mu: Terminating Unfolds (2) http://www.iis.sinica.edu.tw/~scm/?p=49 http://www.iis.sinica.edu.tw/~scm/2008/terminating-unfolds-2/ <p>After seeing <a href="http://www.iis.sinica.edu.tw/~scm/2008/terminating-unfolds-1/">our code</a>, <a href="http://www.cs.nott.ac.uk/~nad/">Nils Anders Danielsson</a> suggested two improvements. Firstly, to wrap the bound in the seed. The terminating <code>unfoldr↓</code> would thus have a simpler type as well as a simpler implemenation:</p> <blockquote><p><code>unfoldr↓ : {a : Set}(b : ℕ -> Set){n : ℕ} -><br /> &nbsp;&nbsp;&nbsp;&nbsp;(f : forall {i} -> b (suc i) -> ⊤ ⊎ (a × b i)) -><br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b n -> [ a ]<br /> unfoldr↓ b {zero} f y = []<br /> unfoldr↓ b {suc i} f y with f y<br /> ... | inj₁ _ = []<br /> ... | inj₂ (x , y') = unfoldr↓ b {i} f y'</code></p></blockquote> <p>The definition passes the termination check, apparently, because <code>unfoldr↓</code> is defined inductively on <code>n</code>.</p> <p>To generate a descending a list, one may invent a datatype <code>Wrap</code> that wraps the seed, whose bound is simply the seed itself:</p> <blockquote><p><code>data Wrap : ℕ -> Set where<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;W : (n : ℕ) -> Wrap n</code></p></blockquote> <p>The list may then be generated by an unfold:</p> <blockquote><p><code>dec : forall {i} -> Wrap (suc i) -> ⊤ ⊎ (ℕ × Wrap i)<br /> dec {i} (W .(suc i)) = inj₂ (i , W i)</code></p> <p>down↓ : ℕ -> [ ℕ ]<br /> down↓ n = unfoldr↓ Wrap dec (W n)<br /> </p></blockquote> <p>Of course, this would defeat my original goal of reusing the non-dependently typed <code>dec</code>, which is probably a bad idea anyway.</p> <p>To show that the bound need not be exact, let&#8217;s try to generate a descending list whose elements are decremented by 2 in each step. We may use this slightly generalised wrapper:</p> <blockquote><p><code>data Wrap2 : ℕ -> Set where<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;W2 : (x : ℕ) -> (bnd : ℕ) -> x ≤ bnd -> Wrap2 bnd</code></p></blockquote> <p>and a function <code>dec2</code> that decrements a <code>suc i</code>-bounded seed by 2 but showing that the new seed is bounded by <code>i</code>:</p> <blockquote><p><code>dec2 : forall {i} -> Wrap2 (suc i) -> ⊤ ⊎ (ℕ × WAlt i)<br /> dec2 {i} (W2 0 .(1 + i) _) = inj₁ tt<br /> dec2 {i} (W2 1 .(1 + i) _) = inj₁ tt<br /> dec2 {i} (W2 (suc (suc n)) .(1 + i) 2+n≤1+i) =<br /> &nbsp;&nbsp;&nbsp; inj₂ (n , W2 n i (suc-≤-weaken-l (≤-pred 2+n≤1+i)))<br /> </code></p></blockquote> <p>The list can then be unfolded by:</p> <blockquote><p><code>down↓2 : ℕ -> [ ℕ ]<br /> down↓2 n = unfoldr↓ Wrap2 dec2 (W2 n n ≤-refl)</code></p></blockquote> <p>where <code>suc-≤-weaken-l</code> is a proof of <code>forall {m n} -> suc m ≤ n -> m ≤ n</code>.</p> <h3>Unfolding a Tree</h3> <p>It is an easy exercise to come up with a tree version of the unfold above:</p> <blockquote><p><code>unfoldT↓ : {a : Set}(b : ℕ -> Set){n : ℕ} -><br /> &nbsp;&nbsp;(f : forall {i} -> b (suc i) -> a ⊎ (b i × b i)) -><br /> &nbsp;&nbsp;&nbsp;&nbsp;B n -> Tree a<br /> unfoldT↓ b {0} f y = Nul<br /> unfoldT↓ b {suc i} f y with f y<br /> ... | inj₁ x = Tip x<br /> ... | inj₂ (y₁ , y₂) =<br /> &nbsp;&nbsp;&nbsp;&nbsp;Bin (unfoldT↓ b {i} f y₁) (unfoldT↓ b {i} f y₂)</code></p></blockquote> <p>To deal with the second task of building a roughly balanced binary tree, one may try this wrapper:</p> <blockquote><p><code>data Split (a : Set): ℕ -> Set where<br /> &nbsp;&nbsp;Sp : (xs : [ a ]) -> (bnd : ℕ) -><br /> &nbsp;&nbsp;&nbsp;&nbsp;length xs bnd -> Split a bnd</code></p></blockquote> <p>and try to code up a generator function <code>split↓</code> having this type:</p> <blockquote><p><code>split↓ : forall {a i} -> Split a (suc i) -> a ⊎ (Split a i × Split a i)<br /> </code></p></blockquote> <p>The function <code>split↓</code> I eventually come up with, however, is much more complicated than I had wished. Even worse, it is now <code>split↓</code> that fails to pass the termination check.</p> <p><a href="http://www.cs.chalmers.se/~ulfn/">Ulf Norell</a> suggested some possible fixes. The difficulties, however, is probably a hint that there is something wrong in my approach in the first place. Rather than trying to fix it, Nils showed me how he would tackle the problem from the beginning.</p> <h3>Using Well-Founded Recursion</h3> <p>Nils showed me how to define <code>unfoldT↓</code> using <em>well-founded recursion</em>. For a simplified explanation, the <a href="http://appserv.cs.chalmers.se/users/ulfn/wiki/agda.php?n=Libraries.StandardLibrary">Standard Library</a> provides a function <code>-rec</code> having type (after normalisation):</p> <blockquote><p><code>-rec : (P : ℕ -> Set) -><br /> &nbsp;&nbsp;(f : (i : ℕ) -> (rec : (j : ℕ) -> j ′ i -> P j) -> P i) -><br /> &nbsp;&nbsp;&nbsp;&nbsp;(x : ℕ) -> P x </code></p></blockquote> <p>With <code>-rec</code> one can define functions on natural numbers by recursion, provided that the argument strictly decreases in each recursive call. <code>P</code> is the type of the result, parameterised by the input. The function <code>f</code> is a template that specifies the body of the recursion which, given <code>i</code>, computes some result of type <code>P i</code>. The functional argument <code>rec</code> is supposed to be the recursive call. The constraint <code>j ′ i</code>, however, guarantees that it accepts only inputs strictly smaller than <code>i</code> (the ordering <code>′ </code> is a variation of <code>&lt;</code> that is more suitable for this purpose). One may perhaps think of <code>-rec</code> as a fixed-point operator computing the fixed-point of <code>f</code>, only that <code>f</code> has to take <code>i</code> before <code>rec</code> because the latter depends on the former.</p> <p>Let us try to define an unfold on trees using <code>-rec</code>. The &#8220;base-functor&#8221; of the datatype <code>Tree⁺ a</code> is <code>F b = a ⊎ b × b</code>. One of the lessons we have learnt is that it would be more convenient for the generating function to return the bound information. We could use a type like this:</p> <blockquote><p><code>F a b k = a ⊎ ∃ (ℕ × ℕ) (\(i , j) -> b i × b j × i ′ k × j ′ k)</code> </p></blockquote> <p>But it is perhaps more reader-friendly to define the base functor as a datatype:</p> <blockquote><p><code>data Tree⁺F (a : Set) (b : ℕ -> Set) : ℕ -> Set where<br /> &nbsp;&nbsp;tip : forall {k} -> a -> Tree⁺F a b k<br /> &nbsp;&nbsp;bin : forall {i j k} -><br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b i -> b j -> i ′ k -> j ′ k -> Tree⁺F a b k<br /> </code></p></blockquote> <p>The generating function for the unfold should thus have type <code>forall {a b i} -> b i -> Tree⁺F a b i</code>.</p> <p>The function <code>unfoldT↓F</code> is the template for <code>unfoldT↓</code>:</p> <blockquote><p><code>unfoldT↓F : {a : Set} {b : ℕ -> Set} -><br /> &nbsp;&nbsp;(f : forall {i} -> b i -> Tree⁺F a b i) -><br /> &nbsp;&nbsp;&nbsp;(n : ℕ) -> ((i : ℕ) -> i ′ n -> b i -> Tree⁺ a) -><br /> &nbsp;&nbsp;&nbsp;&nbsp;b n -> Tree⁺ a<br /> unfoldT↓F f n rec y with f y<br /> &#8230; | tip a = Tip⁺ a<br /> &#8230; | bin {i} {j} y₁ y₂ i&lt;n j&lt;n =<br /> &nbsp;&nbsp;&nbsp;&nbsp;Bin⁺ (rec i i&lt;n y₁) (rec j j&lt;n y₂)<br /> </code></p></blockquote> <p>Now <code>unfoldT↓</code> can be defined by:</p> <blockquote><p><code>unfoldT↓ : {a : Set} {b : ℕ -> Set} -><br /> &nbsp;&nbsp;(f : forall {i} -> b i -> Tree⁺F a b i) -><br /> &nbsp;&nbsp;&nbsp;&nbsp;forall {n} -> b n -> Tree⁺ a<br /> unfoldT↓ {a}{b} f {n} y =<br /> &nbsp;&nbsp;&nbsp;&nbsp;-rec (\n -> b n -> Tree⁺ a) (unfoldT↓F f) n y </code></p></blockquote> <p>All the definition above makes no recursive calls. All the tricks getting us through the termination check are hidden in <code>-rec</code>. How is it defined?</p> <h3>Well-Founded Recursion Defined on ′</h3> <p>Currently, well-founded recursion are defined in <code>Logic.Induction</code> and its sub-modules. They are very interesting modules to study. The definitions there, however, are very abstract. To understand how <code>-rec</code> works, let&#8217;s try to implement our own.</p> <p>This is the definition of <code>′</code> from <code>Data.Nat</code>:</p> <blockquote><p><code>data _≤′_ : Rel ℕ where<br /> ≤′-refl : forall {n} -> n ≤′ n<br /> ≤′-step : forall {m n} -> m ≤′ n -> m ≤′ suc n</code></p> <p>_′_ : Rel ℕ<br /> m ′ n = suc m ≤′ n</p></blockquote> <p>Recall that the recursion template <code>f</code> has type <code>forall i -> (forall j -> j ′ i -> P j) -> P i</code>. That is, given <code>i</code>, <code>f</code> computes <code>P i</code>, provided that we know of a method to compute <code>P j</code> for all <code>j ′ i</code>. Let us try to construct such a method using <code>f</code>. The function <code> guard f i</code> computes <code>f</code>, provided that the input is strictly smaller than <code>i</code>:</p> <blockquote><p><code>guard : {P : ℕ -> Set} -><br /> &nbsp;&nbsp;&nbsp;(forall i -> (forall j -> j ′ i -> P j) -> P i) -><br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forall i -> forall j -> j ′ i -> P j<br /> guard f zero _ ()<br /> guard f .(suc j) j ≤′-refl = f j (guard f j)<br /> guard f (suc i) j (≤′-step j&lt;i) = guard f i j j&lt;i </code></p></blockquote> <p>Observe that <code>guard</code> terminates because it is defined inductively on <code>i</code>. If we discard the type information, all what <code>guard f i j</code> is to make a call to <code>f j</code>. Before doing so, however, it checks through the proof to make sure that <code>j</code> is strictly smaller than <code>i</code>. In <code>f j (guard f j)</code>, the call to <code>guard f j</code> ensures that subsequent calls to <code>f</code> are given arguments smaller than <code>j</code>.</p> <p>Now <code>-rec</code> can be defined by:</p> <blockquote><p><code>-rec : (P : ℕ -> Set) -><br /> &nbsp;&nbsp;&nbsp;(forall i -> (forall j -> j ′ i -> P j) -> P i) -><br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forall n -> P n<br /> -rec P f n = f n (guard f n)</code></p></blockquote> <p>In <code>Logic.Induction.Nat</code>, <code>-rec</code> is an instance of well-founded recursion defined using the concept of <em>accessibility</em>, defined in <code>Logic.Induction.WellFounded</code>. I find them very interesting modules which I hope to understand more.</p> <p><strong>To be continued&#8230;</strong></p> Sat, 12 Apr 2008 16:22:42 +0000 Shin Joel Reymont: Cross-platform bytecode anyone? tag:typepad.com,2003:post-48340944 http://feeds.feedburner.com/~r/TenerifeSkunkworks/~3/268946453/cross-platform.html <p>Suppose I want to generate bytecode or executable code on Mac OSX or Linux and run it on Windows. Assume that I want to do that in a web application and that I only target x86.</p> <p>What are my options?</p> <p>The other qualifier is that the generated code should be .NET or should run in a VM that would, in turn, talk to .NET.</p> <p>I could try Mono but I read someplace that generated assemblies cannot be used on Windows due to signing issues. I'm not sure this is the case, can someone correct me?</p> <p>I could try Factor and write the COM interface on the Windows side. The Factor VM is reasonably small and the bytecode should run without change.</p> <p>What else?</p> <div class="feedflare"> <a href="http://feeds.feedburner.com/~f/TenerifeSkunkworks?a=8YdbBgG"><img src="http://feeds.feedburner.com/~f/TenerifeSkunkworks?i=8YdbBgG" border="0" /></a> <a href="http://feeds.feedburner.com/~f/TenerifeSkunkworks?a=NvKO8Mg"><img src="http://feeds.feedburner.com/~f/TenerifeSkunkworks?i=NvKO8Mg" border="0" /></a> <a href="http://feeds.feedburner.com/~f/TenerifeSkunkworks?a=9FRJRPg"><img src="http://feeds.feedburner.com/~f/TenerifeSkunkworks?i=9FRJRPg" border="0" /></a> <a href="http://feeds.feedburner.com/~f/TenerifeSkunkworks?a=vDRgsTG"><img src="http://feeds.feedburner.com/~f/TenerifeSkunkworks?i=vDRgsTG" border="0" /></a> <a href="http://feeds.feedburner.com/~f/TenerifeSkunkworks?a=5I1ReAg"><img src="http://feeds.feedburner.com/~f/TenerifeSkunkworks?i=5I1ReAg" border="0" /></a> </div><img src="http://feeds.feedburner.com/~r/TenerifeSkunkworks/~4/268946453" height="1" width="1" /> Sat, 12 Apr 2008 13:09:35 +0000 Joel Reymont Neil Mitchell: darcs Feature Request (Part II) tag:blogger.com,1999:blog-7094652.post-3201719101251968153 http://neilmitchell.blogspot.com/2008/04/darcs-feature-request-part-ii.html I <a href="http://neilmitchell.blogspot.com/2008/01/darcs-feature-request.html">previously requested</a> a feature for darcs. I always pull from an http repo, and push over SSH. I have to push using <tt>--no-set-default</tt> and typing the ssh repo in full, which I automate with a <a href="http://darcs.haskell.org/packages/filepath/push.bat">.bat file</a> in each repo.<br /><br />Today I noticed that darcs has <tt>_darcs/prefs/repos</tt>, which seems to list the repo's that darcs has used. In one of my typical repo files, I have an http entry and an SSH entry. To get darcs to behave the way I want, all I need to do is push using the first non-http repo in that list.<br /><br />I have implemented my version of the darcs push command inside my paper tool, the code is all <a href="http://www.cs.york.ac.uk/fp/darcs/paper/Paper/Push.hs">online here</a>. Now I can delete all my push.bat scripts, and just type <tt>paper push</tt> from any darcs repo. As an added bonus, I now don't need to change to the root directory to perform a push.<br /><br />It would be really nice if someone could incorporate this feature into the main darcs codebase. However, I'm quite happy using my paper tool for now. I certainly don't have time to patch, or even build darcs :-) Sat, 12 Apr 2008 11:20:00 +0000 Neil Mitchell Conrad Parker: Release: Sweep 0.9.3 tag:blogger.com,1999:blog-9101292118679422945.post-6484626782594667327 http://blog.kfish.org/2008/04/release-sweep-093.html This is a bugfix release of <a href="http://www.metadecks.org/software/sweep/">Sweep</a>, addressing <a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-1686">CVE-2008-1686</a>. For details, see my earlier post about <a href="http://blog.kfish.org/2008/04/release-libfishsound-091.html">libfishsound 0.9.1</a>. Thanks to Peter Shorthose for managing this release. Sat, 12 Apr 2008 07:50:09 +0000 Conrad Parker Ulisses Costa: Life After People, Documentary http://caos.di.uminho.pt/~ulisses/blog/2008/04/12/life-after-people-documentary/ http://caos.di.uminho.pt/~ulisses/blog/2008/04/12/life-after-people-documentary/ <p><!--post2pdf_exclude-->Last week, after making a work for the university, I was stumbling around and found an excellent documentary to see. Life after people is a documentary from History channel that try to respond the question:</p> <blockquote> <p id="result_box" dir="ltr"><em> What would happen to planet earth if the human race were to suddenly disappear forever?</em></p> </blockquote> <p>The idea is just great! Probably everyone at some point thought about that, and maybe didn&#8217;t have the imagination or the knowledge to get a clear picture like those in this documentary.</p> <p><img src="http://caos.di.uminho.pt/~ulisses/blog/img/buildings_decomposing.jpg" align="left" height="164" width="297" /></p> <p>The documentary have a lot of speculative facts, just a little bit americanized, but the idea of see all the things that we created being destroyed by the land there we live is fascinating.</p> <p>Remember us that our planet is alive, and live by it self, and in some aspects needs help. We are small but at the same time we can do a lot of changes in our planet.</p> <p>Well, I really liked this imaginative documentary, here is the &#8220;trailer&#8221;:</p> <p> </p> <p><a href="http://www.history.com/minisites/life_after_people">Here</a> the oficial site of <a href="http://video.google.com/videoplay?docid=4939078184096254535">the documentary</a>.</p> Sat, 12 Apr 2008 04:42:04 +0000 ulisses Eric Kow (kowey): darcs 2 at last! tag:blogger.com,1999:blog-21123659.post-737614930474400956 http://koweycode.blogspot.com/2008/04/darcs-2-at-last.html I'm sure you've all seen David's <a href="http://thread.gmane.org/gmane.comp.version-control.darcs.devel/7809">announcement</a>: darcs 2.0.0 is out!<br /><h3>what's good</h3>In short, darcs 2.0 is safer and faster.<br /><br />Particularly, the dreaded exponential-time merge bug has now been largely resolved. Let me say it more carefully: while it may still be possible to run into exponential time merges, our improvements to conflict-handling should make it considerably less common. We hope that nobody ever runs into such a situation in practice.<br /><br />Other key points are improved the hashed inventory and pristine cache which darcs more robust (you no longer have to worry about third party tools like Eclipse or Unison messing things up by mucking around with darcs internals), the ssh-connection mode which speeds up SSH-issues a lot and kills the typing-your-password-ten-million-times issue dead (at most you'll have to type it in twice).<br /><br /><h3>what's bad</h3>On the one hand, darcs 2.0.0 should be much smoother and faster for most users. On the other hand, people with large repositories (e.g. GHC-sized) might find certain operations to be somewhat slower. David does not (yet) have ideas on how to make things better for such users, and is even recommending them to switch to something else. If you've got a repository of darcs' size (over 5000 patches, 6 years, 131 contributors) or smaller, you should continue using darcs, because we still think it works better: we're still the only ones around to offer <em>deep</em> cherry picking... something which we think would be hard to do without radically changing the way other VCSes work. If you would like to prove us wrong, please do so and we would be most grateful!<br /><br />Also, taking advantage of darcs 2 will require you to upgrade your repository to the <code>darcs-2</code> format (see <code>darcs convert</code>), which unfortunately, is not compatible with older versions of darcs. People with new repositories should definitely start using this format. People with old repositories should probably do so at the earliest convenient moment, although this means your users will have to upgrade. Please switch to the new format. It will make everybody's lives easier.<br /><br />The final piece of bad news: we're going to have to shift to a lighter weight development model, something which puts less strain on David and the rest of the contributors. The consequences are that patches might get less review [one maintainer and not two], and that you'll be seeing less of David on the mailing lists. The good news in the bad news is that our lighter weight development model is now being supported by increased automation of the administrative stuff. For example, our bug tracker is now integrated with the darcs repository so that it automatically knows when a ticket has been resolved by a patch. This increased automation gives us extra rigour and more time to think about making darcs better. The only thing we need is more of us. If you want a place to hone your Haskell, Perl or C... or if you think you know a thing or two about user interfaces, please spend some time with us.<br /><h3>to sum up...</h3>Have you been hesitating to try darcs out? Well, now is a good time to do so, as our killer bugs have been fixed as well as the kind of minor nuisances that get most of us. Or... are you looking for something to work on? Uncle David needs you!<br /><br />[note: Thanks to David Roundy for comments on a draft of this post] Fri, 11 Apr 2008 17:56:54 +0000 kowey Tom Moertel: That looks about right urn:uuid:8c556b83-dcaa-4044-9f0f-7af434f6f3f7 http://blog.moertel.com/articles/2008/04/11/that-looks-about-right <p>Via <a href="http://www.cwinters.com/news/display/3624">Chris</a>:</p> <pre>$ history | awk '{print $2}' | sort | uniq -c | sort -rn | head 196 git 110 l 102 cd 70 make 34 darcs 30 pushd 23 ssh 23 m 23 ls 20 rm </pre> <p>The <em>l</em> and <em>m</em> commands are aliases:</p> <ul> <li><em>l</em> = <em>ls &#8211;CF</em></li> <li><em>m</em> = <em>less</em></li> </ul> Fri, 11 Apr 2008 16:04:49 +0000 Tom Moertel Bryan O'Sullivan: Hoisted from someone else&#8217;s comments http://www.serpentine.com/blog/2008/04/10/hoisted-from-someone-elses-comments/ http://www.serpentine.com/blog/2008/04/10/hoisted-from-someone-elses-comments/ Marc Ambinder comments on the <a href="http://marcambinder.theatlantic.com/archives/2008/04/advance_praise_for_war_and_dec.php">peculiar cadence of the jacket blurbs</a> for Douglas Feith&#8217;s new book, to which some wag responds with a suggestion for a similar endorsement. <blockquote>Feith&#8217;s book is perfectly rectangular. Its page numbers progress in a pleasing upward sequence. Its evident shortcomings in terms of accuracy are offset by its usefulness in balancing wobbly furniture.</blockquote> Fri, 11 Apr 2008 06:11:47 +0000 Bryan O'Sullivan (bos@serpentine.com) feed-1.0.1.0/tests/files/atom.xml0000644000000000000000000110236713354421723014704 0ustar0000000000000000 RecentChanges Rel4tion http://www.rel4tion.org/recentchanges/ Rel4tion ikiwiki 2015-09-27T20:04:22Z change to people/fr33domlover on Rel4tion http://www.rel4tion.org/recentchanges/change_c989bafb33d2bdcd8f01b408f63b7981e62110a9/ fr33domlover 2015-09-27T20:04:22Z 2015-09-27T20:04:22Z <div id="change-c989bafb33d2bdcd8f01b408f63b7981e62110a9" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=people/fr33domlover.mdwn;h=70e87c497caca5f004d5434a7d6e52b48c01d4f3;hp=2230d818ac803844191d7842bcb24fa4d5e45e14;hb=c989bafb33d2bdcd8f01b408f63b7981e62110a9;hpb=44148b1572c22c8d979bd90a75f82dbe444530df" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">people/fr33domlover</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">08:04:22 PM 09/27/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=c989bafb33d2bdcd8f01b408f63b7981e62110a9&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> me: update paragraph about the open tickets list<br /> </div> change to projects/funbot on Rel4tion http://www.rel4tion.org/recentchanges/change_44148b1572c22c8d979bd90a75f82dbe444530df/ fr33domlover 2015-09-27T19:58:19Z 2015-09-27T19:58:19Z <div id="change-44148b1572c22c8d979bd90a75f82dbe444530df" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot.mdwn;h=ff14943d3018d85e80703df32c9ca2c65c13c83a;hp=d75dd79bb2e9c594f78de4f5897ea7e60d554433;hb=44148b1572c22c8d979bd90a75f82dbe444530df;hpb=1c5f47be50411f5bbcf5b2373d20f6095aeb078f" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot" rel="nofollow">projects/funbot</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:58:19 PM 09/27/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=44148b1572c22c8d979bd90a75f82dbe444530df" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: mention the helper packages<br /> </div> change to access/paste on Rel4tion http://www.rel4tion.org/recentchanges/change_1c5f47be50411f5bbcf5b2373d20f6095aeb078f/ fr33domlover 2015-09-21T17:04:36Z 2015-09-21T17:04:36Z <div id="change-1c5f47be50411f5bbcf5b2373d20f6095aeb078f" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=access/paste.mdwn;h=118bdeb612c6b61cf5b59c8bd5b8ea7072a61ae8;hp=978504f51469a8aa3ad45c5d55be22ba0503e4ef;hb=1c5f47be50411f5bbcf5b2373d20f6095aeb078f;hpb=681de3e0ea89f8597ea39766be861e70f947793f" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=access%2Fpaste" rel="nofollow">access/paste</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">05:04:36 PM 09/21/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=1c5f47be50411f5bbcf5b2373d20f6095aeb078f" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> access: update URL of paste server code<br /> </div> change to projects/funbot/guide on Rel4tion http://www.rel4tion.org/recentchanges/change_681de3e0ea89f8597ea39766be861e70f947793f/ fr33domlover 2015-09-18T21:39:54Z 2015-09-18T21:39:54Z <div id="change-681de3e0ea89f8597ea39766be861e70f947793f" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/guide.mdwn;h=80ec023c5699abad621d01b1818d39db78ccaed0;hp=da0633c981be6cf4f16c67dea4bf1cba63d2f827;hb=681de3e0ea89f8597ea39766be861e70f947793f;hpb=1e55bf1839b514e0524a294fa62ce0eed46ad3e1" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Fguide" rel="nofollow">projects/funbot/guide</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:39:54 PM 09/18/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=681de3e0ea89f8597ea39766be861e70f947793f" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: mention new dependency, funbot-ext-events<br /> </div> change to projects/funbot on Rel4tion http://www.rel4tion.org/recentchanges/change_1e55bf1839b514e0524a294fa62ce0eed46ad3e1/ fr33domlover 2015-09-17T19:44:49Z 2015-09-17T19:44:49Z <div id="change-1e55bf1839b514e0524a294fa62ce0eed46ad3e1" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot.mdwn;h=d75dd79bb2e9c594f78de4f5897ea7e60d554433;hp=f563f5208d711e1946624f45b58eb19e1c8fd688;hb=1e55bf1839b514e0524a294fa62ce0eed46ad3e1;hpb=9abd0dcfc30145da75cd2b246eac79fdd250a57f" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot&amp;do=goto" rel="nofollow">projects/funbot</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:44:49 PM 09/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=1e55bf1839b514e0524a294fa62ce0eed46ad3e1" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: remove dummy text<br /> </div> change to projects/funbot on Rel4tion http://www.rel4tion.org/recentchanges/change_9abd0dcfc30145da75cd2b246eac79fdd250a57f/ fr33domlover 2015-09-17T19:43:38Z 2015-09-17T19:43:38Z <div id="change-9abd0dcfc30145da75cd2b246eac79fdd250a57f" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot.mdwn;h=f563f5208d711e1946624f45b58eb19e1c8fd688;hp=4648b803378bdbf71232640adf24429bf1e0bf59;hb=9abd0dcfc30145da75cd2b246eac79fdd250a57f;hpb=8b70f13be1209a55b3d31122cba2a8a8a31539ab" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot" rel="nofollow">projects/funbot</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:43:38 PM 09/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=9abd0dcfc30145da75cd2b246eac79fdd250a57f&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: minor update and some dummy test text<br /> </div> change to shortcuts on Rel4tion http://www.rel4tion.org/recentchanges/change_8b70f13be1209a55b3d31122cba2a8a8a31539ab/ fr33domlover 2015-09-17T19:22:22Z 2015-09-17T19:22:22Z <div id="change-8b70f13be1209a55b3d31122cba2a8a8a31539ab" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=shortcuts.mdwn;h=96adf1fbd8cb6601d15184bd5ec1854e262c003b;hp=848c9edfe10d412737a37883ff955dbee20089cb;hb=8b70f13be1209a55b3d31122cba2a8a8a31539ab;hpb=4811a57e33da708175ddd6d016e78bd410f4d1ea" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=shortcuts&amp;do=goto" rel="nofollow">shortcuts</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:22:22 PM 09/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=8b70f13be1209a55b3d31122cba2a8a8a31539ab" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> fix notabug shortcut, it shouldn&#39;t url-encode<br /> </div> change to projects/funbot/tickets/5 on Rel4tion http://www.rel4tion.org/recentchanges/change_4811a57e33da708175ddd6d016e78bd410f4d1ea/ fr33domlover 2015-09-17T13:28:45Z 2015-09-17T13:28:45Z <div id="change-4811a57e33da708175ddd6d016e78bd410f4d1ea" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/5.mdwn;h=f3c6b11312ebf91ef89c077beb98b475f5f7760c;hp=993dac4b04ed6e9cfc0796f09a90ee568106b193;hb=4811a57e33da708175ddd6d016e78bd410f4d1ea;hpb=07e5d7d13fc364c06ed1bda3f21fa6a493276859" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F5" rel="nofollow">projects/funbot/tickets/5</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">01:28:45 PM 09/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=4811a57e33da708175ddd6d016e78bd410f4d1ea&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: close memo ticket<br /> </div> change to projects/funbot/tickets/2 on Rel4tion http://www.rel4tion.org/recentchanges/change_07e5d7d13fc364c06ed1bda3f21fa6a493276859/ fr33domlover 2015-09-17T13:22:05Z 2015-09-17T13:22:05Z <div id="change-07e5d7d13fc364c06ed1bda3f21fa6a493276859" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/2.mdwn;h=58c609b1cc1ffb88ad01aa39806bb43d166f67f9;hp=2cc56bb7968465ca690ebcbed6ed5a5213fb8a0c;hb=07e5d7d13fc364c06ed1bda3f21fa6a493276859;hpb=5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F2" rel="nofollow">projects/funbot/tickets/2</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">01:22:05 PM 09/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=07e5d7d13fc364c06ed1bda3f21fa6a493276859&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: close persistence ticket, we have json-state now<br /> </div> change to projects projects/json-state projects/json-state/decisions projects/json-state/forum projects/json-state/news projects/json-state/releases projects/json-state/tickets on Rel4tion http://www.rel4tion.org/recentchanges/change_5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe/ fr33domlover 2015-09-17T13:01:04Z 2015-09-17T13:01:04Z <div id="change-5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects.mdwn;h=345421854fa54c60f59686d3908fe58dd4789069;hp=10ae06877f9882b6bd6f3bb1c5b9dd3c7ab032c1;hb=5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe;hpb=8271123effea28344517c121ee596eb7217e28e2" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects&amp;do=goto" rel="nofollow">projects</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/json-state.mdwn;h=41137012211445b4f3f48fc8e1c036ababdab2b9;hp=0000000000000000000000000000000000000000;hb=5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe;hpb=8271123effea28344517c121ee596eb7217e28e2" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fjson-state&amp;do=goto" rel="nofollow">projects/json-state</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/json-state/decisions.mdwn;h=87e40f15dec6533bbeebf666fa4d2761c0d5adad;hp=0000000000000000000000000000000000000000;hb=5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe;hpb=8271123effea28344517c121ee596eb7217e28e2" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fjson-state%2Fdecisions" rel="nofollow">projects/json-state/decisions</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/json-state/forum.mdwn;h=cdc6c618a5af5e908dc00cabeed27a4f4d5c9125;hp=0000000000000000000000000000000000000000;hb=5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe;hpb=8271123effea28344517c121ee596eb7217e28e2" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fjson-state%2Fforum" rel="nofollow">projects/json-state/forum</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/json-state/news.mdwn;h=c463308444ec8f91a1bfae7dfda2e5fe3d183863;hp=0000000000000000000000000000000000000000;hb=5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe;hpb=8271123effea28344517c121ee596eb7217e28e2" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fjson-state%2Fnews" rel="nofollow">projects/json-state/news</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/json-state/releases.mdwn;h=e4dcf00f9501d98e461c4a6ccd75818cc54b71fc;hp=0000000000000000000000000000000000000000;hb=5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe;hpb=8271123effea28344517c121ee596eb7217e28e2" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fjson-state%2Freleases&amp;do=goto" rel="nofollow">projects/json-state/releases</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/json-state/tickets.mdwn;h=df2d37627427fe3d89829ad503c725de72c7d762;hp=0000000000000000000000000000000000000000;hb=5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe;hpb=8271123effea28344517c121ee596eb7217e28e2" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fjson-state%2Ftickets" rel="nofollow">projects/json-state/tickets</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">01:01:04 PM 09/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=5f6c2df9dfec2c3303dedfbd4712f616e7dd5ebe&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> json-state: new project<br /> </div> change to projects/settings projects/settings/releases on Rel4tion http://www.rel4tion.org/recentchanges/change_8271123effea28344517c121ee596eb7217e28e2/ fr33domlover 2015-09-17T12:59:24Z 2015-09-17T12:59:24Z <div id="change-8271123effea28344517c121ee596eb7217e28e2" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings.mdwn;h=be444e1b1ee23712cdb25e2d3fa155319dd04b38;hp=b7f5ffc7ee35f85a7a7d5094fdc02c578122825e;hb=8271123effea28344517c121ee596eb7217e28e2;hpb=9bd1e350a4f81d79b820eb5a1ab3e644a280399a" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fsettings&amp;do=goto" rel="nofollow">projects/settings</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/releases.mdwn;h=4c6c784b8ea0ca05ff9fd5232a2a195159aa4cae;hp=e4efd9b315242da6e04f9e94546ed3522bdaac68;hb=8271123effea28344517c121ee596eb7217e28e2;hpb=9bd1e350a4f81d79b820eb5a1ab3e644a280399a" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fsettings%2Freleases" rel="nofollow">projects/settings/releases</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:59:24 PM 09/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=8271123effea28344517c121ee596eb7217e28e2" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> settings: update repo link<br /> </div> change to projects/funbot/tickets/15 on Rel4tion http://www.rel4tion.org/recentchanges/change_9bd1e350a4f81d79b820eb5a1ab3e644a280399a/ fr33domlover 2015-09-17T11:51:13Z 2015-09-17T11:51:13Z <div id="change-9bd1e350a4f81d79b820eb5a1ab3e644a280399a" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/15.mdwn;h=a6aef60136eb5939cd49fe3dff12a23214f96914;hp=37a1b5e2695ce312b8f1b8e37078dd4cdbc90898;hb=9bd1e350a4f81d79b820eb5a1ab3e644a280399a;hpb=edabc8116457185ce07d1f61737fa2ce8518bd4b" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F15&amp;do=goto" rel="nofollow">projects/funbot/tickets/15</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">11:51:13 AM 09/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=9bd1e350a4f81d79b820eb5a1ab3e644a280399a" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: settings system is finally complete<br /> </div> change to projects/funbot/manual on Rel4tion http://www.rel4tion.org/recentchanges/change_edabc8116457185ce07d1f61737fa2ce8518bd4b/ fr33domlover 2015-09-16T19:04:22Z 2015-09-16T19:04:22Z <div id="change-edabc8116457185ce07d1f61737fa2ce8518bd4b" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/manual.mdwn;h=d098b5370a8cbf0e60f80e37f23276dda4f2afd1;hp=56ff43060cd3eecca2eb10237bc7303d1ecc8652;hb=edabc8116457185ce07d1f61737fa2ce8518bd4b;hpb=37ec11668e6a6a9418209c5411508d018592519d" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Fmanual" rel="nofollow">projects/funbot/manual</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:04:22 PM 09/16/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=edabc8116457185ce07d1f61737fa2ce8518bd4b&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: update manual with recent changes<br /> </div> change to people/fr33domlover/blog/love-of-wisdom on Rel4tion http://www.rel4tion.org/recentchanges/change_37ec11668e6a6a9418209c5411508d018592519d/ fr33domlover 2015-09-12T13:12:06Z 2015-09-12T13:12:06Z <div id="change-37ec11668e6a6a9418209c5411508d018592519d" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=people/fr33domlover/blog/love-of-wisdom.mdwn;h=a9bfd88c473fc20270af0a83570f8a4b70d12691;hp=0000000000000000000000000000000000000000;hb=37ec11668e6a6a9418209c5411508d018592519d;hpb=29d3a9e5554fb2ea6987f31f968578cc2647e8f3" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover%2Fblog%2Flove-of-wisdom" rel="nofollow">people/fr33domlover/blog/love-of-wisdom</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">01:12:06 PM 09/12/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=37ec11668e6a6a9418209c5411508d018592519d&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> me: another blog post...<br /> </div> change to news/break on Rel4tion http://www.rel4tion.org/recentchanges/change_29d3a9e5554fb2ea6987f31f968578cc2647e8f3/ fr33domlover 2015-09-10T22:01:02Z 2015-09-10T22:01:02Z <div id="change-29d3a9e5554fb2ea6987f31f968578cc2647e8f3" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=news/break.mdwn;h=5eb7a858b10923cf2973d7b6004cb2c1783bea45;hp=0000000000000000000000000000000000000000;hb=29d3a9e5554fb2ea6987f31f968578cc2647e8f3;hpb=34a21b40a95051a6bee7f3c89223b868ebcb64ac" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=news%2Fbreak&amp;do=goto" rel="nofollow">news/break</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">10:01:02 PM 09/10/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=29d3a9e5554fb2ea6987f31f968578cc2647e8f3&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> news: break<br /> </div> change to index on Rel4tion http://www.rel4tion.org/recentchanges/change_34a21b40a95051a6bee7f3c89223b868ebcb64ac/ fr33domlover 2015-09-10T21:50:12Z 2015-09-10T21:50:12Z <div id="change-34a21b40a95051a6bee7f3c89223b868ebcb64ac" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=index.mdwn;h=c68cb36b6224504f98fe4811e08c49a1b07f2df0;hp=126fe28b2815f6a3d4a3095dd5e11485cebb8086;hb=34a21b40a95051a6bee7f3c89223b868ebcb64ac;hpb=ed2a95c268ef060d9dddae5a24b4672911491c72" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=index" rel="nofollow">index</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:50:12 PM 09/10/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=34a21b40a95051a6bee7f3c89223b868ebcb64ac&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> index: remove feedback box<br /> </div> change to projects/funbot/manual on Rel4tion http://www.rel4tion.org/recentchanges/change_ed2a95c268ef060d9dddae5a24b4672911491c72/ fr33domlover 2015-09-10T18:46:34Z 2015-09-10T18:46:34Z <div id="change-ed2a95c268ef060d9dddae5a24b4672911491c72" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/manual.mdwn;h=56ff43060cd3eecca2eb10237bc7303d1ecc8652;hp=44c3d463280d7e275a6f93bd9633289f0872b686;hb=ed2a95c268ef060d9dddae5a24b4672911491c72;hpb=d3cdcea02b8dedede9d590ff8a730cac95a10c58" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Fmanual&amp;do=goto" rel="nofollow">projects/funbot/manual</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">06:46:34 PM 09/10/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=ed2a95c268ef060d9dddae5a24b4672911491c72" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: revise manual<br /> </div> change to projects/funbot/tickets/5 on Rel4tion http://www.rel4tion.org/recentchanges/change_d3cdcea02b8dedede9d590ff8a730cac95a10c58/ fr33domlover 2015-09-10T13:16:52Z 2015-09-10T13:16:52Z <div id="change-d3cdcea02b8dedede9d590ff8a730cac95a10c58" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/5.mdwn;h=993dac4b04ed6e9cfc0796f09a90ee568106b193;hp=606f28657913248cbfe3204aa6f0ffebec656b55;hb=d3cdcea02b8dedede9d590ff8a730cac95a10c58;hpb=1a05dfe95cd5a544f09ef39d5187f6ecf75af3b2" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F5&amp;do=goto" rel="nofollow">projects/funbot/tickets/5</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">01:16:52 PM 09/10/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=d3cdcea02b8dedede9d590ff8a730cac95a10c58" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: document work on memos<br /> </div> change to projects/funbot/tickets/18 on Rel4tion http://www.rel4tion.org/recentchanges/change_1a05dfe95cd5a544f09ef39d5187f6ecf75af3b2/ fr33domlover 2015-08-31T17:35:31Z 2015-08-31T17:35:31Z <div id="change-1a05dfe95cd5a544f09ef39d5187f6ecf75af3b2" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/18.mdwn;h=92ddfe617ac1113a7b96d24857c5e783f55432aa;hp=d9fbdf7bd75e3ce0638d56f2bae98e47270b7baa;hb=1a05dfe95cd5a544f09ef39d5187f6ecf75af3b2;hpb=74c8029879813f445632d74c6b0233496729a757" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F18&amp;do=goto" rel="nofollow">projects/funbot/tickets/18</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">05:35:31 PM 08/31/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=1a05dfe95cd5a544f09ef39d5187f6ecf75af3b2&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: document progress on friendly command access<br /> </div> change to projects/funbot/tickets/18 on Rel4tion http://www.rel4tion.org/recentchanges/change_74c8029879813f445632d74c6b0233496729a757/ fr33domlover 2015-08-30T16:31:13Z 2015-08-30T16:31:13Z <div id="change-74c8029879813f445632d74c6b0233496729a757" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/18.mdwn;h=d9fbdf7bd75e3ce0638d56f2bae98e47270b7baa;hp=f717e312738110e9fa51874c4efa2b918dca6c43;hb=74c8029879813f445632d74c6b0233496729a757;hpb=2f15a1e3784ca2c076afb2654bb313723de8592e" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F18" rel="nofollow">projects/funbot/tickets/18</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">04:31:13 PM 08/30/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=74c8029879813f445632d74c6b0233496729a757&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: plan API for friendly command access<br /> </div> change to projects/funbot/guide on Rel4tion http://www.rel4tion.org/recentchanges/change_2f15a1e3784ca2c076afb2654bb313723de8592e/ fr33domlover 2015-08-28T21:39:21Z 2015-08-28T21:39:21Z <div id="change-2f15a1e3784ca2c076afb2654bb313723de8592e" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/guide.mdwn;h=da0633c981be6cf4f16c67dea4bf1cba63d2f827;hp=bb59f3668fc21acdb2072022cb65808df5becebd;hb=2f15a1e3784ca2c076afb2654bb313723de8592e;hpb=60f4a8590a34861a966b85ad8d7e6c9c8daa7ace" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Fguide" rel="nofollow">projects/funbot/guide</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:39:21 PM 08/28/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=2f15a1e3784ca2c076afb2654bb313723de8592e" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: update guide to explain new state files<br /> </div> change to projects/funbot/tickets/15 on Rel4tion http://www.rel4tion.org/recentchanges/change_60f4a8590a34861a966b85ad8d7e6c9c8daa7ace/ fr33domlover 2015-08-24T19:45:05Z 2015-08-24T19:45:05Z <div id="change-60f4a8590a34861a966b85ad8d7e6c9c8daa7ace" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/15.mdwn;h=37a1b5e2695ce312b8f1b8e37078dd4cdbc90898;hp=a466fadce2c88cde0347c164e7a49c97e32e1b13;hb=60f4a8590a34861a966b85ad8d7e6c9c8daa7ace;hpb=d0ab67c9ff7643ac75e918e3cb295288003e47e4" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F15&amp;do=goto" rel="nofollow">projects/funbot/tickets/15</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:45:05 PM 08/24/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=60f4a8590a34861a966b85ad8d7e6c9c8daa7ace&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: settings now support exploration<br /> </div> change to projects/funbot/tickets/18 on Rel4tion http://www.rel4tion.org/recentchanges/change_d0ab67c9ff7643ac75e918e3cb295288003e47e4/ fr33domlover 2015-08-23T22:01:20Z 2015-08-23T22:01:20Z <div id="change-d0ab67c9ff7643ac75e918e3cb295288003e47e4" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/18.mdwn;h=f717e312738110e9fa51874c4efa2b918dca6c43;hp=0000000000000000000000000000000000000000;hb=d0ab67c9ff7643ac75e918e3cb295288003e47e4;hpb=6e90ed1b9fa935727054984b9252bd79a5dbef6b" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F18&amp;do=goto" rel="nofollow">projects/funbot/tickets/18</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">10:01:20 PM 08/23/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=d0ab67c9ff7643ac75e918e3cb295288003e47e4" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: new ticket: intuitive UI<br /> </div> change to projects/settings/tickets/1 on Rel4tion http://www.rel4tion.org/recentchanges/change_6e90ed1b9fa935727054984b9252bd79a5dbef6b/ fr33domlover 2015-08-23T21:49:11Z 2015-08-23T21:49:11Z <div id="change-6e90ed1b9fa935727054984b9252bd79a5dbef6b" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/tickets/1.mdwn;h=e666ba63a41d9c574d63f73f8e85316a165aa6f9;hp=1212dbea4050efc9f0e06f61ea832babb70099ed;hb=6e90ed1b9fa935727054984b9252bd79a5dbef6b;hpb=9a0291a0846a9ff9cca95b6f7d23938bc2dcdefa" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fsettings%2Ftickets%2F1&amp;do=goto" rel="nofollow">projects/settings/tickets/1</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:49:11 PM 08/23/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=6e90ed1b9fa935727054984b9252bd79a5dbef6b" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> settings: close #1, callbacks and save/load added<br /> </div> change to projects/funbot/guide on Rel4tion http://www.rel4tion.org/recentchanges/change_9a0291a0846a9ff9cca95b6f7d23938bc2dcdefa/ fr33domlover 2015-08-23T19:23:22Z 2015-08-23T19:23:22Z <div id="change-9a0291a0846a9ff9cca95b6f7d23938bc2dcdefa" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/guide.mdwn;h=bb59f3668fc21acdb2072022cb65808df5becebd;hp=70eb2c6b749d1499807a21e0bc4a6ef2614f90a4;hb=9a0291a0846a9ff9cca95b6f7d23938bc2dcdefa;hpb=6f734bfda528f1afb249d33bb438a7a5638f3ad1" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Fguide&amp;do=goto" rel="nofollow">projects/funbot/guide</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:23:22 PM 08/23/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=9a0291a0846a9ff9cca95b6f7d23938bc2dcdefa" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: explain about settings in the guide<br /> </div> change to projects/settings/tickets/1 on Rel4tion http://www.rel4tion.org/recentchanges/change_6f734bfda528f1afb249d33bb438a7a5638f3ad1/ fr33domlover 2015-08-23T09:49:55Z 2015-08-23T09:49:55Z <div id="change-6f734bfda528f1afb249d33bb438a7a5638f3ad1" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/tickets/1.mdwn;h=1212dbea4050efc9f0e06f61ea832babb70099ed;hp=83d3c27561e380877f7214159378ff806b206b2c;hb=6f734bfda528f1afb249d33bb438a7a5638f3ad1;hpb=428482ea05956bcfebaaa68abeb9cdc1105ed129" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fsettings%2Ftickets%2F1" rel="nofollow">projects/settings/tickets/1</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:49:55 AM 08/23/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=6f734bfda528f1afb249d33bb438a7a5638f3ad1" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> settings: plan custom debounce action for saving<br /> </div> change to people/fr33domlover/blog/insanity2 on Rel4tion http://www.rel4tion.org/recentchanges/change_428482ea05956bcfebaaa68abeb9cdc1105ed129/ fr33domlover 2015-08-23T09:49:16Z 2015-08-23T09:49:16Z <div id="change-428482ea05956bcfebaaa68abeb9cdc1105ed129" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=people/fr33domlover/blog/insanity2.mdwn;h=9746b62a83126b3a142ff7d067de559441e39a00;hp=0000000000000000000000000000000000000000;hb=428482ea05956bcfebaaa68abeb9cdc1105ed129;hpb=67577535d19a1fcd1c187d71f41aa012a16d65f3" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover%2Fblog%2Finsanity2&amp;do=goto" rel="nofollow">people/fr33domlover/blog/insanity2</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:49:16 AM 08/23/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=428482ea05956bcfebaaa68abeb9cdc1105ed129&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> blog post...<br /> </div> change to projects/settings/tickets/1 on Rel4tion http://www.rel4tion.org/recentchanges/change_67577535d19a1fcd1c187d71f41aa012a16d65f3/ fr33domlover 2015-08-22T00:42:25Z 2015-08-22T00:42:25Z <div id="change-67577535d19a1fcd1c187d71f41aa012a16d65f3" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/tickets/1.mdwn;h=83d3c27561e380877f7214159378ff806b206b2c;hp=6a63447c56eaeca30431fad77cb1945230f73ac7;hb=67577535d19a1fcd1c187d71f41aa012a16d65f3;hpb=f4aaab3a133c556a0db208924e86bbb312879319" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fsettings%2Ftickets%2F1" rel="nofollow">projects/settings/tickets/1</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:42:25 AM 08/22/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=67577535d19a1fcd1c187d71f41aa012a16d65f3" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> settings: work on callbacks and persistence<br /> </div> change to projects/settings/tickets/1 on Rel4tion http://www.rel4tion.org/recentchanges/change_f4aaab3a133c556a0db208924e86bbb312879319/ fr33domlover 2015-08-21T14:20:30Z 2015-08-21T14:20:30Z <div id="change-f4aaab3a133c556a0db208924e86bbb312879319" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/tickets/1.mdwn;h=6a63447c56eaeca30431fad77cb1945230f73ac7;hp=0000000000000000000000000000000000000000;hb=f4aaab3a133c556a0db208924e86bbb312879319;hpb=2e457435020f1ab3b5836e8efb3cc1228c6b595c" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fsettings%2Ftickets%2F1&amp;do=goto" rel="nofollow">projects/settings/tickets/1</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">02:20:30 PM 08/21/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=f4aaab3a133c556a0db208924e86bbb312879319&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> settings: new ticket: scope and features<br /> </div> change to projects projects/feed-collect projects/feed-collect/decisions projects/feed-collect/forum projects/feed-collect/news projects/feed-collect/releases projects/feed-collect/tickets on Rel4tion http://www.rel4tion.org/recentchanges/change_2e457435020f1ab3b5836e8efb3cc1228c6b595c/ fr33domlover 2015-08-19T10:02:40Z 2015-08-19T10:02:40Z <div id="change-2e457435020f1ab3b5836e8efb3cc1228c6b595c" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects.mdwn;h=10ae06877f9882b6bd6f3bb1c5b9dd3c7ab032c1;hp=898655fcc20c4302682ffe196aa54e3afeb59b36;hb=2e457435020f1ab3b5836e8efb3cc1228c6b595c;hpb=e127857268e2b2c8181245b6693205c98f371648" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects&amp;do=goto" rel="nofollow">projects</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/feed-collect.mdwn;h=6c574d38ef834e36a9a9be69bfcff3a6598fe461;hp=0000000000000000000000000000000000000000;hb=2e457435020f1ab3b5836e8efb3cc1228c6b595c;hpb=e127857268e2b2c8181245b6693205c98f371648" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffeed-collect&amp;do=goto" rel="nofollow">projects/feed-collect</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/feed-collect/decisions.mdwn;h=ae1b64118c25d3e7236d3f239dfb9c423225532a;hp=0000000000000000000000000000000000000000;hb=2e457435020f1ab3b5836e8efb3cc1228c6b595c;hpb=e127857268e2b2c8181245b6693205c98f371648" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffeed-collect%2Fdecisions" rel="nofollow">projects/feed-collect/decisions</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/feed-collect/forum.mdwn;h=21ae61a8f6c6c15ac9fc7d1776599bddbb66bd39;hp=0000000000000000000000000000000000000000;hb=2e457435020f1ab3b5836e8efb3cc1228c6b595c;hpb=e127857268e2b2c8181245b6693205c98f371648" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffeed-collect%2Fforum&amp;do=goto" rel="nofollow">projects/feed-collect/forum</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/feed-collect/news.mdwn;h=b7a2df9ac1e94ba97e0bfbcc56d346a153ad78f1;hp=0000000000000000000000000000000000000000;hb=2e457435020f1ab3b5836e8efb3cc1228c6b595c;hpb=e127857268e2b2c8181245b6693205c98f371648" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffeed-collect%2Fnews&amp;do=goto" rel="nofollow">projects/feed-collect/news</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/feed-collect/releases.mdwn;h=1bf09d72d75f6bce0cf32b591d5fb6245c99d4c9;hp=0000000000000000000000000000000000000000;hb=2e457435020f1ab3b5836e8efb3cc1228c6b595c;hpb=e127857268e2b2c8181245b6693205c98f371648" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffeed-collect%2Freleases&amp;do=goto" rel="nofollow">projects/feed-collect/releases</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/feed-collect/tickets.mdwn;h=99f8e90067ef5c1404674843969b71e2184721aa;hp=0000000000000000000000000000000000000000;hb=2e457435020f1ab3b5836e8efb3cc1228c6b595c;hpb=e127857268e2b2c8181245b6693205c98f371648" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffeed-collect%2Ftickets&amp;do=goto" rel="nofollow">projects/feed-collect/tickets</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">10:02:40 AM 08/19/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=2e457435020f1ab3b5836e8efb3cc1228c6b595c" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> feed-collect: new project (actually about to release)<br /> </div> change to projects/funbot/tickets/6 on Rel4tion http://www.rel4tion.org/recentchanges/change_e127857268e2b2c8181245b6693205c98f371648/ fr33domlover 2015-08-18T17:06:48Z 2015-08-18T17:06:48Z <div id="change-e127857268e2b2c8181245b6693205c98f371648" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/6.mdwn;h=572dde578082b3005aa3534ed3c0a7f92f21f7b3;hp=bffd53261a36337edc4988f732b733d1996f6e5c;hb=e127857268e2b2c8181245b6693205c98f371648;hpb=1f86b06ce5d3930dc77991312d02cba3193084f6" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F6&amp;do=goto" rel="nofollow">projects/funbot/tickets/6</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">05:06:48 PM 08/18/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=e127857268e2b2c8181245b6693205c98f371648" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> fix invalid link<br /> </div> change to projects/funbot/tickets/6 on Rel4tion http://www.rel4tion.org/recentchanges/change_1f86b06ce5d3930dc77991312d02cba3193084f6/ fr33domlover 2015-08-18T14:54:20Z 2015-08-18T14:54:20Z <div id="change-1f86b06ce5d3930dc77991312d02cba3193084f6" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/6.mdwn;h=bffd53261a36337edc4988f732b733d1996f6e5c;hp=685bde0ac57f25f9a469350985154c13330ccf1e;hb=1f86b06ce5d3930dc77991312d02cba3193084f6;hpb=d2bcaf67089dbb1b92c2128b23134e48597ed455" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F6&amp;do=goto" rel="nofollow">projects/funbot/tickets/6</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">02:54:20 PM 08/18/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=1f86b06ce5d3930dc77991312d02cba3193084f6" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: update status of freepost integration<br /> </div> change to projects/funbot/tickets/4 on Rel4tion http://www.rel4tion.org/recentchanges/change_d2bcaf67089dbb1b92c2128b23134e48597ed455/ fr33domlover 2015-08-18T14:02:57Z 2015-08-18T14:02:57Z <div id="change-d2bcaf67089dbb1b92c2128b23134e48597ed455" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/4.mdwn;h=ff2a4540311b77cfbad57c95b57a2090b7eedb9b;hp=071cb7e64ae57c211834ee5cc7e80132209a6bc2;hb=d2bcaf67089dbb1b92c2128b23134e48597ed455;hpb=b61f7736931f2801c6fdd401f29f6fc78b2d900a" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F4&amp;do=goto" rel="nofollow">projects/funbot/tickets/4</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">02:02:57 PM 08/18/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=d2bcaf67089dbb1b92c2128b23134e48597ed455" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: notes on coding of logging system<br /> </div> change to projects/funbot/tickets/16 on Rel4tion http://www.rel4tion.org/recentchanges/change_b61f7736931f2801c6fdd401f29f6fc78b2d900a/ fr33domlover 2015-08-17T09:12:02Z 2015-08-17T09:12:02Z <div id="change-b61f7736931f2801c6fdd401f29f6fc78b2d900a" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/16.mdwn;h=1197b4650734456f81f92dfd314c5a076bb5762c;hp=15bbe0cb556feb7ed9000c36bb42a8396fb12921;hb=b61f7736931f2801c6fdd401f29f6fc78b2d900a;hpb=a9301fc50b752e55af343255a0b54b990646bd43" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F16&amp;do=goto" rel="nofollow">projects/funbot/tickets/16</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:12:02 AM 08/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=b61f7736931f2801c6fdd401f29f6fc78b2d900a&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: close #16, feed reader implemented<br /> </div> change to projects/funbot/tickets/17 on Rel4tion http://www.rel4tion.org/recentchanges/change_a9301fc50b752e55af343255a0b54b990646bd43/ fr33domlover 2015-08-17T09:09:36Z 2015-08-17T09:09:36Z <div id="change-a9301fc50b752e55af343255a0b54b990646bd43" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/17.mdwn;h=7502e27ea78f3f9667ccdad47b77914d283502d2;hp=70f51944ac387924b6f8bf7433631a3baeb038a4;hb=a9301fc50b752e55af343255a0b54b990646bd43;hpb=360a6d980da581e86d5fe7a80c1fa059b668ab93" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F17&amp;do=goto" rel="nofollow">projects/funbot/tickets/17</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:09:36 AM 08/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=a9301fc50b752e55af343255a0b54b990646bd43" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: close #17, the bot should spam less now<br /> </div> change to projects/funbot/tickets/10 projects/irc-fun-bot/tickets/3 on Rel4tion http://www.rel4tion.org/recentchanges/change_360a6d980da581e86d5fe7a80c1fa059b668ab93/ fr33domlover 2015-08-17T00:39:34Z 2015-08-17T00:39:34Z <div id="change-360a6d980da581e86d5fe7a80c1fa059b668ab93" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/10.mdwn;h=f267e06c2e4ba2d221a5e8b493e7d83954a86b98;hp=2dfa6c5aa076d72de618c647a17774e23cacf78f;hb=360a6d980da581e86d5fe7a80c1fa059b668ab93;hpb=f7b4fd5b731cb17a6489352f10a735368f34d46d" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F10" rel="nofollow">projects/funbot/tickets/10</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/3.mdwn;h=ca2600e82be6faed8128f2e92e37f7941fc25415;hp=cf324dd4a0534fb640a72e516a58205b01917e28;hb=360a6d980da581e86d5fe7a80c1fa059b668ab93;hpb=f7b4fd5b731cb17a6489352f10a735368f34d46d" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-bot%2Ftickets%2F3&amp;do=goto" rel="nofollow">projects/irc-fun-bot/tickets/3</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:39:34 AM 08/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=360a6d980da581e86d5fe7a80c1fa059b668ab93&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: close tickets done with<br /> </div> change to projects/funbot/guide projects/funbot/manual on Rel4tion http://www.rel4tion.org/recentchanges/change_f7b4fd5b731cb17a6489352f10a735368f34d46d/ fr33domlover 2015-08-16T22:15:37Z 2015-08-16T22:15:37Z <div id="change-f7b4fd5b731cb17a6489352f10a735368f34d46d" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/guide.mdwn;h=70eb2c6b749d1499807a21e0bc4a6ef2614f90a4;hp=85e000a7ff584f162078086dd4c4a2a4fd960028;hb=f7b4fd5b731cb17a6489352f10a735368f34d46d;hpb=599eada420921192ceae6cb687f8a9950022044f" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Fguide&amp;do=goto" rel="nofollow">projects/funbot/guide</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/manual.mdwn;h=44c3d463280d7e275a6f93bd9633289f0872b686;hp=b67467c06eb95e96e22c9645100359f147b32ad1;hb=f7b4fd5b731cb17a6489352f10a735368f34d46d;hpb=599eada420921192ceae6cb687f8a9950022044f" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Fmanual&amp;do=goto" rel="nofollow">projects/funbot/manual</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">10:15:37 PM 08/16/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=f7b4fd5b731cb17a6489352f10a735368f34d46d" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: update docs<br /> </div> change to projects/funbot/tickets/15 on Rel4tion http://www.rel4tion.org/recentchanges/change_599eada420921192ceae6cb687f8a9950022044f/ fr33domlover 2015-08-16T18:43:49Z 2015-08-16T18:43:49Z <div id="change-599eada420921192ceae6cb687f8a9950022044f" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/15.mdwn;h=a466fadce2c88cde0347c164e7a49c97e32e1b13;hp=9fde6e22407a2eff47b1a666b3d3c274e5e19859;hb=599eada420921192ceae6cb687f8a9950022044f;hpb=2bf9de01bb378e4c9ed11cb7799ee03f12aed21b" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F15&amp;do=goto" rel="nofollow">projects/funbot/tickets/15</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">06:43:49 PM 08/16/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=599eada420921192ceae6cb687f8a9950022044f&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: the settings system needs callbacks<br /> </div> change to projects/funbot/tickets/17 on Rel4tion http://www.rel4tion.org/recentchanges/change_2bf9de01bb378e4c9ed11cb7799ee03f12aed21b/ fr33domlover 2015-08-15T10:34:40Z 2015-08-15T10:34:40Z <div id="change-2bf9de01bb378e4c9ed11cb7799ee03f12aed21b" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/17.mdwn;h=70f51944ac387924b6f8bf7433631a3baeb038a4;hp=0000000000000000000000000000000000000000;hb=2bf9de01bb378e4c9ed11cb7799ee03f12aed21b;hpb=0e840edc3dd9ec79ee219ba94cd63866eed668ea" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F17" rel="nofollow">projects/funbot/tickets/17</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">10:34:40 AM 08/15/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=2bf9de01bb378e4c9ed11cb7799ee03f12aed21b" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: new ticket: avoid spamming with multi-commit pushes<br /> </div> change to projects/funbot/tickets/16 on Rel4tion http://www.rel4tion.org/recentchanges/change_0e840edc3dd9ec79ee219ba94cd63866eed668ea/ fr33domlover 2015-08-13T16:57:09Z 2015-08-13T16:57:09Z <div id="change-0e840edc3dd9ec79ee219ba94cd63866eed668ea" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/16.mdwn;h=15bbe0cb556feb7ed9000c36bb42a8396fb12921;hp=e62242d40f027b0491185be804ed929dc5a4ac7f;hb=0e840edc3dd9ec79ee219ba94cd63866eed668ea;hpb=f8d63be6c0d9119296673fb426e4a67f514397e0" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F16" rel="nofollow">projects/funbot/tickets/16</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">04:57:09 PM 08/13/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=0e840edc3dd9ec79ee219ba94cd63866eed668ea&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: list existing feed related packages to review<br /> </div> change to projects/funbot/tickets/16 on Rel4tion http://www.rel4tion.org/recentchanges/change_f8d63be6c0d9119296673fb426e4a67f514397e0/ fr33domlover 2015-08-13T07:23:09Z 2015-08-13T07:23:09Z <div id="change-f8d63be6c0d9119296673fb426e4a67f514397e0" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/16.mdwn;h=e62242d40f027b0491185be804ed929dc5a4ac7f;hp=0000000000000000000000000000000000000000;hb=f8d63be6c0d9119296673fb426e4a67f514397e0;hpb=5b11daceee3ab50b30197cfa2ab5385b24a8dc73" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F16&amp;do=goto" rel="nofollow">projects/funbot/tickets/16</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:23:09 AM 08/13/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=f8d63be6c0d9119296673fb426e4a67f514397e0" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: new ticket: announce RSS feeds<br /> </div> change to people/fr33domlover/veganism on Rel4tion http://www.rel4tion.org/recentchanges/change_5b11daceee3ab50b30197cfa2ab5385b24a8dc73/ mm 2015-08-13T03:23:02Z 2015-08-13T03:23:02Z <div id="change-5b11daceee3ab50b30197cfa2ab5385b24a8dc73" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=people/fr33domlover/veganism.mdwn;h=b129c47a62a42f0e7094ea66ab4120c70bbf2956;hp=531aa72c9aa9be7cec113b57f7cd0792bd873cc4;hb=5b11daceee3ab50b30197cfa2ab5385b24a8dc73;hpb=bb1d1469396d107bdbbe314e87d3666cf530d297" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover%2Fveganism&amp;do=goto" rel="nofollow">people/fr33domlover/veganism</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Fmm" rel="nofollow">mm</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">web</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">03:23:02 AM 08/13/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=5b11daceee3ab50b30197cfa2ab5385b24a8dc73&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> </div> change to projects projects/vcs-web-hook-parse projects/vcs-web-hook-parse/tickets on Rel4tion http://www.rel4tion.org/recentchanges/change_bb1d1469396d107bdbbe314e87d3666cf530d297/ fr33domlover 2015-08-10T13:29:45Z 2015-08-10T13:29:45Z <div id="change-bb1d1469396d107bdbbe314e87d3666cf530d297" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects.mdwn;h=898655fcc20c4302682ffe196aa54e3afeb59b36;hp=8dc1b5dc39e41e5367ca9ba74882e13effdd2e49;hb=bb1d1469396d107bdbbe314e87d3666cf530d297;hpb=a815429ee204e00c48a0d72cc17456246e6040de" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects" rel="nofollow">projects</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/vcs-web-hook-parse.mdwn;h=5128a0066c64cf95ed693d43e4b463adad27e2cb;hp=0000000000000000000000000000000000000000;hb=bb1d1469396d107bdbbe314e87d3666cf530d297;hpb=a815429ee204e00c48a0d72cc17456246e6040de" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fvcs-web-hook-parse" rel="nofollow">projects/vcs-web-hook-parse</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/vcs-web-hook-parse/tickets.mdwn;h=5dea4f6f5c1bd6d7879c5773f8015c408511e55f;hp=0000000000000000000000000000000000000000;hb=bb1d1469396d107bdbbe314e87d3666cf530d297;hpb=a815429ee204e00c48a0d72cc17456246e6040de" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fvcs-web-hook-parse%2Ftickets&amp;do=goto" rel="nofollow">projects/vcs-web-hook-parse/tickets</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">01:29:45 PM 08/10/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=bb1d1469396d107bdbbe314e87d3666cf530d297&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> vcs-web-hook-parse: new project, added dummy pages<br /> </div> change to projects/funbot/guide projects/irc-fun-bot projects/irc-fun-client projects/irc-fun-messages on Rel4tion http://www.rel4tion.org/recentchanges/change_a815429ee204e00c48a0d72cc17456246e6040de/ fr33domlover 2015-08-09T14:20:44Z 2015-08-09T14:20:44Z <div id="change-a815429ee204e00c48a0d72cc17456246e6040de" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/guide.mdwn;h=85e000a7ff584f162078086dd4c4a2a4fd960028;hp=fe77d4628d012fbba69fb4186054141ac9386e1e;hb=a815429ee204e00c48a0d72cc17456246e6040de;hpb=63c70b3be856df4c006fabdafb301f9f47fa0b42" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Fguide" rel="nofollow">projects/funbot/guide</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot.mdwn;h=d16135bd1e6a372261567b700c147259d4e0f6af;hp=bbd523480202c43f2ad9cb96773d69fa16a29819;hb=a815429ee204e00c48a0d72cc17456246e6040de;hpb=63c70b3be856df4c006fabdafb301f9f47fa0b42" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-bot" rel="nofollow">projects/irc-fun-bot</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-client.mdwn;h=cdac524a07d2eb092f858de8d4d8d8a0cfc0e8d7;hp=052a1471d2b01e04b9a250bd3ce5efda48e973e8;hb=a815429ee204e00c48a0d72cc17456246e6040de;hpb=63c70b3be856df4c006fabdafb301f9f47fa0b42" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-client&amp;do=goto" rel="nofollow">projects/irc-fun-client</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-messages.mdwn;h=b793f23214f70270ed7da5b3699ad9efdabb5e77;hp=8a037a6b3176f58e7585d9442b98292313fca511;hb=a815429ee204e00c48a0d72cc17456246e6040de;hpb=63c70b3be856df4c006fabdafb301f9f47fa0b42" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-messages&amp;do=goto" rel="nofollow">projects/irc-fun-messages</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">02:20:44 PM 08/09/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=a815429ee204e00c48a0d72cc17456246e6040de&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: update repo links after move to dev.<br /> </div> change to projects/rel4tion/roadmap/release-00 on Rel4tion http://www.rel4tion.org/recentchanges/change_63c70b3be856df4c006fabdafb301f9f47fa0b42/ fr33domlover 2015-08-09T12:25:39Z 2015-08-09T12:25:39Z <div id="change-63c70b3be856df4c006fabdafb301f9f47fa0b42" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/rel4tion/roadmap/release-00.mdwn;h=ce02ef19c8176eb80c1fd5ca77cacfd6ef42a296;hp=b0297d9c7d79acf1fb70bf3029570491f97470c0;hb=63c70b3be856df4c006fabdafb301f9f47fa0b42;hpb=4114d7f291fe124c5169e1588bf3ba8828ea1955" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Frel4tion%2Froadmap%2Frelease-00&amp;do=goto" rel="nofollow">projects/rel4tion/roadmap/release-00</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:25:39 PM 08/09/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=63c70b3be856df4c006fabdafb301f9f47fa0b42&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> rel4tion: update roadmap with recent work<br /> </div> change to templates/rmtask on Rel4tion http://www.rel4tion.org/recentchanges/change_4114d7f291fe124c5169e1588bf3ba8828ea1955/ fr33domlover 2015-08-09T11:34:25Z 2015-08-09T11:34:25Z <div id="change-4114d7f291fe124c5169e1588bf3ba8828ea1955" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/rmtask.mdwn;h=e8a5548bd50de46c670e181aa5ab4af0b53336a5;hp=8081803ab94e9f415a0b16ddd06345163bda3541;hb=4114d7f291fe124c5169e1588bf3ba8828ea1955;hpb=a388b2d06814991f93f52276b8dfe18cd1627ef1" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=templates%2Frmtask" rel="nofollow">templates/rmtask</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">11:34:25 AM 08/09/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=4114d7f291fe124c5169e1588bf3ba8828ea1955&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> templates: tweak rmtask colors<br /> </div> change to projects/funbot/guide projects/funbot/manual projects/funbot/tickets/7 projects/irc-fun-bot/tickets/3 on Rel4tion http://www.rel4tion.org/recentchanges/change_a388b2d06814991f93f52276b8dfe18cd1627ef1/ fr33domlover 2015-08-08T22:47:46Z 2015-08-08T22:47:46Z <div id="change-a388b2d06814991f93f52276b8dfe18cd1627ef1" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/guide.mdwn;h=fe77d4628d012fbba69fb4186054141ac9386e1e;hp=6234c5babf562a6e0554c81171beb169c721b6de;hb=a388b2d06814991f93f52276b8dfe18cd1627ef1;hpb=5f028255a061f5e1405aae1f4bcf911086ddc4d9" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Fguide&amp;do=goto" rel="nofollow">projects/funbot/guide</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/manual.mdwn;h=b67467c06eb95e96e22c9645100359f147b32ad1;hp=4fb784a3bbd9efa372438f998917e996c95946d6;hb=a388b2d06814991f93f52276b8dfe18cd1627ef1;hpb=5f028255a061f5e1405aae1f4bcf911086ddc4d9" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Fmanual&amp;do=goto" rel="nofollow">projects/funbot/manual</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/7.mdwn;h=24801b7b18518348ed77f49239ab071f106fe4ab;hp=edc5905349f54c32ce471d98c1434723aca026d5;hb=a388b2d06814991f93f52276b8dfe18cd1627ef1;hpb=5f028255a061f5e1405aae1f4bcf911086ddc4d9" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F7" rel="nofollow">projects/funbot/tickets/7</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/3.mdwn;h=cf324dd4a0534fb640a72e516a58205b01917e28;hp=7ad03fd86056bf4892a412a1fc7c85838a36d170;hb=a388b2d06814991f93f52276b8dfe18cd1627ef1;hpb=5f028255a061f5e1405aae1f4bcf911086ddc4d9" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-bot%2Ftickets%2F3" rel="nofollow">projects/irc-fun-bot/tickets/3</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">10:47:46 PM 08/08/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=a388b2d06814991f93f52276b8dfe18cd1627ef1&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: ticket and doc updates<br /> </div> change to projects/funbot/tickets/2 projects/http-listen/tickets/2 projects/irc-fun-bot/tickets/3 on Rel4tion http://www.rel4tion.org/recentchanges/change_5f028255a061f5e1405aae1f4bcf911086ddc4d9/ fr33domlover 2015-08-06T13:37:56Z 2015-08-06T13:37:56Z <div id="change-5f028255a061f5e1405aae1f4bcf911086ddc4d9" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/2.mdwn;h=2cc56bb7968465ca690ebcbed6ed5a5213fb8a0c;hp=2db571641ae6d30d6d74d401e2e96015b833b811;hb=5f028255a061f5e1405aae1f4bcf911086ddc4d9;hpb=c5b0bd2d44344346599c272f5f9f089cff3cd0cf" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F2" rel="nofollow">projects/funbot/tickets/2</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/http-listen/tickets/2.mdwn;h=083e030badb159f80bb13af5c2ec169c24e4874e;hp=0000000000000000000000000000000000000000;hb=5f028255a061f5e1405aae1f4bcf911086ddc4d9;hpb=c5b0bd2d44344346599c272f5f9f089cff3cd0cf" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fhttp-listen%2Ftickets%2F2" rel="nofollow">projects/http-listen/tickets/2</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/3.mdwn;h=7ad03fd86056bf4892a412a1fc7c85838a36d170;hp=9a3b0698a4094f4fdc31af182d9233e45bdd5928;hb=5f028255a061f5e1405aae1f4bcf911086ddc4d9;hpb=c5b0bd2d44344346599c272f5f9f089cff3cd0cf" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-bot%2Ftickets%2F3&amp;do=goto" rel="nofollow">projects/irc-fun-bot/tickets/3</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">01:37:56 PM 08/06/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=5f028255a061f5e1405aae1f4bcf911086ddc4d9" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> some ticket updates<br /> </div> change to projects/http-listen/tickets/1 on Rel4tion http://www.rel4tion.org/recentchanges/change_c5b0bd2d44344346599c272f5f9f089cff3cd0cf/ fr33domlover 2015-08-05T09:52:09Z 2015-08-05T09:52:09Z <div id="change-c5b0bd2d44344346599c272f5f9f089cff3cd0cf" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/http-listen/tickets/1.mdwn;h=0798820f782a817d904c7c077c4a2395ec33c171;hp=0000000000000000000000000000000000000000;hb=c5b0bd2d44344346599c272f5f9f089cff3cd0cf;hpb=abccb345fea04b01b45eed2a0f56c3c88988ebf0" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fhttp-listen%2Ftickets%2F1&amp;do=goto" rel="nofollow">projects/http-listen/tickets/1</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:52:09 AM 08/05/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=c5b0bd2d44344346599c272f5f9f089cff3cd0cf&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> http-listen: ope ticket about timeouts<br /> </div> change to projects projects/http-listen projects/http-listen/decisions projects/http-listen/forum projects/http-listen/news projects/http-listen/releases projects/http-listen/tickets shortcuts on Rel4tion http://www.rel4tion.org/recentchanges/change_abccb345fea04b01b45eed2a0f56c3c88988ebf0/ fr33domlover 2015-08-05T09:23:20Z 2015-08-05T09:23:20Z <div id="change-abccb345fea04b01b45eed2a0f56c3c88988ebf0" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects.mdwn;h=8dc1b5dc39e41e5367ca9ba74882e13effdd2e49;hp=8a382bc0b1a8668b14aad89aa4bc16711a1ed84a;hb=abccb345fea04b01b45eed2a0f56c3c88988ebf0;hpb=3807c6e1a57de56dd0e0502a1fe7020d089048c5" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects" rel="nofollow">projects</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/http-listen.mdwn;h=7034ea4798f3d24ebcebd4c02cdd3865c245924d;hp=0000000000000000000000000000000000000000;hb=abccb345fea04b01b45eed2a0f56c3c88988ebf0;hpb=3807c6e1a57de56dd0e0502a1fe7020d089048c5" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fhttp-listen&amp;do=goto" rel="nofollow">projects/http-listen</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/http-listen/decisions.mdwn;h=1a96e79ec55bb6bb817136a2ee3c333e4bbf4c36;hp=0000000000000000000000000000000000000000;hb=abccb345fea04b01b45eed2a0f56c3c88988ebf0;hpb=3807c6e1a57de56dd0e0502a1fe7020d089048c5" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fhttp-listen%2Fdecisions" rel="nofollow">projects/http-listen/decisions</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/http-listen/forum.mdwn;h=b4032d88892110263de936a7f9ef6c7f8f7d755b;hp=0000000000000000000000000000000000000000;hb=abccb345fea04b01b45eed2a0f56c3c88988ebf0;hpb=3807c6e1a57de56dd0e0502a1fe7020d089048c5" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fhttp-listen%2Fforum&amp;do=goto" rel="nofollow">projects/http-listen/forum</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/http-listen/news.mdwn;h=a52d3fd6fc03f44418a4db0ccd745b3f53d6d81d;hp=0000000000000000000000000000000000000000;hb=abccb345fea04b01b45eed2a0f56c3c88988ebf0;hpb=3807c6e1a57de56dd0e0502a1fe7020d089048c5" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fhttp-listen%2Fnews" rel="nofollow">projects/http-listen/news</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/http-listen/releases.mdwn;h=36687b2803485acb11d99138e4b5d9b1d40a177a;hp=0000000000000000000000000000000000000000;hb=abccb345fea04b01b45eed2a0f56c3c88988ebf0;hpb=3807c6e1a57de56dd0e0502a1fe7020d089048c5" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fhttp-listen%2Freleases&amp;do=goto" rel="nofollow">projects/http-listen/releases</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/http-listen/tickets.mdwn;h=36e5aefaceccf9996d9dcf98239267d20c895455;hp=0000000000000000000000000000000000000000;hb=abccb345fea04b01b45eed2a0f56c3c88988ebf0;hpb=3807c6e1a57de56dd0e0502a1fe7020d089048c5" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fhttp-listen%2Ftickets&amp;do=goto" rel="nofollow">projects/http-listen/tickets</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=shortcuts.mdwn;h=848c9edfe10d412737a37883ff955dbee20089cb;hp=955fe6213cd0b74a022cb19d9a82986422a5ac7c;hb=abccb345fea04b01b45eed2a0f56c3c88988ebf0;hpb=3807c6e1a57de56dd0e0502a1fe7020d089048c5" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=shortcuts&amp;do=goto" rel="nofollow">shortcuts</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:23:20 AM 08/05/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=abccb345fea04b01b45eed2a0f56c3c88988ebf0" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> http-listen: new project<br /> </div> change to projects/kiwi/data/idan-files/todo.idan on Rel4tion http://www.rel4tion.org/recentchanges/change_3807c6e1a57de56dd0e0502a1fe7020d089048c5/ fr33domlover 2015-08-01T21:21:43Z 2015-08-01T21:21:43Z <div id="change-3807c6e1a57de56dd0e0502a1fe7020d089048c5" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/kiwi/data/idan-files/todo.idan;h=ccb6b70db2f4b5da7fa5d3a70c1ddc8cd9f1a33e;hp=eace9d20f6dd5d83087bc751aead7446aca7e575;hb=3807c6e1a57de56dd0e0502a1fe7020d089048c5;hpb=dd747340fffdb41ba4ff5066356b22dbffd73c98" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fkiwi%2Fdata%2Fidan-files%2Ftodo.idan&amp;do=goto" rel="nofollow">projects/kiwi/data/idan-files/todo.idan</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:21:43 PM 08/01/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=3807c6e1a57de56dd0e0502a1fe7020d089048c5&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> kiwi: tasks-tui requires new entities<br /> </div> change to news/darcs-hub-new on Rel4tion http://www.rel4tion.org/recentchanges/change_dd747340fffdb41ba4ff5066356b22dbffd73c98/ fr33domlover 2015-08-01T11:35:10Z 2015-08-01T11:35:10Z <div id="change-dd747340fffdb41ba4ff5066356b22dbffd73c98" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=news/darcs-hub-new.mdwn;h=d1cbc5d5d35452c5272006db744779409c9b8505;hp=0000000000000000000000000000000000000000;hb=dd747340fffdb41ba4ff5066356b22dbffd73c98;hpb=65c40fbb77b5f797a67493716418ed0115a160cf" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=news%2Fdarcs-hub-new" rel="nofollow">news/darcs-hub-new</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">11:35:10 AM 08/01/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=dd747340fffdb41ba4ff5066356b22dbffd73c98" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> news: new darcs hub<br /> </div> change to projects/vocabularies-hs on Rel4tion http://www.rel4tion.org/recentchanges/change_65c40fbb77b5f797a67493716418ed0115a160cf/ fr33domlover 2015-07-31T08:44:15Z 2015-07-31T08:44:15Z <div id="change-65c40fbb77b5f797a67493716418ed0115a160cf" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/vocabularies-hs.mdwn;h=a21d0e3f8540fe73f4e9591a313c911aed6e6252;hp=c56d2d47ee0fe9de02ef44b045edc516cfe756e6;hb=65c40fbb77b5f797a67493716418ed0115a160cf;hpb=f0986cdfd3174159052ac1d3da6e1180dfb9a644" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fvocabularies-hs&amp;do=goto" rel="nofollow">projects/vocabularies-hs</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">08:44:15 AM 07/31/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=65c40fbb77b5f797a67493716418ed0115a160cf" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> vocabularies-hs: list the new vocabulary-todo<br /> </div> change to projects/kiwi/data/idan-files/todo.idan on Rel4tion http://www.rel4tion.org/recentchanges/change_f0986cdfd3174159052ac1d3da6e1180dfb9a644/ fr33domlover 2015-07-29T23:28:20Z 2015-07-29T23:28:20Z <div id="change-f0986cdfd3174159052ac1d3da6e1180dfb9a644" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/kiwi/data/idan-files/todo.idan;h=eace9d20f6dd5d83087bc751aead7446aca7e575;hp=9dd53e5a54d1add44987b39afc376c6d16a2e467;hb=f0986cdfd3174159052ac1d3da6e1180dfb9a644;hpb=c2ec1d098f1cc5a41435ed5c28bf87fe8f6057da" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fkiwi%2Fdata%2Fidan-files%2Ftodo.idan" rel="nofollow">projects/kiwi/data/idan-files/todo.idan</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">11:28:20 PM 07/29/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=f0986cdfd3174159052ac1d3da6e1180dfb9a644&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> kiwi: manually add uids needed for coding<br /> </div> change to projects/task-management on Rel4tion http://www.rel4tion.org/recentchanges/change_c2ec1d098f1cc5a41435ed5c28bf87fe8f6057da/ fr33domlover 2015-07-29T21:09:24Z 2015-07-29T21:09:24Z <div id="change-c2ec1d098f1cc5a41435ed5c28bf87fe8f6057da" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/task-management.mdwn;h=852b193c67147e825e7426c09906641acf1dc5fa;hp=1f188daada9a5897e3bd452afbf20e11b08a9c01;hb=c2ec1d098f1cc5a41435ed5c28bf87fe8f6057da;hpb=696ceb85012bff250079824aa5ccf4b526031427" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ftask-management" rel="nofollow">projects/task-management</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:09:24 PM 07/29/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=c2ec1d098f1cc5a41435ed5c28bf87fe8f6057da" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> task-management: update project status<br /> </div> change to projects/task-management/tickets/1 projects/task-management/tickets/1/operations projects/task-management/tickets/1/possible-features projects/task-management/tickets/1/ui projects/task-management/tickets/1/views projects/tui on Rel4tion http://www.rel4tion.org/recentchanges/change_696ceb85012bff250079824aa5ccf4b526031427/ fr33domlover 2015-07-29T06:51:46Z 2015-07-29T06:51:46Z <div id="change-696ceb85012bff250079824aa5ccf4b526031427" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/task-management/tickets/1.mdwn;h=e1a899675f53d52ee329abd44daf3a99c7f10cad;hp=1e660face13843a722026d94ec4c2f51b2160069;hb=696ceb85012bff250079824aa5ccf4b526031427;hpb=acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ftask-management%2Ftickets%2F1&amp;do=goto" rel="nofollow">projects/task-management/tickets/1</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/task-management/tickets/1/operations.mdwn;h=6aa5582ae854c858059089ce638194a861d92b72;hp=0000000000000000000000000000000000000000;hb=696ceb85012bff250079824aa5ccf4b526031427;hpb=acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ftask-management%2Ftickets%2F1%2Foperations&amp;do=goto" rel="nofollow">projects/task-management/tickets/1/operations</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/task-management/tickets/1/possible-features.mdwn;h=78a47438fe2c9d00a0ab47f99c0b64c7a801eb0f;hp=0000000000000000000000000000000000000000;hb=696ceb85012bff250079824aa5ccf4b526031427;hpb=acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ftask-management%2Ftickets%2F1%2Fpossible-features" rel="nofollow">projects/task-management/tickets/1/possible-features</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/task-management/tickets/1/ui.mdwn;h=debe4c625183ab3fce85cb2aa7b371e08fb2d29e;hp=0000000000000000000000000000000000000000;hb=696ceb85012bff250079824aa5ccf4b526031427;hpb=acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ftask-management%2Ftickets%2F1%2Fui&amp;do=goto" rel="nofollow">projects/task-management/tickets/1/ui</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/task-management/tickets/1/views.mdwn;h=4c3f149808ad74fba579947f393b4f6a21e62bab;hp=0000000000000000000000000000000000000000;hb=696ceb85012bff250079824aa5ccf4b526031427;hpb=acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ftask-management%2Ftickets%2F1%2Fviews" rel="nofollow">projects/task-management/tickets/1/views</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/tui.mdwn;h=707ea3237ed79a556340a96e60094beb969f0403;hp=ed4986fab9a9c1d0a320e0aebbd548e2046f94a8;hb=696ceb85012bff250079824aa5ccf4b526031427;hpb=acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ftui" rel="nofollow">projects/tui</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">06:51:46 AM 07/29/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=696ceb85012bff250079824aa5ccf4b526031427&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> task-management: more plans<br /> </div> change to projects/task-management/tickets/1 on Rel4tion http://www.rel4tion.org/recentchanges/change_acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e/ fr33domlover 2015-07-28T12:58:05Z 2015-07-28T12:58:05Z <div id="change-acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/task-management/tickets/1.mdwn;h=1e660face13843a722026d94ec4c2f51b2160069;hp=30461446304a9c48ebbcdf9004b5e046abf60f39;hb=acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e;hpb=16c22f6a025e24ca916e8312b3b5fe9146858523" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ftask-management%2Ftickets%2F1" rel="nofollow">projects/task-management/tickets/1</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:58:05 PM 07/28/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=acf45a5f2a24ba04a35e9f0d8d590bf0a41c0f3e" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> minor edit<br /> </div> change to projects projects/tui on Rel4tion http://www.rel4tion.org/recentchanges/change_16c22f6a025e24ca916e8312b3b5fe9146858523/ fr33domlover 2015-07-28T12:56:11Z 2015-07-28T12:56:11Z <div id="change-16c22f6a025e24ca916e8312b3b5fe9146858523" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects.mdwn;h=8a382bc0b1a8668b14aad89aa4bc16711a1ed84a;hp=ec5984beb457557cddbd8d1ea7df80db9d42a705;hb=16c22f6a025e24ca916e8312b3b5fe9146858523;hpb=c8f6c3fca3796e7b4ee0148cab287d6ba5866f6c" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects&amp;do=goto" rel="nofollow">projects</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/tui.mdwn;h=ed4986fab9a9c1d0a320e0aebbd548e2046f94a8;hp=0000000000000000000000000000000000000000;hb=16c22f6a025e24ca916e8312b3b5fe9146858523;hpb=c8f6c3fca3796e7b4ee0148cab287d6ba5866f6c" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ftui" rel="nofollow">projects/tui</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:56:11 PM 07/28/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=16c22f6a025e24ca916e8312b3b5fe9146858523" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> tui: research project<br /> </div> change to projects/funbot/tickets/10 on Rel4tion http://www.rel4tion.org/recentchanges/change_c8f6c3fca3796e7b4ee0148cab287d6ba5866f6c/ fr33domlover 2015-07-27T22:30:00Z 2015-07-27T22:30:00Z <div id="change-c8f6c3fca3796e7b4ee0148cab287d6ba5866f6c" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/10.mdwn;h=2dfa6c5aa076d72de618c647a17774e23cacf78f;hp=7db97bbee99f558e4dc6164e60ef81504d4afe24;hb=c8f6c3fca3796e7b4ee0148cab287d6ba5866f6c;hpb=3d15ff1b8068e97901410a3d210227036bf528c5" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F10&amp;do=goto" rel="nofollow">projects/funbot/tickets/10</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">10:30:00 PM 07/27/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=c8f6c3fca3796e7b4ee0148cab287d6ba5866f6c&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: update status of hello replies<br /> </div> change to projects/funbot/tickets/15 on Rel4tion http://www.rel4tion.org/recentchanges/change_3d15ff1b8068e97901410a3d210227036bf528c5/ fr33domlover 2015-07-27T21:43:34Z 2015-07-27T21:43:34Z <div id="change-3d15ff1b8068e97901410a3d210227036bf528c5" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/15.mdwn;h=9fde6e22407a2eff47b1a666b3d3c274e5e19859;hp=8409ef6dc3f7697857b7aacdfc1d0d03ebd8e97b;hb=3d15ff1b8068e97901410a3d210227036bf528c5;hpb=2af6df1680072d6e73f4bdb07fae2c88a59c76aa" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F15&amp;do=goto" rel="nofollow">projects/funbot/tickets/15</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:43:34 PM 07/27/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=3d15ff1b8068e97901410a3d210227036bf528c5&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: update status of settings system<br /> </div> change to projects/funbot/guide projects/funbot/manual on Rel4tion http://www.rel4tion.org/recentchanges/change_2af6df1680072d6e73f4bdb07fae2c88a59c76aa/ fr33domlover 2015-07-27T21:33:18Z 2015-07-27T21:33:18Z <div id="change-2af6df1680072d6e73f4bdb07fae2c88a59c76aa" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/guide.mdwn;h=6234c5babf562a6e0554c81171beb169c721b6de;hp=9d46ed7be13811c424111b5f29dc6ef641e5754a;hb=2af6df1680072d6e73f4bdb07fae2c88a59c76aa;hpb=617249a360c6973ce7f42ad010f1276c552fbaf1" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Fguide" rel="nofollow">projects/funbot/guide</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/manual.mdwn;h=4fb784a3bbd9efa372438f998917e996c95946d6;hp=f179ef3cc7731f76ad0bae8b31a4e64a3f8d6871;hb=2af6df1680072d6e73f4bdb07fae2c88a59c76aa;hpb=617249a360c6973ce7f42ad010f1276c552fbaf1" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Fmanual&amp;do=goto" rel="nofollow">projects/funbot/manual</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:33:18 PM 07/27/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=2af6df1680072d6e73f4bdb07fae2c88a59c76aa" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: update docs to match the newly added settings system<br /> </div> change to projects projects/funbot/tickets/15 projects/settings projects/settings/decisions projects/settings/forum projects/settings/news projects/settings/releases projects/settings/tickets on Rel4tion http://www.rel4tion.org/recentchanges/change_617249a360c6973ce7f42ad010f1276c552fbaf1/ fr33domlover 2015-07-22T09:46:41Z 2015-07-22T09:46:41Z <div id="change-617249a360c6973ce7f42ad010f1276c552fbaf1" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects.mdwn;h=ec5984beb457557cddbd8d1ea7df80db9d42a705;hp=9d82d759e2e847c0b174cf1bbe7f57d75c14474f;hb=617249a360c6973ce7f42ad010f1276c552fbaf1;hpb=5f4444298ebe3f674b73a843dfc7bbef7a1703e7" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects" rel="nofollow">projects</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/15.mdwn;h=8409ef6dc3f7697857b7aacdfc1d0d03ebd8e97b;hp=a19cd36f2568480158c63ddec2185754cab96f30;hb=617249a360c6973ce7f42ad010f1276c552fbaf1;hpb=5f4444298ebe3f674b73a843dfc7bbef7a1703e7" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F15" rel="nofollow">projects/funbot/tickets/15</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings.mdwn;h=b7f5ffc7ee35f85a7a7d5094fdc02c578122825e;hp=0000000000000000000000000000000000000000;hb=617249a360c6973ce7f42ad010f1276c552fbaf1;hpb=5f4444298ebe3f674b73a843dfc7bbef7a1703e7" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fsettings" rel="nofollow">projects/settings</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/decisions.mdwn;h=7fa56d100854163543f12f83cc1408ae417f9f7e;hp=0000000000000000000000000000000000000000;hb=617249a360c6973ce7f42ad010f1276c552fbaf1;hpb=5f4444298ebe3f674b73a843dfc7bbef7a1703e7" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fsettings%2Fdecisions&amp;do=goto" rel="nofollow">projects/settings/decisions</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/forum.mdwn;h=c3b79bd4409643fdbc11a700b29898f904b315b7;hp=0000000000000000000000000000000000000000;hb=617249a360c6973ce7f42ad010f1276c552fbaf1;hpb=5f4444298ebe3f674b73a843dfc7bbef7a1703e7" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fsettings%2Fforum" rel="nofollow">projects/settings/forum</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/news.mdwn;h=837c8697a653db420eada75ea57630492eb8338e;hp=0000000000000000000000000000000000000000;hb=617249a360c6973ce7f42ad010f1276c552fbaf1;hpb=5f4444298ebe3f674b73a843dfc7bbef7a1703e7" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fsettings%2Fnews&amp;do=goto" rel="nofollow">projects/settings/news</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/releases.mdwn;h=e4efd9b315242da6e04f9e94546ed3522bdaac68;hp=0000000000000000000000000000000000000000;hb=617249a360c6973ce7f42ad010f1276c552fbaf1;hpb=5f4444298ebe3f674b73a843dfc7bbef7a1703e7" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Fsettings%2Freleases" rel="nofollow">projects/settings/releases</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/settings/tickets.mdwn;h=e9398e875cda45921bd91361adb0716986869a15;hp=0000000000000000000000000000000000000000;hb=617249a360c6973ce7f42ad010f1276c552fbaf1;hpb=5f4444298ebe3f674b73a843dfc7bbef7a1703e7" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fsettings%2Ftickets&amp;do=goto" rel="nofollow">projects/settings/tickets</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:46:41 AM 07/22/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=617249a360c6973ce7f42ad010f1276c552fbaf1" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> settings: *another* new project...<br /> </div> change to projects/funbot/tickets/15 on Rel4tion http://www.rel4tion.org/recentchanges/change_5f4444298ebe3f674b73a843dfc7bbef7a1703e7/ fr33domlover 2015-07-21T10:47:23Z 2015-07-21T10:47:23Z <div id="change-5f4444298ebe3f674b73a843dfc7bbef7a1703e7" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/15.mdwn;h=a19cd36f2568480158c63ddec2185754cab96f30;hp=038b445accf173eee423a1a03eab722fe86ef030;hb=5f4444298ebe3f674b73a843dfc7bbef7a1703e7;hpb=76a709cf4777cf20cf18b62c3e557f380b98a145" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F15" rel="nofollow">projects/funbot/tickets/15</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">10:47:23 AM 07/21/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=5f4444298ebe3f674b73a843dfc7bbef7a1703e7" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: notes about settings system<br /> </div> change to projects/funbot/tickets/15 projects/funbot/tickets/4 projects/irc-fun-client/tickets/2 on Rel4tion http://www.rel4tion.org/recentchanges/change_76a709cf4777cf20cf18b62c3e557f380b98a145/ fr33domlover 2015-07-20T12:22:27Z 2015-07-20T12:22:27Z <div id="change-76a709cf4777cf20cf18b62c3e557f380b98a145" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/15.mdwn;h=038b445accf173eee423a1a03eab722fe86ef030;hp=0000000000000000000000000000000000000000;hb=76a709cf4777cf20cf18b62c3e557f380b98a145;hpb=f65b75264b89071970cf00ff16776aa9d5969437" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F15&amp;do=goto" rel="nofollow">projects/funbot/tickets/15</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/4.mdwn;h=071cb7e64ae57c211834ee5cc7e80132209a6bc2;hp=7665ef7035b4387f9ceb84ca9cd79130b81d4d48;hb=76a709cf4777cf20cf18b62c3e557f380b98a145;hpb=f65b75264b89071970cf00ff16776aa9d5969437" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F4" rel="nofollow">projects/funbot/tickets/4</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-client/tickets/2.mdwn;h=ae578674d3cceaaa6cbe726d632bc3c4e87bfd55;hp=0000000000000000000000000000000000000000;hb=76a709cf4777cf20cf18b62c3e557f380b98a145;hpb=f65b75264b89071970cf00ff16776aa9d5969437" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-client%2Ftickets%2F2&amp;do=goto" rel="nofollow">projects/irc-fun-client/tickets/2</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:22:27 PM 07/20/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=76a709cf4777cf20cf18b62c3e557f380b98a145" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: more tickets...<br /> </div> change to projects/irc-fun-messages/tickets/2 on Rel4tion http://www.rel4tion.org/recentchanges/change_f65b75264b89071970cf00ff16776aa9d5969437/ fr33domlover 2015-07-20T09:12:23Z 2015-07-20T09:12:23Z <div id="change-f65b75264b89071970cf00ff16776aa9d5969437" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-messages/tickets/2.mdwn;h=e4e35c75ededb220d4ee788abd52f17ab0510352;hp=b97c3b915afa5aee1527549c97939c20979e11ba;hb=f65b75264b89071970cf00ff16776aa9d5969437;hpb=b0d2b4a707f9b0bbe5373a5ad960487b66142b41" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-messages%2Ftickets%2F2" rel="nofollow">projects/irc-fun-messages/tickets/2</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:12:23 AM 07/20/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=f65b75264b89071970cf00ff16776aa9d5969437" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> irc-fun-messages: rearrangemet done<br /> </div> change to templates/my-open-tickets on Rel4tion http://www.rel4tion.org/recentchanges/change_b0d2b4a707f9b0bbe5373a5ad960487b66142b41/ fr33domlover 2015-07-20T09:09:43Z 2015-07-20T09:09:43Z <div id="change-b0d2b4a707f9b0bbe5373a5ad960487b66142b41" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/my-open-tickets.mdwn;h=d5fc4b5cd9696f21ab75842518f2b7a4f5fd2ebc;hp=6508a11cf60dd90ba71b998848e829d385c495d2;hb=b0d2b4a707f9b0bbe5373a5ad960487b66142b41;hpb=7bc4f88b58f7f0deadb8a79a9823a564cf399b03" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Fmy-open-tickets&amp;do=goto" rel="nofollow">templates/my-open-tickets</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:09:43 AM 07/20/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=b0d2b4a707f9b0bbe5373a5ad960487b66142b41" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> fix template typo<br /> </div> change to people/fr33domlover on Rel4tion http://www.rel4tion.org/recentchanges/change_7bc4f88b58f7f0deadb8a79a9823a564cf399b03/ fr33domlover 2015-07-20T09:06:01Z 2015-07-20T09:06:01Z <div id="change-7bc4f88b58f7f0deadb8a79a9823a564cf399b03" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=people/fr33domlover.mdwn;h=2230d818ac803844191d7842bcb24fa4d5e45e14;hp=f28b90121dbd2ea1427b476ec65ac299d5a362a7;hb=7bc4f88b58f7f0deadb8a79a9823a564cf399b03;hpb=e970fbaa161547af8a7b378220a602c00c128b20" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">people/fr33domlover</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:06:01 AM 07/20/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=7bc4f88b58f7f0deadb8a79a9823a564cf399b03&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> me: use the new template for personal tickets<br /> </div> change to templates/my-open-tickets templates/ticket tickets on Rel4tion http://www.rel4tion.org/recentchanges/change_e970fbaa161547af8a7b378220a602c00c128b20/ fr33domlover 2015-07-20T09:02:35Z 2015-07-20T09:02:35Z <div id="change-e970fbaa161547af8a7b378220a602c00c128b20" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/my-open-tickets.mdwn;h=6508a11cf60dd90ba71b998848e829d385c495d2;hp=0000000000000000000000000000000000000000;hb=e970fbaa161547af8a7b378220a602c00c128b20;hpb=f650bd6d11c9f12fa342e32a1112e0326f5983a0" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Fmy-open-tickets&amp;do=goto" rel="nofollow">templates/my-open-tickets</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/ticket.mdwn;h=69a8e86734dca4aec465f6d7fc4f7d6b958e9a51;hp=7adaf50d169a1a3e068b272427c5ccd010b304ea;hb=e970fbaa161547af8a7b378220a602c00c128b20;hpb=f650bd6d11c9f12fa342e32a1112e0326f5983a0" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Fticket&amp;do=goto" rel="nofollow">templates/ticket</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=tickets.mdwn;h=3b3e0f3959d65e0100c507940f20921dbad4220a;hp=b28306347b2b49e222ec75f0610cfceff8ff7598;hb=e970fbaa161547af8a7b378220a602c00c128b20;hpb=f650bd6d11c9f12fa342e32a1112e0326f5983a0" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=tickets" rel="nofollow">tickets</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:02:35 AM 07/20/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=e970fbaa161547af8a7b378220a602c00c128b20&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> new template my-open-tickets<br /> </div> change to tickets on Rel4tion http://www.rel4tion.org/recentchanges/change_f650bd6d11c9f12fa342e32a1112e0326f5983a0/ fr33domlover 2015-07-20T07:58:09Z 2015-07-20T07:58:09Z <div id="change-f650bd6d11c9f12fa342e32a1112e0326f5983a0" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=tickets.mdwn;h=b28306347b2b49e222ec75f0610cfceff8ff7598;hp=bfc33e243c877bdc29e26067fcccf60865b950f8;hb=f650bd6d11c9f12fa342e32a1112e0326f5983a0;hpb=62e6b115766812a8b7dc1661af21071b300ecf9d" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=tickets&amp;do=goto" rel="nofollow">tickets</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:58:09 AM 07/20/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=f650bd6d11c9f12fa342e32a1112e0326f5983a0&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> tickets: add some notes and hints<br /> </div> change to projects/funbot/tickets/2 on Rel4tion http://www.rel4tion.org/recentchanges/change_62e6b115766812a8b7dc1661af21071b300ecf9d/ fr33domlover 2015-07-20T07:26:24Z 2015-07-20T07:26:24Z <div id="change-62e6b115766812a8b7dc1661af21071b300ecf9d" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/2.mdwn;h=2db571641ae6d30d6d74d401e2e96015b833b811;hp=ddf42fa6367bcebe483831befd8a9739a418eb69;hb=62e6b115766812a8b7dc1661af21071b300ecf9d;hpb=d1a178ecde1695f74f21132068fa479443fd364f" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Ffunbot%2Ftickets%2F2&amp;do=goto" rel="nofollow">projects/funbot/tickets/2</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:26:24 AM 07/20/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=62e6b115766812a8b7dc1661af21071b300ecf9d" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: list options for persistence<br /> </div> change to projects/funbot/tickets/2 on Rel4tion http://www.rel4tion.org/recentchanges/change_d1a178ecde1695f74f21132068fa479443fd364f/ fr33domlover 2015-07-19T21:12:25Z 2015-07-19T21:12:25Z <div id="change-d1a178ecde1695f74f21132068fa479443fd364f" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/2.mdwn;h=ddf42fa6367bcebe483831befd8a9739a418eb69;hp=767aaad5c981d66c8cc3f76b8ffd0b8a172c5840;hb=d1a178ecde1695f74f21132068fa479443fd364f;hpb=9aacf3255d7b2d65ff1a4a3e564e0cc35df5864a" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F2" rel="nofollow">projects/funbot/tickets/2</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:12:25 PM 07/19/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=d1a178ecde1695f74f21132068fa479443fd364f&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: notes on persistent state<br /> </div> change to people/fr33domlover/blog/insanity on Rel4tion http://www.rel4tion.org/recentchanges/change_9aacf3255d7b2d65ff1a4a3e564e0cc35df5864a/ fr33domlover 2015-07-19T20:28:42Z 2015-07-19T20:28:42Z <div id="change-9aacf3255d7b2d65ff1a4a3e564e0cc35df5864a" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=people/fr33domlover/blog/insanity.mdwn;h=ebaa53ca277a0e595b242642aeed217a2d596b54;hp=0000000000000000000000000000000000000000;hb=9aacf3255d7b2d65ff1a4a3e564e0cc35df5864a;hpb=6f2e5495944f200e1e6dd231d85ecbdfbb1aa372" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover%2Fblog%2Finsanity&amp;do=goto" rel="nofollow">people/fr33domlover/blog/insanity</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">08:28:42 PM 07/19/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=9aacf3255d7b2d65ff1a4a3e564e0cc35df5864a" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> me: some blog post...<br /> </div> change to templates/ticket-depends-on on Rel4tion http://www.rel4tion.org/recentchanges/change_6f2e5495944f200e1e6dd231d85ecbdfbb1aa372/ fr33domlover 2015-07-19T09:26:58Z 2015-07-19T09:26:58Z <div id="change-6f2e5495944f200e1e6dd231d85ecbdfbb1aa372" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/ticket-depends-on.mdwn;h=f83eb1c67a3ccac4e181ab4fba9a9dff0a6b6a80;hp=a7f05948f92be66460fa39f2d7ac61c863886994;hb=6f2e5495944f200e1e6dd231d85ecbdfbb1aa372;hpb=b3db4bb425ef79edc4269c0ad57e47685eb888b7" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Fticket-depends-on&amp;do=goto" rel="nofollow">templates/ticket-depends-on</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:26:58 AM 07/19/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=6f2e5495944f200e1e6dd231d85ecbdfbb1aa372&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> if failing, at least fail explicitly<br /> </div> change to templates/ticket-depends-on on Rel4tion http://www.rel4tion.org/recentchanges/change_b3db4bb425ef79edc4269c0ad57e47685eb888b7/ fr33domlover 2015-07-19T09:15:03Z 2015-07-19T09:15:03Z <div id="change-b3db4bb425ef79edc4269c0ad57e47685eb888b7" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/ticket-depends-on.mdwn;h=a7f05948f92be66460fa39f2d7ac61c863886994;hp=0279afa3d1b2bb1796ba9e32955894c6e8436c5e;hb=b3db4bb425ef79edc4269c0ad57e47685eb888b7;hpb=5b2a8e163184284378d48d2419134d6f5e4fabbe" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=templates%2Fticket-depends-on" rel="nofollow">templates/ticket-depends-on</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:15:03 AM 07/19/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=b3db4bb425ef79edc4269c0ad57e47685eb888b7&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> last try, I have no idea about those variables<br /> </div> change to templates/ticket-depends-on on Rel4tion http://www.rel4tion.org/recentchanges/change_5b2a8e163184284378d48d2419134d6f5e4fabbe/ fr33domlover 2015-07-19T09:00:55Z 2015-07-19T09:00:55Z <div id="change-5b2a8e163184284378d48d2419134d6f5e4fabbe" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/ticket-depends-on.mdwn;h=0279afa3d1b2bb1796ba9e32955894c6e8436c5e;hp=6e5d9e5744a8b7f07502689526a0bb398df695a6;hb=5b2a8e163184284378d48d2419134d6f5e4fabbe;hpb=476809c178ec0873f4580309443add856c64f339" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Fticket-depends-on&amp;do=goto" rel="nofollow">templates/ticket-depends-on</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">09:00:55 AM 07/19/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=5b2a8e163184284378d48d2419134d6f5e4fabbe" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> try again<br /> </div> change to templates/ticket-depends-on on Rel4tion http://www.rel4tion.org/recentchanges/change_476809c178ec0873f4580309443add856c64f339/ fr33domlover 2015-07-19T08:43:09Z 2015-07-19T08:43:09Z <div id="change-476809c178ec0873f4580309443add856c64f339" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/ticket-depends-on.mdwn;h=6e5d9e5744a8b7f07502689526a0bb398df695a6;hp=f83eb1c67a3ccac4e181ab4fba9a9dff0a6b6a80;hb=476809c178ec0873f4580309443add856c64f339;hpb=42242ce1aeb36d653d3312ee46603ca1baeb5f78" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Fticket-depends-on&amp;do=goto" rel="nofollow">templates/ticket-depends-on</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">08:43:09 AM 07/19/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=476809c178ec0873f4580309443add856c64f339&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> try to fix local ticket dependency template<br /> </div> change to projects/irc-fun-bot/tickets/5 on Rel4tion http://www.rel4tion.org/recentchanges/change_42242ce1aeb36d653d3312ee46603ca1baeb5f78/ fr33domlover 2015-07-19T00:52:24Z 2015-07-19T00:52:24Z <div id="change-42242ce1aeb36d653d3312ee46603ca1baeb5f78" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/5.mdwn;h=b9debec3ded1ba8f5151f21acfc3ce6f340f9d06;hp=0e5a1030b7c2ef5dbd1c505352d9172e94bf04d3;hb=42242ce1aeb36d653d3312ee46603ca1baeb5f78;hpb=bd0e46e488bbeb8b216f6ab1aefd98083ec61f16" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-bot%2Ftickets%2F5&amp;do=goto" rel="nofollow">projects/irc-fun-bot/tickets/5</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:52:24 AM 07/19/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=42242ce1aeb36d653d3312ee46603ca1baeb5f78&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> fix typo<br /> </div> change to projects/irc-fun-bot/tickets/5 on Rel4tion http://www.rel4tion.org/recentchanges/change_bd0e46e488bbeb8b216f6ab1aefd98083ec61f16/ fr33domlover 2015-07-19T00:48:08Z 2015-07-19T00:48:08Z <div id="change-bd0e46e488bbeb8b216f6ab1aefd98083ec61f16" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/5.mdwn;h=0e5a1030b7c2ef5dbd1c505352d9172e94bf04d3;hp=fac452baf181d140dfd541dd3e348b03f4387a1a;hb=bd0e46e488bbeb8b216f6ab1aefd98083ec61f16;hpb=e048d2ba90a4e6f94784c809618051f00a863627" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-bot%2Ftickets%2F5&amp;do=goto" rel="nofollow">projects/irc-fun-bot/tickets/5</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:48:08 AM 07/19/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=bd0e46e488bbeb8b216f6ab1aefd98083ec61f16&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> irc-fun-bot: close ticket 5, user tracking works<br /> </div> change to templates/tktref on Rel4tion http://www.rel4tion.org/recentchanges/change_e048d2ba90a4e6f94784c809618051f00a863627/ fr33domlover 2015-07-18T16:18:02Z 2015-07-18T16:18:02Z <div id="change-e048d2ba90a4e6f94784c809618051f00a863627" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/tktref.mdwn;h=a0c97a7571758f0b9ecbdd87da123ddc23e3add3;hp=fc64ea4a97b82588f1cb5c72d9875cbedc196f6b;hb=e048d2ba90a4e6f94784c809618051f00a863627;hpb=64fc69565cd08b8438d34b6453840eec6c328677" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Ftktref&amp;do=goto" rel="nofollow">templates/tktref</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">04:18:02 PM 07/18/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=e048d2ba90a4e6f94784c809618051f00a863627&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> try to fix tktlink rendering<br /> </div> change to projects/irc-fun-bot/tickets/5 projects/irc-fun-messages/tickets/1 on Rel4tion http://www.rel4tion.org/recentchanges/change_64fc69565cd08b8438d34b6453840eec6c328677/ fr33domlover 2015-07-18T16:13:16Z 2015-07-18T16:13:16Z <div id="change-64fc69565cd08b8438d34b6453840eec6c328677" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/5.mdwn;h=fac452baf181d140dfd541dd3e348b03f4387a1a;hp=c3fda7dceeea8bd5b28f491e969db289e813a59a;hb=64fc69565cd08b8438d34b6453840eec6c328677;hpb=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-bot%2Ftickets%2F5" rel="nofollow">projects/irc-fun-bot/tickets/5</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-messages/tickets/1.mdwn;h=b591bb5228526e4e0c28619d1d51642bd35db0ca;hp=9ef221af24707831f812d2a735fcfa1ed60c2188;hb=64fc69565cd08b8438d34b6453840eec6c328677;hpb=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-messages%2Ftickets%2F1&amp;do=goto" rel="nofollow">projects/irc-fun-messages/tickets/1</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">04:13:16 PM 07/18/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=64fc69565cd08b8438d34b6453840eec6c328677&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> irc-fun-*: reply analysis added, now track nicknames<br /> </div> change to projects projects/irc-fun-color projects/irc-fun-color/decisions projects/irc-fun-color/forum projects/irc-fun-color/news projects/irc-fun-color/releases projects/irc-fun-color/tickets on Rel4tion http://www.rel4tion.org/recentchanges/change_9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8/ fr33domlover 2015-07-18T15:08:45Z 2015-07-18T15:08:45Z <div id="change-9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects.mdwn;h=9d82d759e2e847c0b174cf1bbe7f57d75c14474f;hp=2f60c659996eb1080d7af967e9041d62d77d84c9;hb=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8;hpb=3d1ea877911070c0d95bb46c10568bde7b956ae6" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects&amp;do=goto" rel="nofollow">projects</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-color.mdwn;h=55a94ef29608a2cf99061cfff38117975ad31fa3;hp=0000000000000000000000000000000000000000;hb=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8;hpb=3d1ea877911070c0d95bb46c10568bde7b956ae6" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-color" rel="nofollow">projects/irc-fun-color</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-color/decisions.mdwn;h=86674bf28a1eac5aa6a8f6bf02a5f3611fae5afe;hp=0000000000000000000000000000000000000000;hb=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8;hpb=3d1ea877911070c0d95bb46c10568bde7b956ae6" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-color%2Fdecisions&amp;do=goto" rel="nofollow">projects/irc-fun-color/decisions</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-color/forum.mdwn;h=f297d56c4a7e9099c511dafd8deeec28222e3e17;hp=0000000000000000000000000000000000000000;hb=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8;hpb=3d1ea877911070c0d95bb46c10568bde7b956ae6" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-color%2Fforum" rel="nofollow">projects/irc-fun-color/forum</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-color/news.mdwn;h=330c55f6fd7f2c4a010044249adcb5456d9658a3;hp=0000000000000000000000000000000000000000;hb=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8;hpb=3d1ea877911070c0d95bb46c10568bde7b956ae6" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-color%2Fnews&amp;do=goto" rel="nofollow">projects/irc-fun-color/news</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-color/releases.mdwn;h=57e5729d6313d999900ce903adcc35389f4686bb;hp=0000000000000000000000000000000000000000;hb=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8;hpb=3d1ea877911070c0d95bb46c10568bde7b956ae6" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-color%2Freleases&amp;do=goto" rel="nofollow">projects/irc-fun-color/releases</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-color/tickets.mdwn;h=707729b4f5edb530c7493f701639e14441ece6fb;hp=0000000000000000000000000000000000000000;hb=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8;hpb=3d1ea877911070c0d95bb46c10568bde7b956ae6" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-color%2Ftickets" rel="nofollow">projects/irc-fun-color/tickets</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">03:08:45 PM 07/18/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=9cf06f38584d7b9c6e9a1c29a8fde5ff7a1836b8&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> irc-fun-color: new project<br /> </div> change to templates/ticket-depends-on on Rel4tion http://www.rel4tion.org/recentchanges/change_3d1ea877911070c0d95bb46c10568bde7b956ae6/ fr33domlover 2015-07-17T12:13:32Z 2015-07-17T12:13:32Z <div id="change-3d1ea877911070c0d95bb46c10568bde7b956ae6" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/ticket-depends-on.mdwn;h=f83eb1c67a3ccac4e181ab4fba9a9dff0a6b6a80;hp=36db3bfac2156cf7980068af0606bf464cd5b6ec;hb=3d1ea877911070c0d95bb46c10568bde7b956ae6;hpb=5361eae41a1dbd74b511f97df16687a0278063d6" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Fticket-depends-on&amp;do=goto" rel="nofollow">templates/ticket-depends-on</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:13:32 PM 07/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=3d1ea877911070c0d95bb46c10568bde7b956ae6" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> template: try to make ticket-depends-on render as a single line<br /> </div> change to templates/tktref on Rel4tion http://www.rel4tion.org/recentchanges/change_5361eae41a1dbd74b511f97df16687a0278063d6/ fr33domlover 2015-07-17T12:07:39Z 2015-07-17T12:07:39Z <div id="change-5361eae41a1dbd74b511f97df16687a0278063d6" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/tktref.mdwn;h=fc64ea4a97b82588f1cb5c72d9875cbedc196f6b;hp=cc3d879c0f434325cf3e0a35768810a4d4c40cb3;hb=5361eae41a1dbd74b511f97df16687a0278063d6;hpb=8acf634f485a70e7f54faa48d5ceaff705849359" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=templates%2Ftktref" rel="nofollow">templates/tktref</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:07:39 PM 07/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=5361eae41a1dbd74b511f97df16687a0278063d6" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> templates: fix typo in tktref<br /> </div> change to templates/ticket-dependants templates/ticket on Rel4tion http://www.rel4tion.org/recentchanges/change_8acf634f485a70e7f54faa48d5ceaff705849359/ fr33domlover 2015-07-17T11:59:11Z 2015-07-17T11:59:11Z <div id="change-8acf634f485a70e7f54faa48d5ceaff705849359" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/ticket-dependants.mdwn;h=8996711ad0b53f3acc95eb2fb577260f56f99589;hp=0000000000000000000000000000000000000000;hb=8acf634f485a70e7f54faa48d5ceaff705849359;hpb=4826e57427eb5fdc542b22534276401ef0e0a6bb" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=templates%2Fticket-dependants" rel="nofollow">templates/ticket-dependants</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/ticket.mdwn;h=7adaf50d169a1a3e068b272427c5ccd010b304ea;hp=cc70c7b4bcf5718cf457b8bc8f9760c3c2dec296;hb=8acf634f485a70e7f54faa48d5ceaff705849359;hpb=4826e57427eb5fdc542b22534276401ef0e0a6bb" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Fticket&amp;do=goto" rel="nofollow">templates/ticket</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">11:59:11 AM 07/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=8acf634f485a70e7f54faa48d5ceaff705849359&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> add ticket reverse dependencies using tags<br /> </div> change to projects/funbot/tickets/10 projects/funbot/tickets/3 projects/funbot/tickets/5 projects/idan/tickets/4 projects/irc-fun-bot/tickets/5 projects/sif/tickets/Remove_doxy_references_to_nonfree_browsers projects/sif/tickets/Remove_unused_doxy_images templates/ticket-depends-on on Rel4tion http://www.rel4tion.org/recentchanges/change_4826e57427eb5fdc542b22534276401ef0e0a6bb/ fr33domlover 2015-07-17T11:27:55Z 2015-07-17T11:27:55Z <div id="change-4826e57427eb5fdc542b22534276401ef0e0a6bb" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/10.mdwn;h=7db97bbee99f558e4dc6164e60ef81504d4afe24;hp=f679a8578818b1b59ef502b4e83d972a2696ba2a;hb=4826e57427eb5fdc542b22534276401ef0e0a6bb;hpb=2a4c41ea9590cd73e0aec952e93f745172a32217" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F10" rel="nofollow">projects/funbot/tickets/10</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/3.mdwn;h=111efa1b015906623afc28c27ee52742a7cddbd8;hp=09da3b9886f12272ba40bde42dcb4ebbd0846de9;hb=4826e57427eb5fdc542b22534276401ef0e0a6bb;hpb=2a4c41ea9590cd73e0aec952e93f745172a32217" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F3" rel="nofollow">projects/funbot/tickets/3</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/5.mdwn;h=606f28657913248cbfe3204aa6f0ffebec656b55;hp=f605f0ecc0227205dad3b5d4b16c4fdb85df5eb5;hb=4826e57427eb5fdc542b22534276401ef0e0a6bb;hpb=2a4c41ea9590cd73e0aec952e93f745172a32217" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F5" rel="nofollow">projects/funbot/tickets/5</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/idan/tickets/4.mdwn;h=a4e6bd9fb63289481b2ebea90b374aa0236a604a;hp=e4f1d49482e7e2c66e404a9a0efcdc0bfb29950d;hb=4826e57427eb5fdc542b22534276401ef0e0a6bb;hpb=2a4c41ea9590cd73e0aec952e93f745172a32217" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fidan%2Ftickets%2F4&amp;do=goto" rel="nofollow">projects/idan/tickets/4</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/5.mdwn;h=c3fda7dceeea8bd5b28f491e969db289e813a59a;hp=215a25f288dfeb80ea06314a6c1b860a1476622e;hb=4826e57427eb5fdc542b22534276401ef0e0a6bb;hpb=2a4c41ea9590cd73e0aec952e93f745172a32217" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-bot%2Ftickets%2F5" rel="nofollow">projects/irc-fun-bot/tickets/5</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/sif/tickets/Remove_doxy_references_to_nonfree_browsers.mdwn;h=944fb3a116e57ee64af34d891fa43327468b0228;hp=5b6919b3d065d46f997e6abb1f63130978a5e9af;hb=4826e57427eb5fdc542b22534276401ef0e0a6bb;hpb=2a4c41ea9590cd73e0aec952e93f745172a32217" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fsif%2Ftickets%2FRemove_doxy_references_to_nonfree_browsers&amp;do=goto" rel="nofollow">projects/sif/tickets/Remove doxy references to nonfree browsers</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/sif/tickets/Remove_unused_doxy_images.mdwn;h=7f4205dd1dd777bbbf86e08147e73c427c66d984;hp=c54d10be8cc161461d2d1b535b29043cdbdb9fb6;hb=4826e57427eb5fdc542b22534276401ef0e0a6bb;hpb=2a4c41ea9590cd73e0aec952e93f745172a32217" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Fsif%2Ftickets%2FRemove_unused_doxy_images&amp;do=goto" rel="nofollow">projects/sif/tickets/Remove unused doxy images</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/ticket-depends-on.mdwn;h=36db3bfac2156cf7980068af0606bf464cd5b6ec;hp=55583bce2cd010f8cf8b3271621428169d3b57a2;hb=4826e57427eb5fdc542b22534276401ef0e0a6bb;hpb=2a4c41ea9590cd73e0aec952e93f745172a32217" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Fticket-depends-on&amp;do=goto" rel="nofollow">templates/ticket-depends-on</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">11:27:55 AM 07/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=4826e57427eb5fdc542b22534276401ef0e0a6bb" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> templates: hopefully improve ticket-depends-on<br /> </div> change to projects/irc-fun-bot/tickets/5 projects/irc-fun-messages/tickets/1 projects/irc-fun-messages/tickets/2 on Rel4tion http://www.rel4tion.org/recentchanges/change_2a4c41ea9590cd73e0aec952e93f745172a32217/ fr33domlover 2015-07-17T10:45:46Z 2015-07-17T10:45:46Z <div id="change-2a4c41ea9590cd73e0aec952e93f745172a32217" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/5.mdwn;h=215a25f288dfeb80ea06314a6c1b860a1476622e;hp=9c3b99793d6df15691e519bd472b674b37b871a6;hb=2a4c41ea9590cd73e0aec952e93f745172a32217;hpb=8fb78e1a159821745b973337a7267635a536f3fc" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-bot%2Ftickets%2F5" rel="nofollow">projects/irc-fun-bot/tickets/5</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-messages/tickets/1.mdwn;h=9ef221af24707831f812d2a735fcfa1ed60c2188;hp=0000000000000000000000000000000000000000;hb=2a4c41ea9590cd73e0aec952e93f745172a32217;hpb=8fb78e1a159821745b973337a7267635a536f3fc" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-messages%2Ftickets%2F1" rel="nofollow">projects/irc-fun-messages/tickets/1</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-messages/tickets/2.mdwn;h=b97c3b915afa5aee1527549c97939c20979e11ba;hp=0000000000000000000000000000000000000000;hb=2a4c41ea9590cd73e0aec952e93f745172a32217;hpb=8fb78e1a159821745b973337a7267635a536f3fc" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-messages%2Ftickets%2F2&amp;do=goto" rel="nofollow">projects/irc-fun-messages/tickets/2</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">10:45:46 AM 07/17/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=2a4c41ea9590cd73e0aec952e93f745172a32217&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> irc-fun-messages: open tickets for work being done<br /> </div> change to projects/funbot/tickets/14 projects/irc-fun-bot/tickets/6 on Rel4tion http://www.rel4tion.org/recentchanges/change_8fb78e1a159821745b973337a7267635a536f3fc/ fr33domlover 2015-07-16T12:50:22Z 2015-07-16T12:50:22Z <div id="change-8fb78e1a159821745b973337a7267635a536f3fc" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/14.mdwn;h=ec589115c87f1e070cf74a0e74bd88fa16320fd5;hp=0000000000000000000000000000000000000000;hb=8fb78e1a159821745b973337a7267635a536f3fc;hpb=18355348f643f521d5172530761ce01abe472ed9" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F14" rel="nofollow">projects/funbot/tickets/14</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/6.mdwn;h=dc30d1b5dbb1e84f25aebe68f2a00c626870469b;hp=0000000000000000000000000000000000000000;hb=8fb78e1a159821745b973337a7267635a536f3fc;hpb=18355348f643f521d5172530761ce01abe472ed9" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-bot%2Ftickets%2F6&amp;do=goto" rel="nofollow">projects/irc-fun-bot/tickets/6</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Ffr33domlover&amp;do=goto" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:50:22 PM 07/16/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=8fb78e1a159821745b973337a7267635a536f3fc&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: tickets for Tox and OTR support<br /> </div> change to projects/irc-fun-bot/tickets/5 on Rel4tion http://www.rel4tion.org/recentchanges/change_18355348f643f521d5172530761ce01abe472ed9/ fr33domlover 2015-07-15T12:43:41Z 2015-07-15T12:43:41Z <div id="change-18355348f643f521d5172530761ce01abe472ed9" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/5.mdwn;h=9c3b99793d6df15691e519bd472b674b37b871a6;hp=17f55a86e0f23d45f9b2edb4f6daec60c2dc0d82;hb=18355348f643f521d5172530761ce01abe472ed9;hpb=143590cacdab92492eb6a14e53c9810f4432cd4b" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-bot%2Ftickets%2F5" rel="nofollow">projects/irc-fun-bot/tickets/5</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:43:41 PM 07/15/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=18355348f643f521d5172530761ce01abe472ed9&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> irc-fun-bot: notes on online user tracking<br /> </div> change to projects/irc-fun-bot/tickets/4 on Rel4tion http://www.rel4tion.org/recentchanges/change_143590cacdab92492eb6a14e53c9810f4432cd4b/ fr33domlover 2015-07-15T12:28:53Z 2015-07-15T12:28:53Z <div id="change-143590cacdab92492eb6a14e53c9810f4432cd4b" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/4.mdwn;h=de50db2d43720e278918b08a15a70fb01065310c;hp=861efebe845edc70875723113005c9292a8181a6;hb=143590cacdab92492eb6a14e53c9810f4432cd4b;hpb=1d7deccdc53477739f5f644a14ddfc08276eb330" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-bot%2Ftickets%2F4" rel="nofollow">projects/irc-fun-bot/tickets/4</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:28:53 PM 07/15/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=143590cacdab92492eb6a14e53c9810f4432cd4b" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> irc-fun-bot: close ticket 4, event handling implemented<br /> </div> change to templates/commit templates/record on Rel4tion http://www.rel4tion.org/recentchanges/change_1d7deccdc53477739f5f644a14ddfc08276eb330/ fr33domlover 2015-07-15T12:28:19Z 2015-07-15T12:28:19Z <div id="change-1d7deccdc53477739f5f644a14ddfc08276eb330" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/commit.mdwn;h=2216a0ee714109c1860d714832f0a8e1935bdc0a;hp=e715dab0360f63da76233aa8d6018cb5511dd183;hb=1d7deccdc53477739f5f644a14ddfc08276eb330;hpb=74e38e99ab317f66273387b40a96f3817a9a269f" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=templates%2Fcommit" rel="nofollow">templates/commit</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/record.mdwn;h=b6d048ab252338b6994ea0ee8b04b73db7a4a778;hp=cf2916d42321a35e788ca8121243e4e3e9a1ca3c;hb=1d7deccdc53477739f5f644a14ddfc08276eb330;hpb=74e38e99ab317f66273387b40a96f3817a9a269f" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Frecord&amp;do=goto" rel="nofollow">templates/record</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:28:19 PM 07/15/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=1d7deccdc53477739f5f644a14ddfc08276eb330" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> templates: update record template link text<br /> </div> change to templates/commit templates/record on Rel4tion http://www.rel4tion.org/recentchanges/change_74e38e99ab317f66273387b40a96f3817a9a269f/ fr33domlover 2015-07-15T11:38:43Z 2015-07-15T11:38:43Z <div id="change-74e38e99ab317f66273387b40a96f3817a9a269f" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/commit.mdwn;h=e715dab0360f63da76233aa8d6018cb5511dd183;hp=0000000000000000000000000000000000000000;hb=74e38e99ab317f66273387b40a96f3817a9a269f;hpb=54fd05dc71003ad59211bf218528dd21f1386759" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=templates%2Fcommit" rel="nofollow">templates/commit</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=templates/record.mdwn;h=cf2916d42321a35e788ca8121243e4e3e9a1ca3c;hp=0000000000000000000000000000000000000000;hb=74e38e99ab317f66273387b40a96f3817a9a269f;hpb=54fd05dc71003ad59211bf218528dd21f1386759" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=templates%2Frecord&amp;do=goto" rel="nofollow">templates/record</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">11:38:43 AM 07/15/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=74e38e99ab317f66273387b40a96f3817a9a269f&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> templates: add git/darcs record templates<br /> </div> change to projects/funbot/guide on Rel4tion http://www.rel4tion.org/recentchanges/change_54fd05dc71003ad59211bf218528dd21f1386759/ fr33domlover 2015-07-15T11:38:19Z 2015-07-15T11:38:19Z <div id="change-54fd05dc71003ad59211bf218528dd21f1386759" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/guide.mdwn;h=9d46ed7be13811c424111b5f29dc6ef641e5754a;hp=ac4c2f5842f1194f7e20148f70c265952cb990f9;hb=54fd05dc71003ad59211bf218528dd21f1386759;hpb=07d23f596ab3b6281acdd9f80768ab64b014bd49" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Fguide" rel="nofollow">projects/funbot/guide</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">11:38:19 AM 07/15/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=54fd05dc71003ad59211bf218528dd21f1386759&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: add list of API features to the guide<br /> </div> change to projects/irc-fun-bot/tickets/4 on Rel4tion http://www.rel4tion.org/recentchanges/change_07d23f596ab3b6281acdd9f80768ab64b014bd49/ fr33domlover 2015-07-15T00:24:59Z 2015-07-15T00:24:59Z <div id="change-07d23f596ab3b6281acdd9f80768ab64b014bd49" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/4.mdwn;h=861efebe845edc70875723113005c9292a8181a6;hp=4cda87eb8f895f7dee560b8df646d1c7d5e4601a;hb=07d23f596ab3b6281acdd9f80768ab64b014bd49;hpb=2f5ca460f67dbbb1503a8bcf0a0f1a815f90ed9a" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Firc-fun-bot%2Ftickets%2F4" rel="nofollow">projects/irc-fun-bot/tickets/4</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:24:59 AM 07/15/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=07d23f596ab3b6281acdd9f80768ab64b014bd49&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> irc-fun-bot: more notes on ticket 4: events types<br /> </div> change to projects/funbot/tickets/13 on Rel4tion http://www.rel4tion.org/recentchanges/change_2f5ca460f67dbbb1503a8bcf0a0f1a815f90ed9a/ fr33domlover 2015-07-15T00:04:20Z 2015-07-15T00:04:20Z <div id="change-2f5ca460f67dbbb1503a8bcf0a0f1a815f90ed9a" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/13.mdwn;h=b6905577f100cfddf71d2589136637129d7016a1;hp=0000000000000000000000000000000000000000;hb=2f5ca460f67dbbb1503a8bcf0a0f1a815f90ed9a;hpb=efaad54e5facfab7d5048a058407a733b987f877" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F13" rel="nofollow">projects/funbot/tickets/13</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">12:04:20 AM 07/15/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=2f5ca460f67dbbb1503a8bcf0a0f1a815f90ed9a" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: new ticket: collecting quotes<br /> </div> change to projects/irc-fun-bot/tickets/4 on Rel4tion http://www.rel4tion.org/recentchanges/change_efaad54e5facfab7d5048a058407a733b987f877/ fr33domlover 2015-07-14T23:48:18Z 2015-07-14T23:48:18Z <div id="change-efaad54e5facfab7d5048a058407a733b987f877" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/irc-fun-bot/tickets/4.mdwn;h=4cda87eb8f895f7dee560b8df646d1c7d5e4601a;hp=d3eb459357a0fe4b6922f45adfaabf25f6753d68;hb=efaad54e5facfab7d5048a058407a733b987f877;hpb=ca1c11f89649f6d8e2ee1e875edf2c4232447c92" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Firc-fun-bot%2Ftickets%2F4&amp;do=goto" rel="nofollow">projects/irc-fun-bot/tickets/4</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">11:48:18 PM 07/14/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=efaad54e5facfab7d5048a058407a733b987f877" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> irc-fun-bot: thoughts on ticket 4<br /> </div> change to people/fr33domlover on Rel4tion http://www.rel4tion.org/recentchanges/change_ca1c11f89649f6d8e2ee1e875edf2c4232447c92/ fr33domlover 2015-07-13T19:56:20Z 2015-07-13T19:56:20Z <div id="change-ca1c11f89649f6d8e2ee1e875edf2c4232447c92" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=people/fr33domlover.mdwn;h=f28b90121dbd2ea1427b476ec65ac299d5a362a7;hp=6f209d9fe79aaea43836b9e010f6879ef7428cbc;hb=ca1c11f89649f6d8e2ee1e875edf2c4232447c92;hpb=6d3a6ade9202b71c87723db5d8d99f95625995d0" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">people/fr33domlover</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">07:56:20 PM 07/13/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=ca1c11f89649f6d8e2ee1e875edf2c4232447c92" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> me: not anymore<br /> </div> change to projects/funbot/manual projects/funbot/tickets/9 on Rel4tion http://www.rel4tion.org/recentchanges/change_6d3a6ade9202b71c87723db5d8d99f95625995d0/ fr33domlover 2015-07-13T16:31:31Z 2015-07-13T16:31:31Z <div id="change-6d3a6ade9202b71c87723db5d8d99f95625995d0" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/manual.mdwn;h=f179ef3cc7731f76ad0bae8b31a4e64a3f8d6871;hp=c664554baaa6d752bb6412372ae3e03c25f88691;hb=6d3a6ade9202b71c87723db5d8d99f95625995d0;hpb=75eade8953e45278446e742d970840fd5686a759" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Fmanual" rel="nofollow">projects/funbot/manual</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/9.mdwn;h=f3728f733ee4e81e0cf9e6aa40df787378449c85;hp=1dcfa08f772cc596392b61eaed26d494b37f4e17;hb=6d3a6ade9202b71c87723db5d8d99f95625995d0;hpb=75eade8953e45278446e742d970840fd5686a759" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F9" rel="nofollow">projects/funbot/tickets/9</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">04:31:31 PM 07/13/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=6d3a6ade9202b71c87723db5d8d99f95625995d0" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> funbot: finish manual and close ticket 9, Jookia&#39;s patch applied - thanks!<br /> </div> change to projects/funbot/tickets/9 on Rel4tion http://www.rel4tion.org/recentchanges/change_75eade8953e45278446e742d970840fd5686a759/ jookia 2015-07-13T15:29:57Z 2015-07-13T15:29:57Z <div id="change-75eade8953e45278446e742d970840fd5686a759" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/9.mdwn;h=1dcfa08f772cc596392b61eaed26d494b37f4e17;hp=b5300d432f0b164e9ddd5cf70630ec04caca47c6;hb=75eade8953e45278446e742d970840fd5686a759;hpb=660f57dbd862851e014100f04657b9b9f20bd8dd" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F9" rel="nofollow">projects/funbot/tickets/9</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Fjookia" rel="nofollow">jookia</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">web</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">03:29:57 PM 07/13/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=75eade8953e45278446e742d970840fd5686a759&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> </div> change to projects/funbot/tickets/9/0001-info-Add-copying-information.patch on Rel4tion http://www.rel4tion.org/recentchanges/change_660f57dbd862851e014100f04657b9b9f20bd8dd/ jookia 2015-07-13T15:25:55Z 2015-07-13T15:25:55Z <div id="change-660f57dbd862851e014100f04657b9b9f20bd8dd" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/funbot/tickets/9/0001-info-Add-copying-information.patch;h=1c6b87a3543f57c62bc123bc441be563c0a18a75;hp=0000000000000000000000000000000000000000;hb=660f57dbd862851e014100f04657b9b9f20bd8dd;hpb=69a4b71b5f5359915ae8a9c17cd12778e577267b" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=projects%2Ffunbot%2Ftickets%2F9%2F0001-info-Add-copying-information.patch" rel="nofollow">projects/funbot/tickets/9/0001-info-Add-copying-information.patch</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?page=people%2Fjookia&amp;do=goto" rel="nofollow">jookia</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">web</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">03:25:55 PM 07/13/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?rev=660f57dbd862851e014100f04657b9b9f20bd8dd&amp;do=revert" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> attachment upload<br /> </div> change to projects/libravatar projects/libravatar/tickets/1 projects/libravatar/tickets/Please_provide_sample_code on Rel4tion http://www.rel4tion.org/recentchanges/change_69a4b71b5f5359915ae8a9c17cd12778e577267b/ fr33domlover 2015-07-13T14:57:10Z 2015-07-13T14:57:10Z <div id="change-69a4b71b5f5359915ae8a9c17cd12778e577267b" class="metadata"> <span class="desc"><br />Changed pages:</span> <span class="pagelinks"> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/libravatar.mdwn;h=da1600f753365123dc129bbbb8d306aa48b76e04;hp=a462742b8577f6ceedb5ce392b9b5fd7a56c97af;hb=69a4b71b5f5359915ae8a9c17cd12778e577267b;hpb=af63fb6ef0b0df0490b806b41ca018701e7eb586" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Flibravatar&amp;do=goto" rel="nofollow">projects/libravatar</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/libravatar/tickets/1.mdwn;h=be178d41ce929dd13f79a42efdce42814cfd781c;hp=0000000000000000000000000000000000000000;hb=69a4b71b5f5359915ae8a9c17cd12778e577267b;hpb=af63fb6ef0b0df0490b806b41ca018701e7eb586" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Flibravatar%2Ftickets%2F1&amp;do=goto" rel="nofollow">projects/libravatar/tickets/1</a> <a href="http://git.rel4tion.org/?p=wiki.git;a=blobdiff;f=projects/libravatar/tickets/Please_provide_sample_code.mdwn;h=0000000000000000000000000000000000000000;hp=e2ef3abcf2e8dc5bb11d3a0d367fc01a4892123d;hb=69a4b71b5f5359915ae8a9c17cd12778e577267b;hpb=af63fb6ef0b0df0490b806b41ca018701e7eb586" title="diff" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/diff.png" alt="diff" /></a><a href="http://www.rel4tion.org/ikiwiki.cgi?page=projects%2Flibravatar%2Ftickets%2FPlease_provide_sample_code&amp;do=goto" rel="nofollow">projects/libravatar/tickets/Please provide sample code</a> </span> <span class="desc"><br />Changed by:</span> <span class="committer"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=goto&amp;page=people%2Ffr33domlover" rel="nofollow">fr33domlover</a> </span> <span class="desc"><br />Commit type:</span> <span class="committype">git</span> <span class="desc"><br />Date:</span> <span class="changedate"><span class="date">02:57:10 PM 07/13/2015</span></span> <span class="desc"><br /></span> <span class="revert"> <a href="http://www.rel4tion.org/ikiwiki.cgi?do=revert&amp;rev=69a4b71b5f5359915ae8a9c17cd12778e577267b" title="revert" rel="nofollow"><img src="http://www.rel4tion.org/recentchanges/../wikiicons/revert.png" alt="revert" /></a> </span> </div> <div class="changelog"> libravatar: reply on ticket 1<br /> </div> feed-1.0.1.0/LICENSE0000644000000000000000000000266613354421723011763 0ustar0000000000000000(c) 2007 Galois Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the author nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ~ feed-1.0.1.0/Setup.hs0000644000000000000000000000012013354421723012371 0ustar0000000000000000module Main where import Distribution.Simple main :: IO () main = defaultMain feed-1.0.1.0/feed.cabal0000644000000000000000000000732213370673351012643 0ustar0000000000000000name: feed version: 1.0.1.0 license: BSD3 license-file: LICENSE category: Text synopsis: Interfacing with RSS (v 0.9x, 2.x, 1.0) + Atom feeds. description: Interfacing with RSS (v 0.9x, 2.x, 1.0) + Atom feeds. . To help working with the multiple feed formats we've ended up with, this set of modules provides parsers, pretty printers and some utility code for querying and just generally working with a concrete representation of feeds in Haskell. . See here for an example of how to create an Atom feed: . For basic reading and editing of feeds, consult the documentation of the Text.Feed.* hierarchy. author: Sigbjorn Finne maintainer: Adam Bergmark homepage: https://github.com/bergmark/feed bug-reports: https://github.com/bergmark/feed/issues cabal-version: >= 1.8 build-type: Simple tested-with: GHC == 7.4.2 , GHC == 7.6.3 , GHC == 7.8.4 , GHC == 7.10.3 , GHC == 8.0.2 , GHC == 8.2.1 , GHC == 8.4.1 , GHC == 8.6.1 data-files: tests/files/*.xml extra-source-files: README.md CHANGELOG.md source-repository head type: git location: https://github.com/bergmark/feed.git library ghc-options: -Wall hs-source-dirs: src extensions: NoImplicitPrelude OverloadedStrings exposed-modules: Text.Atom.Feed Text.Atom.Feed.Export Text.Atom.Feed.Import Text.Atom.Feed.Link Text.Atom.Feed.Validate Text.Atom.Pub Text.Atom.Pub.Export Text.DublinCore.Types Text.Feed.Constructor Text.Feed.Export Text.Feed.Import Text.Feed.Query Text.Feed.Translate Text.Feed.Types Text.Feed.Util Text.RSS.Export Text.RSS.Import Text.RSS.Syntax Text.RSS1.Export Text.RSS1.Import Text.RSS1.Syntax Text.RSS1.Utils other-modules: Data.Text.Util Data.XML.Compat build-depends: base >= 4 && < 4.13 , base-compat >= 0.9 && < 0.11 , bytestring >= 0.9 && < 0.11 , old-locale == 1.0.* , old-time >= 1 && < 1.2 , safe == 0.3.* , text < 1.3 , time < 1.9 , time-locale-compat == 0.1.* , utf8-string < 1.1 , xml-types >= 0.3.6 && < 0.4 , xml-conduit >= 1.3 && < 1.9 test-suite tests ghc-options: -Wall hs-source-dirs: tests main-is: Main.hs type: exitcode-stdio-1.0 extensions: NoImplicitPrelude OverloadedStrings other-modules: Paths_feed Example Example.CreateAtom Text.Atom.Tests Text.Feed.Util.Tests Text.RSS.Equals Text.RSS.Export.Tests Text.RSS.Import.Tests Text.RSS.Tests Text.RSS.Utils build-depends: base >= 4 && < 4.13 , base-compat >= 0.9 && < 0.11 , HUnit >= 1.2 && < 1.7 , feed , old-time >= 1 && < 1.2 , test-framework == 0.8.* , test-framework-hunit == 0.3.* , text < 1.3 , time < 1.9 , xml-types >= 0.3.6 && < 0.4 , xml-conduit >= 1.3 && < 1.9 test-suite readme ghc-options: -Wall -pgmL markdown-unlit main-is: README.lhs extensions: NoImplicitPrelude OverloadedStrings type: exitcode-stdio-1.0 build-depends: base >= 4 && < 4.13 , base-compat >= 0.9 && < 0.11 , text , xml-types , feed , xml-conduit , xml-types build-tool-depends: markdown-unlit:markdown-unlit == 0.4.* feed-1.0.1.0/README.md0000644000000000000000000001163613370672700012233 0ustar0000000000000000# Feed [![feed](https://img.shields.io/hackage/v/feed.svg)](http://hackage.haskell.org/package/feed) [![Build Status](https://travis-ci.org/bergmark/feed.svg?branch=master)](https://travis-ci.org/bergmark/feed) ## Goal Interfacing with *RSS* (v 0.9x, 2.x, 1.0) + *Atom* feeds. - Parsers - Pretty Printers - Querying To help working with the multiple feed formats we've ended up with this set of modules providing parsers, pretty printers and some utility code for querying and just generally working with a concrete representation of feeds in Haskell. For basic reading and editing of feeds, consult the documentation of the Text.Feed.* hierarchy. ## Usage Building an Atom feed is similar to building an RSS feed, but we'll arbitrarily pick Atom here: We'd like to generate the XML for a minimal working example. Constructing our base `Feed` can use the smart constructor called `nullFeed`: *This is a pattern the library maintains for smart constructors. If you want the minimum viable 'X', use the 'nullX' constructor.* ```haskell {-# LANGUAGE OverloadedStrings #-} module Main where import Prelude.Compat hiding (take) import Data.Text import Data.XML.Types as XML import qualified Data.Text.Lazy as Lazy import qualified Text.Atom.Feed as Atom import qualified Text.Atom.Feed.Export as Export (textFeed) myFeed :: Atom.Feed myFeed = Atom.nullFeed "http://example.com/atom.xml" -- ^ id (Atom.TextString "Example Website") -- ^ title "2017-08-01" -- ^ last updated ``` Now we can export the feed to `Text`. ```haskell renderFeed :: Atom.Feed -> Maybe Lazy.Text renderFeed = Export.textFeed ``` ``` > renderFeed myFeed Example Website http://example.com/atom.xml 2017-08-01 ``` The `TextContent` sum type allows us to specify which type of text we're providing. ```haskell data TextContent = TextString Text | HTMLString Text | XHTMLString XML.Element deriving (Show) ``` A feed isn't very useful without some content though, so we'll need to build up an `Entry`. ```haskell data Post = Post { _postedOn :: Text , _url :: Text , _content :: Text } examplePosts :: [Post] examplePosts = [ Post "2000-02-02T18:30:00Z" "http://example.com/2" "Bar." , Post "2000-01-01T18:30:00Z" "http://example.com/1" "Foo." ] ``` Our `Post` data type will need to be converted into an `Entry` in order to use it in the top level `Feed`. The required fields for an entry are an url "id" from which an entry's presence can be validated, a title for the entry, and a posting date. In this example we'll also add authors, link, and the entries actual content, since we have all of this available in the `Post` provided. ```haskell toEntry :: Post -> Atom.Entry toEntry (Post date url content) = (Atom.nullEntry url -- The ID field. Must be a link to validate. (Atom.TextString (take 20 content)) -- Title date) { Atom.entryAuthors = [Atom.nullPerson {Atom.personName = "J. Smith"}] , Atom.entryLinks = [Atom.nullLink url] , Atom.entryContent = Just (Atom.HTMLContent content) } ``` From the base feed we created earlier, we can add further details (`Link` and `Entry` content) as well as map our `toEntry` function over the posts we'd like to include in the feed. ```haskell feed :: Atom.Feed feed = myFeed { Atom.feedEntries = fmap toEntry examplePosts , Atom.feedLinks = [Atom.nullLink "http://example.com/"] } ``` ``` > renderFeed feed Example Website http://example.com/atom.xml 2017-08-01 http://example.com/2 Bar. 2000-02-02T18:30:00Z J. Smith Bar. http://example.com/1 Foo. 2000-01-01T18:30:00Z J. Smith Foo. ``` See [here](https://github.com/bergmark/feed/blob/master/tests/Example/CreateAtom.hs) for this content as an uninterrupted running example. ```haskell -- Dummy main needed to compile this file with markdown-unlit main :: IO () main = return () ``` feed-1.0.1.0/CHANGELOG.md0000644000000000000000000000406013370673305012560 0ustar0000000000000000#### 1.0.1.0 * Support for GHC 8.6.x libraries * Add `textFeed` and `textRSS` helpers (thanks to Francesco Ariis) # 1.0.0.0 * Thanks to Dmitry Dzhus feed has been modernized to use the `text`, `xml-types` and `xml-conduit` libraries. ### 0.3.12.0 * Adds support for some fallback parsing of atom feeds to XMLFeed (thanks to Joey Hess) #### 0.3.11.1 * Add missing file to dist of test-suite (thanks to Sergei Trofimovich) #### 0.3.11.0 * Add `toFeedDateStringUTC` which uses UTCTime rather than ClockTime (thanks to Emanuel Borsboom) * Now with explicit export lists! #### 0.3.10.4 * Fix toFeedDateString time format, It used %s (seconds since epoch) instead of %S (seconds of minute), and %m (month) instead of %M (minute) (thanks to Emanuel Borsboom) #### 0.3.10.3 * RSS Export: avoid attribute (thanks to Roman Cheplyaka) #### 0.3.10.2 * The Atom RFC says that when a link element doesn't specify the "rel" attribute, i.e. link relation, it should be interpreted as an "alternate" relation. This makes the feed and item query functions treat a missing relation as "alternate". #### 0.3.10.1 * Allow `HUnit 1.3.*` ### 0.3.10.0 * Add `Text.Feed.Import.parseFeedSource :: XmlSource s => s -> Maybe Feed` (thanks to Dmitry Dzhus) #### 0.3.9.7 * Add missing modules in test-suite #### 0.3.9.6 * Fixed the "cloud" having attribute "register" instead of "registerProcedure": https://validator.w3.org/feed/docs/rss2.html#ltcloudgtSubelementOfLtchannelgt (thanks to Daniele Francesconi) #### 0.3.9.5 * Fix typo "skipDayss" -> "skipDays" in `Text.RSS.Export` (thanks to Daniele Francesconi) #### 0.3.9.4 * Update maintainer information * Add test suite #### 0.3.9.3 * Add support for utf8-string >= 1 && < 1.1 and time 1.5.* #### 0.3.9.1 * add ref to github repo + .cabal tidying. ### 0.3.9 * tidy up compilation with ghc-7.6(.3), bumped version (but no functional changes.) ### 0.3.8 * cabal build fixes. ### 0.3.7 * parsing: made be optional. * <entry> parsing: try <published> if <updated> is missing. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������