diff -u aef-dynamic.tpl.php aef-dynamic.tpl.php
--- aef-dynamic.tpl.php	2009-12-29 07:21:58.000000000 -0800
+++ aef-dynamic.tpl.php	2011-01-17 14:03:21.000000000 -0800
@@ -13,70 +13,140 @@
   drupal_add_css(drupal_get_path('module', 'aef') . '/aef.css');
   drupal_add_js(array('aef' => array('table_id' => array($aef_id))), 'setting');
   drupal_add_js(array('aef' => array('form_id' => array($form_id))), 'setting');
+  drupal_add_js(drupal_get_path('module', 'aef') .'/aef.js');
 ?>
 
-<fieldset class = "collapsible aef_new_filter">
-  <legend><a href ="#"><?php print t('Apply new filter'); ?></a></legend>
-
-  <table class = "aef_exposed">
-    <tbody>
-    <?php foreach ($widgets as $id => $widget): ?>
-    <tr>
-      <td><?php print $select_filter[$id]; ?></td>
-      <td><?php print $widget->operator; ?></td>
-      <td><?php print $widget->widget; ?></td>
-    </tr>
-    </tbody>
-    <?php endforeach; ?>
-  </table>
-  <div class = "buttons">
-    <?php print $button ?>
+<?php // THE SELECT A FILTER FIELDSET ?>
+<fieldset class="fieldset collapsible aef_new_filter">
+  <legend>
+      <span class="fieldset-title-text"><a href="#"><?php print t('Apply new filter'); ?></a></span>
+  </legend>
+  <div class="fieldset-content clear-block ">
+    <select name="choose_filter" class="form-select" id="choose-filter">
+      <option value="choose">&#60choose...&#62</option>
+      <?php if ($user_can_save_filters): ?>
+        <option value="load_saved_filter">&#60Load Saved Filter Set...&#62</option>
+      <?php endif; ?>
+      <?php foreach($filter_options as $id => $label): ?>
+        <option value="<?php print $id ?>"><?php print $label ?></option>
+      <?php endforeach; ?>
+    </select>
+    <?php if ($user_can_save_filters): ?>
+      <a href="/admin/settings/aef_manage_saved_filter_sets" class="manage_saved_fitlers">Manage Saved Filter Sets</a>
+    <?php endif; ?>
+    <table class="aef_exposed">
+      <tbody>
+        <?php if ($user_can_save_filters): ?>
+          <tr class="aef_exposed_filter_row" id="row-load_saved_filter">
+            <td><?php print $load_filter_set_select; ?></td>
+            <td><?php print $load_filter_set_button; ?></td>
+          </tr>
+        <?php endif; ?>
+      <?php foreach ($widgets as $id => $widget): ?>
+      <tr class="aef_exposed_filter_row" id="<?php print str_replace('filter-', 'row-', $id) ?>">
+        <td><?php print $select_filter[$id]; ?></td>
+        <td><?php print $widget->operator; ?></td>
+        <td><?php print $widget->widget; ?></td>
+        <td><?php print $button ?></td>
+      </tr>
+      <?php endforeach; ?>
+      </tbody>
+    </table>
   </div>
 </fieldset>
 
+<?php // If there is at least one type of filter grouping ?>
 <?php if (count ($where_groups) || count ($group_by_groups)): ?>
-<?php
-  // Add table javascript.
-  drupal_add_js('misc/tableheader.js');
-  drupal_add_js(drupal_get_path('module', 'aef') .'/aef.js');
-  foreach (array_keys($where_groups) as $id) {
-    drupal_add_tabledrag($aef_id, 'match', 'sibling', 'aef-region-select', 'aef-region-'. $id, NULL, FALSE);
-    drupal_add_tabledrag($aef_id, 'order', 'sibling', 'aef-weight', 'aef-weight-'. $id);
-  }
-?>
-
-<fieldset class ="collapsible aef_applied_filters <?php if($ajax_on) print 'aef_ajax_on';?>">
-  <legend><a href ="#"><?php print t('Applied filters'); ?></a></legend>
-  <div class="description">
-    <span>
-      <?php print $advanced_help . t('Groups are grouping using <strong>@all_filter_group</strong> you can change this here: ', array('@all_filter_group' => $selected_all_group_op)) . '</span>' . $all_groups_operator; ?>
-  </div>
-  <?php if (count ($where_groups)): ?>
-  <table id ="<?php print $aef_id;?>">
-    <thead>
-      <tr class = "aef">
-        <th><?php print t('Fitler'); ?></th>
-        <th><?php print t('Operator'); ?></th>
-        <th><?php print t('Value'); ?></th>
-        <th class = "aef_post_hide"><?php print t('In group'); ?></th>
-        <th><?php print t('Position'); ?></th>
-        <th class = "aef_right"><?php print t('Operations'); ?></th>
-      </tr>
-    </thead>
+  <?php
+    // Add table javascript.
+    drupal_add_js('misc/tableheader.js');
+    //drupal_add_js(drupal_get_path('module', 'aef') .'/aef.js');
+    foreach (array_keys($where_groups) as $id) {
+      drupal_add_tabledrag($aef_id, 'match', 'sibling', 'aef-region-select', 'aef-region-'. $id, NULL, FALSE);
+      drupal_add_tabledrag($aef_id, 'order', 'sibling', 'aef-weight', 'aef-weight-'. $id);
+    }
+  ?>
+
+  <?php // If there is at least one type of filter grouping ?>
+  <fieldset class ="fieldset collapsible aef_applied_filters <?php if($ajax_on) print 'aef_ajax_on';?>">
+    <legend>
+        <span class="fieldset-title-text"><a href ="#"><?php print t('Applied filters'); ?></a></span>
+    </legend>
+    <div class="fieldset-content clear-block ">
+      <div class="description">
+        <span>
+          <?php print $advanced_help . t('Groups are grouping using <strong>@all_filter_group</strong> you can change this here: ', array('@all_filter_group' => $selected_all_group_op)) . '</span>' . $all_groups_operator; ?>
+      </div>
+      
+      <?php if (count ($where_groups)): ?>
+      <table id ="<?php print $aef_id;?>">
+        <thead>
+          <tr class = "aef">
+            <th><?php print t('Filter'); ?></th>
+            <th><?php print t('Operator'); ?></th>
+            <th><?php print t('Value'); ?></th>
+            <th class = "aef_post_hide"><?php print t('In group'); ?></th>
+            <th><?php print t('Position'); ?></th>
+            <th class = "aef_right"><?php print t('Operations'); ?></th>
+          </tr>
+        </thead>
 
