#! /bin/bash # # Written by Gilbert S # # This script is a comprehensive WordPress focused tool used to automate many WordPress # related troubleshooting and maintenance procedures. This tool must be run from a # directory containing a WordPress installation as the user who owns the installation. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. clear # defines cP based on pwd cPuser() { pwd | awk -F "/" '{print $3}' } # Checks if you are using root instead of the user cannotBeRoot() { if [[ $EUID = 0 ]]; then echo "This script must be run as local non root user." 1>&2 exit 1 2> /dev/null fi } : # Creates a 24 character random strong password by using SHA to hash the date, runs through base64, and then outputs the top 24 characters tempPwd() { date +%s | sha256sum | base64 | head -c 24 ; echo } # Installs a brand new WordPress installation for dir defined by $installPath installWP() { mkdir $installPath cd $installPath curl -O https://wordpress.org/latest.tar.gz tar -zxf latest.tar.gz cd wordpress cp -rf . .. cd .. rm -R wordpress cp wp-config-sample.php wp-config.php # random use of perl for funzies perl -pi -e "s/database_name_here/$newdbname/g" wp-config.php perl -pi -e "s/username_here/$newdbuser/g" wp-config.php perl -pi -e "s/password_here/$newdbpass/g" wp-config.php perl -i -pe' BEGIN { @chars = ("a" .. "z", "A" .. "Z", 0 .. 9); push @chars, split //, "!@#$%^&*()-_ []{}<>~\`+=,.;:/?|"; sub salt { join "", map $chars[ rand @chars ], 1 .. 64 } } s/put your unique phrase here/salt()/ge ' wp-config.php mkdir wp-content/uploads chmod 775 wp-content/uploads rm latest.tar.gz } # Enables gzip and leverage browser caching enableGzip(){ cat <> .htaccess # Compress HTML, CSS, JavaScript, Text, XML and fonts AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/vnd.ms-fontobject AddOutputFilterByType DEFLATE application/x-font AddOutputFilterByType DEFLATE application/x-font-opentype AddOutputFilterByType DEFLATE application/x-font-otf AddOutputFilterByType DEFLATE application/x-font-truetype AddOutputFilterByType DEFLATE application/x-font-ttf AddOutputFilterByType DEFLATE application/x-javascript AddOutputFilterByType DEFLATE application/xhtml+xml AddOutputFilterByType DEFLATE application/xml AddOutputFilterByType DEFLATE font/opentype AddOutputFilterByType DEFLATE font/otf AddOutputFilterByType DEFLATE font/ttf AddOutputFilterByType DEFLATE image/svg+xml AddOutputFilterByType DEFLATE image/x-icon AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/javascript AddOutputFilterByType DEFLATE text/plain AddOutputFilterByType DEFLATE text/xml # Remove browser bugs (only needed for really old browsers) BrowserMatch ^Mozilla/4 gzip-only-text/html BrowserMatch ^Mozilla/4\.0[678] no-gzip BrowserMatch \bMSIE !no-gzip !gzip-only-text/html Header append Vary User-Agent EOF } # Enables Leverage browser caching leverageBC(){ cat <> .htaccess ExpiresActive On # Images ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/gif "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType image/webp "access plus 1 year" ExpiresByType image/svg+xml "access plus 1 year" ExpiresByType image/x-icon "access plus 1 year" # Video ExpiresByType video/mp4 "access plus 1 year" ExpiresByType video/mpeg "access plus 1 year" # CSS, JavaScript ExpiresByType text/css "access plus 1 month" ExpiresByType text/javascript "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" # Others ExpiresByType application/pdf "access plus 1 month" ExpiresByType application/x-shockwave-flash "access plus 1 month" EOF } # The WordPress install dialogue installDiag(){ installPath=$(whiptail --inputbox "Designate the desired install path." 8 78 $(pwd) --title "Set Path" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then newdbname=$(whiptail --inputbox "Designate name for new DB to be used by this installation. The name should always be preceeded by cP username_ or you may not be able to create the matching DB." 8 78 $(cPuser)_ --title "New Database" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then newdbuser=$(whiptail --inputbox "Designate user name for new DB. The name should always be preceeded by cP username_ or you may not be able to create the matching DB" 8 78 $(cPuser)_ --title "New User" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then newdbpass=$(whiptail --passwordbox "Designate new password for $newdbuser . The default password is a random strong PW, you can leave the prefilled values if you like." 8 78 $(tempPwd) --title "Set Password" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then installWP enableGzip leverageBC result="WordPress has been installed with the following DB info: Path: $installPath DB: $newdbname User: $newdbuser PW: $newdbpass PLEASE MANUALLY CREATE THE MATCHING DATABASE! (copy paste ftw!)" whiptail --msgbox "$result" 20 78 unset newdbpass exit else exitcancel fi else exitcancel fi else exitcancel fi else exitcancel fi } # Cancel and exit function exitcancel(){ result="You have selected Cancel." whiptail --msgbox "$result" 20 78 exit } # Checks if there is a WordPress isntallation present in your current DIR, if not, it offers to install one. WPinstallCheck() { if [[ ! -f ./wp-config.php ]]; then if (whiptail --title "No WP installed" --yesno "No WordPress installation found in current directory, would you like to install one?." 8 78) then installDiag else result="This script only functions in a directory that contains a WordPress installation. Please make sure that you're in the right directory." whiptail --msgbox "$result" 20 78 exit fi fi } # Executes sanity checks before defining further variables or functions cannotBeRoot && WPinstallCheck && # -------------------------------------------------------------------------------------- # Defining variables used by functions: DB=$(grep DB_NAME wp-config.php | cut -d \' -f 4) DBuser=$(grep DB_USER wp-config.php | cut -d \' -f 4) DBpass=$(grep DB_PASSWORD wp-config.php | cut -d \' -f 4) DBhost=$(grep DB_HOST wp-config.php | cut -d \' -f 4) # Defining functions used throughout script # Displays the WordPress version installed wpVersion() { grep wp_version wp-includes/version.php | gawk -v FPAT="[^']+" -v RS=';' 'NF==2{print $2}' } # pulls the table prefix from wp-config.php. This is a function instead of variable to work better in MySQL syntax tPrefix() { grep -i table_prefix wp-config.php | cut -d \' -f 2 } # Uses info found in local wp-config.php to create a mysql dump named wp-DB_backup.sql siteURL() { mysql -u $DBuser -p$DBpass -e "use $DB; SELECT * FROM $(tPrefix)options WHERE option_name = 'home';" | awk 'NR > 1{print $3}' } # Provides the domain name set in "home" option, without transport protocol prefix domain() { mysql -u $DBuser -p$DBpass -e "use $DB; SELECT * FROM $(tPrefix)options WHERE option_name = 'home';" | awk 'NR > 1{print $3}' | awk -F "/" '{print $NF}' } # Backs up the database defined in wp-config.php as wp-DB_backup.sql databasebk() { mysqldump -u $DBuser -p$DBpass $DB > wp-DB_backup.sql | whiptail --gauge "backing up DB ..." 6 60 33 } # Uses info found in local wp-config.php to restore WordPress DB taken with this scipt named wp-DB_backup.sql databaseresto() { mysql -u $DBuser -p$DBpass -h $DBhost $DB < wp-DB_backup.sql | whiptail --gauge "restoring DB ..." 6 60 50 } # Returns a rounded value for total DB size DBsize() { mysql -u $DBuser -p$DBpass -e 'SELECT table_schema AS "Database name", SUM(data_length + index_length) / 1024 / 1024 AS "Size (MB)" FROM information_schema.TABLES GROUP BY table_schema;'| grep $DB | awk '{printf "%0.2f\n", $2}' } # Returns number of users for DB numberOfUsers() { mysql -u $DBuser -p$DBpass -e "use $DB; select count(*) from $(tPrefix)users;" | awk 'NR > 1 {print $1}' } # Will toggle WP debug mode ON by modifying wp-config.php wpDebugOn() { grep "'WP_DEBUG', false" wp-config.php 2>/dev/null if [ $? -eq 0 ]; then sed -i "s/WP_DEBUG', false/WP_DEBUG', true/g" wp-config.php else grep "'WP_DEBUG', true" wp-config.php 2>/dev/null if [ $? -eq 1 ]; then echo "define('WP_DEBUG', true);" >> wp-config.php fi fi } #Toggles debug and script_debug to OFF by modifying wp-config.php wpDebugOff() { grep "'WP_DEBUG', true" wp-config.php 2>/dev/null if [ $? -eq 0 ]; then sed -i "s/WP_DEBUG', true/WP_DEBUG', false/g" wp-config.php fi } # Disables all plugins disablePlugins() { mysql -u $DBuser -p$DBpass <1 {print $2}' } # Lists installed WordPress themes listThemes() { ls -d ./wp-content/themes/*/ | awk -F "/" '{print $4}' } # Sets current theme to TwentySeventeen theme (sanity check if installed part of call) defaultTheme() { mysql -u $DBuser -p$DBpass </dev/null | openssl x509 -noout -issuer | awk -F "=" '{print $NF}' } # Returns the subject of SSL cert sslSubject() { echo | openssl s_client -connect $(domain):443 2>/dev/null | openssl x509 -noout -subject | awk -F "=" '{print $NF}' } # Checks expiration, issuer and subject to determine status of SSL cert sslCheck() { if true | openssl s_client -connect $(domain):443 2>/dev/null | \ openssl x509 -noout -checkend 0; then if [ $(sslIssuer) = $(sslSubject) ] then echo "Valid SSL certificate was found, but it is self-signed." else echo "A valid SSL certificate was found." fi else echo "SSL certificate is expired." fi } # This function will change all URLs from current URL to a URL designated. changeSiteURL() { mysql -u $DBuser -p$DBpass <&2 2>&1 1>&3 ) # Selections for menu structure exitstatus=$? if [ $exitstatus = 0 ] then case $CHOICE in "1)") DBoptions=$(whiptail --title "DB Options" --menu "Make your choice" 16 100 9 \ "1)" "Database Info" \ "2)" "Backup DB" \ "3)" "Restore DB" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then case $DBoptions in "1)") result="DB information for $(siteURL): WordPress Version = $(wpVersion) DB Name = $DB DB User = $DBuser DB Host = $DBhost Table Prefix = $(tPrefix) DB Size = $(DBsize) MB # of Users = $(numberOfUsers) " ;; "2)") databasebk result="The database $DB has been backed up to wp-DB_backup.sql (always check to be certin)." ;; "3)") databaseresto result="The database $DB has been restored for the user $DBuser ." ;; esac else result="You've chosen to cancel." fi ;; "2)") debugOptions=$(whiptail --title "Debug Options" --menu "Make your choice" 16 100 9 \ "1)" "Enable Debug mode" \ "2)" "Disable Debug mode" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then case $debugOptions in "1)") wpDebugOn result="Debug mode has been toggled ON." ;; "2)") wpDebugOff result="Debug mode has been toggled OFF." ;; esac else result="You've chosen to cancel." fi ;; "3)") pluginArray=($(wp plugin list | awk 'NR > 1 { print $1 }')) pluginParams=( --checklist --separate-output "Select" 20 78 14 ) i=0 pluginSelection=$(while [[ $i -lt ${#pluginArray[@]} ]] do for x in $(wp plugin list | awk 'NR > 1 { print $2 }') do if [ $x = active ] then pluginParams+=( "${pluginArray[$i]}" " " on ) i=$[$i+1] else pluginParams+=( "${pluginArray[$i]}" " " off ) i=$[$i+1] fi done done whiptail "${pluginParams[@]}" 3>&2 2>&1 1>&3) exitstatus=$? if [ $exitstatus = 0 ] then disablePlugins toggleSelected result="The following plugins are now active: $pluginSelection" else result="No changes have been made" fi ;; "4)") userOptions=$(whiptail --title "User Options" --menu "Make your choice" 16 100 9 \ "1)" "Create Temp User" \ "2)" "Remove Temp User" \ "3)" "Change User Passwords" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then case $userOptions in "1)") tempPass=$(tempPwd) mkTempUser result="You have created a temporary user. As per policy: DO NOT FORGET TO REMOVE IT AFTERWARDS! Username: imh-support. Password: $tempPass" unset tempPass ;; "2)") rmTempUser result="You have removed the temp user: imh-support." ;; "3)") userArray=($(listUsers)) userParams=( --title "Select User" --menu --separate-output "Select" 20 78 14 ) i=0 userSelection=$(while [[ $i -lt ${#userArray[@]} ]] do for x in $(listUsers) do userParams+=( ${userArray[$i]} "" ) i=$[$i+1] done done whiptail "${userParams[@]}" 3>&2 2>&1 1>&3) exitstatus=$? if [ $exitstatus = 0 ] then PASSWORD=$(whiptail --passwordbox "please enter your new password for $userSelection" 8 78 --title "password dialog" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then setNewPW result="You have successfully change the password for: User: $userSelection to: $PASSWORD" unset PASSWORD else result="User selected Cancel." fi else result="No changes have been made." fi ;; esac else result="You've chosen to cancel." fi ;; "5)") if (whiptail --title "Change site URL" --yesno "This script will change the current site URL from $(siteURL) to a new one (such as https:// or post clone/migration). $(sslCheck) This will make changes in the Database $DB and .htaccess file! DO NOT PROCEED IF YOU DO NOT HAVE A BACKUP AVAILABLE OF $DB Proceed?" 18 78) then NewURL=$(whiptail --inputbox "What is the new URL for $(siteURL)? Please include https:// or http:// in your answer. Check your spelling, no additional prompts will be generated after your selection!" 12 78 $(siteURL) --title "New URL" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then changeSiteURL result="You have selected Ok and entered $NewURL NOW UPDATING URLS. May systems have mercy on your soul if you ignored to make a backup and now need one." else result="You've chosedn to cancel." fi else result="If you need to take a backup of $DB you can use the automatic backup option found in the \"DB Options\" of this script (Always doublecheck the backup with a \"less wp-DB_backup.sql\")." fi ;; "6)") themeOptions=$(whiptail --title "Theme Options" --menu "Make your choice" 16 100 9 \ "1)" "Set to default theme (TwentySeventeen)" \ "2)" "Toggle from installed themes" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then case $themeOptions in "1)") if [ -d "./wp-content/themes/twentyseventeen" ] then defaultTheme result="Switched active theme to TwentySeventeen." else result="The theme TwenySeventeen is not installed, cannont switch to desired theme. Use theme toggler instead." fi ;; "2)") themeArray=($(listThemes)) themeParams=( --radiolist --separate-output "Select" 20 78 14 ) i=0 themeSelection=$(while [[ $i -lt ${#themeArray[@]} ]] do for x in $(listThemes) do themeParams+=( "${themeArray[$i]}" " " off ) i=$[$i+1] done done whiptail "${themeParams[@]}" 3>&2 2>&1 1>&3) exitstatus=$? if [ $exitstatus = 0 ] then themeName=$(grep -i "Theme Name:" ./wp-content/themes/$themeSelection/style.css | awk -F ":" '{print $2}') setTheme result="The following theme has been activated: $themeSelection theme name: $themeName" else result="No changes have been made" fi ;; esac else result="You've chosen to cancel." fi ;; "7)") installclone=$(whiptail --title "Install/Clone" --menu "Make your choice" 16 100 9 \ "1)" "Install fresh WP" \ "2)" "Clone WP to other location" \ "3)" "Pack install for migration" \ "4)" "Switch live/staging" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then case $installclone in "1)") installDiag ;; "2)") result="Doesn't do squat yet." ;; "3)") pkgWP result="The backup has been completed and is named '$(date +%Y-%m-%d)_WP-move.tar.gz' " ;; "4)") result="Doesn't do squat yet." ;; esac else result="You've chosen to cancel." fi ;; "<--") exit ;; esac whiptail --msgbox "$result" 20 78 else exit fi done exit clear