Public/Utilities.ps1

function Use-PodeWebTemplates {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $Title,

        [Parameter()]
        [string]
        $Logo,

        [Parameter()]
        [string]
        $FavIcon,

        [Parameter()]
        [ValidateSet('Auto', 'Light', 'Dark', 'Midnight', 'Sepia', 'Forest', 'Terminal', 'Custom')]
        [string]
        $Theme = 'Auto',

        [Parameter()]
        [string[]]
        $EndpointName,

        [Parameter()]
        [ValidateSet('None', 'Default', 'Simple', 'Strict')]
        [string]
        $Security = 'Default',

        [Parameter()]
        [ValidateSet('Sse', 'Http')]
        [string]
        $ResponseType = 'Sse',

        [Parameter()]
        [string]
        $SseSecret,

        [switch]
        $NoPageFilter,

        [switch]
        $HideSidebar,

        [switch]
        $UseHsts,

        [switch]
        $RootRedirect
    )

    # has Pode.Web already been initialised?
    if (Get-PodeWebState -Name 'enabled') {
        throw 'Pode.Web templates have already been enabled.'
    }

    # get a favicon path
    if ([string]::IsNullOrWhiteSpace($FavIcon)) {
        $FavIcon = '/pode.web-static/images/favicon.ico'
    }

    # tell Pode to export the module for auto-loading
    Export-PodeModule -Name Pode.Web

    # if available, do we need an IIS sub-path?
    $appPath = Get-PodeIISApplicationPath
    if ([string]::IsNullOrWhiteSpace($appPath) -or ($appPath -eq '/')) {
        $appPath = [string]::Empty
    }
    Set-PodeWebState -Name 'app-path' -Value ($appPath.ToLowerInvariant())

    # setup settings
    Set-PodeWebState -Name 'enabled' -Value $true
    Set-PodeWebState -Name 'title' -Value ([System.Net.WebUtility]::HtmlEncode($Title))
    Set-PodeWebState -Name 'logo' -Value (Add-PodeWebAppPath -Url $Logo)
    Set-PodeWebState -Name 'favicon' -Value (Add-PodeWebAppPath -Url $FavIcon)
    Set-PodeWebState -Name 'no-page-filter' -Value $NoPageFilter.IsPresent
    Set-PodeWebState -Name 'hide-sidebar' -Value $HideSidebar.IsPresent
    Set-PodeWebState -Name 'root-redirect' -Value $RootRedirect.IsPresent
    Set-PodeWebState -Name 'social' -Value ([ordered]@{})
    Set-PodeWebState -Name 'pages' -Value @{}
    Set-PodeWebState -Name 'groups' -Value @{}
    Set-PodeWebState -Name 'default-nav' -Value $null
    Set-PodeWebState -Name 'endpoint-name' -Value $EndpointName
    Set-PodeWebState -Name 'custom-css' -Value @()
    Set-PodeWebState -Name 'custom-js' -Value @()
    Set-PodeWebState -Name 'resp-type' -Value $ResponseType.ToLowerInvariant()

    # themes
    Set-PodeWebState -Name 'theme' -Value $Theme.ToLowerInvariant()
    Set-PodeWebState -Name 'custom-themes' -Value @{
        Default = $null
        Themes  = [ordered]@{}
    }

    # system urls
    Set-PodeWebSystemUrlDefaults

    # public and view folders
    $templatePath = Get-PodeWebTemplatePath
    Add-PodeStaticRoute -Path '/pode.web-static' -Source (Join-PodeWebPath $templatePath 'Public')
    Add-PodeViewFolder -Name 'pode.web.views' -Source (Join-PodeWebPath $templatePath 'Views')

    # setup default security headers
    Set-PodeWebSecurity -Security $Security -UseHsts:$UseHsts

    # initialise SSE connections
    if (Test-PodeWebResponseType -Type Sse) {
        if ([string]::IsNullOrEmpty($SseSecret)) {
            $SseSecret = Get-PodeServerDefaultSecret
        }

        Enable-PodeSseSigning -Strict -Secret $SseSecret
    }

    # add an empty root route, which simply redirects to the first available page
    if ($RootRedirect) {
        Add-PodeRoute -Method Get -Path '/' -EndpointName $EndpointName -ScriptBlock {
            # get first page and redirect
            $page = Get-PodeWebFirstPublicPage
            if ($null -ne $page) {
                Move-PodeResponseUrl -Url $page.Url
                return
            }

            # fail if no pages found
            Set-PodeResponseStatus -Code 421
        }
    }
}