-    <tbody>
-      <?php foreach ($where_groups as $group_id => $group) : ?>
+        <tbody>
+          <?php foreach ($where_groups as $group_id => $group) : ?>
 
+              <tr class = "aef_group group_<?php print $group_id; ?>">
+                <td class = "region">
+                  <?php print t('Group: %group', array('%group' => $group_id)); ?>
+                </td>
+                <td class = "region" colspan = "2"><span>
+                  <?php print t('Group operator: ') . '</span>'. $group_operator[$group_id]; ?>
+                </td>
+                <td class = "region aef_right"  colspan = "3">
+                  <?php print $put_in_group[$group_id] . '<span>' . t('Put filters here: ');?> </span>
+                </td>
+              </tr>
+
+              <tr class="region-message region-<?php print $group_id; ?>-message <?php print count($group) == 0 ? 'region-empty' : 'region-populated'; ?>">
+                <td colspan="5"><em><?php print t('This group is empty'); ?></em></td>
+              </tr>
+
+              <?php if (count($group)): ?>
+                <?php $row = 0; ?>
+                <?php foreach ($group as $delta => $filter) : ?>
+                    <tr class = "draggable <?php print ($row++ % 2)?'odd':'even'; ?>">
+                      <td><?php print $filter['filter']; ?></td>
+                      <td><?php print $filter['operator']; ?></td>
+                      <td><?php print $filter['value']; ?></td>
+                      <td class = "aef_post_hide"><?php print $filter['group']; ?></td>
+                      <td><?php print $filter['weight']; ?></td>
+                      <td class = "aef_right"><?php print $filter['delete']; ?></td>
+                    </tr>
+                <?php endforeach; ?>
+              <?php endif; ?>
+          <?php endforeach; ?>
+        </tbody>
+      </table>
+      <?php endif; ?>
+    
+      <?php if (count($group_by_groups)) : ?>
+        <table>
+        <thead>
+          <tr class = "aef">
+            <th><?php print t('Fitler'); ?></th>
+            <th><?php print t('Operator'); ?></th>
+            <th><?php print t('Value'); ?></th>
+            <th class = "aef_right"><?php print t('Operations'); ?></th>
+          </tr>
+        </thead>
+        <tbody>
+        <?php foreach ($group_by_groups as $group_id => $group) : ?>
           <tr class = "aef_group group_<?php print $group_id; ?>">
             <td class = "region">
-              <?php print t('Group: %group', array('%group' => $group_id)); ?>
+              <?php print t('Group: Group BY'); ?>
             </td>
-            <td class = "region" colspan = "2"><span>
+            <td class = "region" colspan = "5"><span>
               <?php print t('Group operator: ') . '</span>'. $group_operator[$group_id]; ?>
             </td>
-            <td class = "region aef_right"  colspan = "3">
-              <?php print $put_in_group[$group_id] . '<span>' . t('Put filters here: ');?> </span>
-            </td>
           </tr>
 
           <tr class="region-message region-<?php print $group_id; ?>-message <?php print count($group) == 0 ? 'region-empty' : 'region-populated'; ?>">
@@ -86,76 +156,51 @@
           <?php if (count($group)): ?>
             <?php $row = 0; ?>
             <?php foreach ($group as $delta => $filter) : ?>
-                <tr class = "draggable <?php print ($row++ % 2)?'odd':'even'; ?>">
+                <tr class = " <?php print ($row++ % 2)?'odd':'even'; ?>">
                   <td><?php print $filter['filter']; ?></td>
                   <td><?php print $filter['operator']; ?></td>
                   <td><?php print $filter['value']; ?></td>
-                  <td class = "aef_post_hide"><?php print $filter['group']; ?></td>
-                  <td><?php print $filter['weight']; ?></td>
                   <td class = "aef_right"><?php print $filter['delete']; ?></td>
                 </tr>
             <?php endforeach; ?>
           <?php endif; ?>
-      <?php endforeach; ?>
-    </tbody>
-  </table>
-  <?php endif; ?>
-  <?php if (count($group_by_groups)) : ?>
-    <table>
-    <thead>
-      <tr class = "aef">
-        <th><?php print t('Fitler'); ?></th>
-        <th><?php print t('Operator'); ?></th>
-        <th><?php print t('Value'); ?></th>
-        <th class = "aef_right"><?php print t('Operations'); ?></th>
-      </tr>
-    </thead>
-    <tbody>
-    <?php foreach ($group_by_groups as $group_id => $group) : ?>
-      <tr class = "aef_group group_<?php print $group_id; ?>">
-        <td class = "region">
-          <?php print t('Group: Group BY'); ?>
-        </td>
-        <td class = "region" colspan = "5"><span>
-          <?php print t('Group operator: ') . '</span>'. $group_operator[$group_id]; ?>
-        </td>
-      </tr>
-
-      <tr class="region-message region-<?php print $group_id; ?>-message <?php print count($group) == 0 ? 'region-empty' : 'region-populated'; ?>">
-        <td colspan="5"><em><?php print t('This group is empty'); ?></em></td>
-      </tr>
-
-      <?php if (count($group)): ?>
-        <?php $row = 0; ?>
-        <?php foreach ($group as $delta => $filter) : ?>
-            <tr class = " <?php print ($row++ % 2)?'odd':'even'; ?>">
-              <td><?php print $filter['filter']; ?></td>
-              <td><?php print $filter['operator']; ?></td>
-              <td><?php print $filter['value']; ?></td>
-              <td class = "aef_right"><?php print $filter['delete']; ?></td>
-            </tr>
         <?php endforeach; ?>
+        </tbody>
+        </table>
       <?php endif; ?>
