diff --git a/css_injector.admin.inc b/css_injector.admin.inc
deleted file mode 100644
index 96666cd..0000000
--- a/css_injector.admin.inc
+++ /dev/null
@@ -1,270 +0,0 @@
-<?php
-/**
- * @file css_injector.admin.inc
- * Administrative interface for CSS Injector.
- */
-
-/**
- * Form builder function for CSS Injector's main admin page.
- */
-function css_injector_admin_form($form, &$form_state) {
-  $rules = _css_injector_load_rule(NULL, TRUE);
-  $path = drupal_get_path('module', 'css_injector') . '/';
-  $form = array();
-  $form['#tree'] = TRUE;
-
-  // TODO: Turn these 'submit' buttons back into image_buttons when
-  // http://drupal.org/node/873070 lands. Currently you can't use
-  // #submit with an image_button.
-  foreach ($rules as $rule) {
-    $form['rules'][$rule['crid']]['#rule'] = $rule;
-    $form['rules'][$rule['crid']]['edit'] = array(
-      '#type' => 'submit',
-      '#value' => t('Edit rule'),
-      '#name' => 'edit' . $rule['crid'],
-      '#submit' => array('css_injector_admin_edit_button'),
-      '#crid' => $rule['crid'],
-    );
-    $form['rules'][$rule['crid']]['delete'] = array(
-      '#type' => 'submit',
-      '#value' => t('Delete rule'),
-      '#name' => 'delete' . $rule['crid'],
-      '#submit' => array('css_injector_admin_delete_button'),
-      '#crid' => $rule['crid'],
-    );
-  }
-  return $form;
-}
-
-/**
- * Edit button callback for the CSS rule listing form.
- */
-function css_injector_admin_edit_button($form, &$form_state) {
-  $button = $form_state['triggering_element'];
-  $crid = $button['#crid'];
-  $form_state['redirect'] = 'admin/config/development/css-injector/edit/' . $crid;
-}
-
-/**
- * Delete button callback for the CSS rule listing form.
- * Redirects the user to the confirmation form.
- */
-function css_injector_admin_delete_button($form, &$form_state) {
-  $button = $form_state['triggering_element'];
-  $crid = $button['#crid'];
-  $form_state['redirect'] = 'admin/config/development/css-injector/delete/' . $crid;
-}
-
-/**
- * Theme function for the CSS Injector admin overview form.
- */
-function theme_css_injector_admin_form($variables) {
-  $form = $variables['form'];
-  $headers = array(t('Title'), t('Location'), t('Actions'));
-  $rows = array();
-  if (!empty($form['rules'])) {
-    foreach (element_children($form['rules']) as $crid) {
-      $row = array();
-      $rule = $form['rules'][$crid]['#rule'];
-      $row[] = check_plain($rule['title']);
-      $row[] = _css_injector_rule_uri($rule['crid']);
-      $row[] = drupal_render($form['rules'][$crid]);
-      $rows[] = $row;
-    }
-  }
-
-  $link = l(t('Create a new rule'), 'admin/config/development/css-injector/add');
-  $row = array();
-  if (empty($rows)) {
-    $row[] = array(
-      'data' => t('No CSS injection rules have been set up yet. !url.', array('!url' => $link)),
-      'colspan' => 3,
-    );
-  }
-  else {
-    $row[] = array(
-      'data' => t('!url.', array('!url' => $link)),
-      'colspan' => 3,
-    );
-  }
-  $rows[] = $row;
-
-  $output = theme('table', array('header' => $headers, 'rows' => $rows));
-  $output .= drupal_render_children($form);
-  return $output;
-}
-
-/**
- * Form builder function for the CSS rule edit form.
- */
-function css_injector_edit($form, $form_state, $crid = NULL) {
-  if (isset($crid)) {
-    $rule = _css_injector_load_rule($crid, TRUE);
-
-    $path = _css_injector_rule_uri($rule['crid']);
-    if (file_exists($path)) {
-      $rule['css_text'] = file_get_contents($path);
-    }
-    else {
-      $rule['css_text'] = '';
-    }
-  }
-  else {
-    $rule = array(
-      'title' => '',
-      'rule_type' => CSS_INJECTOR_PAGES_NOTLISTED,
-      'rule_conditions' => '',
-      'media' => 'all',
-      'preprocess' => 1,
-      'css_text' => '',
-    );
-  }
-
-  if (isset($crid)) {
-    $form['crid'] = array(
-      '#type' => 'value',
-      '#value' => $crid,
-    );
-  }
-  $form['title'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Title'),
-    '#default_value' => $rule['title'],
-    '#required' => TRUE,
-  );
-
-  $form['css_text'] = array(
-    '#type' => 'textarea',
-    '#title' => t('CSS code'),
-    '#rows' => 10,
-    '#default_value' => $rule['css_text'],
-    '#required' => TRUE,
-  );
-
-  // Shamelessly ripped from block.module. Who doesn't use this snippet
-  // of code, really?
-  $php_access = (user_access('use PHP for settings') && module_exists('php'));
-  $options = array(
-    CSS_INJECTOR_PAGES_NOTLISTED => t('Add on every page except the listed pages.'),
-    CSS_INJECTOR_PAGES_LISTED => t('Add on only the listed pages.')
-  );
-  $description = t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
-
-  if ($php_access) {
-    $options[CSS_INJECTOR_PHP] = t('Add if the following PHP code outputs a nonzero value (PHP-mode, experts only).');
-    $description .= ' ' . t('If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can break your Drupal site.', array('%php' => '<?php ?>'));
-  }
-  $form['conditional']['rule_type'] = array(
-    '#type' => 'radios',
-    '#title' => t('Add the CSS on specific pages'),
-    '#options' => $options,
-    '#default_value' => $rule['rule_type'],
-  );
-  $form['conditional']['rule_conditions'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Pages'),
-    '#default_value' => $rule['rule_conditions'],
-    '#description' => $description,
-  );
-
-
-  $form['media'] = array(
-    '#type' => 'select',
-    '#title' => t('Media'),
-    '#options' => array('all' => t('All'), 'screen' => t('Screen'), 'print' => t('Print')),
-    '#default_value' => $rule['media'],
-  );
-
-  $form['preprocess'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Preprocess CSS'),
-    '#default_value' => $rule['preprocess'],
-  );
-
-  $form['buttons']['save'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-    '#submit' => array('css_injector_edit_save'),
-  );
-  $form['buttons']['save_and_continue'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save and Continue Editing'),
-    '#submit' => array('css_injector_edit_save_and_continue'),
-  );
-
-  if (!empty($rule['crid'])) {
-    $form['buttons']['delete'] = array(
-      '#type' => 'submit',
-      '#value' => t('Delete'),
-      '#crid' => $rule['crid'],
-      '#submit' => array('css_injector_admin_delete_button'),
-    );
-  }
-
-  return $form;
-}
-
-/**
- * Validation callback for the CSS rule edit form.
- */
-function css_injector_edit_validate($form, &$form_state) {
-  $directory = drupal_realpath('public://css_injector');
-  if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
-    form_error($form, t('The directory %directory is not writable', array('%directory' => $directory)));
-  }
-}
-
-/**
- * Submit button callback for the CSS rule edit form.
- */
-function css_injector_edit_save($form, &$form_state) {
-  $rule = $form_state['values'];
-  $crid = !empty($form_state['values']['crid']) ? $form_state['values']['crid'] : NULL;
-  drupal_write_record('css_injector_rule', $rule, empty($crid) ? array() : 'crid');
-  $form_state['values']['rule'] = $rule;
-  file_unmanaged_save_data($rule['css_text'], _css_injector_rule_uri($rule['crid']), FILE_EXISTS_REPLACE);
-  _css_injector_load_rule(NULL, TRUE);
-
-  drupal_set_message(t('Your CSS injection rule %title was saved.', array('%title' => $rule['title'])));
-  $form_state['redirect'] = 'admin/config/development/css-injector';
-}
-
-/**
- * Save and continue callback for the CSS rule edit form.
- */
-function css_injector_edit_save_and_continue($form, &$form_state) {
-  css_injector_edit_save($form, $form_state);
-  $form_state['redirect'] = 'admin/config/development/css-injector/edit/' . $form_state['values']['rule']['crid'];
-}
-
-
-/**
- * Menu callback -- ask for confirmation of rule deletion.
- */
-function css_injector_delete_confirm($form, &$form_state, $crid) {
-  $form['crid'] = array(
-    '#type' => 'value',
-    '#value' => $crid,
-  );
-
-  $rule = _css_injector_load_rule($crid);
-  return confirm_form($form,
-    t('Are you sure you want to delete %title?', array('%title' => $rule['title'])),
-    isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/development/css-injector',
-    t('This action cannot be undone.'),
-    t('Delete'),
-    t('Cancel')
-  );
-}
-
-/**
- * Execute node deletion.
- */
-function css_injector_delete_confirm_submit($form, &$form_state) {
-  if ($form_state['values']['confirm']) {
-    _css_injector_delete_rule($form_state['values']['crid']);
-  }
-
-  $form_state['redirect'] = 'admin/config/development/css-injector';
-  return;
-}
diff --git a/css_injector.info b/css_injector.info
index a9bab50..8eb66ac 100644
--- a/css_injector.info
+++ b/css_injector.info
@@ -2,9 +2,6 @@ name = CSS Injector
 description = Adds CSS to the page output based on configurable rules.
 core = 7.x
 
