#!/bin/bash
#
# Drupal management script.
#

BASE=${BASE:="/var/www/data"}
SITES=${SITES:="$BASE/drupal"}
#SERIES="6 7 8 9"
#LATEST_SERIES="9"
SERIES="6 7 8"
LATEST_SERIES="8"

# Ensure we have a proper path, useful when called through cron
export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'

# Read a parameter from user
function drupal_user_input {
  local input
  param="$1"
  default="$2"
  shift 2

  if echo $param | grep -q 'passwd'; then
    read -s -rep "$* (defaults to $default): " input
  else
    read -rep "$* (defaults to $default): " input
  fi

  if [ -z "$input" ]; then
    export $param="$default"
  else
    export $param="$input"
  fi
}

# Get drupal major version
function drupal_get_major {
  echo $1 | sed -e 's/\(^.\).*/\1/'
}

# Check for existing installations
function drupal_check_existing {
  if [ -e "$BASE/drupal-$1" ]; then
    if [ "$SILENT" != "yes" ]; then
      echo "Folder $BASE/drupal-$1 already exists, skipping"
    fi
    exit
  fi
}

# Check for non existing installations
function drupal_check_not_existing {
  if [ ! -e "$BASE/drupal-$1" ]; then
    if [ "$SILENT" != "yes" ]; then
      echo "Folder $BASE/drupal-$1 does not exist, skipping"
    fi
    exit 1
  fi
}

# Iterate through all drupal instances
function drupal_iterate {
  local command="$1"

  if [ -z "$command" ]; then
    return
  fi

  if [ "$command" != "cron" ] && [ "$SILENT" != "yes" ]; then
    echo "Issuing $command in all installed instances..."
  fi

  for version in $SERIES; do
    # Setup base folder
    base="$BASE/drupal-$version"

    if [ ! -d "$base/sites" ]; then
      continue
    fi

    # Only cron action is available for drupal 6
    if [ "$version" == "6" ] && [ "$command" != "cron" ]; then
      continue
    fi

    # Setup site folders with .onion sites in the end of the list
    # and ignoring site names without dots
    cd $base/sites
    drupals="`ls -1 -I default -I all -I example.sites.php -I '*.onion' | grep "\." | xargs`"
    drupals="$drupals `ls -1 | grep '.onion$' | xargs`"

    # Issue updates
    for drupal in $drupals; do
      if [ -e "$drupal/settings.php" ]; then
        hash="`sha1sum $drupal/settings.php | cut -d ' ' -f 1`"
        # Process sites just once, avoiding symlinks
        if echo $settings_hash | grep -q -v "$command:$hash"; then
          settings_hash="$settings_hash-$command:$hash"
          if [ "$command" != "cron" ] && [ "$SILENT" != "yes" ]; then
            echo "Processing $drupal..."
          fi

          if [ "$command" == "update" ] || [ "$command" == "cron-update" ]; then
            shift
            drupal_update $command $drupal $*
          else
            drush -l $drupal $*
          fi
        fi
      fi
    done

    # Fix permissions
    #if [ "`whoami`" == "root" ]; then
    #  chown -R drupal.drupal $base/modules
    #  chown -R drupal.drupal $base/themes
    #  if [ -e "$base/sites/all/modules" ]; then
    #    chown -R drupal.drupal $base/sites/all/modules
    #  fi
    #  if [ -e "$base/sites/all/themes" ]; then
    #    chown -R drupal.drupal $base/sites/all/themes
    #  fi
    #fi
  done
}

# Update a drupal instance
function drupal_update {
  local method="$1"
  local instance="$2"
  local confirm
  local line
  shift 2

  if [ "$method" == "cron-update" ]; then
    confirm="-y"
  fi

  # See https://drupal.org/node/823146#comment-3319070
  drush -l $instance up -u 1 --pipe | \
    grep -v -E 'Unknown|Up-to-date|Atualizado|Desconhecido|^Array$|^\($|^\)$|OK' | while read line ; do
    # Avoid automated core updates
    code="`echo $line | cut -d " " -f1`"
    if [ "$code" == "drupal" ]; then
      echo "Core update available: $line"
    else
      echo "Updating $code on instance $instance..."
      drush -l $instance up -u 1 $confirm $* $code
    fi
  done
}

