diff --git README.txt README.txt
index afcd30d..f2e48c8 100644
--- README.txt
+++ README.txt
@@ -1,7 +1,14 @@
 CSS Injector
 ============
-Allows administrators to inject CSS into the page output based on configurable rules. It's useful for adding simple CSS tweaks without modifying a site's official theme -- for example, a 'nighttime' color scheme could be added during certain hours. The CSS is added using Drupal's standard drupal_add_css() function and respects page caching, etc.
+Allows administrators to inject CSS into the page output based on configurable
+rules. It's useful for adding simple CSS tweaks without modifying a site's
+official theme -- for example, a 'nighttime' color scheme could be added during
+certain hours. The CSS is added using Drupal's standard drupal_add_css()
+function and respects page caching, etc.
 
-This module is definitely not a replacement for full-fledged theming, but it provides site administrators with a quick and easy way of tweaking things without diving into full-fledged theme hacking.
+This module is definitely not a replacement for full-fledged theming, but it
+provides site administrators with a quick and easy way of tweaking things
+without diving into full-fledged theme hacking.
 
-An important point to keep in mind is that in Drupal, themes always get last crack at display. Theme CSS is applied after modules. That means themes can and will override CSS put into play by CSS Injector module. If you want to override CSS that a theme is already applying, use CSS rules the way they were intended -- higher specificity rules, or the !important flag.
+The rules provided by CSS Injector typically are loaded last, even after the
+theme CSS, although another module could override these.
diff --git css_injector.admin.inc css_injector.admin.inc
index 9f9f8f5..9bee370 100644
--- css_injector.admin.inc
+++ css_injector.admin.inc
@@ -1,27 +1,35 @@
 <?php
 // $Id: css_injector.admin.inc,v 1.1.2.10 2010/03/08 00:02:22 eaton Exp $
+/**
+ * @file css_injector.admin.inc
+ * Administrative interface for CSS Injector.
+ */
 
 /**
- * Page callback for CSS Injector's main admin page.
+ * Form builder function for CSS Injector's main admin page.
  */