-files[] = css_injector.admin.inc
-files[] = css_injector.install
-files[] = css_injector.module
 files[] = css_injector.test
 
-configure = admin/config/development/css-injector
\ No newline at end of file
+dependencies[] = ctools
diff --git a/css_injector.install b/css_injector.install
index f960c07..af11cbb 100644
--- a/css_injector.install
+++ b/css_injector.install
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * @file
  * Install, update and uninstall functions for the css_injector module.
@@ -17,27 +16,59 @@ function css_injector_install() {
  */
 function css_injector_schema() {
   $schema['css_injector_rule'] = array(
+    'description' => t('Table storing CSS injector rule definitions.'),
+    'export' => array(
+      'key' => 'name',
+      'primary key' => 'crid',
+      'identifier' => 'rule', // Exports will be defined as $rule
+      'default hook' => 'css_injector_rule',
+      'save callback' => 'css_injector_rule_save',
+      'delete callback' => 'css_injector_rule_delete',
+      'api' => array(
+        'owner' => 'css_injector',
+        'api' => 'css_injector_rules',  // Base name for api include files.
+        'minimum_version' => 1,
+        'current_version' => 1,
+      ),
+    ),
     'fields' => array(
+      'name' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'description' => 'Unique ID for rules. Used to identify them programmatically.'),
+      'admin_title' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'description' => 'The administrative title.',
+      ),
       'crid' => array(
         'description' => 'The primary identifier for the CSS injection rule',
         'type' => 'serial',
         'unsigned' => TRUE,
-        'not null' => TRUE),
-      'title' => array(
-        'description' => 'The descriptive title of the CSS injection rule',
+        'not null' => TRUE
+      ),
+      'description' => array(
         'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE),
-      'rule_type' => array(
-        'description' => 'The type of rule to use when determining if the CSS should be injected',
+        'length' => '255',
+        'description' => 'A human readable name of a rule.',
+      ),
+      'css_text' => array(
+        'type' => 'blob',
+        'not null' => FALSE,
+        'size' => 'big',
+        'serialize' => TRUE,
+      ),
+      'page_visibility' => array(
+        'description' => 'Boolean indicating whether the rule has a white or black list for page visibility.',
         'type' => 'int',
-        'unsigned' => TRUE,
         'not null' => TRUE,
-        'default' => 0),
-      'rule_conditions' => array(
-        'description' => 'The data to evaluate when determining if the CSS should be injected',
+        'default' => 0,
+      ),
+      'page_visibility_pages' => array(
         'type' => 'text',
-        'not null' => TRUE),
+        'size' => 'big',
+        'description' => 'A list of pages to either hide or show the JavaScript.',
+      ),
       'media' => array(
         'description' => 'The media type of the CSS file (screen, print, etc.)',
         'type' => 'varchar',
@@ -48,9 +79,13 @@ function css_injector_schema() {
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
-        'default' => 0),
+        'default' => 0
+      ),
     ),
     'primary key' => array('crid'),
+    'unique keys' => array(
+      'name' => array('name'),
+    ),
   );
   return $schema;
 }