-    <?php endforeach; ?>
-    </tbody>
-    </table>
-  <?php endif; ?>
-  <div id = "aef_need_refresh" class="warning aef_hidden"><span class="warning tabledrag-changed">*</span><?php print t('Changes made in this table will not be saved until you click on refresh.') ?></div>
-  <div class = "aef_buttons">
-    <?php
-      print $reset_button;
-      print $refresh_button;
-      print $new_group_button;
-    ?>
-  </div>
-</fieldset>
+    
+      <div id = "aef_need_refresh" class="warning aef_hidden"><span class="warning tabledrag-changed">*</span><?php print t('Changes made in this table will not be saved until you click on refresh.') ?></div>
+      <?php if ($user_can_save_filters): ?>
+        <div class="aef_save_filters">
+          <table>
+            <tr>
+              <td><?php print $save_filter_set_name; ?></td>
+              <td><?php print $save_filter_set_rid; ?></td>
+              <td><?php print $save_filter_set_button; ?></td>
+            </tr>
+          </table>
+        </div>
+      <?php endif; ?>
+      <div class = "aef_buttons">
+        <?php
+          print $reset_button;
+          print $refresh_button;
+          print $export_csv;
+          print $new_group_button;
+          print '<br />';
+        ?>
+      </div>
+    </div>
+  </fieldset>
+  
 <?php else: ?>
-<?php
-  // at least add the table drag for row 0
-  drupal_add_js('misc/tableheader.js');
-  drupal_add_js(drupal_get_path('module', 'aef') .'/aef.js');
-  drupal_add_tabledrag($aef_id, 'match', 'sibling', 'aef-region-select', 'aef-region-0', NULL, FALSE);
-  drupal_add_tabledrag($aef_id, 'order', 'sibling', 'aef-weight', 'aef-weight-0');
-?>
-<table id ="<?php print $aef_id;?>"></table>
+  
+  <?php
+    // at least add the table drag for row 0
+    drupal_add_js('misc/tableheader.js');
+    drupal_add_js(drupal_get_path('module', 'aef') .'/aef.js');
+    drupal_add_tabledrag($aef_id, 'match', 'sibling', 'aef-region-select', 'aef-region-0', NULL, FALSE);
+    drupal_add_tabledrag($aef_id, 'order', 'sibling', 'aef-weight', 'aef-weight-0');
+  ?>
+  <table id ="<?php print $aef_id;?>"></table>
 <?php endif; ?>
diff -u aef.css aef.css
--- aef.css	2009-12-28 11:32:27.000000000 -0800
+++ aef.css	2011-01-11 14:12:07.000000000 -0800
@@ -1,11 +1,21 @@
 /* $Id: aef.css,v 1.1.2.2 2009/12/28 19:32:27 dagmar Exp $ */
 
-table.aef_exposed .form-item {
-  margin:0;
+.aef_save_filters {
+  clear: both;
+}
+
+#edit-save-filter-set-button,
+#edit-load-filter-set-button {
+  margin-top:12px;
+  float:right;
 }
 
