diff --git a/pushtape.install b/pushtape.install
index 9e4329e..957f4e0 100644
--- a/pushtape.install
+++ b/pushtape.install
@@ -1,10 +1,276 @@
 <?php
+
+/**
+ * @file
+ * Installation code for Pushtape Kickstart.
+ */
+
+/**
+ * Implements hook_install_tasks().
+ */
+function pushtape_install_tasks() {
+  // Since any module can add a drupal_set_message, this can bug the user
+  // when we display this page. For a better user experience,
+  // remove all the messages.
+  drupal_get_messages(NULL, TRUE);
+  $tasks = array();
+  $current_task = variable_get('install_task', 'done');
+  $install_demo_site = variable_get('pushtape_demo_site', FALSE);
+
+  $tasks['pushtape_configure_site_form'] = array(
+    'display_name' => st('Configure site'),
+    'type' => 'form',
+  );
+  $tasks['pushtape_install_additional_modules'] = array(
+    'display_name' => $install_demo_site ? st('Install demo site') : st('Install additional functionality'),
+    'type' => 'batch',
+    // Show this task only after the Kickstart steps have bene reached.
+    'display' => strpos($current_task, 'pushtape_') !== FALSE,
+  );
+
+  return $tasks;
+}
+
+/**
+ * Implements hook_install_tasks_alter().
+ */
+function pushtape_install_tasks_alter(&$tasks, $install_state) {
+  $tasks['install_select_profile']['display'] = FALSE;
+  $tasks['install_finished']['function'] = 'pushtape_install_finished';
+
+  _pushtape_set_theme('flux');
+}
+
+/**
+ * Force-set a theme at any point during the execution of the request.
+ *
+ * Drupal doesn't give us the option to set the theme during the installation
+ * process and forces enable the maintenance theme too early in the request
+ * for us to modify it in a clean way.
+ */
+function _pushtape_set_theme($target_theme) {
+  if ($GLOBALS['theme'] != $target_theme) {
+    unset($GLOBALS['theme']);
+
+    drupal_static_reset();
+    $GLOBALS['conf']['maintenance_theme'] = $target_theme;
+    _drupal_maintenance_theme();
+  }
+}
+
+/**
+ * Task callback: returns the form allowing the user to add example site content on install.
+ */
+function pushtape_configure_site_form() {
+  drupal_set_title(st('Configure site'));
+
+  // Prepare all the options for sample content.
+  $options = array(
+    '1' => st('Yes'),
+    '0' => st('No'),
+  );
+  $form['functionality'] = array(
+    '#type' => 'fieldset',
+    '#title' => st('Functionality'),
+  );
+  $form['functionality']['install_demo_site'] = array(
+    '#type' => 'radios',
+    '#title' => st('Do you want to install the demo site?'),
+    '#description' => st('Shows you everything Pushtape Kickstart can do. Includes a custom theme, sample content and products.'),
+    '#options' => $options,
+    '#default_value' => '1',
+  );
+
+  $options_selection = array(
+    'events' => 'Additional <strong>blocks</strong> for featuring events content.',
+    'admin' => 'Custom <strong>admin menu</strong> designed for site owners.',
+    'news' => '<strong>Blog</strong> functionality.',
+    'audio' => '<strong>Social</strong> logins and links for sharing products via social networks.',
+  );
+  $form['functionality']['extras'] = array(
+    '#type' => 'checkboxes',
+    '#options' => $options_selection,
+    '#title' => t("Install additional functionality"),
+    '#states' => array(
+      'visible' => array(
+        ':input[name="install_demo_site"]' => array('value' => '0'),
+      ),
+    ),
+  );
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => st('Create and Finish'),
+    '#weight' => 15,
+  );
+  return $form;
+}
+
+/**
+ * Submit callback: creates the requested sample content.
+ */
+function pushtape_configure_site_form_submit(&$form, &$form_state) {
+  variable_set('pushtape_demo_site', $form_state['values']['install_demo_site']);
+  variable_set('pushtape_selected_extras', $form_state['values']['extras']);
+
+}
+
+/**
+ * Task callback: uses Batch API to import modules based on user selection.
+ * Installs all demo site modules if requested, or any modules providing
+ * additional functionality to the base install.
+ */
+function pushtape_install_additional_modules() {
+  $install_demo_site = variable_get('pushtape_demo_site', FALSE);
+  if ($install_demo_site) {
+    $modules = array(
+      'pushtape_ui',
+      'pushtape_news',
+      'pushtape_events',
+      'pushtape_audio',
+      'pushtape_discography',
+      'pushtape_admin',
+    );
+  }
+  else {
+    $modules = array(
+      'pushtape_content',
+      'pushtape_lite_search',
+      'pushtape_user',
+      'pushtape_migrate',
+    );
+    $selected_extras = variable_get('pushtape_selected_extras', array());
+    if (!empty($selected_extras['pushtape_ui'])) {
+      $modules[] = 'pushtape_ui';
+    }
+    if (!empty($selected_extras['discography'])) {
+      $modules[] = 'pushtape_discography';
+    }
+    if (!empty($selected_extras['news'])) {
+      $modules[] = 'pushtape_news';
+    }
+    if (!empty($selected_extras['events'])) {
+      $modules[] = 'pushtape_events';
+    }
+    if (!empty($selected_extras['admin'])) {
+      $modules[] = 'pushtape_admin';
+    }
+    if (!empty($selected_extras['Discography'])) {
+      $modules[] = 'pushtape_audio';
+    }
+  }
+
+  // Resolve the dependencies now, so that module_enable() doesn't need
+  // to do it later for each individual module (which kills performance).
+  $files = system_rebuild_module_data();
+  $modules_sorted = array();
+  foreach ($modules as $module) {
+    if ($files[$module]->requires) {
+      // Create a list of dependencies that haven't been installed yet.
+      $dependencies = array_keys($files[$module]->requires);
+      $dependencies = array_filter($dependencies, '_pushtape_filter_dependencies');
+      // Add them to the module list.
+      $modules = array_merge($modules, $dependencies);
+    }
+  }
+  $modules = array_unique($modules);
+  foreach ($modules as $module) {
+    $modules_sorted[$module] = $files[$module]->sort;
+  }
+  arsort($modules_sorted);
+
+  $operations = array();
+  foreach ($modules_sorted as $module => $weight) {
+    $operations[] = array('_pushtape_enable_module', array($module, $files[$module]->info['name']));
+  }
+  $operations[] = array('_pushtape_flush_caches', array(t('Flushed caches.')));
+
+  $batch = array(
+    'title' => $install_demo_site ? t('Installing demo site') : t('Installing additional functionality'),
+    'operations' => $operations,
+    'file' => drupal_get_path('profile', 'pushtape') . '/pushtape.install_callbacks.inc',
+  );
+
+  return $batch;
+}
+
+/**
+ * array_filter() callback used to filter out already installed dependencies.
+ */
+function _pushtape_filter_dependencies($dependency) {
+  return !module_exists($dependency);
+}
+
+/**
+ * Custom installation task; perform final steps and redirect the user to the new site if there are no errors.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   A message informing the user about errors if there was some.
+ */
+function pushtape_install_finished(&$install_state) {
+  drupal_set_title(st('@drupal installation complete', array('@drupal' => drupal_install_profile_distribution_name())), PASS_THROUGH);
+  $messages = drupal_set_message();
+
+  // Flush all caches to ensure that any full bootstraps during the installer
+  // do not leave stale cached data, and that any content types or other items
+  // registered by the install profile are registered correctly.
+  drupal_flush_all_caches();
+
+  // Remember the profile which was used.
+  variable_set('install_profile', drupal_get_profile());
+
+  // Install profiles are always loaded last
+  db_update('system')
+    ->fields(array('weight' => 1000))
+    ->condition('type', 'module')
+    ->condition('name', drupal_get_profile())
+    ->execute();
+
+  // Cache a fully-built schema.
+  drupal_get_schema(NULL, TRUE);
+
+  // Run cron to populate update status tables (if available) so that users
+  // will be warned if they've installed an out of date Drupal version.
+  // Will also trigger indexing of profile-supplied content or feeds.
+  drupal_cron_run();
+
+  if (isset($messages['error'])) {
+    $output = '<p>' . (isset($messages['error']) ? st('Review the messages above before visiting <a href="@url">your new site</a>.', array('@url' => url(''))) : st('<a href="@url">Visit your new site</a>.', array('@url' => url('')))) . '</p>';
+    return $output;
+  }
+  else {
+    // Since any module can add a drupal_set_message, this can bug the user
+    // when we redirect him to the front page. For a better user experience,
+    // remove all the message that are only "notifications" message.
+    drupal_get_messages('status', TRUE);
+    drupal_get_messages('completed', TRUE);
+    // Migrate adds its messages under the wrong type, see #1659150.
+    drupal_get_messages('ok', TRUE);
+
+    // If we don't install drupal using Drush, redirect the user to the front
+    // page.
+    if (!drupal_is_cli()) {
+      if (module_exists('overlay')) {
+        drupal_goto('', array('fragment' => 'overlay=admin/help/first-steps'));
+      }
+      else {
+        // get the user to a sensible starting place
+        drupal_goto('admin/help/first-steps');
+      }
+    }
+  }
+}
+
 /**
 * Implement hook_install().
 *
 * Perform actions to set up the site for this profile.
 */
-function pushtape_install() {  
+function pushtape_install() {
   // Enable themes
   theme_enable(array('seven', 'flux'));
   variable_set('admin_theme', 'flux');