-function css_injector_admin_form() {
+function css_injector_admin_form($form, &$form_state) {
   $rules = _css_injector_load_rule(NULL, TRUE);
-  $path = drupal_get_path('module', 'css_injector') .'/';
+  $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' => 'image_button',
-      '#title' => t('Edit rule'),
-      '#src' => $path .'text-editor.png',
+      '#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' => 'image_button',
-      '#title' => t('Delete rule'),
-      '#src' => $path .'edit-delete.png',
+      '#type' => 'submit',
+      '#value' => t('Delete rule'),
+      '#name' => 'delete' . $rule['crid'],
       '#submit' => array('css_injector_admin_delete_button'),
       '#crid' => $rule['crid'],
     );
@@ -33,9 +41,9 @@ function css_injector_admin_form() {
  * Edit button callback for the CSS rule listing form.
  */
 function css_injector_admin_edit_button($form, &$form_state) {
-  $button = $form_state['clicked_button'];
+  $button = $form_state['triggering_element'];
   $crid = $button['#crid'];
-  $form_state['redirect'] = 'admin/settings/css_injector/edit/'. $crid;
+  $form_state['redirect'] = 'admin/config/development/css_injector/edit/' . $crid;
 }
 
 /**
@@ -43,16 +51,17 @@ function css_injector_admin_edit_button($form, &$form_state) {
  * Redirects the user to the confirmation form.
  */
 function css_injector_admin_delete_button($form, &$form_state) {
-  $button = $form_state['clicked_button'];
+  $button = $form_state['triggering_element'];
   $crid = $button['#crid'];
-  $form_state['redirect'] = 'admin/settings/css_injector/delete/'. $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($form) {
-  $headers = array(t('Title'), t('File path'), t('Actions'));
+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) {
@@ -65,7 +74,7 @@ function theme_css_injector_admin_form($form) {
     }
   }
 
-  $link = l(t('Create a new rule'), 'admin/settings/css_injector/add');
+  $link = l(t('Create a new rule'), 'admin/config/development/css_injector/add');
   $row = array();
   if (empty($rows)) {
     $row[] = array(
@@ -81,23 +90,19 @@ function theme_css_injector_admin_form($form) {
   }
   $rows[] = $row;
 
-  $output .= theme('table', $headers, $rows);
-  $output .= drupal_render($form);
+  $output = theme('table', array('header' => $headers, 'rows' => $rows));
+  $output .= drupal_render_children($form);
   return $output;
 }
 
 /**
- * Constructor for the CSS rule edit form.
+ * Form builder function for the CSS rule edit form.
  */
-function css_injector_edit($form_state, $crid = NULL) {
+function css_injector_edit($form, $form_state, $crid = NULL) {
   if (isset($crid)) {
     $rule = _css_injector_load_rule($crid, TRUE);
-    $form['crid'] = array(
-      '#type' => 'value',
-      '#value' => $crid,
-    );
 
-    $path = file_create_path(_css_injector_rule_path($rule['crid']));
+    $path = _css_injector_rule_path($rule['crid']);
     if (file_exists($path)) {
       $rule['css_text'] = file_get_contents($path);
     }
@@ -108,7 +113,7 @@ function css_injector_edit($form_state, $crid = NULL) {
   else {
     $rule = array(
       'title' => '',
-      'rule_type' => 0,
+      'rule_type' => CSS_INJECTOR_PAGES_NOTLISTED,
       'rule_conditions' => '',
       'media' => 'all',
       'preprocess' => 1,
@@ -116,6 +121,12 @@ function css_injector_edit($form_state, $crid = NULL) {
     );
   }
 
+  if (isset($crid)) {
+    $form['crid'] = array(
+      '#type' => 'value',
+      '#value' => $crid,
+    );
+  }
   $form['title'] = array(
     '#type' => 'textfield',
     '#title' => t('Title'),
@@ -133,33 +144,30 @@ function css_injector_edit($form_state, $crid = NULL) {
 
   // Shamelessly ripped from block.module. Who doesn't use this snippet
   // of code, really?
-  $access = user_access('use PHP for block visibility');
-  if ($rule['rule_type'] == 2 && !$access) {
-    $form['conditional'] = array();
-    $form['conditional']['rule_type'] = array('#type' => 'value', '#value' => 2);
-    $form['conditional']['rule_conditions'] = array('#type' => 'value', '#value' => $rule['rule_conitions']);
-  }
-  else {
-    $options = array(t('Add on every page except the listed pages.'), 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>'));
+  $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 ($access) {
-      $options[] = t('Add if the following PHP code returns <code>TRUE</code> (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,
-    );
+  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',
@@ -181,7 +189,7 @@ function css_injector_edit($form_state, $crid = NULL) {
   );
   $form['buttons']['save_and_continue'] = array(
     '#type' => 'submit',
-    '#value' => t('Save and Continue'),
+    '#value' => t('Save and Continue Editing'),
     '#submit' => array('css_injector_edit_save_and_continue'),
   );
 
@@ -189,8 +197,8 @@ function css_injector_edit($form_state, $crid = NULL) {
     $form['buttons']['delete'] = array(
       '#type' => 'submit',
       '#value' => t('Delete'),
-      '#submit' => array('css_injector_admin_delete_button'),
       '#crid' => $rule['crid'],
+      '#submit' => array('css_injector_admin_delete_button'),
     );
   }
 
@@ -201,39 +209,40 @@ function css_injector_edit($form_state, $crid = NULL) {
  * Validation callback for the CSS rule edit form.
  */
 function css_injector_edit_validate($form, &$form_state) {
-  $rule = $form_state['values'];
-  $directory = file_directory_path();
-  file_check_directory($directory, 1);
-  $form_state['rule'] = $rule;
+  $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['rule'];
-  drupal_write_record('css_injector_rule', $rule, empty($rule['crid']) ? NULL : 'crid');
-
-  file_save_data($rule['css_text'], file_create_path(_css_injector_rule_path($rule['crid'])), FILE_EXISTS_REPLACE);
+  $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('Your CSS injection rule was saved.');
-  $form_state['redirect'] = 'admin/settings/css_injector';
+  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/settings/css_injector/edit/' . $form_state['rule']['crid'];
- } 
+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_state, $crid) {
+function css_injector_delete_confirm($form, &$form_state, $crid) {
   $form['crid'] = array(
     '#type' => 'value',
     '#value' => $crid,
@@ -242,7 +251,7 @@ function css_injector_delete_confirm(&$form_state, $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/settings/css_injector',
+    isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/development/css_injector',
     t('This action cannot be undone.'),
     t('Delete'),
     t('Cancel')
@@ -257,6 +266,6 @@ function css_injector_delete_confirm_submit($form, &$form_state) {
     _css_injector_delete_rule($form_state['values']['crid']);
   }
 
-  $form_state['redirect'] = 'admin/settings/css_injector';
+  $form_state['redirect'] = 'admin/config/development/css_injector';
   return;
 }
diff --git css_injector.info css_injector.info
index d922c48..fbd37f8 100644
--- css_injector.info
+++ css_injector.info
@@ -1,4 +1,9 @@
 ;$Id: css_injector.info,v 1.1.2.2 2008/08/10 01:56:58 add1sun Exp $
 name = CSS Injector
 description = Adds CSS to the page output based on configurable rules.
-core = 6.x
+core = 7.x
+
+files[] = css_injector.admin.inc
+files[] = css_injector.install
+files[] = css_injector.module
+files[] = css_injector.test
diff --git css_injector.install css_injector.install
index 8442947..75bf8cf 100644
--- css_injector.install
+++ css_injector.install
@@ -2,14 +2,19 @@
 // $Id: css_injector.install,v 1.1.2.3 2010/03/07 23:59:47 eaton Exp $
 
 /**
- * Implementation of hook_install().
+ * @file
+ * Install, update and uninstall functions for the css_injector module.
+ *
+ */
+
+/**
+ * Implements hook_install().
  */
 function css_injector_install() {
-  drupal_install_schema('css_injector');
 }
 
 /**
- * Implementation of hook_schema().
+ * Implements hook_schema().
  */
 function css_injector_schema() {
   $schema['css_injector_rule'] = array(
@@ -52,22 +57,37 @@ function css_injector_schema() {
 }
 
 /**
- * Implementation of hook_uninstall().
+ * Implements hook_uninstall().
  */
 function css_injector_uninstall() {
   cache_clear_all('css_injector:*', 'cache', TRUE);
-  $results = db_query("SELECT * FROM {css_injector_rule}");
-  while ($rule = db_fetch_array($results)) {
-    file_delete(file_create_path($rule['file_path']));
+  $rules = db_query("SELECT * FROM {css_injector_rule}", array(), array('fetch' => PDO::FETCH_ASSOC))->fetchAllAssoc('crid');
+  foreach($rules as $id => $rule) {
+    file_unmanaged_delete(_css_injector_rule_path($id));
   }
-  db_query("DROP TABLE {css_injector_rule}");
+  db_drop_table('css_injector_rule');
 }
 
 /**
- * Removes file path field from table
+ * 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_update_6000() {
-  $ret = array();
-  db_drop_field($ret, 'css_injector_rule', 'file_path');
-  return $ret;
+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;
+    }
+  }
+  $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' => t('Not writable'),
+    ),
+  );
+  return $requirements;
 }
diff --git css_injector.module css_injector.module
index fcd3b65..cc5573e 100644
--- css_injector.module
+++ css_injector.module
@@ -8,25 +8,40 @@
  * a site's official theme.
  */
 
+
+/**
+ * Deploy this CSS snippet on every page except the listed pages.
+ */
+define('CSS_INJECTOR_PAGES_NOTLISTED', 0);
+
 /**
- * Implementation of hook_help().
+ * Deploy this CSS snippet on only the listed pages.
+ */
+define('CSS_INJECTOR_PAGES_LISTED', 1);
+
+/**
+ * Deploy this CSS snippet only if the associated PHP code returns TRUE.
+ */
+define('CSS_INJECTOR_PHP', 2);
+
+/**
+ * Implements hook_help().
  */
 function css_injector_help($path, $arg) {
   $output = '';
   switch ($path) {
-    case 'admin/settings/modules#description':
+    case 'admin/config/modules#description':
       $output .= t('Allows administrators to inject CSS into the page output based on configurable rules.');
       break;
-    case 'admin/settings/css_injector':
-      $output .= '<p>'. t('Use CSS injection rules to add small snippets of CSS to the page output when specific criteria are met. For example, a simple rule could change the page background color at night or float a particular div to the right on node editing pages.') .'</p>';
-      $output .= '<p>'. t('An important point to keep in mind is that in Drupal, themes always get last crack at display. Theme CSS is applied after modules. That means themes can and will override CSS put into play by CSS Injector module. If you want to override CSS that a theme is already applying, use CSS rules the way they were intended -- higher specificity rules, or the !important flag.') .'</p>';
+    case 'admin/config/development/css_injector':
+      $output .= '<p>' . t('Use CSS injection rules to add small snippets of CSS to the page output when specific criteria are met. For example, a simple rule could change the page background color at night or float a particular div to the right on node editing pages.') . '</p>';
       break;
   }
   return $output;
 }
 
 /**
- * Implementation of hook_init().
+ * Implements hook_init().
  * Checks to see whether any CSS files should be added to the current page,
  * based on rules configured by the site administrator.
  */
@@ -34,18 +49,39 @@ function css_injector_init() {
   $css_rules = _css_injector_load_rule();
   foreach ($css_rules as $css_rule) {
     if (_css_injector_evaluate_rule($css_rule)) {
-      drupal_add_css(file_create_path(_css_injector_rule_path($css_rule['crid'])), 'module', $css_rule['media'], $css_rule['preprocess']);
+      $filepath = _css_injector_rule_path($css_rule['crid']);
+      drupal_add_css($filepath, array('type' => 'file', 'media' => $css_rule['media'], 'preprocess' => $css_rule['preprocess']));
     }
   }
 }
 
 /**
- * Implementation of hook_menu().
+ * Implements hook_css_alter().
+ * Since we're trying to give the administrator complete control, we'll move
+ * CSS that this module has added to a high weight, higher even than the theme's
+ * 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) {
+    if (_css_injector_evaluate_rule($css_rule)) {
+      $filepath = _css_injector_rule_path($css_rule['crid']);
+      $css[$filepath]['weight'] = 200 + $css_rule['crid'];
+    }
+  }
+}
+
+/**
+ * Implements hook_menu().
  * Defines menu callbacks for CSS Injector's configuration pages.
  */
 function css_injector_menu() {
   $items = array(
-    'admin/settings/css_injector' => 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',
@@ -53,7 +89,7 @@ function css_injector_menu() {
       'access arguments' => array('administer css injection'),
       'file' => 'css_injector.admin.inc',
     ),
-    'admin/settings/css_injector/edit' => array(
+    'admin/config/development/css_injector/edit' => array(
       'title' => 'Edit CSS injector rule',
       'page callback' => 'drupal_get_form',
       'page arguments' => array('css_injector_edit'),
@@ -61,7 +97,7 @@ function css_injector_menu() {
       'file' => 'css_injector.admin.inc',
       'type' => MENU_CALLBACK,
     ),
-    'admin/settings/css_injector/add' => array(
+    'admin/config/development/css_injector/add' => array(
       'title' => 'Add CSS injector rule',
       'page callback' => 'drupal_get_form',
       'page arguments' => array('css_injector_edit'),
@@ -69,7 +105,7 @@ function css_injector_menu() {
       'file' => 'css_injector.admin.inc',
       'type' => MENU_CALLBACK,
     ),
-    'admin/settings/css_injector/delete' => array(
+    'admin/config/development/css_injector/delete' => array(
       'title' => 'Delete CSS injector rule',
       'page callback' => 'drupal_get_form',
       'page arguments' => array('css_injector_delete_confirm'),
@@ -82,21 +118,25 @@ function css_injector_menu() {
 }
 
 /**
- * Implementation of hook_theme().
+ * Implements hook_theme().
  */
 function css_injector_theme() {
   $items['css_injector_admin_form'] = array(
-    'arguments' => array('form' => array()),
+    'render element' => 'form',
     'file' => 'css_injector.admin.inc',
   );
   return $items;
 }
 
 /**
- * Implementation of hook_perm().
+ * Implements hook_permission().
  */
-function css_injector_perm() {
-  return array('administer css injection');
+function css_injector_permission() {
+  return array(
+    'administer css injection' => array(
+      'title' => t('Administer CSS Injection'),
+    ),
+  );
 }
 
 /**
@@ -104,15 +144,16 @@ function css_injector_perm() {
  */
 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}");
-      while ($rule = db_fetch_array($results)) {
-        $rules[$rule['crid']] = $rule;
+      $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);
     }
@@ -131,32 +172,45 @@ function _css_injector_load_rule($crid = NULL, $reset = FALSE) {
  */
 function _css_injector_delete_rule($crid) {
   if ($rule = _css_injector_load_rule($crid)) {
-    file_delete(file_create_path(_css_injector_rule_path($crid)));
-    db_query("DELETE FROM {css_injector_rule} WHERE crid = %d", $crid);
+    file_unmanaged_delete(_css_injector_rule_path($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'])));
   }
 }
 
 /**
  * Helper function to determine whether a rule's conditions are met.
+ *
+ * @param $css_rule
+ *   Array describing the rule.
  */
+
 function _css_injector_evaluate_rule($css_rule = array()) {
   // Match path if necessary.
   if (!empty($css_rule['rule_conditions'])) {
-    if ($css_rule['rule_type'] < 2) {
+    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 ($path != $_GET['q']) {
         $page_match = $page_match || drupal_match_path($_GET['q'], $css_rule['rule_conditions']);
       }
-      // When $css_rule['rule_type'] has a value of 0, the rule is matched on
+      // 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 1, it is displayed only on those pages listed in
-      // $css_rule['rule_type'].
+      // 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);
     }
     else {
-      $page_match = drupal_eval($css_rule['rule_conditions']);
+      if (module_exists('php')) {
+        $page_match = php_eval($css_rule['rule_conditions']);
+      }
+      else {
+        $page_match = FALSE;
+      }
     }
   }
   else {
@@ -166,12 +220,32 @@ function _css_injector_evaluate_rule($css_rule = array()) {
 }
 
 /**
- * Helper function to get file path for a rule
+ * 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 integer identifying the CSS Rule ID (crid)
  */
 function _css_injector_rule_path($crid) {
   if (!empty($crid)) {
-    $directory = file_directory_path();
-    return $directory . '/css_injector_'. $crid .'.css';
+    $local_path = drupal_realpath(_css_injector_rule_uri($crid));
+    // Now remove the part before the drupal root.
+    // The +1 gets rid of the leading '/'.
+    $local_path = substr_replace($local_path, '', 0, strlen(DRUPAL_ROOT) + 1);
+    return $local_path;
   }
   return NULL;
 }
+
+/**
+ * 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 css_injector.test css_injector.test
new file mode 100644
index 0000000..74dbd86
--- /dev/null
+++ css_injector.test
@@ -0,0 +1,116 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Tests for css_injector.
+ */
+
+class CSSInjectorTest extends DrupalWebTestCase {
+
+  protected $privileged_user;
+
+  public static function getInfo() {
+    return array(
+      'name' => t('CSS Injector Functionality'),
+      'description' => t('CSS Injector Functionality.'),
+      'group' => 'CSS Injector',
+    );
+  }
+  function setUp() {
+    parent::setUp(array('css_injector', 'php'));
+    $privileged_user = $this->drupalCreateUser(array('administer css injection', 'use PHP for settings'));
+    $this->drupalLogin($privileged_user);
+  }
+
+  /**
+   * Test the Administrative UI, making sure it does what it ought to do.
+   *
+   * - Create 3 rules:
+   *   - A basic rule that will appear on every page.
+   *   - A basic rule that will appear on only one page.
+   */
+  function testCSSInjectionUI() {
+
+    $base_url = 'admin/config/development/css_injector';
+    $add_url = $base_url . '/add';
+
+    // To add to these rules, just copy and paste.
+    $rules = array(
+      1 => array(
+        'crid' => 1,
+        'title' => t('Rule 1: pink background on all pages'),
+        'css_text' => '.content { background-color: pink; }',
+        'rule_type' => CSS_INJECTOR_PAGES_NOTLISTED, // add on every page except
+        'rule_conditions' => '',  // no conditions
+        'pages_with' => array('user'), // Test page where it should show up.
+        'pages_without' => array(),
+      ),
+      2 => array(
+        'crid' => 2,
+        'title' => t('Rule 2: blue background on admin page'),
+        'css_text' => '.content { background-color: blue; }',
+        'rule_type' => CSS_INJECTOR_PAGES_LISTED, // add on listed pages.
+        'rule_conditions' => 'user',  // show only on /user
+        'pages_with' => array('user'), // Test page where it should show up.
+        'pages_without' => array(''), // front page
+      ),
+      3 => array(
+        'crid' => 3,
+        'title' => t('Rule 3: blue background on admin page'),
+        'css_text' => '.content { background-color: green; }',
+        'rule_type' => CSS_INJECTOR_PHP, // add on listed pages.
+        'rule_conditions' => '<?php print (arg(0) == "admin"); ?>',  // show only on /admin
+        'pages_with' => array('admin/config/development/css_injector'), // Test page where it should show up.
+        'pages_without' => array('user'), // front page should not have this one.
+      ),
+
+    );
+
+    foreach ($rules as $index => $rule) {
+      // Create the rule.
+      $edit = array(
+        'title' => $rule['title'],
+        'css_text' => $rule['css_text'],
+        'rule_type' => $rule['rule_type'],
+        'rule_conditions' => $rule['rule_conditions'],
+      );
+      $this->drupalPost($add_url, $edit, t('Save'));
+      $this->assertRaw(t('Your CSS injection rule %rule was saved', array('%rule' => $rule['title'])));
+      $file = 'css_injector_' . $index . '.css';
+
+      // visit 'pages_with' to see if it's there.
+      foreach ($rule['pages_with'] as $page) {
+        $this->drupalGet($page);
+        $this->assertRaw($file, t('Rule %rule file %file was found on page %page', array('%rule' => $rule['crid'], '%file' => $file, '%page' => $page )));
+      }
+      // visit 'pages_without' and assert that the CSS is not there.
+      foreach ($rule['pages_without'] as $page) {
+        $this->drupalGet($page);
+        $this->assertNoRaw($file, t('Rule %rule file %file not found on page %page', array('%rule' => $rule['crid'], '%file' => $file, '%page' => $page )));
+      }
+
+      $buffer = file_get_contents(_css_injector_rule_uri($rule['crid']));
+      $this->assertIdentical($rule['css_text'], $buffer, t('The file being used has the contents expected'));
+    }
+
+    // Now delete each one and make sure that things work correctly.
+    foreach ($rules as $index => $rule) {
+      $delete_url = $base_url . '/delete/' . $index;
+      $this->drupalPost($delete_url, array(), t('Delete'));
+      $this->assertRaw(t('The CSS rule %rule has been deleted', array('%rule' => $rule['title'])));
+
+      // visit 'pages_with' to verify it's no longer there.
+      foreach ($rule['pages_with'] as $page) {
+        $this->drupalGet($page);
+        $this->assertNoRaw($file, t('Rule %rule file %file no longer found on page %page', array('%rule' => $rule['crid'], '%file' => $file, '%page' => $page )));
+      }
+      // visit 'pages_without' and assert that the CSS is not there either.
+      foreach ($rule['pages_without'] as $page) {
+        $this->drupalGet($page);
+        $this->assertNoRaw($file, t('Rule %rule file %file not found on page %page', array('%rule' => $rule['crid'], '%file' => $file, '%page' => $page )));
+      }
+
+    }
+  }
+}
\ No newline at end of file