-table.aef_exposed td {
-  padding:0 0.5em;
+.aef_exposed_filter_row {
+  display:none;
+}
+
+table.aef_exposed .form-item {
+  margin:0;
 }
 
 table.aef_exposed tbody {
diff -u aef.info aef.info
--- aef.info	2010-07-11 00:29:21.000000000 -0700
+++ aef.info	2011-01-11 09:33:19.000000000 -0800
@@ -4,7 +4,6 @@
 dependencies[] = views
 package = Views
 core = 6.x
-version = 6.x-dev
 php = 5.0
 
 ; Information added by drupal.org packaging script on 2010-07-11
diff -u aef.install aef.install
--- aef.install	2009-12-26 13:13:41.000000000 -0800
+++ aef.install	2011-01-11 14:07:28.000000000 -0800
@@ -2,22 +2,94 @@
 // $Id: aef.install,v 1.1 2009/12/26 21:13:41 dagmar Exp $;
 
 /**
- * Implementation of hook_requirements().
+ * @file
+ * Implementation of hook_schema().
  */
-function hook_requirements($phase) {
-  $requirements = array();
-
-  $t = get_t();
+function aef_schema() {
+  $schema = array();
 
-  $views_api_version = views_api_version();
-  $requirements['views'] = array(
-    'title' => $t('Views'),
-    'value' => views_api_version(),
+  $schema['aef_saved_filters'] = array(
+    'fields' => array(
+      'fid' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+      ),
+      'uid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+      ),
+      'rid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'unsigned' => FALSE,
+      ),
+      'vid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+      ),
+      'view_name' => array(
+        'type' => 'varchar',
+        'length' => '32',
+        'not null' => TRUE,
+      ),
+      'view_display' => array(
+        'type' => 'varchar',
+        'length' => '32',
+        'not null' => TRUE,
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => '32',
+        'default' => '',
+        'not null' => TRUE,
+      ),
+      'filters' => array(
+        'type' => 'text',
+        'size' => 'big',
+      ),
+    ),
+    'primary key' => array('fid'),
   );
 
-  if (version_compare($views_api_version, 3) < 0) {
-    $requirements['php']['description'] = $t('Your views version is too old. Advanced Exposed Forms requires at least Views 3 alpha-1.');
-    $requirements['php']['severity'] = REQUIREMENT_ERROR;
-  }
-  return $requirements;
+  return $schema;
+}
+
+/**
+ * Implementation of hook_install().
+ */
+function aef_install() {
+  drupal_install_schema('aef');
+}
+
+/**
+ * Implementation of hook_uninstall().
+ */
+function aef_uninstall() {
+  drupal_uninstall_schema('aef');
 }
+
+/**
+ * Implementation of hook_requirements().
+ */
+// function aef_requirements($phase) {
+//   $requirements = array();
+// 
+//   $t = get_t();
+// 
+//   $views_api_version = views_api_version();
+//   $requirements['views'] = array(
+//     'title' => $t('Views'),
+//     'value' => views_api_version(),
+//   );
+// 
+//   if (version_compare($views_api_version, 3) < 0) {
+//     $requirements['php']['description'] = $t('Your views version is too old. Advanced Exposed Forms requires at least Views 3 alpha-1.');
+//     $requirements['php']['severity'] = REQUIREMENT_ERROR;
+//   }
+//   return $requirements;
+// }
+
+
diff -u aef.js aef.js
--- aef.js	2009-12-29 06:29:53.000000000 -0800
+++ aef.js	2011-01-07 15:42:36.000000000 -0800
@@ -1,6 +1,18 @@
 // $Id: aef.js,v 1.1.2.4 2009/12/29 14:29:53 dagmar Exp $
 
+Drupal.behaviors.aefChooseFilter = function(context) {
+  $('#choose-filter').change(function() {
+    $('.aef_exposed_filter_row').each(function(){$(this).hide()});
+    var rowId = '#row-' + $("#choose-filter").val();
+    $(rowId).show();
+    var inputId = '#edit-aef-selected-filter-' + $('#choose-filter').val().replace(/_/g, "-");
+    $(inputId).attr('checked','checked');
+  });
+}
+
+// Useless function! Was causing errors.
 Drupal.behaviors.aefVisualEffects = function(context) {
+  
   $(".aef_post_hide").hide();
 
   $(".icon-button").each(function() {
diff -u aef.module aef.module
--- aef.module	2009-12-28 11:32:27.000000000 -0800
+++ aef.module	2011-01-26 09:14:26.000000000 -0800
@@ -51,10 +51,180 @@
       'file' => 'aef.theme.inc',
       'template' => 'aef-dynamic',
     ),
+    'aef_manage_saved_filter_sets' => array(
+      'arguments' => array('form' => NULL),
+      'file' => 'aef.theme.inc',
+    ),
+  );
+}
+
+/**
+ * Implements hook_perm().
+ */
+function aef_perm() {
+  return array('save filters');
+}
+
+/**
+ * Implements hook_menu().
+ */
+function aef_menu() {
+  $items = array();
+  
+  $items['admin/settings/aef_manage_saved_filter_sets'] = array(
+    'title' => 'AEF Saved Filter Sets',
+    'description' => 'Manage Saved Filter Sets for Advanced Exposed Forms',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('aef_manage_saved_filter_sets_form'),
+    'access arguments' => array('save filters'),
+  );
+  
+  $items['aef/export/csv/%/%'] = array(
+    'title' => 'AEF Export CSV',
+    'description' => 'Export view as CSV using Advanced Exposed Forms Applied Filters',
+    'page callback' => 'aef_export_csv_page',
+    'page arguments' => array(3,4),
+    'access arguments' => array(''),
+  );
+  
+  return $items;
+}
+
+/**
+ * The form for managing saved filter sets
+ */
+function aef_manage_saved_filter_sets_form(&$form_state) {
+  global $user;
+  
+  // If operation is set to delete filters, we have rebuilt the form
+  // From the submit function and we need to return the confirm form instead
+  if ($form_state['values']['op'] == "Delete Selected Filters") {
+    return aef_manage_saved_filter_sets_confirm_form($form_state);
+  }
+  // Our form array
+  $form = array();
+  // An array of the saved filter fids. To be used for the checkboxes
+  $filters = array();
+  
+  // Theming to be handled in a theme function
+  $form['#theme'] = 'aef_manage_saved_filter_sets';
+  
+  // Initial 'Roles' that aren't really roles, but aef knows what to do with
+  $roles = array(
+    '-1' => t('Only me'),
+    '0' => t('All users'),
+  );
+  
+  foreach (user_roles() as $rid => $role) {
+    $roles[$rid] = t('Users of role: !role', array('!role' => $role));
+  }
+          
+  $resource = db_query("SELECT * FROM {aef_saved_filters}");
+  
+  while ($saved_filter = db_fetch_object($resource)) {
+    $rid = $saved_filter->rid;
+    if ($saved_filter->uid == $user->uid || $user->uid == 1 || $rid == 0 || array_key_exists($rid, $user->roles)) {
+      $display_path = views_get_view($saved_filter->view_name)->display[$saved_filter->view_display]->display_options['path'];
+      // Filter name links to the view it belongs to with a trigger to load the filter set
+      $form['name'][$saved_filter->fid] = array(
+        '#value' => l($saved_filter->name, $display_path, array('query' => 'load_filter_set=' . $saved_filter->fid)),
+      );
+      // The user that created this
+      $form['creator'][$saved_filter->fid] = array(
+        '#value' => theme('username', user_load($saved_filter->uid)),
+      );
+      // Who can use this filter
+      $form['access'][$saved_filter->fid] = array(
+        '#value' => $roles[$saved_filter->rid],
+      );
+      // The name of the view
+      $form['view_name'][$saved_filter->fid] = array(
+        '#value' => $saved_filter->view_name,
+      );
+      // The view display id
+      $form['view_display'][$saved_filter->fid] = array(
+        '#value' => $saved_filter->view_display,
+      );
+      // Store the filter id as a key for the checkboxes options
+      $filters[$saved_filter->fid] = '';
+    }
+  }
+  
+  // Our checkboxes to perform operations on filters (only delete for now)
+  $form['filters'] = array(
+    '#type' => 'checkboxes',
+    '#options' => $filters,
+  );
+  
+  // Delete checked items
+  $form['delete'] = array(
+    '#type' => 'submit',
+    '#value' => t('Delete Selected Filters'),
+  );
+  
+  // Need this for the 'select all' checkbox in the form
+  drupal_add_js('misc/form.js', 'core');
+  
+  return $form;
+}
+
+/**
+ * Submit handler for the manage filter sets form
+ */
+function aef_manage_saved_filter_sets_form_submit($form, &$form_state) {
+  // Filter out non-checked boxes
+  $filters = array_filter($form_state['values']['filters']);
+  if (!empty($filters)) {
+    // If we still have some filters, we rebuild which will redirect to the
+    // Deletion confirmation form
+    $form_state['rebuild'] = TRUE;
+  }
+}
+
+/**
+ * The confirmation form for deleting filter sets
+ */
+function aef_manage_saved_filter_sets_confirm_form(&$form_state) {
+  // Our form array
+  $form = array();
+  // Get the checked filters
+  $filters = array_filter($form_state['values']['filters']);
+  // 
+  $form['filters'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
+  foreach ($filters as $filter) {
+    $sql = "SELECT name FROM {aef_saved_filters} WHERE fid = %d";
+    $name = db_result(db_query($sql, $filter));
+    $form['filters'][$filter] = array(
+      '#type' => 'hidden',
+      '#value' => $name,
+      '#prefix' => '<li>' . $name,
+      '#suffix' => '</li>',
+    );
+  }
+  $form['#submit'][] = 'aef_manage_saved_filter_sets_confirm_form_submit';
+  return confirm_form($form,
+    t('Are you sure you want to delete the listed filter sets?'),
+    'admin/settings/aef_manage_saved_filter_sets',
+    t('This action cannot be undone.'),
+    t('Yes I am Sure!'),
+    t('Cancel')
   );
 }
 
 /**
+ * Submit handler for the filter deletion confirmation form
+ */
+function aef_manage_saved_filter_sets_confirm_form_submit($form, &$form_state) {
+  if ($form_state['values']['confirm']) {
+    foreach ($form_state['values']['filters'] as $key => $filter) {
+      $sql = "DELETE FROM {aef_saved_filters} WHERE fid = %d";
+      db_query($sql, $key);
+    }
+  }
+  drupal_set_message(t('The items have been deleted.'));
+}
+
+/**
  * Implementation of hook_views_pre_execute().
  *
  * If all_groups_operator is OR, we have to ensure that non exposed filters
@@ -88,3 +258,82 @@
     }
   }
 }
+
+/**
+ * Outputs a view as a csv. Specifically to be used for
+ * Views filtered using AEF. Other views should be output
+ * to CSV using something like views bonus pack
+ * 
+ * @param $view_name
+ *   The name of the view
+ * 
+ * @param $display_id
+ *   The Display ID within the specified view
+ * 
+ * @todo Add handling for multivalued fields. Maybe some
+ * type of settings page for the file name too?
+ */
+function aef_export_csv_page($view_name, $display_id) {
+  // Get the view and set the display and execute
+  $view = views_get_view($view_name);
+  $view->set_display($display_id);
+  $view->execute();
+  // Set-up the file name for output
+  $filename = $view_name . '-' . $display_id . '.csv';
+  // Get an array of field aliases
+  $fields_to_export = array();
+  $field_aliases = aef_views_get_field_aliases($view);
+  // Add any fields to the $fields_to_export that have aliases
+  foreach ($view->field as $field => $value) {
+    if ($field_aliases[$field]) {
+      $fields_to_export[$field] = $field_aliases[$field];
+    }
+  }
+  
+  // Set the http header so it will prompt user for download when script is done
+  drupal_set_header('Content-Disposition: attachment; filename="'. $filename .'"');
+  
+  // Reuseable array for each csv row
+  $row = array();
+  // Loop through the fields that we are exporting and output them as headers
+  foreach ($fields_to_export as $field => $alias) {
+    $row[] = $field;
+  }
+  $output = implode(',', $row) ."\r\n";
+  
+  // Loop through the results
+  foreach ($view->result as $result) {
+    $row = array();
+    // Output each field wrapped in quotes, separated by commas
+    foreach ($fields_to_export as $field => $alias) {
+      $row[] = '"' . str_replace('"', '""', $result->{$alias}) . '"';
+    }
+    $output .= implode(',', $row) ."\r\n";
+  }
+  
+  // Print the output and exit so no other Drupal stuff gets in there
+  print $output;
+  exit;
+}
+
+/**
+ * Returns an array of field aliases for a view in the format:
+ * array($field_name => $alias). Essentially a Look-up table
+ * to quickly retrieve a views field alias for it's full views
+ * field name
+ * 
+ * @todo Currently doesn't return items for multi-valued fields... Need
+ * to figure that one out...
+ * 
+ * @param $view
+ *    The rendered view object
+ */
+function aef_views_get_field_aliases($view) {
+  $aliases = array();
+  foreach ($view->query->fields as $field) {
+    $aliases[$field['field']] = $field['alias'];
+  }
+  return $aliases;
+}
+
+
diff -u aef.theme.inc aef.theme.inc
--- aef.theme.inc	2009-12-28 12:35:33.000000000 -0800
+++ aef.theme.inc	2011-01-26 10:02:35.000000000 -0800
@@ -7,21 +7,65 @@
  * Variables definitions to use in aef-dynamic.tpl.php
  */
 
+/**
+ * Preprocess function for the aef exposed filter form template
+ * Sets up variables for use in the template.
+ */
 function template_preprocess_aef_dynamic(&$vars) {
+  
+  // Gotta have that javascript
+  drupal_add_js('misc/collapse.js');
+  
+  // Grab some useful variables
   $form = &$vars['form'];
   $view = $form['#parameters'][1]['view'];
-
+  
   // Create variables for filters to select
+  $filter_options = array();
   foreach ($form['#info'] as $id => $filter) {
+    // An array of available filters to select from
     $vars['select_filter'][$id] = drupal_render($form['aef_selected_filter'][$filter['value']]);
+    if ($filter['value'] == 'dyfield') {
+      // Special handling for dynamic fields module
+      $filter_options[$filter['value']] = check_plain('<' . $filter['label'] . '...>');
+    }
+    else {
+      // Also create an array of available filters for <select> drop down
+      $filter_options[$filter['value']] = $filter['label'];
+    }
   }
-  $vars['reset_button'] = drupal_render($form['reset']);
+  
+  // Assign the options for selecting a filter
+  $vars['filter_options'] = $filter_options;
+  
+  // The operator for all the groups
   $vars['all_groups_operator'] = drupal_render($form['all_groups_operator']);
+  
+  // Some ids for HTML elements and themeing
   $vars['aef_id'] = views_css_safe("aef_{$view->name}_{$view->current_display}");
   $vars['form_id'] = views_css_safe("{$view->name}_{$view->current_display}");
+  
+  // Controls
   $vars['new_group_button'] = drupal_render($form['new_group']);
   $vars['refresh_button'] = drupal_render($form['refresh']);
-
+  $vars['reset_button'] = drupal_render($form['reset_form']);
+  
+  // Permission to save/load filters?
+  $vars['user_can_save_filters'] = user_access('save filters');
+  
+  // Loading a filter Set
+  $vars['load_filter_set_button'] = drupal_render($form['load_filter_set_button']);
+  $vars['load_filter_set_select'] = drupal_render($form['load_filter_set_select']);
+  
+  //Saving a filter Set
+  $vars['save_filter_set_button'] = drupal_render($form['save_filter_set_button']);
+  $vars['save_filter_set_name'] = drupal_render($form['save_filter_set_name']);
+  $vars['save_filter_set_rid'] = drupal_render($form['save_filter_set_rid']);
+  
+  // The button to initiate a csv export
+  $vars['export_csv'] = drupal_render($form['export_csv']);
+  
+  // Get the currently selected operator for all the groups
   $exposed_form = $view->display_handler->get_plugin('exposed_form');
   $vars['selected_all_group_op'] = $exposed_form->session['all_groups_operator'];
 
@@ -89,9 +133,80 @@
       unset($form['group_operator_' . $id]);
     }
   }
-
+  
   // Call to parent function the render the others
   // elements of the exposed form.
   template_preprocess_views_exposed_form($vars);
+  
+}
+
+/**
+ * Theme function for the manage filter sets table
+ */
+function theme_aef_manage_saved_filter_sets($form) {
+  // Add the table header cells
+  $header = array(
+    theme('table_select_header_cell'),
+    array('data' => t('Filter Set Name')),
+    array('data' => t('Creator')),
+    array('data' => t('Access')),
+    array('data' => t('View Name')),
+    array('data' => t('View Display')),
+  );
+  
+  // Array for table row data. 
+  $rows = array();
+  
+  // Loop through non property items (items without a #)
+  // And render out the elements to the row
+  foreach (element_children($form['name']) as $key) {
+    $rows[] = array(//row
+      'data' => array( //row data
+        array( //Cell2
+          'data' => drupal_render($form['filters'][$key]),
+          'class' => '',
+        ),
+        array( //Cell1
+          'data' => drupal_render($form['name'][$key]),
+          'class' => '',
+        ),
+        array( //Cell1
+          'data' => drupal_render($form['creator'][$key]),
+          'class' => '',
+        ),
+        array( //Cell2
+          'data' => drupal_render($form['access'][$key]),
+          'class' => '',
+        ),
+        array( //Cell2
+          'data' => drupal_render($form['view_name'][$key]),
+          'class' => '',
+        ),
+        array( //Cell2
+          'data' => drupal_render($form['view_display'][$key]),
+          'class' => '',
+        ),
+      ),//endrow
+      'class' => '', //row class
+    );//endrow
+  }
+  
+  // Output using the theme_table function
+  $output = theme('table', $header, $rows, array('class' => 'aef_manage_saved_filter_sets'));
+  // Render remaining elements (should just be the delete button) and return
+  $output .= drupal_render($form);
+  return $output;
+}
+
+function aef_id_safe($string) {
+  // Replace with dashes anything that isn't a-zA-Z, numbers, dashes, or
+  // underscores.
+  $string = drupal_strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string));
+  // If the first character is not a-z, add 'id' in front.
+  // Don't use ctype_alpha since it's locale aware.
+  if (!ctype_lower($string{0})) {
+    $string = 'id'. $string;
+  }
+  return $string;
 }
 
