{- git-annex assistant webapp configurators for Internet Archive - - Copyright 2013 Joey Hess - - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, OverloadedStrings #-} module Assistant.WebApp.Configurators.IA where import Assistant.WebApp.Common import qualified Assistant.WebApp.Configurators.AWS as AWS #ifdef WITH_S3 import qualified Remote.S3 as S3 import qualified Remote.Helper.AWS as AWS import Assistant.MakeRemote #endif import qualified Remote import qualified Types.Remote as Remote import Types.StandardGroups import Types.Remote (RemoteConfig) import Logs.Remote import qualified Annex.Url as Url import Creds import Assistant.Gpg import qualified Data.Text as T import qualified Data.Map as M import Data.Char import Network.URI iaConfigurator :: Widget -> Handler Html iaConfigurator = page "Add an Internet Archive repository" (Just Configuration) data IAInput = IAInput { accessKeyID :: Text , secretAccessKey :: Text , mediaType :: MediaType , itemName :: Text } extractCreds :: IAInput -> AWS.AWSCreds extractCreds i = AWS.AWSCreds (accessKeyID i) (secretAccessKey i) {- IA defines only a few media types currently, or the media type - may be omitted - - We add a few other common types, mapped to what we've been told - is the closest match. -} data MediaType = MediaImages | MediaAudio | MediaVideo | MediaText | MediaSoftware | MediaOmitted deriving (Eq, Ord, Enum, Bounded) {- Format a MediaType for entry into the IA metadata -} formatMediaType :: MediaType -> String formatMediaType MediaText = "texts" formatMediaType MediaImages = "image" formatMediaType MediaSoftware = "software" formatMediaType MediaVideo = "movies" formatMediaType MediaAudio = "audio" formatMediaType MediaOmitted = "" {- A default collection to use for each Mediatype. -} collectionMediaType :: MediaType -> Maybe String collectionMediaType MediaText = Just "opensource" collectionMediaType MediaImages = Just "opensource" -- not ideal collectionMediaType MediaSoftware = Just "opensource" -- not ideal collectionMediaType MediaVideo = Just "opensource_movies" collectionMediaType MediaAudio = Just "opensource_audio" collectionMediaType MediaOmitted = Just "opensource" {- Format a MediaType for user display. -} showMediaType :: MediaType -> String showMediaType MediaText = "texts" showMediaType MediaImages = "photos & images" showMediaType MediaSoftware = "software" showMediaType MediaVideo = "videos & movies" showMediaType MediaAudio = "audio & music" showMediaType MediaOmitted = "other" iaInputAForm :: Maybe CredPair -> MkAForm IAInput iaInputAForm defcreds = IAInput <$> accessKeyIDFieldWithHelp (T.pack . fst <$> defcreds) <*> AWS.secretAccessKeyField (T.pack . snd <$> defcreds) <*> areq (selectFieldList mediatypes) "Media Type" (Just MediaOmitted) <*> areq (textField `withExpandableNote` ("Help", itemNameHelp)) "Item Name" Nothing where mediatypes :: [(Text, MediaType)] mediatypes = map (\t -> (T.pack $ showMediaType t, t)) [minBound..] itemNameHelp :: Widget itemNameHelp = [whamlet|
Each item stored in the Internet Archive must have a unique name.
Once you create the item, a special directory will appear # with a name matching the item name. Files you put in that directory # will be uploaded to your Internet Archive item. |] iaCredsAForm :: Maybe CredPair -> MkAForm AWS.AWSCreds iaCredsAForm defcreds = AWS.AWSCreds <$> accessKeyIDFieldWithHelp (T.pack . fst <$> defcreds) <*> AWS.secretAccessKeyField (T.pack . snd <$> defcreds) #ifdef WITH_S3 previouslyUsedIACreds :: Annex (Maybe CredPair) previouslyUsedIACreds = previouslyUsedCredPair AWS.creds S3.remote $ AWS.isIARemoteConfig . Remote.config #endif accessKeyIDFieldWithHelp :: Maybe Text -> MkAForm Text accessKeyIDFieldWithHelp def = AWS.accessKeyIDField help def where help = [whamlet| Get Internet Archive access keys |] getAddIAR :: Handler Html getAddIAR = postAddIAR postAddIAR :: Handler Html #ifdef WITH_S3 postAddIAR = iaConfigurator $ do defcreds <- liftAnnex previouslyUsedIACreds ((result, form), enctype) <- liftH $ runFormPost $ renderBootstrap $ iaInputAForm defcreds case result of FormSuccess input -> liftH $ do let name = escapeBucket $ T.unpack $ itemName input AWS.makeAWSRemote initSpecialRemote S3.remote PublicGroup (extractCreds input) name $ M.fromList $ catMaybes [ Just $ configureEncryption NoEncryption , Just ("type", "S3") , Just ("host", S3.iaHost) , Just ("bucket", escapeHeader name) , Just ("x-archive-meta-title", escapeHeader $ T.unpack $ itemName input) , if mediaType input == MediaOmitted then Nothing else Just ("x-archive-mediatype", formatMediaType $ mediaType input) , (,) <$> pure "x-archive-meta-collection" <*> collectionMediaType (mediaType input) -- Make item show up ASAP. , Just ("x-archive-interactive-priority", "1") , Just ("preferreddir", name) ] _ -> $(widgetFile "configurators/addia") #else postAddIAR = error "S3 not supported by this build" #endif getEnableIAR :: UUID -> Handler Html getEnableIAR = postEnableIAR postEnableIAR :: UUID -> Handler Html #ifdef WITH_S3 postEnableIAR = iaConfigurator . enableIARemote #else postEnableIAR _ = error "S3 not supported by this build" #endif #ifdef WITH_S3 enableIARemote :: UUID -> Widget enableIARemote uuid = do defcreds <- liftAnnex previouslyUsedIACreds ((result, form), enctype) <- liftH $ runFormPost $ renderBootstrap $ iaCredsAForm defcreds case result of FormSuccess creds -> liftH $ do m <- liftAnnex readRemoteLog let name = fromJust $ M.lookup "name" $ fromJust $ M.lookup uuid m AWS.makeAWSRemote enableSpecialRemote S3.remote PublicGroup creds name M.empty _ -> do description <- liftAnnex $ T.pack <$> Remote.prettyUUID uuid $(widgetFile "configurators/enableia") #endif {- Convert a description into a bucket item name, which will also be - used as the repository name, and the preferreddir. - IA seems to need only lower case, and no spaces. -} escapeBucket :: String -> String escapeBucket = map toLower . replace " " "-" {- IA S3 API likes headers to be URI escaped, escaping spaces looks ugly. -} escapeHeader :: String -> String escapeHeader = escapeURIString (\c -> isUnescapedInURI c && c /= ' ') getRepoInfo :: RemoteConfig -> Widget getRepoInfo c = do ua <- liftAnnex Url.getUserAgent exists <- liftIO $ catchDefaultIO False $ fst <$> Url.exists url [] ua [whamlet| Internet Archive item $if (not exists)

The page will only be available once some files # have been uploaded, and the Internet Archive has processed them. |] where bucket = fromMaybe "" $ M.lookup "bucket" c #ifdef WITH_S3 url = S3.iaItemUrl bucket #else url = "" #endif