# Get the current installed version of a given series
function drupal_current {
  local series="$1"

  if [ -z "$series" ]; then
    series="$LATEST_SERIES"
  fi

  if [ -e "$BASE/drupal-${series}" ]; then
    readlink $BASE/drupal-${series} | sed -e 's/^drupal-//'
  else
    echo 0
  fi
}

# Get the latest version of a given series
# Operate only on stable releases, filtering out dev, alpha, beta and rc releases
# https://drupal.stackexchange.com/questions/23700/how-to-find-download-latest-drupal-version-via-bash#23704
function drupal_latest {
  local series="$1"

  if [ -z "$series" ]; then
    series="$LATEST_SERIES"
  fi

  if [ "$series" == "9" ]; then
    latest="`wget -O- -q https://www.drupal.org/node/3060/release/feed | grep "<link>" | grep "releases/9." | cut -d "/" -f 7 | cut -d '<' -f 1 | sort -r | head -1`"
    echo $latest
  else
    latest="`wget -O- -q https://updates.drupal.org/release-history/drupal/${series}.x | grep -oPm1 "(?<=<download_link>)[^<]+" | grep -v -- '-dev' | grep -v -- '-alpha' | grep -v -- '-beta' | grep -v -- '-rc' | head -1`"
    latest="`basename $latest`"
    echo $latest | sed -e 's/^drupal-//' -e 's/.tar.gz$//'
  fi
}

# Deploy a fresh drupal tree
function drupal_download {
  if [ -z "$1" ]; then
    echo "Usage: `basename $0` download <version> [--upgrade]"
    exit 1
  else
    new="$1"
  fi

  # Check for latest releases if no specific version was given
  if [ "$new" == "9" ]; then
    drupal_series="9"
    new="`drupal_latest 9`"
  elif [ "$new" == "8" ]; then
    drupal_series="8"
    new="`drupal_latest 8`"
  elif [ "$new" == "7" ]; then
    drupal_series="7"
    new="`drupal_latest 7`"
  elif [ "$new" == "6" ]; then
    drupal_series="6"
    new="`drupal_latest 6`"
  else
    drupal_series="`drupal_get_major $new`"
  fi

  cd $BASE
  drupal_check_existing $new

  # Deploy a fresh drupal tree
  wget http://ftp.drupal.org/files/projects/drupal-$new.tar.gz
  tar zxvf drupal-$new.tar.gz && rm drupal-$new.tar.gz

  if [ "`whoami`" == "root" ]; then
    chown -R drupal.drupal drupal-$new/
  fi

  # Upgrade mode, erase sites folder as the previous should be copied.
  if [ "$2" == "--upgrade" ]; then
    cd drupal-$new && rm -rf sites
  fi

  # Make symlink if needed.
  if [ ! -e "$BASE/drupal-$drupal_series/CHANGELOG.txt" ]; then
    # Deal with a possibly empty drupal folder
    rmdir $BASE/drupal-$drupal_series &> /dev/null
    ( cd $BASE && ln -s drupal-$new drupal-$drupal_series )
  fi
}

