File: //tmp/update-and-clean-bh.1763074004.sh
#!/usr/bin/env bash
####################################################### MANUAL #########################################################
# #
# This script was created to execute on the root af any WordPress installation, remove stray copies of BH and MOJO #
# plugin, and install, update and activate official BH plugin. #
# #
# Recommended usage: #
# > ./update-and-clean-bh.sh -r -vv #
# In this configuration the script will remove any copy of the BH plugin under unexpected paths, and install (or #
# update) the official BH plugin. #
# Once installed, the BH plugin will be activated only if there was a previous active copy of the same plugin #
# Note that, if there isn't any active copy of BH plugin, it is impossible for the script to update the plugin #
# #
# Various parameters can be used to tweak behaviour of the script: #
# -d -> Dry run; the script will execute as usual, but won't apply any change to current installation. #
# -v|-vv|-vvv -> Verbosity level; the higher, the more info you will get out of the script. #
# -f -> Force install; if provided, BH plugin will be installed, updated and activated, no matter what #
# current status is. #
# -r -> Remove others; if provided, out-of-spec installations of BH plugin, and all installations of Mojo #
# plugin, will be permanently removed from the site. #
# -n -> When set, even if a valid copy of BH is installed, it will delete it and install BH from scratch. #
# -h -> Prints the manual. #
########################################################################################################################
######################################################## GLOBALS #######################################################
## Whether last execution of search function found some matches
matchesFound=0;
## Whether any of the matches is active
hasActiveMatch=0;
## If previous flag is 1, contains rowIndex for first active match; default is -1 to distinguish it from valid indexes
activeMatch=-1;
## List of all installation plugins, retrieved only one time
allPlugins=();
## Matching plugins, populated with results of searchPlugin function
matchingPlugins=();
## Whether has BH plugin
hasBHPlugin=0;
## Index of the BH plugin, if found; default is -1 to distinguish it from valid indexes
BHPlugin=-1;
## Whether has BH-Alike plugins
hasOtherBHPlugins=0;
## Index of other BH plugin installations, if any.
otherBHPlugins=();
## Whether has at least one active copy of BH
hasActiveBH=0;
## If previous flag is 1, contains rowIndex for first active BH plugin; default is -1 to distinguish it from valid indexes
activeBH=-1;
## Whether BH plugin is installed and needs updates.
isBHUpToDate=0;
## Whether has Mojo-Alike plugins
hasMojoPlugins=0;
## Index of other Mojo plugin installations, if any.
mojoPlugins=();
## Whether has at least one active copy of Mojo; default is -1 to distinguish it from valid indexes
hasActiveMojo=0;
## If previous flag is 1, contains rowIndex for first active Mojo plugin; default is -1 to distinguish it from valid indexes
activeMojo=-1;
###################################################### UTILITIES #######################################################
## Search a specific plugin by field (title|file|name).
function searchPluginByField() {
local FIELD=$1;
local SEARCH=$2;
# check if field is supported
case "$FIELD" in
"name")
log "## Searching for matches of \"${SEARCH}\" in plugin names"
columnIndex=0
;;
"file")
log "## Searching for matches of \"${SEARCH}\" in plugin files"
columnIndex=1
;;
"title")
log "## Searching for matches of \"${SEARCH}\" in plugin titles"
columnIndex=2
;;
*)
log 'ERROR: Unsupported search field';
exit 1;
;;
esac
# retrieve all plugins
getAllPlugins
for rowIndex in "${!allPlugins[@]}"; do
row=${allPlugins[rowIndex]}
IFS='|' read -ra columns <<< "$row"
log "### Processing: ${columns[0]} -> $FIELD = ${columns[$columnIndex]}"
if [[ ${columns[$columnIndex]} =~ $SEARCH ]]; then
matchingPlugins[$rowIndex]="${rowIndex}"
matchesFound=1
fi
done
}
# removes official BH plugin from result set, after searching for BH-Alike plugins
function removeBHPluginFromMatches() {
if [[ $hasBHPlugin -eq 0 ]]; then return; fi
local tempMatches=()
for match in "${matchingPlugins[@]}"; do
if [[ $match != $BHPlugin ]]; then
tempMatches+=("$match")
fi
done
if [[ ${#tempMatches[@]} -eq 0 ]]; then
matchesFound=0
fi
matchingPlugins=("${tempMatches[@]}")
}
## Checks if it has at least an active copy of Bluehost
function hasActiveMatch() {
if [[ ${#matchingPlugins[@]} -eq 0 ]]; then return; fi
for rowIndex in "${!matchingPlugins[@]}"; do
row=${allPlugins[rowIndex]}
IFS='|' read -ra columns <<< "$row"
if [[ ${columns[3]} = "active" ]]; then
hasActiveMatch=1
activeMatch=$rowIndex
break
fi
done
}
## Search for exact match of Bluehost plugin
function searchBluehostPlugin() {
log "# SEARCHING FOR BH PLUGIN"
log "" 2
# search for matches
resetMatchingPlugins
searchPluginByField "file" "^bluehost-wordpress-plugin/bluehost-wordpress-plugin.php$"
# print results
printSearchResults
hasBHPlugin=$matchesFound
BHPlugin="${!matchingPlugins[@]}"
}
## Search for occurrences of plugins that resemble Bluehost plugin
function searchBluehostAlikePlugins() {
log "# SEARCHING FOR BH-ALIKE PLUGIN"
log "" 1
# search for matches
resetMatchingPlugins
searchPluginByField "title" "The Bluehost Plugin"
searchPluginByField "file" ".*/bluehost-wordpress-plugin.php"
searchPluginByField "name" ".*bluehost-wordpress-plugin.*"
# set flag if we have at least one active installation
hasActiveMatch
# remove official BH from the matches
removeBHPluginFromMatches
# print results
printSearchResults
hasActiveBH=$hasActiveMatch
activeBH=$activeMatch
hasOtherBHPlugins=$matchesFound
otherBHPlugins=("${matchingPlugins[@]}")
}
## Search for MOJO plugin
function searchMojoAlikePlugins() {
log "# SEARCHING FOR MOJO-ALIKE PLUGIN"
log "" 2
# search for matches
resetMatchingPlugins
searchPluginByField "title" "MOJO Marketplace"
searchPluginByField "file" ".*/mojo-marketplace.php"
searchPluginByField "name" ".*mojo-marketplace-wp-plugin.*"
# set flag if we have at least one active installation
hasActiveMatch
# print results
printSearchResults
hasActiveMojo=$hasActiveMatch
activeMojo=$activeMatch
hasMojoPlugins=$matchesFound
mojoPlugins=("${matchingPlugins[@]}")
}
## Reset matchingPlugins array and matchesFound flag
function resetMatchingPlugins() {
matchingPlugins=()
matchesFound=0
hasActiveMatch=0
activeMatch=-1
}
## Retrieve all existing plugins.
function getAllPlugins() {
if [[ ${#allPlugins[@]} -gt 0 ]]; then return; fi
while IFS="," read -r name file title status update
do
name="${name//\"/}"
name="${name//\'/}"
file="${file//\"/}"
file="${file//\'/}"
title="${title//\"/}"
title="${title//\'/}"
allPlugins+=("$name|$file|$title|$status|$update")
done < <(tail -n +2 <(wp plugin list --fields=name,file,title,status,update --format=csv --skip-plugins --skip-themes --skip-packages))
}
## Install BH plugin
function installBHPlugin() {
log "" 1
log "# Installing BH plugin"
if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi
attempt=1
maxAttempts=5
waitBetweenRetries=2
while [[ $attempt -le $maxAttempts ]];
do
log ""
log "## Attempt $attempt out of $maxAttempts..."
wp plugin install https://cdn.hiive.space/plugins/bluehost-wordpress-plugin.zip --skip-plugins --skip-themes --skip-packages
verifyBHPluginInstallation
if [[ $hasBHPlugin -eq 1 ]]; then
log ""
log "# Successfully installed BH plugin"
break;
fi
(( attempt++ ))
if [[ $attempt -le $maxAttempts ]]; then
log "## Attempt failed; waiting for retry in $waitBetweenRetries seconds..."
sleep "$waitBetweenRetries"
else
log ""
log "# Failed to install BH plugin"
fi
done
log "" 1
}
## Force install BH plugin
function forceInstallBHPlugin() {
log "" 1
log "# Force installing BH plugin (to update inactive copy)"
if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi
wp plugin install https://cdn.hiive.space/plugins/bluehost-wordpress-plugin.zip --skip-plugins --skip-themes --skip-packages --force
log "" 1
}
## Activate BH plugin
function activateBHPlugin() {
log "" 1
log "# Activating BH plugin"
if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi
wp plugin activate bluehost-wordpress-plugin --skip-plugins --skip-themes --skip-packages
log "" 1
}
## Update BH plugin
function updateBHPlugin() {
if [[ $hasBHPlugin -eq 0 ]]; then return; fi
log "" 1
log "# Updating BH plugin"
if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi
attempt=1
maxAttempts=5
waitBetweenRetries=2
while [[ $attempt -le $maxAttempts ]];
do
log ""
log "## Attempt $attempt out of $maxAttempts..."
wp plugin update bluehost-wordpress-plugin --skip-themes --skip-packages --skip-plugins=$( wp plugin list --fields=name --skip-themes --skip-packages --skip-plugins --format=csv | grep -v bluehost-wordpress-plugin | tail -n+2 | tr -d ' ' | tr -d '"' | tr '\n' ',' )
verifyBHPluginUpdateStatus
if [[ $isBHUpToDate -eq 1 ]]; then
log ""
log "# Successfully updated BH plugin"
break;
fi
(( attempt++ ))
if [[ $attempt -le $maxAttempts ]]; then
log "## Attempt failed; waiting for retry in $waitBetweenRetries seconds..."
sleep "$waitBetweenRetries"
else
log "# Failed to update BH plugin; do one final attempt to force install"
wp plugin install https://cdn.hiive.space/plugins/bluehost-wordpress-plugin.zip --skip-plugins --skip-themes --skip-packages --force
verifyBHPluginUpdateStatus
if [[ $isBHUpToDate -eq 1 ]]; then
log ""
log "# Successfully updated BH plugin"
else
log ""
log "# Failed to update BH plugin"
fi
fi
done
log "" 1
}
## Verify whether BH installation was successful
function verifyBHPluginInstallation() {
wp plugin is-installed bluehost-wordpress-plugin --skip-plugins --skip-themes --skip-packages
if [[ $? -eq 0 ]]; then
hasBHPlugin=1
log "## BH plugin is installed"
else
hasBHPlugin=0
log "## BH plugin isn't installed"
fi
}
## Verify whether BH activation was successful
function verifyBHPluginActivation() {
wp plugin is-active bluehost-wordpress-plugin --skip-plugins --skip-themes --skip-packages
if [[ $? -eq 0 ]]; then
hasActiveBH=1
log "## BH plugin is active"
else
hasActiveBH=0
log "## BH plugin isn't active"
fi
}
## Verify whether BH plugin is up to date
function verifyBHPluginUpdateStatus() {
status=$(wp plugin list --fields=name,update --format=csv --skip-themes --skip-packages --skip-plugins=$( wp plugin list --fields=name --skip-themes --skip-packages --skip-plugins --format=csv | grep -v bluehost-wordpress-plugin | tail -n+2 | tr -d ' ' | tr -d '"' | tr '\n' ',' ) | grep bluehost-wordpress-plugin, | cut -d',' -f2)
if [[ $status == 'none' ]]; then
isBHUpToDate=1;
log "## BH plugin is up to date"
elif [[ $status == 'available' ]]; then
isBHUpToDate=0;
log "## BH plugin needs to be updated"
else
log "## Couldn't verify BH plugin update status"
fi;
}
## Deactivate stray instances of BH plugin
function deactivateOtherBHPlugins() {
if [[ $hasOtherBHPlugins -eq 0 ]]; then return; fi
log "" 1
log "# Deactivating other copies of BH plugin"
deactivatePlugins "${otherBHPlugins[@]}"
}
## Removes stray instances of BH plugin
function removeOtherBHPlugins() {
if [[ $hasOtherBHPlugins -eq 0 || $removeOther -eq 0 ]]; then return; fi
log "" 1
log "# Removing other copies of BH plugin"
removePlugins "${otherBHPlugins[@]}"
}
## Removes instances of Mojo plugin
function removeMojoPlugins() {
if [[ $hasMojoPlugins -eq 0 || $removeOther -eq 0 ]]; then return; fi
log "" 1
log "# Removing copies of Mojo plugin"
removePlugins "${mojoPlugins[@]}"
}
## Activate the specified plugin
function activatePlugin() {
pluginToActivate=$1
if [[ $pluginToActivate -eq -1 ]]; then return; fi
IFS='|' read -ra columns <<< "${allPlugins[pluginToActivate]}"
log "" 1
log "## Activating ${columns[0]}..."
if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi
wp plugin activate "${columns[0]}" --skip-plugins --skip-themes --skip-packages
}
## Deactivate plugins
function deactivatePlugins() {
pluginsToDeactivate=("$@")
if [[ ${#pluginsToDeactivate[@]} -eq 0 ]]; then return; fi
for rowIndex in "${pluginsToDeactivate[@]}"; do
IFS='|' read -ra columns <<< "${allPlugins[rowIndex]}"
log "## Deactivating ${columns[0]}..."
if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; continue; fi
wp plugin deactivate "${columns[0]}" --skip-plugins --skip-themes --skip-packages
done
log "" 1
}
## Remove plugins from this installation
function removePlugins() {
pluginsToRemove=("$@")
if [[ ${#pluginsToRemove[@]} -eq 0 ]]; then return; fi
for rowIndex in "${pluginsToRemove[@]}"; do
IFS='|' read -ra columns <<< "${allPlugins[rowIndex]}"
log "## Removing ${columns[0]}..."
if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; continue; fi
wp plugin delete "${columns[0]}" --skip-plugins --skip-themes --skip-packages
done
log "" 1
}
## Log what's going on
function log() {
local message=$1
local logLevel=$2
if [[ $logLevel = "" ]]; then
while [[ "${message:0:1}" = "#" ]]; do
((logLevel++))
message="${message:1}"
done
fi
if [[ $logLevel -le $verbosity ]]; then
if [[ $message != "" ]]; then
printf '%0.s#' $(seq 1 $logLevel)
fi
printf "$message"
echo
fi
}
## Pretty-print search results
function printSearchResults() {
local columnSizes=()
local rowSize=16
if [[ $matchesFound -eq 1 && $verbosity -ge 2 ]]; then
log "## Matches found:"
for rowIndex in "${matchingPlugins[@]}"; do
IFS='|' read -ra columns <<< "${allPlugins[rowIndex]}"
for columnIndex in "${!columns[@]}"; do
if [[ columnSizes[columnIndex] -lt ${#columns[columnIndex]} ]]; then
columnSizes[columnIndex]="${#columns[columnIndex]}";
fi
done
done
for columnSize in ${columnSizes[@]}; do
let rowSize+=$columnSize
done
printf '%.0s-' $(seq 1 $rowSize)
echo
for rowIndex in "${matchingPlugins[@]}"; do
IFS='|' read -ra columns <<< "${allPlugins[rowIndex]}"
printf "| "
for columnIndex in "${!columns[@]}"; do
padding="${columnSizes[columnIndex]}"
printf "%-${padding}s | " "${columns[columnIndex]}"
done
echo
printf '%.0s-' $(seq 1 $rowSize)
echo
done
else
log "## No match found :("
fi
log "" 2
}
################################################### PRE-FLIGHT TESTS ###################################################
## 1. Verify we're in a WordPress installation directory.
if ! [[ -f ./wp-config.php ]]; then
log 'ERROR: not in a WordPress installation';
exit 1;
fi
## 2. Verify WP CLI is available
if ! command -v wp &> /dev/null; then
log 'ERROR: WP CLI not available';
exit 1;
fi
dryRun=0
verbosity=0
forceInstall=0
removeOther=0
newInstallation=0
while getopts "vfrdhn" opt; do
case "$opt" in
v)
((verbosity++))
;;
d)
dryRun=1
;;
f)
forceInstall=1
;;
r)
removeOther=1
;;
n)
newInstallation=1
;;
h)
echo "NAME"
echo
echo "Bluehost updating script"
echo
echo "DESCRIPTION"
echo
echo "Install, activate and updates official BH plugin"
echo "Optionally removes out-of-spec installation of BH and Mojo"
echo
echo "SYNOPSIS"
echo
echo "update-bluehost [-v] [-f] [-r] [-d] [-h]"
echo
echo "PARAMETERS"
echo
echo "-d -> Dry run; the script will execute as usual, but won't apply any change to current installation."
echo "-v|-vv|-vvv -> Verbosity level; the higher, the more info you will get out of the script."
echo "-f -> Force install; if provided, BH plugin will be installed, updated and activated, no matter what current status is."
echo "-r -> Remove others; if provided, out-of-spec installations of BH plugin, and all installations of Mojo plugin, will be permanently removed from the site."
echo "-n -> When set, even if a valid copy of BH is installed, it will delete it and install BH from scratch."
echo "-h -> Prints this manual."
exit 0
;;
*)
log "USAGE: $0 [-v] [-f] [-r] [-d] [-n] [-h]"
exit 1
;;
esac
done
if [[ $verbosity -eq 0 ]]; then verbosity=1; fi
##################################################### MAIN SCRIPT ######################################################
# Search for occurrences of Mojo plugin, including official one
searchMojoAlikePlugins
# Search for BlueHost plugin.
searchBluehostPlugin
verifyBHPluginUpdateStatus
# Search for other occurrences of BlueHost plugin
searchBluehostAlikePlugins
# Register some flags that will be used to evaluate the final result
(( hadBHPlugin = hasBHPlugin || hasOtherBHPlugins ))
wasBHActive=$hasActiveBH
wasBHUpToDate=$isBHUpToDate
# Make sure to remove stray copies of BH, and run on latest
if [[ $hasBHPlugin -eq 0 && $hasOtherBHPlugins -eq 0 ]]; then
log "# No copy of the BH plugin found on this installation"
if [[ $hasMojoPlugins -eq 1 || $forceInstall -eq 1 ]]; then
log "## Proceeding to install a fresh copy"
installBHPlugin
if [[ $hasBHPlugin -eq 1 && ( $hasActiveMojo -eq 1 || $forceInstall -eq 1 ) ]]; then
activateBHPlugin
fi
else
exit 0
fi
else
log "# Found at least one copy of the BH plugin"
deactivateOtherBHPlugins
if [[ $hasBHPlugin -eq 1 && $newInstallation -eq 1 ]]; then
log "# Removing BH plugin to install it from scratch"
removePlugins "$BHPlugin"
fi
if [[ $hasBHPlugin -eq 0 || $newInstallation -eq 1 ]]; then
installBHPlugin
fi
if [[ $hasBHPlugin -eq 1 ]]; then
# if we're positive about BH installation, proceed with activation and removal of stray copies
if [[ $hasActiveBH -eq 1 || $hasActiveMojo -eq 1 || $forceInstall -eq 1 ]]; then
# activate and update BH
activateBHPlugin
updateBHPlugin
verifyBHPluginActivation
# if we're positive about BH activation, proceed with removal of stray copies
if [[ $hasActiveBH -eq 1 ]]; then
removeOtherBHPlugins
else
if [[ $activeBH -ge 0 ]]; then activatePlugin $activeBH; fi
fi
else
# if we had an inactive copy of the plugin, force install it, to make sure it is updated.
if [[ $BHPlugin -ge 0 ]]; then forceInstallBHPlugin; fi
# then, just remove stray copies
removeOtherBHPlugins
fi
else
# if we couldn't install BH plugin, reactivate the one previously active
if [[ $hasActiveBH -eq 1 && $activeBH -ge 0 ]]; then
activatePlugin $activeBH
fi
fi
fi
# Migrate from MOJO to BH
log ""
if [[ $hasMojoPlugins -eq 1 ]]; then
log "# Found at least one copy of MOJO plugin"
if [[ $hasBHPlugin -eq 1 ]]; then
if [[ $hasActiveMojo -eq 1 && $hasActiveBH -eq 0 ]]; then
# activate and update BH
activateBHPlugin
updateBHPlugin
verifyBHPluginActivation
# if we're positive about BH activation, proceed with removal of mojo
if [[ $hasActiveBH -eq 1 ]]; then
removeMojoPlugins
else
if [[ $activeMojo -ge 0 ]]; then activatePlugin $activeMojo; fi
fi
else
# if we don't have to activate BH, just remove Mojo
removeMojoPlugins
fi
else
log "# Couldn't replace MOJO for a copy of BH plugin"
fi
else
log "# No copy of MOJO plugin found on this installation"
fi
# Evaluate the final result
log ""
verifyBHPluginInstallation
verifyBHPluginActivation
verifyBHPluginUpdateStatus
# In order to consider process a success we need to either:
# - skip installation because we have no BH initially, and we're not forcing the installation
# - we have BH installed but deactivated, because this was the initial state and we're not forcing the installation
# - we have BH installed, activated and updated, because we originally had BH installed and active.
(( status = ( ! hadBHPlugin && ! hasBHPlugin && ! forceInstall ) || ( hasBHPlugin && ( ( ! wasBHActive && ! forceInstall ) || ( hasActiveBH && isBHUpToDate ) ) ) ))
# In case of failure, we need to flag cases that are more critical than others
# - if plugin was active before and it isn't now
# - or if was installed before and script removed it
# we lost connection to the site, and need to manually review
# In this case we can accept any copy of BH plugin, so we double check for BH-Alike plugins.
searchBluehostAlikePlugins > /dev/null;
(( hasBHPlugin = hasBHPlugin || hasOtherBHPlugins ))
(( critical = ( wasBHActive && ! hasActiveBH ) || ( hadBHPlugin && ! hasBHPlugin ) ))
# print result line for easier debugging
log ""
if [[ $critical -eq 1 ]];
then log "# OPERATIONS LOG: $(pwd) | $(whoami) | CRITICAL"
elif [[ $status -eq 1 ]];
then log "# OPERATIONS LOG: $(pwd) | $(whoami) | SUCCESS"
else log "# OPERATIONS LOG: $(pwd) | $(whoami) | FAILURE"
fi
log "## Initial status: BH Installed -> $hadBHPlugin; BH Active -> $wasBHActive; BH Up to date -> $wasBHUpToDate"
log "## Final status: BH Installed -> $hasBHPlugin; BH Active -> $hasActiveBH; BH Up to date -> $isBHUpToDate"
log ""