function Import-PodeWebStylesheet {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $Url
    )

    Set-PodeWebState -Name 'custom-css' -Value  (@(Get-PodeWebState -Name 'custom-css') + (Add-PodeWebAppPath -Url $Url))
}

function Import-PodeWebJavaScript {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $Url
    )

    Set-PodeWebState -Name 'custom-js' -Value  (@(Get-PodeWebState -Name 'custom-js') + (Add-PodeWebAppPath -Url $Url))
}

function Set-PodeWebSocial {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateSet('GitHub', 'Twitter', 'Facebook', 'LinkedIn', 'Twitch', 'GitLab', 'Instagram', 'Telegram',
            'Pinterest', 'Slack', 'Discord', 'BitBucket', 'Jira', 'YouTube')]
        [string]
        $Type,

        [Parameter(Mandatory = $true)]
        [string]
        $Url,

        [Parameter()]
        [string]
        $Tooltip
    )

    if ([string]::IsNullOrWhiteSpace($Tooltip)) {
        $Tooltip = $Type
    }

    $socials = Get-PodeWebState -Name 'social'
    $socials[$Type] = @{
        Url     = $Url
        Tooltip = $Tooltip
    }
}

function Get-PodeWebTheme {
    [CmdletBinding()]
    param(
        [switch]
        $IgnoreCookie
    )

    $theme = [string]::Empty

    # check cookies
    if (!$IgnoreCookie) {
        $theme = Get-PodeWebCookie -Name 'theme'
        if (($null -ne $theme) -and ![string]::IsNullOrWhiteSpace($theme.Value)) {
            $theme = $theme.Value
        }
    }

    # check auth data
    if ([string]::IsNullOrWhiteSpace($theme)) {
        $theme = Get-PodeWebAuthTheme -User (Get-PodeAuthUser)
    }

    # check state
    if ([string]::IsNullOrWhiteSpace($theme)) {
        $theme = (Get-PodeWebState -Name 'theme')
    }

    # if 'custom', set as default custom theme
    if ($theme -ieq 'custom') {
        $theme = (Get-PodeWebState -Name 'custom-themes').Default
    }

    if ([string]::IsNullOrWhiteSpace($theme)) {
        $theme = 'Auto'
    }

    return $theme.ToLowerInvariant()
}

function Test-PodeWebTheme {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]
        $Name,

        [Parameter()]
        [ValidateSet('Any', 'Inbuilt', 'Custom')]
        [string]
        $Type = 'Any'
    )

    $found = $false

    if ($Type -iin 'Inbuilt', 'Any') {
        $found = Test-PodeWebThemeInbuilt -Name $Name
    }

    if (!$found -and ($Type -iin 'Custom', 'Any')) {
        $found = Test-PodeWebThemeCustom -Name $Name
    }

    return $found
}

function Get-PodeWebUsername {
    [CmdletBinding()]
    param()

    $authData = Get-PodeWebAuthData
    return (Get-PodeWebAuthUsername -AuthData $authData)
}

function Set-PodeWebCustomThemeDefault {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $Name
    )

    # test that the custom theme exists
    if (!(Test-PodeWebTheme -Name $Name -Type 'Custom')) {
        throw "The custom theme '$($Name)' does not exist"
    }

    $customThemes = Get-PodeWebState -Name 'custom-themes'
    $customThemes.Default = $Name.ToLowerInvariant()
}

