content: -- | Settings are centralized, as much as possible, into this file. This
  -- includes database connection settings, static file locations, etc.
  -- In addition, you can configure a number of different aspects of Yesod
  -- by overriding methods in the Yesod typeclass. That instance is
  -- declared in the Foundation.hs file.
  module Settings where
  
  import ClassyPrelude.Yesod
  import Control.Exception           (throw)
  import Data.Aeson                  (Result (..), fromJSON, withObject, (.!=),
                                      (.:?))
  import Data.FileEmbed              (embedFile)
  import Data.Yaml                   (decodeEither')
  import Database.Persist.Postgresql (PostgresConf)
  import Language.Haskell.TH.Syntax  (Exp, Name, Q)
  import Network.Wai.Handler.Warp    (HostPreference)
  import Yesod.Default.Config2       (applyEnvValue, configSettingsYml)
  import Yesod.Default.Util          (WidgetFileSettings, widgetFileNoReload,
                                      widgetFileReload)
  
  -- | Runtime settings to configure this application. These settings can be
  -- loaded from various sources: defaults, environment variables, config files,
  -- theoretically even a database.
  data AppSettings = AppSettings
      { appStaticDir              :: String
      -- ^ Directory from which to serve static files.
      , appUploadDir              :: String
      -- ^ Directory for storing uploaded files
      , appDatabaseConf           :: PostgresConf
      -- ^ Configuration settings for accessing the database.
      , appRoot                   :: Maybe Text
      -- ^ Base for all generated URLs.
      , appHost                   :: HostPreference
      -- ^ Host/interface the server should bind to.
      , appPort                   :: Int
      -- ^ Port to listen on
      , appIpFromHeader           :: Bool
      -- ^ Get the IP address from the header when logging. Useful when sitting
      -- behind a reverse proxy.
  
      , appDetailedRequestLogging :: Bool
      -- ^ Use detailed request logging system
      , appShouldLogAll           :: Bool
      -- ^ Should all log messages be displayed?
      , appReloadTemplates        :: Bool
      -- ^ Use the reload version of templates
      , appMutableStatic          :: Bool
      -- ^ Assume that files in the static dir may change after compilation
      , appSkipCombining          :: Bool
      -- ^ Perform no stylesheet/script combining
  
      -- Example app-specific configuration values.
      , appFooter              :: Text
      , appHeadExtra           :: Text
      -- ^ Footer of the page
      , appAnalytics           :: Maybe Text
      -- ^ Google Analytics code
      , appSphinxPort          :: Int
      , appSphinxHost          :: Text
      , appSiteName            :: Text
      , appGeoIPCityPath       :: Text
      , appTimezone            :: Int
      , appStylesheet          :: Text
      , appMaxLenOfPostTitle   :: Int
      , appMaxLenOfPostName    :: Int
      , appCaptcha             :: Text
      , appHighlight           :: Text
      , appMaxLenOfFileName    :: Int
      , appMetaKeywords        :: Text
      , appMetaDescription     :: Text
      , appTitleDelimiter      :: Text
      , appFfmpeg              :: Text
      , appExiftool            :: Text
      , appAnimatedThumbs      :: Bool
      , appRandomBanners       :: Bool
      , appRestartCmd          :: Text
      , appJsDesktopNotification :: Bool
      , appJsPostsAutoloadInterval :: Int
      , appJsMathjax :: Bool
      , appJsEventsource :: Bool
      , appJsExpandFilesInPost :: Bool
      , appJsBoardStatsUpdate :: Int
      , appJsAnimationSpeed :: Int
      , appJsPopupMessageDisplayTime :: Int
      , appJsPopupPostDelayBeforeDisplaying :: Int
      , appJsPopupPostRemovingOnMouseOut :: Bool
      , appJsPopupPostDelayBeforeRemoving :: Int
      , appJsVideoRememberVolume :: Bool
      , appJsVideoDefaultVolume :: Int
      , appJsVideoAutoplay :: Bool
      , appJsVideoLoop :: Bool
      , appJsVideoScaleWidth :: Int
      , appJsVideoScaleHeight :: Int
      , appJsVimeoEnable :: Bool
      , appJsCoubEnable :: Bool
      , appJsYoutubeEnable :: Bool
      , appJsYoutubeLoadTitles :: Bool
      , appJsYoutubeHtml5 :: Bool
      , appJsUrlVideoWidth :: Int
      , appJsUrlVideoHeight :: Int
      , appJsFlashHeight :: Int
      , appJsFlashWidth :: Int
      , appJsIframelyAPIKey :: Text
      , appJsYoutubeAPIKey :: Text
      , appJsCssWidePosts :: Bool
      , appJsCssSideMargin :: Int
      , appJsCssHideFilesNames :: Bool
      , appJsCssHideFilesInfo :: Bool
      , appJsCssHideMarkupButtons :: Bool
      , appJsCssAttachmentThumbWidth :: Int
      , appJsCssCustom :: Text
      }
  
  instance FromJSON AppSettings where
      parseJSON = withObject "AppSettings" $ \o -> do
          let defaultDev =
  #if DEVELOPMENT
                  True
  #else
                  False
  #endif
          appUploadDir              <- o .: "upload-dir"
          appStaticDir              <- o .: "static-dir"
          appDatabaseConf           <- o .: "database"
          appRoot                   <- o .:? "approot"
          appHost                   <- fromString <$> o .: "host"
          appPort                   <- o .: "port"
          appIpFromHeader           <- o .: "ip-from-header"
  
          appDetailedRequestLogging <- o .:? "detailed-logging" .!= defaultDev
          appShouldLogAll           <- o .:? "should-log-all"   .!= defaultDev
          appReloadTemplates        <- o .:? "reload-templates" .!= defaultDev
          appMutableStatic          <- o .:? "mutable-static"   .!= defaultDev
          appSkipCombining          <- o .:? "skip-combining"   .!= defaultDev
  
          appFooter                 <- o .: "footer"
          appAnalytics              <- o .:? "analytics"
          appHeadExtra              <- o .: "headExtra"
  
          appSphinxPort             <- o .: "sphinx-port"
          appSphinxHost             <- o .: "sphinx-host"
  
          appSiteName               <- o .: "sitename"
          appGeoIPCityPath          <- o .: "geoipcitypath"
          appTimezone               <- o .: "timezone"
          appStylesheet             <- o .: "stylesheet"
          appMaxLenOfPostTitle      <- o .: "maxLenOfPostTitle"
          appMaxLenOfPostName       <- o .: "maxLenOfPostName"
          appCaptcha                <- o .: "captcha"
          appHighlight              <- o .: "highlight"
          appMaxLenOfFileName       <- o .: "maxLenOfFileName"
          appMetaKeywords           <- o .: "metaKeywords"
          appMetaDescription        <- o .: "metaDescription"
          appTitleDelimiter         <- o .: "titleDelimiter"
  
          appFfmpeg                 <- o .: "ffmpeg"
          appExiftool               <- o .: "exiftool"
          appAnimatedThumbs         <- o .: "animatedThumbs"
          appRandomBanners          <- o .: "randomBanners"
          appRestartCmd             <- o .: "restartCmd"
  
          appJsDesktopNotification <- o .: "jsDesktopNotification"
          appJsPostsAutoloadInterval <- o .: "jsPostsAutoloadInterval"
          appJsMathjax <- o .: "jsMathjax"
          appJsEventsource <- o .: "jsEventsource"
          appJsExpandFilesInPost <- o .: "jsExpandFilesInPost"
          appJsBoardStatsUpdate <- o .: "jsBoardStatsUpdate"
          appJsAnimationSpeed <- o .: "jsAnimationSpeed"
          appJsPopupMessageDisplayTime <- o .: "jsPopupMessageDisplayTime"
          appJsPopupPostDelayBeforeDisplaying <- o .: "jsPopupPostDelayBeforeDisplaying"
          appJsPopupPostRemovingOnMouseOut <- o .: "jsPopupPostRemovingOnMouseOut"
          appJsPopupPostDelayBeforeRemoving <- o .: "jsPopupPostDelayBeforeRemoving"
          appJsVideoRememberVolume  <- o .: "jsVideoRememberVolume"
          appJsVideoDefaultVolume  <- o .: "jsVideoDefaultVolume"
          appJsVideoAutoplay <- o .: "jsVideoAutoplay"
          appJsVideoLoop <- o .: "jsVideoLoop"
          appJsVideoScaleWidth <- o .: "jsVideoScaleWidth"
          appJsVideoScaleHeight <- o .: "jsVideoScaleHeight"
          appJsVimeoEnable <- o .: "jsVimeoEnable"
          appJsCoubEnable <- o .: "jsCoubEnable"
          appJsYoutubeEnable <- o .: "jsYoutubeEnable"
          appJsYoutubeLoadTitles <- o .: "jsYoutubeLoadTitles"
          appJsYoutubeHtml5 <- o .: "jsYoutubeHtml5"
          appJsUrlVideoWidth <- o .: "jsUrlVideoWidth"
          appJsUrlVideoHeight <- o .: "jsUrlVideoHeight"
          appJsFlashHeight <- o .: "jsFlashHeight"
          appJsFlashWidth <- o .: "jsFlashWidth"
          appJsIframelyAPIKey         <- o .: "jsIframelyAPIKey"
          appJsYoutubeAPIKey          <- o .: "jsYoutubeAPIKey"
          appJsCssWidePosts <- o .: "jsCssWidePosts"
          appJsCssSideMargin <- o .: "jsCssSideMargin"
          appJsCssHideFilesNames <- o .: "jsCssHideFilesNames"
          appJsCssHideFilesInfo <- o .: "jsCssHideFilesInfo"
          appJsCssHideMarkupButtons <- o .: "jsCssHideMarkupButtons"
          appJsCssAttachmentThumbWidth <- o .: "jsCssAttachmentThumbWidth"
          appJsCssCustom <- o .: "jsCssCustom"
  
          return AppSettings {..}
  
  -- | Settings for 'widgetFile', such as which template languages to support and
  -- default Hamlet settings.
  --
  -- For more information on modifying behavior, see:
  --
  -- https://github.com/yesodweb/yesod/wiki/Overriding-widgetFile
  widgetFileSettings :: WidgetFileSettings
  widgetFileSettings = def
  
  -- | How static files should be combined.
  combineSettings :: CombineSettings
  combineSettings = def
  
  -- The rest of this file contains settings which rarely need changing by a
  -- user.
  
  widgetFile :: String -> Q Exp
  widgetFile = (if appReloadTemplates compileTimeAppSettings
                  then widgetFileReload
                  else widgetFileNoReload)
                widgetFileSettings
  
  -- | Raw bytes at compile time of @config/settings.yml@
  configSettingsYmlBS :: ByteString
  configSettingsYmlBS = $(embedFile configSettingsYml)
  
  -- | @config/settings.yml@, parsed to a @Value@.
  configSettingsYmlValue :: Value
  configSettingsYmlValue = either throw id $ decodeEither' configSettingsYmlBS
  
  -- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.
  compileTimeAppSettings :: AppSettings
  compileTimeAppSettings =
      case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of
          Error e -> error e
          Success settings -> settings
  
  -- The following two functions can be used to combine multiple CSS or JS files
  -- at compile time to decrease the number of http requests.
  -- Sample usage (inside a Widget):
  --
  -- > $(combineStylesheets 'StaticR [style1_css, style2_css])
  
  combineStylesheets :: Name -> [Route Static] -> Q Exp
  combineStylesheets = combineStylesheets'
      (appSkipCombining compileTimeAppSettings)
      combineSettings
  
  combineScripts :: Name -> [Route Static] -> Q Exp
  combineScripts = combineScripts'
      (appSkipCombining compileTimeAppSettings)
      combineSettings
  