@@ -59,35 +94,35 @@ function css_injector_schema() {
  * Implements hook_uninstall().
  */
 function css_injector_uninstall() {
-  cache_clear_all('css_injector:*', 'cache', TRUE);
-  $rules = db_query("SELECT * FROM {css_injector_rule}", array(), array('fetch' => PDO::FETCH_ASSOC))->fetchAllAssoc('crid');
-  module_load_include('module', 'css_injector');
-  foreach($rules as $crid => $rule) {
-    file_unmanaged_delete(_css_injector_rule_uri($crid));
+ ctools_include('export');
+  $rules = ctools_export_load_object('css_injector_rule');
+  foreach ($rules as $name => $rule) {
+    file_unmanaged_delete(_css_injector_rule_path($rule->crid));
   }
-  db_drop_table('css_injector_rule');
 }
 
 /**
  * Implements hook_requirements().
+ *
  * We'll use this to prevent installation of the module if the file directory
  * is not available and writable.
  */
-function css_injector_requirements($phase) {
-  $status = REQUIREMENT_OK;
-  $dir = 'public://css_injector';
-  if (!file_prepare_directory($dir, FILE_MODIFY_PERMISSIONS)) {
-    if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
-      $status = REQUIREMENT_ERROR;
+function unionen_inject_requirements($phase) {
+  $requirements = array();
+  if ($phase == 'runtime') {
+    $requirements['css_injector_cache_dir'] = array(
+      'title' => t('CSS injector cache dir'),
+      'severity' => REQUIREMENT_OK,
+      'value' => t('Exists'),
+    );
+
+    $path = 'public://css_injector';
+    if (!file_prepare_directory($path, FILE_CREATE_DIRECTORY)) {
+      $requirements['css_injector_cache_dir']['description'] = t('The CSS injector cache directory, %path could not be created due to a misconfigured files directory. Please ensure that the files directory is correctly configured and that the webserver has permission to create directories.', array('%path' => file_uri_target($path)));
+      $requirements['css_injector_cache_dir']['severity'] = REQUIREMENT_ERROR;
+      $requirements['css_injector_cache_dir']['value'] = t('Unable to create');
     }
   }
-  $requirements = array(
-    'css_injector' => array(
-      'title' => t('CSS Injector directory writable'),
-      'description' => $status == REQUIREMENT_OK ? t('CSS Injector Directory %dir is writable', array('%dir' => $dir)) : t('Directory %dir is not writable', array('%dir' => $dir)),
-      'severity' => $status,
-      'value' => $status == REQUIREMENT_OK ? t('Writable') : t('Not writable'),
-    ),
-  );
+
   return $requirements;
 }
diff --git a/css_injector.module b/css_injector.module
index a9b43ec..b5a2b9e 100644
--- a/css_injector.module
+++ b/css_injector.module
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * @file
  * Allows administrators to inject CSS into the page output based on
@@ -45,12 +44,28 @@ function css_injector_help($path, $arg) {
  * based on rules configured by the site administrator.
  */
 function css_injector_init() {
-  $css_rules = _css_injector_load_rule();
-  foreach ($css_rules as $css_rule) {
-    if (_css_injector_evaluate_rule($css_rule)) {
-      $file_uri = _css_injector_rule_uri($css_rule['crid']);
-      drupal_add_css($file_uri, array('type' => 'file', 'group' => CSS_THEME, 'media' => $css_rule['media'], 'preprocess' => $css_rule['preprocess']));
+  // Load all the rules
+  ctools_include('export');
+  $rules = ctools_export_load_object('css_injector_rule');
+  foreach ($rules as $name => $rule) {
+    // check if the rule is disabled in the ctools UI, if so skip it
+    if (isset($rule->disabled) && $rule->disabled == TRUE) {
+      continue;
+    }
+    // Check the page visibility settings.
+    if (_css_injector_visibility_pages($rule) == FALSE) {
+      continue;
     }
+
+    // Add the css
+    $css = _css_injector_rule_path($rule->crid);
+
+    drupal_add_css($css, array(
+      'type' => 'file',
+      'group' => CSS_THEME,
+      'media' => $rule->media,
+      'preprocess' => $rule->preprocess,
+    ));
   }
 }
 
@@ -61,171 +76,186 @@ function css_injector_init() {
  * CSS files. Currently the weight is 200 + the crid, which is currently higher
  * than Bartik's CSS.
  *
- * @param $css
- *   The array of CSS files.
  */
-function css_injector_css_alter(&$css) {
-  $css_rules = _css_injector_load_rule();
-  foreach ($css_rules as $css_rule) {
-    $file_uri = _css_injector_rule_uri($css_rule['crid']);
+function css_injector_css_alter() {
+  ctools_include('export');
+  $rules = ctools_export_load_object('css_injector_rule');
+  foreach ($rules as $name => $rule) {
+    $file_uri = _css_injector_rule_uri($rule->crid);
     if (!empty($css[$file_uri])) {
-      $css[$file_uri]['weight'] = 200 + $css_rule['crid'];
+      $css[$file_uri]['weight'] = 200 + $rule->crid;
     }
   }
 }
 
 /**
- * Implements hook_menu().
- * Defines menu callbacks for CSS Injector's configuration pages.
+ * Implements hook_permission().
  */
-function css_injector_menu() {
-  $items = array(
-    'admin/config/development/css-injector' => array(
-      'title' => 'CSS injector',
-      'description' => 'Add CSS to the page output based on configurable rules.',
-      'page callback' => 'drupal_get_form',
-      'page arguments' => array('css_injector_admin_form'),
-      'access arguments' => array('administer css injection'),
-      'file' => 'css_injector.admin.inc',
-    ),
-    'admin/config/development/css-injector/edit' => array(
-      'title' => 'Edit CSS injector rule',
-      'page callback' => 'drupal_get_form',
-      'page arguments' => array('css_injector_edit'),
-      'access arguments' => array('administer css injection'),
-      'file' => 'css_injector.admin.inc',
-      'type' => MENU_CALLBACK,
-    ),
-    'admin/config/development/css-injector/add' => array(
-      'title' => 'Add CSS injector rule',
-      'page callback' => 'drupal_get_form',
-      'page arguments' => array('css_injector_edit'),
-      'access arguments' => array('administer css injection'),
-      'file' => 'css_injector.admin.inc',
-      'type' => MENU_CALLBACK,
-    ),
-    'admin/config/development/css-injector/delete' => array(
-      'title' => 'Delete CSS injector rule',
-      'page callback' => 'drupal_get_form',
-      'page arguments' => array('css_injector_delete_confirm'),
-      'access arguments' => array('administer css injection'),
-      'file' => 'css_injector.admin.inc',
-      'type' => MENU_CALLBACK,
+function css_injector_permission() {
+  return array(
+    'administer css injection' => array(
+      'title' => t('Administer CSS Injection'),
     ),
   );
-  return $items;
 }
 
 /**
- * Implements hook_theme().
+ * Implementation of hook_ctools_plugin_directory().
+ *
+ * In order for CTools to be able to provide an interface for administering the
+ * configuration presets, we have to expose the preset schema to the Export UI.
  */
-function css_injector_theme() {
-  $items['css_injector_admin_form'] = array(
-    'render element' => 'form',
-    'file' => 'css_injector.admin.inc',
-  );
-  return $items;
+function css_injector_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'ctools' && $plugin == 'export_ui') {
+    return 'plugins/' . $plugin;
+  }
 }
 
 /**
- * Implements hook_permission().
+ * Implements 'load callback' for css_injector_rule exportables.
  */
-function css_injector_permission() {
-  return array(
-    'administer css injection' => array(
-      'title' => t('Administer CSS Injection'),
-    ),
-  );
+function css_injector_rule_load($name) {
+  ctools_include('export');
+  $result = ctools_export_load_object('css_injector_rule', 'names', array($name));
+  if (isset($result[$name])) {
+    return $result[$name];
+  }
 }
 
 /**
- * Helper function to load all CSS injection rules.
+ * Implements 'load multiple callback' for css_injector_rule exportables.
  */
-function _css_injector_load_rule($crid = NULL, $reset = FALSE) {
-  static $rules;
-  // TODO: Change to drupal_static_fast pattern.
-  if (!isset($rules) || $reset) {
-    if (!$reset && ($cache = cache_get('css_injector:rules')) && !empty($cache->data)) {
-      $rules = $cache->data;
-    }
-    else {
-      $rules = array();
-      $results = db_query("SELECT * FROM {css_injector_rule}", array(), array('fetch' => PDO::FETCH_ASSOC))->fetchAllAssoc('crid');
-      foreach ($results as $id => $rule) {
-        $rules[$id] = $rule;
-      }
-      cache_set('css_injector:rules', $rules);
-    }
+function css_injector_rule_load_multiple(array $names) {
+  ctools_include('export');
+  $results = ctools_export_load_object('unionen_inject_rule', 'names', $names);
+  return array_filter($results);
+}
+
+/**
+ * Save a single Unionen inject rule. This custom save callback is required in
+ * order to write (or update) the JavaScript file on the filesystem
+ */
+function css_injector_rule_save($rule) {
+  $schema = ctools_export_get_schema('css_injector_rule');
+  $export = $schema['export'];
+
+  // Objects should have a serial primary key. If not, simply fail to write.
+  if (empty($export['primary key'])) {
+    return FALSE;
   }
 
-  if (is_numeric($crid)) {
-    return $rules[$crid];
+  $key = $export['primary key'];
+  if ($rule->export_type & EXPORT_IN_DATABASE) {
+    // Existing record.
+    $update = array($key);
   }
   else {
-    return $rules;
+    // New record.
+    $update = array();
+    $rule->export_type = EXPORT_IN_DATABASE;
+  }
+
+  // Rule is passed by reference, and the crid is written into it upon save.
+  $success = drupal_write_record('css_injector_rule', $rule, $update);
+  if ($success == FALSE || empty($rule->crid)) {
+    return FALSE;
+  }
+
+  // Write the CSS file to the filesystem.
+  $file_written = file_unmanaged_save_data(
+    $rule->css_text, _css_injector_rule_uri($rule->crid),
+    FILE_EXISTS_REPLACE
+  );
+  return ($file_written != FALSE) && is_numeric($rule->crid);
+}
+
+/**
+ * Delete a single CSS injector rule. Override is required to clean up the
+ * filesystem
+ */
+function css_injector_rule_delete($rule) {
+  $schema = ctools_export_get_schema('css_injector_rule');
+  $export = $schema['export'];
+
+  // If we were sent an object, get the export key from it. Otherwise
+  // assume we were sent the export key.
+  $value = is_object($rule) ? $rule->{$export['key']} : $rule;
+  db_delete('css_injector_rule')
+    ->condition($export['key'], $value)
+    ->execute();
+
+  // Delete the CSS file from the filesystem.
+  return file_unmanaged_delete(_css_injector_rule_uri($rule->crid));
+}
+
+/**
+ * Return the URI for a crid.
+ * @param $crid
+ *   The integer identifying the CSS Rule ID (crid)
+ */
+function _css_injector_rule_uri($crid) {
+  if (!empty($crid)) {
+    $uri = 'public://css_injector/css_injector_' . $crid . '.css';
+    return $uri;
   }
 }
 
 /**
- * Helper function to delete an existing rule and its accompanying file.
+ * Helper function to get file path for a rule.
+ * This will get the path relative to DRUPAL_ROOT,
+ * as in 'sites/default/files/css_injector/css_injector_99.css'.
+ *
+ * @param $crid
+ *   The Unionen inject rule unique ID
  */
-function _css_injector_delete_rule($crid) {
-  if ($rule = _css_injector_load_rule($crid)) {
-    file_unmanaged_delete(_css_injector_rule_uri($crid));
-    db_delete('css_injector_rule')
-      ->condition('crid', $crid)
-      ->execute();
-    drupal_set_message(t('The CSS rule %title has been deleted.', array('%title' => $rule['title'])));
+function _css_injector_rule_path($crid) {
+  if (!empty($crid)) {
+    $local_path = file_create_url(_css_injector_rule_uri($crid));
+    //Now remove the part before the drupal root.
+    $local_path = preg_replace('/^' . preg_quote($GLOBALS['base_url'], '/') . '\//', '', $local_path);
+    return $local_path;
   }
+  return NULL;
 }
 
 /**
- * Helper function to determine whether a rule's conditions are met.
+ * Based on visibility setting this function returns TRUE if the CSS injector
+ * rule code should be added to the current page and otherwise FALSE.
  *
- * @param $css_rule
- *   Array describing the rule.
+ * Code ported from googleanalytics.module
  */
+function _css_injector_visibility_pages($rule) {
+  $visibility = $rule->page_visibility;
+  $setting_pages = $rule->page_visibility_pages;
 
-function _css_injector_evaluate_rule($css_rule = array()) {
   // Match path if necessary.
-  if (!empty($css_rule['rule_conditions'])) {
-    if ($css_rule['rule_type'] < CSS_INJECTOR_PHP) {
-      $path = drupal_get_path_alias($_GET['q']);
-      // Compare with the internal and path alias (if any).
-      $page_match = drupal_match_path($path, $css_rule['rule_conditions']);
+  if (!empty($setting_pages)) {
+    // Convert path to lowercase. This allows comparison of the same path
+    // with different case. Ex: /Page, /page, /PAGE.
+    $pages = drupal_strtolower($setting_pages);
+    if ($visibility < 2) {
+      // Convert the Drupal path to lowercase
+      $path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
+      // Compare the lowercase internal and lowercase path alias (if any).
+      $page_match = drupal_match_path($path, $pages);
       if ($path != $_GET['q']) {
-        $page_match = $page_match || drupal_match_path($_GET['q'], $css_rule['rule_conditions']);
+        $page_match = $page_match || drupal_match_path($_GET['q'], $pages);
       }
-      // When $css_rule['rule_type'] has a value of
-      // CSS_INJECTOR_PAGES_NOTLISTED, the rule is matched on
-      // all pages except those listed in $css_rule['rule_conditions'].
-      // When set to CSS_INJECTOR_PAGES_LISTED, it is displayed only on those
-      // pages listed in $css_rule['rule_type'].
-      $page_match = !($css_rule['rule_type'] xor $page_match);
+      // When $visibility has a value of 0, the tracking code is displayed on
+      // all pages except those listed in $pages. When set to 1, it
+      // is displayed only on those pages listed in $pages.
+      $page_match = !($visibility xor $page_match);
+    }
+    elseif (module_exists('php')) {
+      $page_match = php_eval($setting_pages);
     }
     else {
-      if (module_exists('php')) {
-        $page_match = php_eval($css_rule['rule_conditions']);
-      }
-      else {
-        $page_match = FALSE;
-      }
+      $page_match = FALSE;
     }
   }
   else {
     $page_match = TRUE;
   }
+
   return $page_match;
 }
-
-/**
- * Return the URI for a crid.
- * @param $crid
- *   The integer identifying the CSS Rule ID (crid)
- */
-function _css_injector_rule_uri($crid) {
-  if (!empty($crid)) {
-    $uri = 'public://css_injector/css_injector_' . $crid . '.css';
-    return $uri;
-  }
-}
\ No newline at end of file
diff --git a/exportable-886188-14.patch b/exportable-886188-14.patch
new file mode 100644
index 0000000..a388fc2
--- /dev/null
+++ b/exportable-886188-14.patch
@@ -0,0 +1,923 @@
+diff --git a/css_injector.admin.inc b/css_injector.admin.inc
+deleted file mode 100644
+index 96666cd..0000000
+--- a/css_injector.admin.inc
++++ /dev/null
+@@ -1,270 +0,0 @@
+-<?php
+-/**
+- * @file css_injector.admin.inc
+- * Administrative interface for CSS Injector.
+- */
+-
+-/**
+- * Form builder function for CSS Injector's main admin page.
+- */
+-function css_injector_admin_form($form, &$form_state) {
+-  $rules = _css_injector_load_rule(NULL, TRUE);
+-  $path = drupal_get_path('module', 'css_injector') . '/';
+-  $form = array();
+-  $form['#tree'] = TRUE;
+-
+-  // TODO: Turn these 'submit' buttons back into image_buttons when
+-  // http://drupal.org/node/873070 lands. Currently you can't use
+-  // #submit with an image_button.
+-  foreach ($rules as $rule) {
+-    $form['rules'][$rule['crid']]['#rule'] = $rule;
+-    $form['rules'][$rule['crid']]['edit'] = array(
+-      '#type' => 'submit',
+-      '#value' => t('Edit rule'),
+-      '#name' => 'edit' . $rule['crid'],
+-      '#submit' => array('css_injector_admin_edit_button'),
+-      '#crid' => $rule['crid'],
+-    );
+-    $form['rules'][$rule['crid']]['delete'] = array(
+-      '#type' => 'submit',
+-      '#value' => t('Delete rule'),
+-      '#name' => 'delete' . $rule['crid'],
+-      '#submit' => array('css_injector_admin_delete_button'),
+-      '#crid' => $rule['crid'],
+-    );
+-  }
+-  return $form;
+-}
+-
+-/**
+- * Edit button callback for the CSS rule listing form.
+- */
+-function css_injector_admin_edit_button($form, &$form_state) {
+-  $button = $form_state['triggering_element'];
+-  $crid = $button['#crid'];
+-  $form_state['redirect'] = 'admin/config/development/css-injector/edit/' . $crid;
+-}
+-
+-/**
+- * Delete button callback for the CSS rule listing form.
+- * Redirects the user to the confirmation form.
+- */
+-function css_injector_admin_delete_button($form, &$form_state) {
+-  $button = $form_state['triggering_element'];
+-  $crid = $button['#crid'];
+-  $form_state['redirect'] = 'admin/config/development/css-injector/delete/' . $crid;
+-}
+-
+-/**
+- * Theme function for the CSS Injector admin overview form.
+- */
+-function theme_css_injector_admin_form($variables) {
+-  $form = $variables['form'];
+-  $headers = array(t('Title'), t('Location'), t('Actions'));
+-  $rows = array();
+-  if (!empty($form['rules'])) {
+-    foreach (element_children($form['rules']) as $crid) {
+-      $row = array();
+-      $rule = $form['rules'][$crid]['#rule'];
+-      $row[] = check_plain($rule['title']);
+-      $row[] = _css_injector_rule_uri($rule['crid']);
+-      $row[] = drupal_render($form['rules'][$crid]);
+-      $rows[] = $row;
+-    }
+-  }
+-
+-  $link = l(t('Create a new rule'), 'admin/config/development/css-injector/add');
+-  $row = array();
+-  if (empty($rows)) {
+-    $row[] = array(
+-      'data' => t('No CSS injection rules have been set up yet. !url.', array('!url' => $link)),
+-      'colspan' => 3,
+-    );
+-  }
+-  else {
+-    $row[] = array(
+-      'data' => t('!url.', array('!url' => $link)),
+-      'colspan' => 3,
+-    );
+-  }
+-  $rows[] = $row;
+-
+-  $output = theme('table', array('header' => $headers, 'rows' => $rows));
+-  $output .= drupal_render_children($form);
+-  return $output;
+-}
+-
+-/**
+- * Form builder function for the CSS rule edit form.
+- */
+-function css_injector_edit($form, $form_state, $crid = NULL) {
+-  if (isset($crid)) {
+-    $rule = _css_injector_load_rule($crid, TRUE);
+-
+-    $path = _css_injector_rule_uri($rule['crid']);
+-    if (file_exists($path)) {
+-      $rule['css_text'] = file_get_contents($path);
+-    }
+-    else {
+-      $rule['css_text'] = '';
+-    }
+-  }
+-  else {
+-    $rule = array(
+-      'title' => '',
+-      'rule_type' => CSS_INJECTOR_PAGES_NOTLISTED,
+-      'rule_conditions' => '',
+-      'media' => 'all',
+-      'preprocess' => 1,
+-      'css_text' => '',
+-    );
+-  }
+-
+-  if (isset($crid)) {
+-    $form['crid'] = array(
+-      '#type' => 'value',
+-      '#value' => $crid,
+-    );
+-  }
+-  $form['title'] = array(
+-    '#type' => 'textfield',
+-    '#title' => t('Title'),
+-    '#default_value' => $rule['title'],
+-    '#required' => TRUE,
+-  );
+-
+-  $form['css_text'] = array(
+-    '#type' => 'textarea',
+-    '#title' => t('CSS code'),
+-    '#rows' => 10,
+-    '#default_value' => $rule['css_text'],
+-    '#required' => TRUE,
+-  );
+-
+-  // Shamelessly ripped from block.module. Who doesn't use this snippet
+-  // of code, really?
+-  $php_access = (user_access('use PHP for settings') && module_exists('php'));
+-  $options = array(
+-    CSS_INJECTOR_PAGES_NOTLISTED => t('Add on every page except the listed pages.'),
+-    CSS_INJECTOR_PAGES_LISTED => t('Add on only the listed pages.')
+-  );
+-  $description = t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
+-
+-  if ($php_access) {
+-    $options[CSS_INJECTOR_PHP] = t('Add if the following PHP code outputs a nonzero value (PHP-mode, experts only).');
+-    $description .= ' ' . t('If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can break your Drupal site.', array('%php' => '<?php ?>'));
+-  }
+-  $form['conditional']['rule_type'] = array(
+-    '#type' => 'radios',
+-    '#title' => t('Add the CSS on specific pages'),
+-    '#options' => $options,
+-    '#default_value' => $rule['rule_type'],
+-  );
+-  $form['conditional']['rule_conditions'] = array(
+-    '#type' => 'textarea',
+-    '#title' => t('Pages'),
+-    '#default_value' => $rule['rule_conditions'],
+-    '#description' => $description,
+-  );
+-
+-
+-  $form['media'] = array(
+-    '#type' => 'select',
+-    '#title' => t('Media'),
+-    '#options' => array('all' => t('All'), 'screen' => t('Screen'), 'print' => t('Print')),
+-    '#default_value' => $rule['media'],
+-  );
+-
+-  $form['preprocess'] = array(
+-    '#type' => 'checkbox',
+-    '#title' => t('Preprocess CSS'),
+-    '#default_value' => $rule['preprocess'],
+-  );
+-
+-  $form['buttons']['save'] = array(
+-    '#type' => 'submit',
+-    '#value' => t('Save'),
+-    '#submit' => array('css_injector_edit_save'),
+-  );
+-  $form['buttons']['save_and_continue'] = array(
+-    '#type' => 'submit',
+-    '#value' => t('Save and Continue Editing'),
+-    '#submit' => array('css_injector_edit_save_and_continue'),
+-  );
+-
+-  if (!empty($rule['crid'])) {
+-    $form['buttons']['delete'] = array(
+-      '#type' => 'submit',
+-      '#value' => t('Delete'),
+-      '#crid' => $rule['crid'],
+-      '#submit' => array('css_injector_admin_delete_button'),
+-    );
+-  }
+-
+-  return $form;
+-}
+-
+-/**
+- * Validation callback for the CSS rule edit form.
+- */
+-function css_injector_edit_validate($form, &$form_state) {
+-  $directory = drupal_realpath('public://css_injector');
+-  if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
+-    form_error($form, t('The directory %directory is not writable', array('%directory' => $directory)));
+-  }
+-}
+-
+-/**
+- * Submit button callback for the CSS rule edit form.
+- */
+-function css_injector_edit_save($form, &$form_state) {
+-  $rule = $form_state['values'];
+-  $crid = !empty($form_state['values']['crid']) ? $form_state['values']['crid'] : NULL;
+-  drupal_write_record('css_injector_rule', $rule, empty($crid) ? array() : 'crid');
+-  $form_state['values']['rule'] = $rule;
+-  file_unmanaged_save_data($rule['css_text'], _css_injector_rule_uri($rule['crid']), FILE_EXISTS_REPLACE);
+-  _css_injector_load_rule(NULL, TRUE);
+-
+-  drupal_set_message(t('Your CSS injection rule %title was saved.', array('%title' => $rule['title'])));
+-  $form_state['redirect'] = 'admin/config/development/css-injector';
+-}
+-
+-/**
+- * Save and continue callback for the CSS rule edit form.
+- */
+-function css_injector_edit_save_and_continue($form, &$form_state) {
+-  css_injector_edit_save($form, $form_state);
+-  $form_state['redirect'] = 'admin/config/development/css-injector/edit/' . $form_state['values']['rule']['crid'];
+-}
+-
+-
+-/**
+- * Menu callback -- ask for confirmation of rule deletion.
+- */
+-function css_injector_delete_confirm($form, &$form_state, $crid) {
+-  $form['crid'] = array(
+-    '#type' => 'value',
+-    '#value' => $crid,
+-  );
+-
+-  $rule = _css_injector_load_rule($crid);
+-  return confirm_form($form,
+-    t('Are you sure you want to delete %title?', array('%title' => $rule['title'])),
+-    isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/development/css-injector',
+-    t('This action cannot be undone.'),
+-    t('Delete'),
+-    t('Cancel')
+-  );
+-}
+-
+-/**
+- * Execute node deletion.
+- */
+-function css_injector_delete_confirm_submit($form, &$form_state) {
+-  if ($form_state['values']['confirm']) {
+-    _css_injector_delete_rule($form_state['values']['crid']);
+-  }
+-
+-  $form_state['redirect'] = 'admin/config/development/css-injector';
+-  return;
+-}
+diff --git a/css_injector.info b/css_injector.info
+index a9bab50..8eb66ac 100644
+--- a/css_injector.info
++++ b/css_injector.info
+@@ -2,9 +2,6 @@ name = CSS Injector
+ description = Adds CSS to the page output based on configurable rules.
+ core = 7.x
+ 
+-files[] = css_injector.admin.inc
+-files[] = css_injector.install
+-files[] = css_injector.module
+ files[] = css_injector.test
+ 
+-configure = admin/config/development/css-injector
+\ No newline at end of file
++dependencies[] = ctools
+diff --git a/css_injector.install b/css_injector.install
+index f960c07..af11cbb 100644
+--- a/css_injector.install
++++ b/css_injector.install
+@@ -1,5 +1,4 @@
+ <?php
+-
+ /**
+  * @file
+  * Install, update and uninstall functions for the css_injector module.
+@@ -17,27 +16,59 @@ function css_injector_install() {
+  */
+ function css_injector_schema() {
+   $schema['css_injector_rule'] = array(
++    'description' => t('Table storing CSS injector rule definitions.'),
++    'export' => array(
++      'key' => 'name',
++      'primary key' => 'crid',
++      'identifier' => 'rule', // Exports will be defined as $rule
++      'default hook' => 'css_injector_rule',
++      'save callback' => 'css_injector_rule_save',
++      'delete callback' => 'css_injector_rule_delete',
++      'api' => array(
++        'owner' => 'css_injector',
++        'api' => 'css_injector_rules',  // Base name for api include files.
++        'minimum_version' => 1,
++        'current_version' => 1,
++      ),
++    ),
+     'fields' => array(
++      'name' => array(
++        'type' => 'varchar',
++        'length' => '255',
++        'description' => 'Unique ID for rules. Used to identify them programmatically.'),
++      'admin_title' => array(
++        'type' => 'varchar',
++        'length' => '255',
++        'description' => 'The administrative title.',
++      ),
+       'crid' => array(
+         'description' => 'The primary identifier for the CSS injection rule',
+         'type' => 'serial',
+         'unsigned' => TRUE,
+-        'not null' => TRUE),
+-      'title' => array(
+-        'description' => 'The descriptive title of the CSS injection rule',
++        'not null' => TRUE
++      ),
++      'description' => array(
+         'type' => 'varchar',
+-        'length' => 255,
+-        'not null' => TRUE),
+-      'rule_type' => array(
+-        'description' => 'The type of rule to use when determining if the CSS should be injected',
++        'length' => '255',
++        'description' => 'A human readable name of a rule.',
++      ),
++      'css_text' => array(
++        'type' => 'blob',
++        'not null' => FALSE,
++        'size' => 'big',
++        'serialize' => TRUE,
++      ),
++      'page_visibility' => array(
++        'description' => 'Boolean indicating whether the rule has a white or black list for page visibility.',
+         'type' => 'int',
+-        'unsigned' => TRUE,
+         'not null' => TRUE,
+-        'default' => 0),
+-      'rule_conditions' => array(
+-        'description' => 'The data to evaluate when determining if the CSS should be injected',
++        'default' => 0,
++      ),
++      'page_visibility_pages' => array(
+         'type' => 'text',
+-        'not null' => TRUE),
++        'size' => 'big',
++        'description' => 'A list of pages to either hide or show the JavaScript.',
++      ),
+       'media' => array(
+         'description' => 'The media type of the CSS file (screen, print, etc.)',
+         'type' => 'varchar',
+@@ -48,9 +79,13 @@ function css_injector_schema() {
+         'type' => 'int',
+         'unsigned' => TRUE,
+         'not null' => TRUE,
+-        'default' => 0),
++        'default' => 0
++      ),
+     ),
+     'primary key' => array('crid'),
++    'unique keys' => array(
++      'name' => array('name'),
++    ),
+   );
+   return $schema;
+ }
+@@ -59,35 +94,35 @@ function css_injector_schema() {
+  * Implements hook_uninstall().
+  */
+ function css_injector_uninstall() {
+-  cache_clear_all('css_injector:*', 'cache', TRUE);
+-  $rules = db_query("SELECT * FROM {css_injector_rule}", array(), array('fetch' => PDO::FETCH_ASSOC))->fetchAllAssoc('crid');
+-  module_load_include('module', 'css_injector');
+-  foreach($rules as $crid => $rule) {
+-    file_unmanaged_delete(_css_injector_rule_uri($crid));
++ ctools_include('export');
++  $rules = ctools_export_load_object('css_injector_rule');
++  foreach ($rules as $name => $rule) {
++    file_unmanaged_delete(_css_injector_rule_path($rule->crid));
+   }
+-  db_drop_table('css_injector_rule');
+ }
+ 
+ /**
+  * Implements hook_requirements().
++ *
+  * We'll use this to prevent installation of the module if the file directory
+  * is not available and writable.
+  */
+-function css_injector_requirements($phase) {
+-  $status = REQUIREMENT_OK;
+-  $dir = 'public://css_injector';
+-  if (!file_prepare_directory($dir, FILE_MODIFY_PERMISSIONS)) {
+-    if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
+-      $status = REQUIREMENT_ERROR;
++function unionen_inject_requirements($phase) {
++  $requirements = array();
++  if ($phase == 'runtime') {
++    $requirements['css_injector_cache_dir'] = array(
++      'title' => t('CSS injector cache dir'),
++      'severity' => REQUIREMENT_OK,
++      'value' => t('Exists'),
++    );
++
++    $path = 'public://css_injector';
++    if (!file_prepare_directory($path, FILE_CREATE_DIRECTORY)) {
++      $requirements['css_injector_cache_dir']['description'] = t('The CSS injector cache directory, %path could not be created due to a misconfigured files directory. Please ensure that the files directory is correctly configured and that the webserver has permission to create directories.', array('%path' => file_uri_target($path)));
++      $requirements['css_injector_cache_dir']['severity'] = REQUIREMENT_ERROR;
++      $requirements['css_injector_cache_dir']['value'] = t('Unable to create');
+     }
+   }
+-  $requirements = array(
+-    'css_injector' => array(
+-      'title' => t('CSS Injector directory writable'),
+-      'description' => $status == REQUIREMENT_OK ? t('CSS Injector Directory %dir is writable', array('%dir' => $dir)) : t('Directory %dir is not writable', array('%dir' => $dir)),
+-      'severity' => $status,
+-      'value' => $status == REQUIREMENT_OK ? t('Writable') : t('Not writable'),
+-    ),
+-  );
++
+   return $requirements;
+ }
+diff --git a/css_injector.module b/css_injector.module
+index a9b43ec..b5a2b9e 100644
+--- a/css_injector.module
++++ b/css_injector.module
+@@ -1,5 +1,4 @@
+ <?php
+-
+ /**
+  * @file
+  * Allows administrators to inject CSS into the page output based on
+@@ -45,12 +44,28 @@ function css_injector_help($path, $arg) {
+  * based on rules configured by the site administrator.
+  */
+ function css_injector_init() {
+-  $css_rules = _css_injector_load_rule();
+-  foreach ($css_rules as $css_rule) {
+-    if (_css_injector_evaluate_rule($css_rule)) {
+-      $file_uri = _css_injector_rule_uri($css_rule['crid']);
+-      drupal_add_css($file_uri, array('type' => 'file', 'group' => CSS_THEME, 'media' => $css_rule['media'], 'preprocess' => $css_rule['preprocess']));
++  // Load all the rules
++  ctools_include('export');
++  $rules = ctools_export_load_object('css_injector_rule');
++  foreach ($rules as $name => $rule) {
++    // check if the rule is disabled in the ctools UI, if so skip it
++    if (isset($rule->disabled) && $rule->disabled == TRUE) {
++      continue;
++    }
++    // Check the page visibility settings.
++    if (_css_injector_visibility_pages($rule) == FALSE) {
++      continue;
+     }
++
++    // Add the css
++    $css = _css_injector_rule_path($rule->crid);
++
++    drupal_add_css($css, array(
++      'type' => 'file',
++      'group' => CSS_THEME,
++      'media' => $rule->media,
++      'preprocess' => $rule->preprocess,
++    ));
+   }
+ }
+ 
+@@ -61,171 +76,186 @@ function css_injector_init() {
+  * CSS files. Currently the weight is 200 + the crid, which is currently higher
+  * than Bartik's CSS.
+  *
+- * @param $css
+- *   The array of CSS files.
+  */
+-function css_injector_css_alter(&$css) {
+-  $css_rules = _css_injector_load_rule();
+-  foreach ($css_rules as $css_rule) {
+-    $file_uri = _css_injector_rule_uri($css_rule['crid']);
++function css_injector_css_alter() {
++  ctools_include('export');
++  $rules = ctools_export_load_object('css_injector_rule');
++  foreach ($rules as $name => $rule) {
++    $file_uri = _css_injector_rule_uri($rule->crid);
+     if (!empty($css[$file_uri])) {
+-      $css[$file_uri]['weight'] = 200 + $css_rule['crid'];
++      $css[$file_uri]['weight'] = 200 + $rule->crid;
+     }
+   }
+ }
+ 
+ /**
+- * Implements hook_menu().
+- * Defines menu callbacks for CSS Injector's configuration pages.
++ * Implements hook_permission().
+  */
+-function css_injector_menu() {
+-  $items = array(
+-    'admin/config/development/css-injector' => array(
+-      'title' => 'CSS injector',
+-      'description' => 'Add CSS to the page output based on configurable rules.',
+-      'page callback' => 'drupal_get_form',
+-      'page arguments' => array('css_injector_admin_form'),
+-      'access arguments' => array('administer css injection'),
+-      'file' => 'css_injector.admin.inc',
+-    ),
+-    'admin/config/development/css-injector/edit' => array(
+-      'title' => 'Edit CSS injector rule',
+-      'page callback' => 'drupal_get_form',
+-      'page arguments' => array('css_injector_edit'),
+-      'access arguments' => array('administer css injection'),
+-      'file' => 'css_injector.admin.inc',
+-      'type' => MENU_CALLBACK,
+-    ),
+-    'admin/config/development/css-injector/add' => array(
+-      'title' => 'Add CSS injector rule',
+-      'page callback' => 'drupal_get_form',
+-      'page arguments' => array('css_injector_edit'),
+-      'access arguments' => array('administer css injection'),
+-      'file' => 'css_injector.admin.inc',
+-      'type' => MENU_CALLBACK,
+-    ),
+-    'admin/config/development/css-injector/delete' => array(
+-      'title' => 'Delete CSS injector rule',
+-      'page callback' => 'drupal_get_form',
+-      'page arguments' => array('css_injector_delete_confirm'),
+-      'access arguments' => array('administer css injection'),
+-      'file' => 'css_injector.admin.inc',
+-      'type' => MENU_CALLBACK,
++function css_injector_permission() {
++  return array(
++    'administer css injection' => array(
++      'title' => t('Administer CSS Injection'),
+     ),
+   );
+-  return $items;
+ }
+ 
+ /**
+- * Implements hook_theme().
++ * Implementation of hook_ctools_plugin_directory().
++ *
++ * In order for CTools to be able to provide an interface for administering the
++ * configuration presets, we have to expose the preset schema to the Export UI.
+  */
+-function css_injector_theme() {
+-  $items['css_injector_admin_form'] = array(
+-    'render element' => 'form',
+-    'file' => 'css_injector.admin.inc',
+-  );
+-  return $items;
++function css_injector_ctools_plugin_directory($module, $plugin) {
++  if ($module == 'ctools' && $plugin == 'export_ui') {
++    return 'plugins/' . $plugin;
++  }
+ }
+ 
+ /**
+- * Implements hook_permission().
++ * Implements 'load callback' for css_injector_rule exportables.
+  */
+-function css_injector_permission() {
+-  return array(
+-    'administer css injection' => array(
+-      'title' => t('Administer CSS Injection'),
+-    ),
+-  );
++function css_injector_rule_load($name) {
++  ctools_include('export');
++  $result = ctools_export_load_object('css_injector_rule', 'names', array($name));
++  if (isset($result[$name])) {
++    return $result[$name];
++  }
+ }
+ 
+ /**
+- * Helper function to load all CSS injection rules.
++ * Implements 'load multiple callback' for css_injector_rule exportables.
+  */
+-function _css_injector_load_rule($crid = NULL, $reset = FALSE) {
+-  static $rules;
+-  // TODO: Change to drupal_static_fast pattern.
+-  if (!isset($rules) || $reset) {
+-    if (!$reset && ($cache = cache_get('css_injector:rules')) && !empty($cache->data)) {
+-      $rules = $cache->data;
+-    }
+-    else {
+-      $rules = array();
+-      $results = db_query("SELECT * FROM {css_injector_rule}", array(), array('fetch' => PDO::FETCH_ASSOC))->fetchAllAssoc('crid');
+-      foreach ($results as $id => $rule) {
+-        $rules[$id] = $rule;
+-      }
+-      cache_set('css_injector:rules', $rules);
+-    }
++function css_injector_rule_load_multiple(array $names) {
++  ctools_include('export');
++  $results = ctools_export_load_object('unionen_inject_rule', 'names', $names);
++  return array_filter($results);
++}
++
++/**
++ * Save a single Unionen inject rule. This custom save callback is required in
++ * order to write (or update) the JavaScript file on the filesystem
++ */
++function css_injector_rule_save($rule) {
++  $schema = ctools_export_get_schema('css_injector_rule');
++  $export = $schema['export'];
++
++  // Objects should have a serial primary key. If not, simply fail to write.
++  if (empty($export['primary key'])) {
++    return FALSE;
+   }
+ 
+-  if (is_numeric($crid)) {
+-    return $rules[$crid];
++  $key = $export['primary key'];
++  if ($rule->export_type & EXPORT_IN_DATABASE) {
++    // Existing record.
++    $update = array($key);
+   }
+   else {
+-    return $rules;
++    // New record.
++    $update = array();
++    $rule->export_type = EXPORT_IN_DATABASE;
++  }
++
++  // Rule is passed by reference, and the crid is written into it upon save.
++  $success = drupal_write_record('css_injector_rule', $rule, $update);
++  if ($success == FALSE || empty($rule->crid)) {
++    return FALSE;
++  }
++
++  // Write the CSS file to the filesystem.
++  $file_written = file_unmanaged_save_data(
++    $rule->css_text, _css_injector_rule_uri($rule->crid),
++    FILE_EXISTS_REPLACE
++  );
++  return ($file_written != FALSE) && is_numeric($rule->crid);
++}
++
++/**
++ * Delete a single CSS injector rule. Override is required to clean up the
++ * filesystem
++ */
++function css_injector_rule_delete($rule) {
++  $schema = ctools_export_get_schema('css_injector_rule');
++  $export = $schema['export'];
++
++  // If we were sent an object, get the export key from it. Otherwise
++  // assume we were sent the export key.
++  $value = is_object($rule) ? $rule->{$export['key']} : $rule;
++  db_delete('css_injector_rule')
++    ->condition($export['key'], $value)
++    ->execute();
++
++  // Delete the CSS file from the filesystem.
++  return file_unmanaged_delete(_css_injector_rule_uri($rule->crid));
++}
++
++/**
++ * Return the URI for a crid.
++ * @param $crid
++ *   The integer identifying the CSS Rule ID (crid)
++ */
++function _css_injector_rule_uri($crid) {
++  if (!empty($crid)) {
++    $uri = 'public://css_injector/css_injector_' . $crid . '.css';
++    return $uri;
+   }
+ }
+ 
+ /**
+- * Helper function to delete an existing rule and its accompanying file.
++ * Helper function to get file path for a rule.
++ * This will get the path relative to DRUPAL_ROOT,
++ * as in 'sites/default/files/css_injector/css_injector_99.css'.
++ *
++ * @param $crid
++ *   The Unionen inject rule unique ID
+  */
+-function _css_injector_delete_rule($crid) {
+-  if ($rule = _css_injector_load_rule($crid)) {
+-    file_unmanaged_delete(_css_injector_rule_uri($crid));
+-    db_delete('css_injector_rule')
+-      ->condition('crid', $crid)
+-      ->execute();
+-    drupal_set_message(t('The CSS rule %title has been deleted.', array('%title' => $rule['title'])));
++function _css_injector_rule_path($crid) {
++  if (!empty($crid)) {
++    $local_path = file_create_url(_css_injector_rule_uri($crid));
++    //Now remove the part before the drupal root.
++    $local_path = preg_replace('/^' . preg_quote($GLOBALS['base_url'], '/') . '\//', '', $local_path);
++    return $local_path;
+   }
++  return NULL;
+ }
+ 
+ /**
+- * Helper function to determine whether a rule's conditions are met.
++ * Based on visibility setting this function returns TRUE if the CSS injector
++ * rule code should be added to the current page and otherwise FALSE.
+  *
+- * @param $css_rule
+- *   Array describing the rule.
++ * Code ported from googleanalytics.module
+  */
++function _css_injector_visibility_pages($rule) {
++  $visibility = $rule->page_visibility;
++  $setting_pages = $rule->page_visibility_pages;
+ 
+-function _css_injector_evaluate_rule($css_rule = array()) {
+   // Match path if necessary.
+-  if (!empty($css_rule['rule_conditions'])) {
+-    if ($css_rule['rule_type'] < CSS_INJECTOR_PHP) {
+-      $path = drupal_get_path_alias($_GET['q']);
+-      // Compare with the internal and path alias (if any).
+-      $page_match = drupal_match_path($path, $css_rule['rule_conditions']);
++  if (!empty($setting_pages)) {
++    // Convert path to lowercase. This allows comparison of the same path
++    // with different case. Ex: /Page, /page, /PAGE.
++    $pages = drupal_strtolower($setting_pages);
++    if ($visibility < 2) {
++      // Convert the Drupal path to lowercase
++      $path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
++      // Compare the lowercase internal and lowercase path alias (if any).
++      $page_match = drupal_match_path($path, $pages);
+       if ($path != $_GET['q']) {
+-        $page_match = $page_match || drupal_match_path($_GET['q'], $css_rule['rule_conditions']);
++        $page_match = $page_match || drupal_match_path($_GET['q'], $pages);
+       }
+-      // When $css_rule['rule_type'] has a value of
+-      // CSS_INJECTOR_PAGES_NOTLISTED, the rule is matched on
+-      // all pages except those listed in $css_rule['rule_conditions'].
+-      // When set to CSS_INJECTOR_PAGES_LISTED, it is displayed only on those
+-      // pages listed in $css_rule['rule_type'].
+-      $page_match = !($css_rule['rule_type'] xor $page_match);
++      // When $visibility has a value of 0, the tracking code is displayed on
++      // all pages except those listed in $pages. When set to 1, it
++      // is displayed only on those pages listed in $pages.
++      $page_match = !($visibility xor $page_match);
++    }
++    elseif (module_exists('php')) {
++      $page_match = php_eval($setting_pages);
+     }
+     else {
+-      if (module_exists('php')) {
+-        $page_match = php_eval($css_rule['rule_conditions']);
+-      }
+-      else {
+-        $page_match = FALSE;
+-      }
++      $page_match = FALSE;
+     }
+   }
+   else {
+     $page_match = TRUE;
+   }
++
+   return $page_match;
+ }
+-
+-/**
+- * Return the URI for a crid.
+- * @param $crid
+- *   The integer identifying the CSS Rule ID (crid)
+- */
+-function _css_injector_rule_uri($crid) {
+-  if (!empty($crid)) {
+-    $uri = 'public://css_injector/css_injector_' . $crid . '.css';
+-    return $uri;
+-  }
+-}
+\ No newline at end of file
+diff --git a/plugins/export_ui/css_injector_ctools_export_ui.inc b/plugins/export_ui/css_injector_ctools_export_ui.inc
+new file mode 100644
+index 0000000..5153141
+--- /dev/null
++++ b/plugins/export_ui/css_injector_ctools_export_ui.inc
+@@ -0,0 +1,122 @@
++<?php
++/**
++ * @file
++ * Defines this Export UI plugin.
++ */
++$plugin = array(
++  'schema' => 'css_injector_rule',  // As defined in hook_schema().
++  'access' => 'administer css injector',  // Define a permission users must have to access these pages.
++  // Define the menu item.
++  'menu' => array(
++    'menu item' => 'css_injector',
++    'menu prefix' => 'admin/config',
++    'menu title' => 'CSS injector',
++    'menu description' => 'Settings for CSS injector rules. Allows for the insertion of arbitrary CSS on pages.',
++  ),
++
++  // Define user interface texts.
++  'title singular' => t('rule'),
++  'title plural' => t('rules'),
++  'title singular proper' => t('CSS injector rule'),
++  'title plural proper' => t('CSS injector rules'),
++
++  // Define the names of the functions that provide the add/edit forms.
++  'form' => array(
++    'settings' => 'css_injector_ctools_export_ui_form',
++    'validate' => 'css_injector_ctools_export_ui_form_validate',
++    'submit' => 'css_injector_ctools_export_ui_form_submit',
++  ),
++);
++
++/**
++ * Define the preset add/edit form.
++ */
++function css_injector_ctools_export_ui_form(&$form, &$form_state) {
++  $rule = $form_state['item'];
++
++  $form['description'] = array(
++    '#type' => 'textarea',
++    '#title' => t('Description'),
++    '#rows' => 5,
++    '#default_value' => $rule->description,
++    '#required' => FALSE,
++    '#description' => t('Enter a description of the CSS rule'),
++  );
++
++  $form['css_text'] = array(
++    '#type' => 'textarea',
++    '#title' => t('CSS code'),
++    '#rows' => 10,
++    '#default_value' => $rule->css_text,
++    '#required' => TRUE,
++    '#description' => t('The CSS. Please be careful, no validation is done'),
++  );
++
++  // Shamelessly ripped from block.module. Who doesn't use this snippet
++  // of code, really?
++  $php_access = (user_access('use PHP for settings') && module_exists('php'));
++  $options = array(
++    CSS_INJECTOR_PAGES_NOTLISTED => t('Add on every page except the listed pages.'),
++    CSS_INJECTOR_PAGES_LISTED => t('Add on only the listed pages.')
++  );
++  $description = t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
++
++  if ($php_access) {
++    $options[CSS_INJECTOR_PHP] = t('Add if the following PHP code outputs a nonzero value (PHP-mode, experts only).');
++    $description .= ' ' . t('If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can break your Drupal site.', array('%php' => '<?php ?>'));
++  }
++  // Page visibility fieldset. Code ported from googleanalytics.module
++  $form['pages'] = array(
++    '#type' => 'fieldset',
++    '#title' => t('Pages'),
++    '#collapsible' => TRUE,
++    '#collapsed' => FALSE,
++  );
++  $title = t('Pages');
++  $description = t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
++  $options = array(
++    0 => t('Every page except the listed pages'),
++    1 => t('The listed pages only'),
++  );
++  $form['pages']['page_visibility'] = array(
++    '#type' => 'radios',
++    '#title' => t('Add tracking to specific pages'),
++    '#options' => $options,
++    '#default_value' => $rule->page_visibility,
++  );
++  $form['pages']['page_visibility_pages'] = array(
++    '#type' => 'textarea',
++    '#title' => $title,
++    '#title_display' => 'invisible',
++    '#default_value' => $rule->page_visibility_pages,
++    '#description' => $description,
++    '#rows' => 10,
++  );
++  $form['media'] = array(
++    '#type' => 'select',
++    '#title' => t('Media'),
++    '#options' => array('all' => t('All'), 'screen' => t('Screen'), 'print' => t('Print')),
++    '#default_value' => $rule->media,
++    '#description' => t('Choose which media type the css rule apply to.'),
++  );
++  $form['preprocess'] = array(
++    '#type' => 'checkbox',
++    '#title' => t('Preprocess CSS'),
++    '#default_value' => $rule->preprocess,
++    '#description' => t('Check if the css should be inlcuded in preprocess.'),
++  );
++}
++
++/**
++ * Validation handler for the preset edit form.
++ */
++function css_injector_ctools_export_ui_form_validate($form, &$form_state) {
++  // Nothing here, move on.
++}
++
++/**
++ * Submit handler for the preset edit form.
++ */
++function css_injector_ctools_export_ui_form_submit($form, &$form_state) {
++  // Nothing here, move on.
++}
diff --git a/plugins/export_ui/css_injector_ctools_export_ui.inc b/plugins/export_ui/css_injector_ctools_export_ui.inc
new file mode 100644
index 0000000..5153141
--- /dev/null
+++ b/plugins/export_ui/css_injector_ctools_export_ui.inc
@@ -0,0 +1,122 @@
+<?php
+/**
+ * @file
+ * Defines this Export UI plugin.
+ */
+$plugin = array(
+  'schema' => 'css_injector_rule',  // As defined in hook_schema().
+  'access' => 'administer css injector',  // Define a permission users must have to access these pages.
+  // Define the menu item.
+  'menu' => array(
+    'menu item' => 'css_injector',
+    'menu prefix' => 'admin/config',
+    'menu title' => 'CSS injector',
+    'menu description' => 'Settings for CSS injector rules. Allows for the insertion of arbitrary CSS on pages.',
+  ),
+
+  // Define user interface texts.
+  'title singular' => t('rule'),
+  'title plural' => t('rules'),
+  'title singular proper' => t('CSS injector rule'),
+  'title plural proper' => t('CSS injector rules'),
+
+  // Define the names of the functions that provide the add/edit forms.
+  'form' => array(
+    'settings' => 'css_injector_ctools_export_ui_form',
+    'validate' => 'css_injector_ctools_export_ui_form_validate',
+    'submit' => 'css_injector_ctools_export_ui_form_submit',
+  ),
+);
+
+/**
+ * Define the preset add/edit form.
+ */
+function css_injector_ctools_export_ui_form(&$form, &$form_state) {
+  $rule = $form_state['item'];
+
+  $form['description'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Description'),
+    '#rows' => 5,
+    '#default_value' => $rule->description,
+    '#required' => FALSE,
+    '#description' => t('Enter a description of the CSS rule'),
+  );
+
+  $form['css_text'] = array(
+    '#type' => 'textarea',
+    '#title' => t('CSS code'),
+    '#rows' => 10,
+    '#default_value' => $rule->css_text,
+    '#required' => TRUE,
+    '#description' => t('The CSS. Please be careful, no validation is done'),
+  );
+
+  // Shamelessly ripped from block.module. Who doesn't use this snippet
+  // of code, really?
+  $php_access = (user_access('use PHP for settings') && module_exists('php'));
+  $options = array(
+    CSS_INJECTOR_PAGES_NOTLISTED => t('Add on every page except the listed pages.'),
+    CSS_INJECTOR_PAGES_LISTED => t('Add on only the listed pages.')
+  );
+  $description = t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
+
+  if ($php_access) {
+    $options[CSS_INJECTOR_PHP] = t('Add if the following PHP code outputs a nonzero value (PHP-mode, experts only).');
+    $description .= ' ' . t('If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can break your Drupal site.', array('%php' => '<?php ?>'));
+  }
+  // Page visibility fieldset. Code ported from googleanalytics.module
+  $form['pages'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Pages'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+  );
+  $title = t('Pages');
+  $description = t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
+  $options = array(
+    0 => t('Every page except the listed pages'),
+    1 => t('The listed pages only'),
+  );
+  $form['pages']['page_visibility'] = array(
+    '#type' => 'radios',
+    '#title' => t('Add tracking to specific pages'),
+    '#options' => $options,
+    '#default_value' => $rule->page_visibility,
+  );
+  $form['pages']['page_visibility_pages'] = array(
+    '#type' => 'textarea',
+    '#title' => $title,
+    '#title_display' => 'invisible',
+    '#default_value' => $rule->page_visibility_pages,
+    '#description' => $description,
+    '#rows' => 10,
+  );
+  $form['media'] = array(
+    '#type' => 'select',
+    '#title' => t('Media'),
+    '#options' => array('all' => t('All'), 'screen' => t('Screen'), 'print' => t('Print')),
+    '#default_value' => $rule->media,
+    '#description' => t('Choose which media type the css rule apply to.'),
+  );
+  $form['preprocess'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Preprocess CSS'),
+    '#default_value' => $rule->preprocess,
+    '#description' => t('Check if the css should be inlcuded in preprocess.'),
+  );
+}
+
+/**
+ * Validation handler for the preset edit form.
+ */
+function css_injector_ctools_export_ui_form_validate($form, &$form_state) {
+  // Nothing here, move on.
+}
+
+/**
+ * Submit handler for the preset edit form.
+ */
+function css_injector_ctools_export_ui_form_submit($form, &$form_state) {
+  // Nothing here, move on.
+}