function Add-PodeWebCustomTheme {
    [CmdletBinding(DefaultParameterSetName = 'Url')]
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $Name,

        [Parameter()]
        [ValidateSet('None', 'Light', 'Dark', 'Midnight', 'Terminal')]
        [string]
        $Base = 'None',

        [Parameter(Mandatory = $true, ParameterSetName = 'Url')]
        [string]
        $Url,

        [Parameter(ParameterSetName = 'Config')]
        [ValidateSet('Normal', 'Light', 'Dark')]
        [string]
        $ColourScheme,

        [Parameter(ParameterSetName = 'Config')]
        [string[]]
        $FontFamily,

        [Parameter(ParameterSetName = 'Config')]
        [hashtable]
        $BackgroundColourConfig,

        [Parameter(ParameterSetName = 'Config')]
        [hashtable]
        $BorderColourConfig,

        [Parameter(ParameterSetName = 'Config')]
        [hashtable]
        $TextColourConfig,

        [Parameter(ParameterSetName = 'Config')]
        [hashtable]
        $NavColourConfig,

        [Parameter(ParameterSetName = 'Config')]
        [hashtable]
        $ToastColourConfig,

        [Parameter(ParameterSetName = 'Config')]
        [hashtable]
        $CalendarIconColourConfig,

        [Parameter(ParameterSetName = 'Config')]
        [hashtable]
        $ChartColourConfig,

        [Parameter(ParameterSetName = 'Config')]
        [ValidateSet('Light', 'Dark', 'HighContrast')]
        [string]
        $CodeEditorTheme,

        [Parameter(ParameterSetName = 'Config')]
        [ValidateSet('Light', 'Dark')]
        [string]
        $CodeTheme
    )

    # is the theme already inbuilt?
    if (Test-PodeWebTheme -Name $Name -Type 'Inbuilt') {
        throw "There is already an inbuilt theme for '$($Name)' defined"
    }

    # is the theme already defined?
    if (Test-PodeWebTheme -Name $Name -Type 'Custom') {
        throw "There is already a custom theme for '$($Name)' defined"
    }

    # if using config, set appropriate URL, and create route if it doesn't already exist
    if ($PSCmdlet.ParameterSetName -ieq 'Config') {
        # build the url
        $Url = Get-PodeWebCustomThemeRoutePath
        $Url += "?name=$($Name)"

        # add route
        Add-PodeWebCustomThemeRoute
    }

    # add the custom theme
    $Name = $Name.ToLowerInvariant()
    $customThemes = Get-PodeWebState -Name 'custom-themes'

    $customThemes.Themes[$Name] = @{
        Url      = (Add-PodeWebAppPath -Url $Url)
        Base     = $Base
        IsStatic = ($PSCmdlet.ParameterSetName -ieq 'Url')
        Config   = @{
            ColourScheme             = $ColourScheme
            FontFamily               = $FontFamily
            BackgroundColourConfig   = $BackgroundColourConfig
            BorderColourConfig       = $BorderColourConfig
            TextColourConfig         = $TextColourConfig
            NavColourConfig          = $NavColourConfig
            ToastColourConfig        = $ToastColourConfig
            CalendarIconColourConfig = $CalendarIconColourConfig
            CodeEditorTheme          = $CodeEditorTheme
            CodeTheme                = $CodeTheme
            ChartColourConfig        = $ChartColourConfig
        }
    }

    # set as theme if first one
    $currentTheme = Get-PodeWebState -Name 'theme'
    if ($currentTheme -ieq 'custom') {
        Set-PodeWebState -Name 'theme' -Value $Name
    }

    if ([string]::IsNullOrWhiteSpace($customThemes.Default)) {
        $customThemes.Default = $Name
    }
}

function New-PodeWebBackgroundColourConfig {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]
        $Page,

        [Parameter()]
        [string]
        $Hero,

        [Parameter()]
        [string]
        $Primary,

        [Parameter()]
        [string]
        $Secondary,

        [Parameter()]
        [string]
        $Tertiary
    )

    return @{
        Page      = (Test-PodeWebColour -Colour $Page -AllowEmpty)
        Hero      = (Test-PodeWebColour -Colour $Hero -AllowEmpty)
        Primary   = (Test-PodeWebColour -Colour $Primary -AllowEmpty)
        Secondary = (Test-PodeWebColour -Colour $Secondary -AllowEmpty)
        Tertiary  = (Test-PodeWebColour -Colour $Tertiary -AllowEmpty)
    }
}

function New-PodeWebBorderColourConfig {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]
        $Primary,

        [Parameter()]
        [string]
        $Secondary,

        [Parameter()]
        [string]
        $Tertiary
    )

    return @{
        Primary   = (Test-PodeWebColour -Colour $Primary -AllowEmpty)
        Secondary = (Test-PodeWebColour -Colour $Secondary -AllowEmpty)
        Tertiary  = (Test-PodeWebColour -Colour $Tertiary -AllowEmpty)
    }
}

function New-PodeWebTextColourConfig {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]
        $Primary,

        [Parameter()]
        [string]
        $Secondary,

        [Parameter()]
        [string]
        $Tertiary,

        [Parameter()]
        [string]
        $Link,

        [Parameter()]
        [string]
        $HoverPrimary,

        [Parameter()]
        [string]
        $HoverSecondary,

        [Parameter()]
        [string]
        $Disabled,

        [Parameter()]
        [string]
        $Enabled
    )

    return @{
        Primary           = (Test-PodeWebColour -Colour $Primary -AllowEmpty)
        Secondary         = (Test-PodeWebColour -Colour $Secondary -AllowEmpty)
        Tertiary          = (Test-PodeWebColour -Colour $Tertiary -AllowEmpty)
        Link              = (Test-PodeWebColour -Colour $Link -AllowEmpty)
        'Hover-Primary'   = (Test-PodeWebColour -Colour $HoverPrimary -AllowEmpty)
        'Hover-Secondary' = (Test-PodeWebColour -Colour $HoverSecondary -AllowEmpty)
        Disabled          = (Test-PodeWebColour -Colour $Disabled -AllowEmpty)
        Enabled           = (Test-PodeWebColour -Colour $Enabled -AllowEmpty)
    }
}