# Upgrade a drupal instance using upstream source
function drupal_upgrade {
  if [ "$#" != "1" ] && [ "$#" != "2" ]; then
    echo "Usage: `basename $0` upgrade <series|old_version> [new_version]"
    exit 1
  fi

  # Get versions
  old="$1"
  new="$2"

  # Fix versions if just a series is given
  if [ "$old" == "9" ] && [ -z "$new" ]; then
    old="`drupal_current 9`"
    new="`drupal_latest  9`"
    drupal_series="9"
  elif [ "$old" == "8" ] && [ -z "$new" ]; then
    old="`drupal_current 8`"
    new="`drupal_latest  8`"
    drupal_series="8"
  elif [ "$old" == "7" ] && [ -z "$new" ]; then
    old="`drupal_current 7`"
    new="`drupal_latest  7`"
    drupal_series="7"
  elif [ "$old" == "6" ] && [ -z "$new" ]; then
    old="`drupal_current 6`"
    new="`drupal_latest  6`"
    drupal_series="6"
  else
    old_major="`drupal_get_major $old`"
    new_major="`drupal_get_major $new`"
    drupal_series="$new_major"
  fi

  if [ "$old_major" != "$new_major" ]; then
    echo "Major versions doesn't match"
    exit 1
  fi

  drupal_check_existing $new
  drupal_check_not_existing $old

  cd $BASE

  # Deploy a fresh drupal tree
  drupal_download $new --upgrade

  # Ensure we're in the new drupal folder
  cd $BASE/drupal-$new

  # Copy files
  for file in sites; do
    if [ ! -h "../drupal-$old/$file" ]; then
      cp -alf ../drupal-$old/$file . &> /dev/null
    else
      # Symlink handling
      ln -sf $file `readlink ../drupal-$old/$file`
    fi
  done

  extra_folders=""

  # Extra folder
  for extra_folder in $extra_folders; do
    if [ -d ../drupal-$old/$extra_folder ]; then
      cp -Rp ../drupal-$old/$extra_folder .
    fi
  done

  # Copy installation profiles
  rsync -av --exclude=default ../drupal-$old/profiles/ profiles/

  # Copy installation themes and modules for Drupal 8 or newer
  if [ "$new_major" != "5" ] && [ "$new_major" != "6" ] && [ "$new_major" != "7" ]; then
    rsync -av ../drupal-$old/modules/    modules/
    rsync -av ../drupal-$old/themes/     themes/
    rsync -av ../drupal-$old/libraries/  libraries/
  fi

  # Change symlink to point to the new location
  cd $BASE ; rm -rf drupal-$drupal_series && ln -sf drupal-$new drupal-$drupal_series

  # Done
  echo "Audit: `du -hs drupal-$old`"
  echo "Audit: `du -hs drupal-$new`"
  echo "Check procedure and remove drupal-$old once you make sure that everything is fine."
}

# Run a drupal makefile
function drupal_make {
  if [ -z "$1" ]; then
    echo "Usage: `basename $0` make <series>"
    exit 1
  fi

  series="$1"
  base="$BASE/drupal-$series"
  makefile="/usr/local/share/drupal/drupal$series.make"
  makefile_themes="/usr/local/share/drupal/themes$series.make"

  if [ -e "$makefile" ]; then
    if [ ! -e "$base" ]; then
      echo "Please download drupal code at $base first"
      exit 1
    fi
    drush dl drush_make
    ( cd $base && drush make -y --no-core $makefile )
    ( cd $base && drush make -y --no-core $makefile_themes )
  else
    echo "Makefile not found: $makefile"
    exit 1
  fi
}

# Ensure base is absolute
if [ ! -d "$BASE" ]; then
  echo "Folder $BASE does not exist"
  exit 1
else
  BASE="`cd $BASE &> /dev/null && pwd`"
fi

# Main procedure
if [ -z "$1" ]; then
  echo "Usage: `basename $0` <cron|download|update|updatedb|upgrade|current|latest|run|make> [arguments]"
  exit 1
elif [ "$1" == "cron" ]; then
  drupal_iterate cron
elif [ "$1" == "cron-update" ]; then
  SILENT="yes"
  drupal_iterate pm-refresh &> /dev/null
  drupal_iterate cron-update
elif [ "$1" == "download" ] || [ "$1" == "dl" ]; then
  shift
  drupal_download $*
elif [ "$1" == "update" ]; then
  shift
  drupal_iterate pm-refresh
  drupal_iterate update $*

  # Update alone might not trigger updatedb in a farm for all instances.
  drupal_iterate updatedb $*
  drupal_iterate cc all $*
elif [ "$1" == "updatedb" ]; then
  shift
  drupal_iterate updatedb $*
elif [ "$1" == "upgrade" ]; then
  shift
  drupal_upgrade $*
elif [ "$1" == "current" ]; then
  shift
  drupal_current $*
elif [ "$1" == "latest" ]; then
  shift
  drupal_latest $*
elif [ "$1" == "run" ]; then
  shift
  drupal_iterate $*
elif [ "$1" == "make" ]; then
  shift
  drupal_make $*
else
  echo "No action $1"
  exit 1
fi