321 lines
13 KiB
Bash
Executable File
321 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
#shellcheck disable=SC1003,SC2312
|
||
# SC1003=Want to escape a single quote?
|
||
# SC2312 Consider invoking this command separately
|
||
|
||
## SCRIPT INFORMATION
|
||
# ------------------------------------------------------------------------------
|
||
# Name : add directory item (add-directory-item.sh)
|
||
# License : Non-Profit Open Software License ("Non-Profit OSL") 3.0
|
||
# Dependencies : pcre2grep, tidy
|
||
# Author : 12bytes.org
|
||
# Code : cin/pages: Alternative Information Directory - Codeberg.org
|
||
# : https://codeberg.org/12bytes/aid
|
||
#
|
||
# Comment : this script is used to create an HTML text block containing
|
||
# : all required information for adding a new website to the
|
||
# : directory.
|
||
#
|
||
## LICENSE
|
||
# ------------------------------------------------------------------------------
|
||
# This program is free software as afforded by the Non-Profit Open Software
|
||
# License ("Non-Profit OSL") 3.0, a copy of which should have been included. The
|
||
# license can be viewed on-line at: https://opensource.org/licenses/NPOSL-3.0
|
||
# ------------------------------------------------------------------------------
|
||
#
|
||
|
||
## TODO
|
||
# we could grab website favicons
|
||
# curl --output="${sDomain}.WHAT?" "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&url=${sUrl}&size=16"
|
||
# curl --output="${sDomain}.WHAT?" "https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${sUrl}&size=16"
|
||
# "t<n>" can be 0 to 3
|
||
# size seems to default to 16x16 if not specified
|
||
# another one...
|
||
# https://api.faviconkit.com/&${sDomain}/32
|
||
|
||
sScriptVersion='20230823'
|
||
sItem=''
|
||
sCat=''
|
||
iAddedPrimary=0
|
||
# char encoding/replacements for titles, descriptions
|
||
#declare -A aaPrintChars=(['&']='&' ['<']='<' ['>']='>' ['“']='"' ['”']='"' ['„']='"' ['❛']="'" ['❜']="'" ['‛']="'" ['`']="'" ['´']="'")
|
||
# if using iconv
|
||
declare -A aaPrintChars=(['&']='&' ['<']='<' ['>']='>' ['„']='"' ['❛']="'" ['❜']="'" ['`']="'")
|
||
# encode unsafe chars for URLs
|
||
declare -A aaUrlChars=(['"']='%22' ['<']='%3C' ['>']='%3E' ['{']='%7B' ['}']='%7D' ['|']='%7C' ['\']='%5C' ['^']='%5E' ['`']='%60' [' ']='%20')
|
||
|
||
a=(
|
||
''
|
||
' --------------------------------------------------------'
|
||
' ADD AN ITEM TO THE ALTERNATIVE INFORMATION DIRECTORY'
|
||
' --------------------------------------------------------'
|
||
''
|
||
'NOTES:'
|
||
''
|
||
'DO NOT EDIT THE INDEX.HTML FILE DIRECTLY. To edit the directory, edit the'
|
||
'appropiate file in the /cats directory, then select the "Rebuild the directory"'
|
||
'item from the menu.'
|
||
''
|
||
'Use the following template when adding a secondary website:'
|
||
'<channel owner> (<platform name>)'
|
||
'Example: Mr. Kitten (YouTube)'
|
||
''
|
||
"build.sh v${sScriptVersion}"
|
||
)
|
||
printf '%s\n' "${a[@]}"
|
||
|
||
# gen the list of categories from the files in the /cats folder
|
||
i="$(find 'cats/' -maxdepth 1 -type 'f' | wc -l)"
|
||
if [[ "${i}" -lt 1 ]] ; then
|
||
printf '%s\n' 'ERROR: No files were found in the /cats directory.'
|
||
exit
|
||
fi
|
||
for s in 'cats/'* ; do
|
||
aCatFiles+=("$(basename "${s}")")
|
||
done
|
||
|
||
while true ; do
|
||
if [[ -z "${sCat}" ]] ; then
|
||
a=('Select a category' 'Rebuild the directory' 'Quit')
|
||
elif [[ "${iAddedPrimary}" -eq 0 ]] ; then
|
||
a=('Add primary website' 'Quit')
|
||
else
|
||
a=('Add secondary website' 'Commit' 'Quit')
|
||
fi
|
||
|
||
[[ -n "${sCat}" ]] && printf '\n%s\n' "Selected category: ${sCat}"
|
||
|
||
printf '\n'
|
||
select sSelect in "${a[@]}" ; do
|
||
printf '\n'
|
||
case "${sSelect}" in
|
||
('Select a category')
|
||
sItem=''
|
||
select sCat in "${aCatFiles[@]}"
|
||
do
|
||
break
|
||
done
|
||
if [[ ! -f "cats/${sCat}" ]] ; then
|
||
a=(
|
||
'ERROR: File not found.'
|
||
'Press any key to return to the menu.'
|
||
)
|
||
printf '%s\n' "${a[@]}"
|
||
read -rsN1
|
||
continue 2
|
||
fi
|
||
;;
|
||
|
||
('Add primary website'|'Add secondary website')
|
||
read -erp 'Website title: '
|
||
sTitle="${REPLY}"
|
||
if grep --fixed-strings --ignore-case ">${sTitle}<" -- '../index.html' > '/dev/null' ; then
|
||
a=(
|
||
"NOTICE: ${sTitle} is already in the directory."
|
||
'Press any key to return to the menu.'
|
||
)
|
||
printf '%s\n' "${a[@]}"
|
||
read -rsN1
|
||
continue 2
|
||
fi
|
||
|
||
# strip/encode unsafe chars for title
|
||
sTitle="$(perl -fp -e 's|[^[:print:]]||g' <<< "${sTitle}")"
|
||
sTitle="$(iconv --to-code='ASCII//TRANSLIT' <<< "${sTitle}")"
|
||
sTitle="$(sed -E 's/[[:space:]]{2,}/ /g' <<< "${sTitle}")"
|
||
for sKey in "${!aaPrintChars[@]}" ; do
|
||
sTitle="${sTitle//"${sKey}"/"${aaPrintChars[${sKey}]}"}"
|
||
done
|
||
|
||
if [[ "${sSelect}" = 'Add primary website' ]] ; then
|
||
if [[ -n "${sItem}" ]] ; then
|
||
a=(
|
||
'Changes must be committed before adding another item.'
|
||
'Press any key to return to the menu.'
|
||
)
|
||
printf '%s\n' "${a[@]}"
|
||
read -rsN1
|
||
continue 2
|
||
fi
|
||
|
||
# website description
|
||
read -erp 'One-line website description: '
|
||
sDescrip="${REPLY}"
|
||
if [[ -n "${sDescrip}" ]] ; then
|
||
# strip/encode unsafe chars for description
|
||
sDescrip="$(perl -fp -e 's|[^[:print:]]||g' <<< "${sDescrip}")"
|
||
sDescrip="$(iconv --to-code='ASCII//TRANSLIT' <<< "${sDescrip}")"
|
||
sDescrip="$(sed -E 's/[[:space:]]{2,}/ /g' <<< "${sDescrip}")"
|
||
for sKey in "${!aaPrintChars[@]}" ; do
|
||
sDescrip="${sDescrip//"${sKey}"/"${aaPrintChars[${sKey}]}"}"
|
||
done
|
||
else
|
||
sDescrip='No description available.'
|
||
fi
|
||
sItem="<div class=\"gridItem\" tabindex=\"0\"><h3>${sTitle}</h3><p>${sDescrip}</p><ul>"
|
||
aPreview=(
|
||
"PRIMARY TITLE: ${sTitle}"
|
||
"DESCRIPTION: ${sDescrip}"
|
||
)
|
||
else
|
||
aPreview+=("SECONDARY TITLE: ${sTitle}")
|
||
fi
|
||
|
||
# website url
|
||
sERE='^https?:\/\/[a-z0-9]+\.[a-z0-9]+[a-z0-9.]*\/'
|
||
while true ; do
|
||
read -erp 'Website URL: '
|
||
sUrl="${REPLY}"
|
||
[[ "${sUrl}" =~ ${sERE} ]] && break
|
||
a=(
|
||
'WARNING: The URL appears to be invalid.'
|
||
'Do you want to accept it anyway? [y/N]'
|
||
)
|
||
printf '%s\n' "${a[@]}"
|
||
read -rsN1
|
||
[[ "${REPLY}" = 'y' ]] && break
|
||
done
|
||
|
||
# strip/encode unsafe chars in url
|
||
sCleanUrl="$(perl -fp -e 's|[^[:print:]]||g' <<< "${sUrl}")"
|
||
sCleanUrl="$(iconv --to-code='ASCII//TRANSLIT' <<< "${sCleanUrl}")"
|
||
for sKey in "${!aaUrlChars[@]}" ; do
|
||
sCleanUrl="${sCleanUrl//"${sKey}"/"${aaUrlChars[${sKey}]}"}"
|
||
done
|
||
|
||
s="$(pcre2grep --only-matching=1 'https?:\/\/(?:www\.)?(.+)' <<< "${sCleanUrl}")"
|
||
if grep --fixed-strings --ignore-case "${s}" -- '../index.html' > '/dev/null' ; then
|
||
sItem=''
|
||
sCat=''
|
||
a=(
|
||
'NOTICE: This URL is already in the directory.'
|
||
'Press any key to return to the menu.'
|
||
)
|
||
printf '%s\n' "${a[@]}"
|
||
read -rsN1
|
||
continue 2
|
||
elif [[ "${sCleanUrl}" != "${sUrl}" ]] ; then
|
||
xdg-open "${sCleanUrl}" &> '/dev/null' & disown
|
||
a=(
|
||
"Original URL : ${sUrl}"
|
||
"Cleaned URL : ${sCleanUrl}"
|
||
'The cleaned URL was opened in your browser.'
|
||
'Is the URL correct? [Y/n]'
|
||
)
|
||
printf '%s\n' "${a[@]}"
|
||
read -rsN1
|
||
[[ "${REPLY}" = 'n' ]] && sItem='' && sCat='' && continue 2
|
||
fi
|
||
sItem+="<li><a href=\"${sCleanUrl}\">${sTitle}</a></li>"
|
||
aPreview+=("URL: ${sCleanUrl}")
|
||
|
||
[[ "${sSelect}" = 'Add primary website' ]] && iAddedPrimary=1
|
||
;;
|
||
|
||
('Rebuild the directory'|'Commit')
|
||
iAddedPrimary=0
|
||
|
||
printf '%s\n' 'Backing up directory files...'
|
||
s="$(date '+%F_%T')"
|
||
mkdir --parents -- "backups/${s}/cats/"
|
||
cp -- '../about.html' '../downloads.html' '../index.html' '../submissions.html' "backups/${s}/"
|
||
cp --recursive -- 'cats/' "backups/${s}/"
|
||
|
||
aFiles=('index.html') # files to process in the loop
|
||
if [[ "${sSelect}" = 'Commit' ]] ; then
|
||
a=(
|
||
'Item preview:'
|
||
"${aPreview[@]}"
|
||
'Accept this submission? [Y/n]'
|
||
)
|
||
printf '%s\n\n ' "${a[@]}"
|
||
read -rsN1
|
||
[[ "${REPLY}" = 'n' ]] && sItem='' && sCat='' && continue 2
|
||
|
||
printf '%s\n' 'Creating category file...'
|
||
printf '%s\n' "${sItem}</ul></div>" >> "cats/${sCat}"
|
||
sort --ignore-case --ignore-leading-blanks --output="cats/${sCat}" -- "cats/${sCat}"
|
||
else
|
||
printf '%s\n' 'Rebuild all directory files? [Y/n]'
|
||
read -rsN1
|
||
[[ "${REPLY}" = 'n' ]] && continue 2
|
||
printf '%s\n' 'Rebuilding category files...'
|
||
for sFile in "${aCatFiles[@]}" ; do
|
||
sort --ignore-case --ignore-leading-blanks --output="cats/${sFile}" -- "cats/${sFile}"
|
||
done
|
||
aFiles+=('about.html' 'downloads.html' 'submissions.html') # files to process in the loop
|
||
fi
|
||
sItem='' sCat=''
|
||
|
||
sDate="$(date --utc '+%b %d, %Y (GMT)')"
|
||
for sFile in "${aFiles[@]}" ; do
|
||
printf '%s\n' "Building ${sFile}..."
|
||
|
||
aFile=("$(<'parts/head.html')")
|
||
aFile+=("$(<"parts/${sFile}")")
|
||
if [[ "${sFile}" = 'index.html' ]] ; then
|
||
for s in "${aCatFiles[@]}" ; do
|
||
# format anchor links in grid
|
||
s1="${s//' '/}"
|
||
s1="${s1//'&'/}"
|
||
s1="${s1^^}"
|
||
s2="${s//'&'/'&'}" # encode '&' chars for link text
|
||
aFile+=(
|
||
"<h2 class=\"cat\" id=\"${s1}\">${s2}</h2>"
|
||
'<div class="grid">'
|
||
"$(<"cats/${s}")"
|
||
'</div>'
|
||
)
|
||
done
|
||
aFile+=('</div>')
|
||
aFile=("${aFile[*]//'{TITLE}'/'Alternative Information Directory'}")
|
||
aFile=("${aFile[*]//'{UTC_DATE}'/"${sDate}"}")
|
||
elif [[ "${sFile}" = 'about.html' ]] ; then
|
||
aFile=("${aFile[*]//'{TITLE}'/'About | Alternative Information Directory'}")
|
||
elif [[ "${sFile}" = 'downloads.html' ]] ; then
|
||
aFile=("${aFile[*]//'{TITLE}'/'Downloads | Alternative Information Directory'}")
|
||
elif [[ "${sFile}" = 'submissions.html' ]] ; then
|
||
aFile=("${aFile[*]//'{TITLE}'/'Submissions | Alternative Information Directory'}")
|
||
fi
|
||
aFile+=("$(<'parts/tail.html')")
|
||
|
||
printf '%s\n' 'Running tidy...'
|
||
if ! tidy -errors -quiet <<< "${aFile[*]}" ; then
|
||
a=(
|
||
''
|
||
"ERROR: tidy encountered a problem with ${sFile}."
|
||
''
|
||
'Press any key to continue.'
|
||
)
|
||
printf '%s\n' "${a[@]}"
|
||
read -rsN1
|
||
fi
|
||
|
||
printf '%s\n' "${aFile[@]}" > "../${sFile}"
|
||
done
|
||
|
||
xdg-open '../index.html' &> '/dev/null' & disown
|
||
|
||
a=(
|
||
'Finished!'
|
||
''
|
||
'The index.html page was opened in your browser.'
|
||
''
|
||
)
|
||
printf '%s\n' "${a[@]}"
|
||
;;
|
||
|
||
('Quit')
|
||
exit
|
||
;;
|
||
|
||
(*) printf '%s\n' 'Invalid choice.'
|
||
continue 2
|
||
;;
|
||
esac
|
||
break
|
||
done
|
||
done
|
||
|
||
exit
|