function New-PodeWebNavColourConfig {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]
        $Background,

        [Parameter()]
        [string]
        $Border,

        [Parameter()]
        [string]
        $Text,

        [Parameter()]
        [string]
        $HoverText
    )

    return @{
        Background   = (Test-PodeWebColour -Colour $Background -AllowEmpty)
        Border       = (Test-PodeWebColour -Colour $Border -AllowEmpty)
        Text         = (Test-PodeWebColour -Colour $Text -AllowEmpty)
        'Hover-Text' = (Test-PodeWebColour -Colour $HoverText -AllowEmpty)
    }
}

function New-PodeWebToastColourConfig {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]
        $BackgroundPrimary,

        [Parameter()]
        [string]
        $BackgroundSecondary,

        [Parameter()]
        [string]
        $Border,

        [Parameter()]
        [string]
        $TextPrimary,

        [Parameter()]
        [string]
        $TextSecondary
    )

    return @{
        'Primary-Background'   = (Test-PodeWebColour -Colour $BackgroundPrimary -AllowEmpty)
        'Secondary-Background' = (Test-PodeWebColour -Colour $BackgroundSecondary -AllowEmpty)
        Border                 = (Test-PodeWebColour -Colour $Border -AllowEmpty)
        'Primary-Text'         = (Test-PodeWebColour -Colour $TextPrimary -AllowEmpty)
        'Secondary-Text'       = (Test-PodeWebColour -Colour $TextSecondary -AllowEmpty)
    }
}

function New-PodeWebCalendarIconColourConfig {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]
        $Primary,

        [Parameter()]
        [string]
        $Hover
    )

    return @{
        Indicator         = $Primary
        'Indicator-Hover' = $Hover
    }
}

function New-PodeWebChartColourConfig {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string[]]
        $Point,

        [Parameter()]
        [string]
        $Grid,

        [Parameter()]
        [string]
        $Tick,

        [Parameter()]
        [string]
        $Border
    )

    return @{
        Point  = (Test-PodeWebColour -Colour $Point -AllowEmpty)
        Grid   = (Test-PodeWebColour -Colour $Grid -AllowEmpty)
        Tick   = (Test-PodeWebColour -Colour $Tick -AllowEmpty)
        Border = (Test-PodeWebColour -Colour $Border -AllowEmpty)
    }
}

function Join-PodeWebPath {
    param(
        [Parameter()]
        [string]
        $Path,

        [Parameter()]
        [string]
        $ChildPath,

        [switch]
        $ReplaceSlashes
    )

    if ([string]::IsNullOrWhiteSpace($Path)) {
        $result = $ChildPath
    }
    elseif ([string]::IsNullOrWhiteSpace($ChildPath)) {
        $result = $Path
    }
    else {
        $result = (Join-Path $Path $ChildPath)
    }

    if ($ReplaceSlashes) {
        $result = ($result -ireplace '\\', '/')
    }

    return $result
}

function Set-PodeWebAuth {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $Authentication,

        [Parameter()]
        [string]
        $UsernameProperty,

        [Parameter()]
        [string]
        $GroupProperty,

        [Parameter()]
        [string]
        $AvatarProperty,

        [Parameter()]
        [string]
        $ThemeProperty
    )

    Set-PodeWebState -Name 'auth' -Value $Authentication
    Set-PodeWebState -Name 'auth-props' -Value @{
        Username = $UsernameProperty
        Group    = $GroupProperty
        Avatar   = $AvatarProperty
        Theme    = $ThemeProperty
        Logout   = $false
    }

    # set default failure/success urls
    $auth = Get-PodeAuth -Name $Authentication

    if ([string]::IsNullOrWhiteSpace($auth.Failure.Url)) {
        $auth.Failure.Url = (Add-PodeWebAppPath -Url '/')
    }

    if ([string]::IsNullOrWhiteSpace($auth.Success.Url)) {
        $auth.Success.Url = (Add-PodeWebAppPath -Url '/')
    }

    if ($PassThru) {
        return $pageMeta
    }
}