diff -u aef_dynamic_exposed_form_plugin.inc aef_dynamic_exposed_form_plugin.inc
--- aef_dynamic_exposed_form_plugin.inc	2009-12-29 07:21:58.000000000 -0800
+++ aef_dynamic_exposed_form_plugin.inc	2011-01-26 09:26:36.000000000 -0800
@@ -1,7 +1,23 @@
 <?php
 // $Id: aef_dynamic_exposed_form_plugin.inc,v 1.1.2.4 2009/12/29 15:21:58 dagmar Exp $
 
+/**
+ * Defines an exposed form plugin that allows users
+ * to apply instances of a filter. Instances of filters
+ * can be ordered and grouped using AND or OR operators
+ * to create a Dynamic Filters unattainable through the
+ * standard views exposed form.
+ * 
+ * AEF uses the $_SESSION variable to store filter info
+ * which is different than the standard views method
+ * which uses the $_GET variable and the query string
+ * in the URL. Since $_SESSION is persistent within the
+ * browser session, filters remain active even if the user
+ * navigates away from the page.
+ */
 class aef_dynamic_exposed_form_plugin extends views_plugin_exposed_form_basic {
+  
+  // Some properties
   var $session;
   var $last_filter_id;
   var $start_group_id;
@@ -9,13 +25,17 @@
   /**
    * Overriden methods from Exposed Form Basic.
    */
-
+  
+  /**
+   * Sort of a 'constructor'
+   */
   function init(&$view, &$display, $options) {
     parent::init($view, $display, $options);
-
+    
+    // Load the aef filter information from $_SESSION
     $this->session = &$_SESSION['aef'][$this->view->name][$this->view->current_display];
-
     $this->last_filter_id = NULL;
+    
     if (empty($this->session)) {
       $this->session = array(
         'filters' => array(),
@@ -34,7 +54,10 @@
       }
     }
   }
-
+  
+  /**
+   * Shows up in the Views UI when choosing a exposed form
+   */
   function summary_title() {
     return t('Dynamic exposed filters');
   }
@@ -45,6 +68,13 @@
    */
   function exposed_form_alter(&$form, &$form_state) {
     parent::exposed_form_alter($form, $form_state);
+    
+    // Load a filter set if it's in the query string
+    if ($_GET['load_filter_set'] && is_numeric($_GET['load_filter_set'])) {
+      $this->load_filter_set($_GET['load_filter_set']);
+    }
+    
+    global $user;
 
     $form['#theme'] = 'aef_dynamic';
     $form['#tree'] = TRUE;
@@ -52,6 +82,9 @@
     $form['aef_selected_filter'] = array(
       '#type' => 'radios',
     );
+    
+    // Image path for the delete icon
+    $delete_img_path = drupal_get_path('module', 'aef') .'/images/delete.png';
 
     // Go through each handler to get its label.
     foreach ($this->view->display_handler->handlers as $type => $value) {
@@ -67,9 +100,6 @@
     }
     $form['aef_selected_filter']['#options'] = $filters;
 
-    // Build select box to define group operators
-    $groups = $this->get_groups();
-
     /* Views build the exposed form in this way:
      *
      * First, call to views_exposed_form()
@@ -82,14 +112,14 @@
      * to build new form elements. The solution is create an extra form
      * even it is not necessary and remove it in the render step if it
      * is not needed.
-     */
-
+    */
+    
+    // Build select box to define group operators
+    $groups = $this->get_groups();
     //add the extra group
     $groups[] = array(
       'op' => 'AND',
     );
-
-    $delete_img_path = '/'. drupal_get_path('module', 'aef') .'/images/delete.png';
     $in_group = array();
     foreach ($groups as $id => $group) {
       $form['group_operator_' . $id] = array(
@@ -151,9 +181,9 @@
         '#attributes' => array(
           'src' =>  $delete_img_path,
           'title' => t('Delete this filter'),
-          'class' => 'icon-button',
+          //'class' => 'icon-button',
         ),
-        //'#theme' => 'image_button',
+        '#theme' => 'image_button',
       );
 
       $form['filter_' . $filter['id']]['group'] = array(
@@ -185,41 +215,151 @@
         'class' => 'warning-on-change',
       ),
     );
-
+    
+    /* --- Elements for saving filter sets --- */
+    
+    $form['save_filter_set_button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Save Filter Set'),
+    );
+    
+    $form['save_filter_set_name'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Filter Set Name'),
+      '#size' => 16,
+    );
+    
+    $options = array(
+      '-1' => t('Only me'),
+      '0' => t('All users'),
+    );
+    
+    foreach (user_roles() as $rid => $role) {
+      $options[$rid] = t('Users of role: !role', array('!role' => $role));
+    }
+    
+    $form['save_filter_set_rid'] = array(
+      '#type' => 'select',
+      '#title' => t('Filter Set Access'),
+      '#options' => $options,
+    );
+    
+    /* --- Elements for Loading filter sets --- */
+    
+    $options = array();
+    
+    $sql = "SELECT * FROM {aef_saved_filters}
+            WHERE view_name = '%s'
+            AND view_display = '%s'";
+            
+    $resource = db_query($sql, $this->view->name, $this->view->current_display);
+
+    while ($saved_filter = db_fetch_object($resource)) {
+      $rid = $saved_filter->rid;
+      if ($user->uid == 1 || $rid == 0 || array_key_exists($rid, $user->roles) || $saved_filter->uid == $user->uid) {
+        $options[$saved_filter->fid] = $saved_filter->name;
+      }
+    }
+    
+    $form['load_filter_set_select'] = array(
+      '#type' => 'select',
+      '#title' => t('Select a Filter'),
+      '#options' => $options,
+    );
+    
+    $form['load_filter_set_button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Load Filter Set'),
+    );
+    
+    /* --- Control Buttons --- */
+    
     $form['new_group'] = array(
       '#type' => 'submit',
       '#value' => t('New group'),
     );
+    
+    $form['reset_form'] = array(
+      '#type' => 'submit',
+      '#value' => t('Reset'),
+    );
 
     $form['refresh'] = array(
       '#type' => 'submit',
       '#value' => t('Refresh'),
     );
+    
+    $form['export_csv'] = array(
+      '#type' => 'submit',
+      '#value' => t('Export CSV'),
+    );
+    
   }
-
+  
+  /**
+   * Validation Function for the exposed form
+   */
+  function exposed_form_validate(&$form, &$form_state) {
+    if ($form_state['clicked_button']['#name'] == 'op' && $form_state['clicked_button']['#value'] == t('Save Filter Set')) {
+      if ($form_state['values']['save_filter_set_name'] == '') {
+        form_set_error('save_filter_set_name', 'Please specify a name for your saved filter set');
+      }
+    }
+  }
+  
+  /**
+   * Submit Function for the exposed form
+   */
   function exposed_form_submit(&$form, &$form_state, &$exclude) {
+    //dpm($form_state);
+    /* CHECKING FOR WHICH BUTTON IS PRESSED */
+    // If we're resetting the form
     if ($form_state['clicked_button']['#name'] == 'op' && $form_state['clicked_button']['#value'] == t('Reset')) {
       $form_state = array();
       $form_state['values'] = array();
       $this->reset_form($form, $form_state);
       return;
     }
+    
+    // Create a new group
     if ($form_state['clicked_button']['#name'] == 'op' && $form_state['clicked_button']['#value'] == t('New group')) {
       $this->add_group();
       return;
     }
+    
+    // Save a filter set
+    if ($form_state['clicked_button']['#name'] == 'op' && $form_state['clicked_button']['#value'] == t('Save Filter Set')) {
+      $this->save_filter_set($form_state);
+      return;
+    }
+    
+    // Load a Filter set
+    if ($form_state['clicked_button']['#name'] == 'op' && $form_state['clicked_button']['#value'] == t('Load Filter Set')) {
+      $fid = $form_state['values']['load_filter_set_select'];
+      $this->load_filter_set($fid);
+      return;
+    }
+    
+    // Load a Filter set
+    if ($form_state['clicked_button']['#name'] == 'op' && $form_state['clicked_button']['#value'] == t('Export CSV')) {
+      drupal_goto('aef/export/csv/' . $this->view->name . '/' . $this->view->current_display);
+      return;
+    }
+    
+    // If we're removing a filter
     if (!empty($form_state['clicked_button']['#id']) && strpos($form_state['clicked_button']['#id'], 'edit-delete-') !== FALSE) {
       $filter_id = intval(str_replace('edit-delete-', '', $form_state['clicked_button']['#id']));
       $this->remove_filter($filter_id, $form, $form_state);
       return;
     }
-
+    
+    // If nothing above evaluates, we're applying a filter or refreshing
     $form_state['view']->exposed_raw_input['selected_filter'] = $form_state['values']['aef_selected_filter'];
 
     // Storage group operators
     $groups_operators = array();
     foreach ($form_state['values'] as $id => $value) {
-      $expode = explode('_', $id);
+      $explode = explode('_', $id);
       if (count($explode) == 2) {
         list($str, $gr) = $explode;
         if ($str = 'group' && is_numeric($gr)) {
@@ -241,13 +381,14 @@
         $this->view->exposed_raw_input[$key] = $value;
       }
     }
-
+    
+    // If we're refreshing the page
     if ($form_state['clicked_button']['#name'] == 'op' && $form_state['clicked_button']['#value'] == t('Refresh')) {
-     $this->refresh();
+      $this->refresh();
       return;
     }
 
-    // Check submit options are not being called form pager
+    // Check submit options are not being called from pager
     if (!empty($GLOBALS['_SERVER']['HTTP_REFERER']) && empty($_GET['page'])) {
       $use_ajax = $this->view->display_handler->get_option('use_ajax');
       if (!$use_ajax || ($use_ajax && empty($_GET['js']))) {
@@ -258,6 +399,8 @@
         }
       }
     }
+    unset($form_state['clicked_button']);
+    //dpm($form_state);
   }
 
   function query() {
@@ -289,6 +432,46 @@
   /**
    * Filters
    */
+  
+  /**
+   * Saves a filter set to the database
+   */
+  function save_filter_set(&$form_state) {
+    global $user;
+    $saved_filters = new stdClass();
+    $saved_filters->uid = $user->uid;
+    $saved_filters->rid = $form_state['values']['save_filter_set_rid'];
+    $saved_filters->vid = $form_state['view']->vid;
+    $saved_filters->view_name = $form_state['view']->name;
+    $saved_filters->view_display = $form_state['view']->current_display;
+    $saved_filters->name = $form_state['values']['save_filter_set_name'];
+    $saved_filters->filters = serialize($form_state['exposed_form_plugin']->session);
+    // Check if this name already exists for possible overwrite
+    $sql = "SELECT fid FROM {aef_saved_filters}
+            WHERE view_name = '%s'
+            AND view_display = '%s'
+            AND name = '%s'";
+    $fid = db_result(db_query($sql, $saved_filters->view_name, $saved_filters->view_display, $saved_filters->name));
+    if ($fid) {
+      $saved_filters->fid = $fid;
+    }
+    drupal_write_record('aef_saved_filters', $saved_filters, $saved_filters->fid ?'fid' : array());
+    drupal_set_message('Filter Set Saved.');
+    // Reset the form elements using parent so we don't reset
+    // the currently applied filters in $_SESSION['aef']
+    // parent::reset_form($form, $form_state);
+  }
+  
+  /**
+   * Loads a filter set from the database
+   */
+  function load_filter_set($fid) {
+    $filters = array();
+    $filters = unserialize(db_result(db_query("SELECT filters FROM {aef_saved_filters} WHERE fid = %d", $fid)));
+    if ($filters) {
+      $this->session = $filters;
+    }
+  }
 
   function get_next_filter_id() {
     if (count($this->session['filters']) > 0) {
@@ -315,7 +498,7 @@
     $value = $this->view->exposed_raw_input[$identifier];
     $group = $this->view->exposed_raw_input['put_in_group'];
     $weight = $this->view->exposed_raw_input['weight_' . $this->get_next_filter_id()];
-    if (!$identifier) {
+    if (!$identifier || $identifier == 'dyfield') {
       return;
     }
     $this->add_filter($identifier, $operator, $value, $weight, $group);
@@ -349,8 +532,7 @@
 
     $id = $this->get_next_filter_id();
 
-    // In order to simplify UI group by filters will be storaged in an
-    // unique group
+    // In order to simplify UI group by filters will be stored in a unique group
     if ($handler->options['group_type'] != 'group') {
       $group = 'group_by'; // use an non numeric id to isolate from where groups
     }
@@ -610,21 +792,26 @@
    * TODO: Make more research about this ugly function.
    *
    * This function try to return a more human readable value when a filter
-   * is applied.
+   * is applied. It is used the preprocess function for the template
+   * to produce the text describing the currently applied filters
    *
    * Since views never displayed before this kind of values outside exposed
    * forms elements, we have to try to approximate the value with this function.
    *
    * For now only support numeric, and strings values with multiple and single
    * values.
+   * 
+   * NEW: Added Date Support
    */
   function process_value($filter) {
     $handler = $this->view->filter[$filter['fi']];
-
+    
     $raw_value = $filter['va'];
-
+    
+    // Sometimes the raw value is an array
     if (is_array($raw_value)) {
       if (isset($handler->value_options)) {
+        // If there are multiple values selected
         $return = array();
         foreach ($handler->value_options as $key => $label) {
           if (isset($raw_value[$key])) {
@@ -633,6 +820,33 @@
         }
         return implode(', ', $return);
       }
+      elseif (isset($handler->date_handler)) {
+        // Get the date format. I'm sure there are functions for this...
+        // Start with grabbing the current display_id
+        $display_id = $this->view->current_display;
+        // You gotta dig deep to find information about the chosen date format
+        if (isset($this->view->display[$display_id]->handler->options['fields'])) {
+          // If the 'fields' array is set, then we are using overridden values
+          $date_format = $this->view->display[$display_id]->handler->options['fields'][$filter['fi']]['format'];
+        }
+        else {
+          // If we're not using overridden values in the view, we have to pull from the default
+          $date_format = $this->view->display['default']->handler->options['fields'][$filter['fi']]['format'];
+        }
+        // If this is a date field, we need to clean up which dates are being shown
+        if ($filter['op'] == 'between' || $filter['op'] == 'not between') {
+          // If it's a date range we need to show the upper and lower params
+          return format_date(strtotime($filter['va']['min']), $date_format, '', 0) . ' and ' . format_date(strtotime($filter['va']['max']), $date_format, '', 0);
+        }
+        elseif ($filter['op'] == 'empty' || $filter['op'] == 'not empty') {
+          // Don't need to show anything for these
+          return '';
+        }
+        else {
+          // Otherwise we just need to show the one value
+          return format_date(strtotime($filter['va']['value']), $date_format, '', 0);
+        }
+      }
       else {
         foreach ($raw_value as $key => $raw) {
           if (trim($raw) == '') {
Common subdirectories: images and images
