? 207024-forum-taxonomy-v2.patch ? 229660-contact-theme-username-D7_0.patch ? 696_access_log_filter_40.patch ? 696_access_log_filter_41.patch ? jsomers_353847_ ? jsomers_353847_3_0.patch ? jsomers_353847_4.patch ? jsomers_353847_5.patch ? lock-251792-71-D6.patch ? modules/contact/.contact.pages.inc.swp ? sites/default/files ? sites/default/settings.php Index: misc/form.js =================================================================== RCS file: /cvs/drupal/drupal/misc/form.js,v retrieving revision 1.8 diff -u -p -r1.8 form.js --- misc/form.js 26 Apr 2009 19:18:44 -0000 1.8 +++ misc/form.js 27 Apr 2009 18:12:51 -0000 @@ -66,6 +66,11 @@ Drupal.behaviors.multiselectSelector = { $('.multiselect input:radio[value="' + this.id.substr(5) + '"]') .attr('checked', true); }); + $('.multiselect input:not(.multiselectSelector-processed)', context) + .addClass('multiselectSelector-processed').change(function() { + $('.multiselect input:radio[value="'+ this.id.substr(5) +'"]') + .attr('checked', true); + }); } }; Index: modules/statistics/statistics.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/statistics/statistics.admin.inc,v retrieving revision 1.22 diff -u -p -r1.22 statistics.admin.inc --- modules/statistics/statistics.admin.inc 26 Apr 2009 19:44:39 -0000 1.22 +++ modules/statistics/statistics.admin.inc 27 Apr 2009 18:12:52 -0000 @@ -10,6 +10,9 @@ * Menu callback; presents the "recent hits" page. */ function statistics_recent_hits() { + $filtertype = 'hits'; + $filter = statistics_build_filter_query($filtertype); + $header = array( array('data' => t('Timestamp'), 'field' => 'a.timestamp', 'sort' => 'desc'), array('data' => t('Page'), 'field' => 'a.path'), @@ -25,6 +28,11 @@ function statistics_recent_hits() { ->limit(30) ->setHeader($header); + // Build query condition. + foreach ($filter['where'] as $key => $where) { + $query->condition($where['field'], $filter['args'][$key], $where['operator']); + } + $result = $query->execute(); $rows = array(); foreach ($result as $log) { @@ -39,7 +47,8 @@ function statistics_recent_hits() { $rows[] = array(array('data' => t('No statistics available.'), 'colspan' => 4)); } - $output = theme('table', $header, $rows); + $output = drupal_get_form('statistics_filter_form', 'hits'); + $output .= theme('table', $header, $rows); $output .= theme('pager', NULL); return $output; } @@ -48,6 +57,9 @@ function statistics_recent_hits() { * Menu callback; presents the "top pages" page. */ function statistics_top_pages() { + $filtertype = 'pages'; + $filter = statistics_build_filter_query($filtertype); + $header = array( array('data' => t('Hits'), 'field' => 'hits', 'sort' => 'desc'), array('data' => t('Page'), 'field' => 'path'), @@ -68,6 +80,15 @@ function statistics_top_pages() { ->limit(30) ->setHeader($header); + // Build query condition. + if (count($filter['where'])) { + $condition = db_and(); + foreach ($filter['where'] as $key => $where) { + $condition = $condition->condition($where['field'], $filter['args'][$key], $where['operator']); + } + $query->condition($condition); + } + $count_query = db_select('accesslog'); $count_query->addExpression('COUNT(DISTINCT path)'); $query->setCountQuery($count_query); @@ -83,7 +104,9 @@ function statistics_top_pages() { } drupal_set_title(t('Top pages in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))), PASS_THROUGH); - $output = theme('table', $header, $rows); + + $output = drupal_get_form('statistics_filter_form', $filtertype); + $output .= theme('table', $header, $rows); $output .= theme('pager', NULL); return $output; } @@ -92,6 +115,8 @@ function statistics_top_pages() { * Menu callback; presents the "top visitors" page. */ function statistics_top_visitors() { + $filtertype = 'visitors'; + $filter = statistics_build_filter_query($filtertype); $header = array( array('data' => t('Hits'), 'field' => 'hits', 'sort' => 'desc'), @@ -116,6 +141,15 @@ function statistics_top_visitors() { ->limit(30) ->setHeader($header); + // Build query condition. + if (count($filter['where'])) { + $condition = db_and(); + foreach ($filter['where'] as $key => $where) { + $condition = $condition->condition($where['field'], $filter['args'][$key], $where['operator']); + } + $query->condition($condition); + } + $count_query = db_select('accesslog'); $count_query->addExpression('COUNT(DISTINCT CONCAT(CAST(uid AS char), hostname))'); $query->setCountQuery($count_query); @@ -133,7 +167,9 @@ function statistics_top_visitors() { } drupal_set_title(t('Top visitors in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))), PASS_THROUGH); - $output = theme('table', $header, $rows); + + $output = drupal_get_form('statistics_filter_form', $filtertype); + $output .= theme('table', $header, $rows); $output .= theme('pager', NULL); return $output; } @@ -144,6 +180,9 @@ function statistics_top_visitors() { function statistics_top_referrers() { drupal_set_title(t('Top referrers in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))), PASS_THROUGH); + $filtertype = 'referrers'; + $filter = statistics_build_filter_query($filtertype); + $header = array( array('data' => t('Hits'), 'field' => 'hits', 'sort' => 'desc'), array('data' => t('Url'), 'field' => 'url'), @@ -161,6 +200,15 @@ function statistics_top_referrers() { ->limit(30) ->setHeader($header); + // Build query condition. + if (count($filter['where'])) { + $condition = db_and(); + foreach ($filter['where'] as $key => $where) { + $condition = $condition->condition($where['field'], $filter['args'][$key], $where['operator']); + } + $query->condition($condition); + } + $count_query = db_select('accesslog'); $count_query->addExpression('COUNT(DISTINCT url)'); $count_query @@ -178,7 +226,8 @@ function statistics_top_referrers() { $rows[] = array(array('data' => t('No statistics available.'), 'colspan' => 3)); } - $output = theme('table', $header, $rows); + $output = drupal_get_form('statistics_filter_form', $filtertype); + $output .= theme('table', $header, $rows); $output .= theme('pager', NULL); return $output; } @@ -224,6 +273,111 @@ function statistics_access_log($aid) { } /** + * Form builder; Return form for logging filters. + * + * @param $type Type of logging. + * + * @ingroup forms + * @see statistics_filter_form_submit() + */ +function statistics_filter_form($form_state, $type) { + $session = isset($_SESSION['statistics_filter']) ? $_SESSION['statistics_filter'] : array(); + $session = (is_array($session) && isset($session[$type]) ) ? $session[$type] : array(); + $filters = statistics_filters(); + + $i = 0; + $form['filters'] = array( + '#type' => 'fieldset', + '#title' => t('Hide !type where', array('!type' => $filters[$type]['#title'])), + '#theme' => 'statistics_filters', + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + + $form['filters']['type'] = array( + '#type' => 'value', + '#value' => $type, + ); + + // get only type specific filters. + $filters = $filters[$type]['#filters']; + + foreach ($session as $filter) { + list($filtertype, $filtervalue) = $filter; + $params = array('%property' => $filters[$filtertype]['title'] , '%value' => $filtervalue); + if ($i++ > 0) { + $form['filters']['current'][] = array('#markup' => t('and where %property is %value', $params)); + } + else { + $form['filters']['current'][] = array('#markup' => t('%property is %value', $params)); + } + } + + foreach ($filters as $key => $filter) { + $names[$key] = $filter['title']; + $form['filters']['status'][$key] = array( + '#type' => 'textfield', + '#size' => 50, + ); + } + + $form['filters']['filter'] = array( + '#type' => 'radios', + '#options' => $names, + ); + + $form['filters']['buttons']['submit'] = array( + '#type' => 'submit', + '#value' => (count($session) ? t('Refine') : t('Filter')), + ); + if (count($session)) { + $form['filters']['buttons']['undo'] = array( + '#type' => 'submit', + '#value' => t('Undo'), + ); + $form['filters']['buttons']['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset'), + ); + } + + drupal_add_js('misc/form.js'); + + return $form; +} + +/** + * Process result from statistics filter form. + */ +function statistics_filter_form_submit($form, &$form_state) { + $op = $form_state['values']['op']; + $type = $form_state['values']['type']; + $filters = statistics_filters(); + // get only type specific filters. + $filters = $filters[$type]['#filters']; + switch ($op) { + case t('Filter'): + case t('Refine'): + if (isset($form_state['values']['filter'])) { + $filter = $form_state['values']['filter']; + if (isset($form_state['values'][$filter])) { + $_SESSION['statistics_filter'][$type][] = array($filter, $form_state['values'][$filter]); + } + } + break; + case t('Undo'): + array_pop($_SESSION['statistics_filter'][$type]); + break; + case t('Reset'): + $_SESSION['statistics_filter'][$type] = array(); + break; + } + + $form_state['redirect'] = 'admin/reports/' . $type; + return; +} + +/** * Form builder; Configure access logging. * * @ingroup forms @@ -262,3 +416,48 @@ function statistics_settings_form() { return system_settings_form($form, TRUE); } + +/** + * Theme user administration filter form. + * + * @ingroup themeable + */ +function theme_statistics_filter_form(&$form) { + $output = '
'; + $output .= drupal_render($form['filters']); + $output .= '
'; + $output .= drupal_render_children($form); + return $output; +} + +/** + * Theme statistics filter selector. + * + * @ingroup themeable + */ +function theme_statistics_filters(&$form) { + $output = ''; + + return $output; +} Index: modules/statistics/statistics.module =================================================================== RCS file: /cvs/drupal/drupal/modules/statistics/statistics.module,v retrieving revision 1.302 diff -u -p -r1.302 statistics.module --- modules/statistics/statistics.module 26 Apr 2009 09:14:32 -0000 1.302 +++ modules/statistics/statistics.module 27 Apr 2009 18:12:52 -0000 @@ -38,6 +38,22 @@ function statistics_help($path, $arg) { } /** + * Implementation of hook_theme(). + */ +function statistics_theme() { + return array( + 'statistics_filter_form' => array( + 'arguments' => array('form' => NULL), + 'file' => 'statistics.admin.inc', + ), + 'statistics_filters' => array( + 'arguments' => array('form' => NULL), + 'file' => 'statistics.admin.inc', + ), + ); +} + +/** * Implementation of hook_exit(). * * This is where statistics are gathered on page accesses. @@ -346,6 +362,105 @@ function statistics_block_view($delta = } /** + * List statistics filters that can be applied. + */ +function statistics_filters() { + // Regular filters + $filters = array(); + + $filters['hits'] = array( + '#title' => t('recent hits'), + '#filters' => array( + 'page' => array( + 'title' => t('page'), + 'where' => array('field' => 'a.path', 'operator' => 'NOT LIKE'), + 'join' => '', + ), + 'uid' => array( + 'title' => t('userid'), + 'where' => array('field' => 'a.uid', 'operator' => '!='), + 'join' => '', + ), + 'user' => array( + 'title' => t('username'), + 'where' => array('field' => 'u.name', 'operator' => 'NOT LIKE'), + 'join' => '', + ), + ), + ); + $filters['pages'] = array( + '#title' => t('top pages'), + '#filters' => array( + 'path' => array( + 'title' => t('path'), + 'where' => array('field' => 'path', 'operator' => 'NOT LIKE'), + 'join' => '', + ), + 'title' => array( + 'title' => t('title'), + 'where' => array('field' => 'title', 'operator' => 'NOT LIKE'), + 'join' => '', + ), + ), + ); + $filters['referrers'] = array( + '#title' => t('top referrers'), + '#filters' => array( + 'url' => array( + 'title' => t('url'), + 'where' => array('field' => 'url', 'operator' => 'NOT LIKE'), + 'join' => '', + ), + ), + ); + $filters['visitors'] = array( + '#title' => t('top visitors'), + '#filters' => array( + 'path' => array( + 'title' => t('username'), + 'where' => array('field' => 'u.name', 'operator' => 'NOT LIKE'), + 'join' => '', + ), + 'hostname' => array( + 'title' => t('hostname'), + 'where' => array('field' => 'a.hostname', 'operator' => 'NOT LIKE'), + 'join' => '', + ), + ), + ); + + return $filters; +} + +/** + * Build query for statistic filters based on session and statistic type. + * + * @param $type + * Type of filter. + */ +function statistics_build_filter_query($type) { + $filters = statistics_filters(); + // get type specific filters. + $filters = $filters[$type]['#filters']; + + if (!isset($_SESSION['statistics_filter'][$type])) { + return array('where' => array(), 'join' => '', 'args' => array()); + } + + // Build query. + $where = $args = $join = array(); + foreach ($_SESSION['statistics_filter'][$type] as $filter) { + list($key, $value) = $filter; + $where[] = $filters[$key]['where']; + $join[] = $filters[$key]['join']; + $args[] = $value; + } + $join = !empty($join) ? ' ' . implode(' ', array_unique($join)) : ''; + + return array('where' => $where, 'join' => $join, 'args' => $args); +} + +/** * It is possible to adjust the width of columns generated by the * statistics module. */ Index: modules/statistics/statistics.test =================================================================== RCS file: /cvs/drupal/drupal/modules/statistics/statistics.test,v retrieving revision 1.8 diff -u -p -r1.8 statistics.test --- modules/statistics/statistics.test 13 Apr 2009 10:40:13 -0000 1.8 +++ modules/statistics/statistics.test 27 Apr 2009 18:12:52 -0000 @@ -70,3 +70,72 @@ class StatisticsBlockVisitorsTestCase ex $this->assertRaw(t('The IP address %ip was deleted.', array('%ip' => $test_ip_address)), t('IP address deleted.')); } } + +class StatisticsAdminTestCase extends DrupalWebTestCase { + function getInfo() { + return array( + 'name' => t('Statistics administration'), + 'description' => t('Test admininstration page functionality.'), + 'group' => t('Statistics') + ); + } + + function setUp() { + parent::setUp('statistics'); + } + + /** + * Add some entries to access/referrer/recent hits/top pages and filter them. + */ + function testStatisticsAdmin() { + // Create some log entries. + // access log + $access_text1 = $this->randomName(); + $access_text2 = $this->randomName(); + db_insert('accesslog') + ->fields(array( + 'title' => $access_text1, + 'path' => $access_text1, + 'url' => 'http://example.com', + 'hostname' => '192.168.1.1', + 'uid' => 0, + 'sid' => 10, + 'timer' => 10, + 'timestamp' => REQUEST_TIME, + )) + ->execute(); + db_insert('accesslog') + ->fields(array( + 'title' => $access_text2, + 'path' => $access_text2, + 'url' => 'http://sub.example.com', + 'hostname' => '192.168.1.2', + 'uid' => 0, + 'sid' => 10, + 'timer' => 10, + 'timestamp' => REQUEST_TIME, + )) + ->execute(); + + // Create admin user to filter the logs. + $admin_user = $this->drupalCreateUser(array('access statistics')); + $this->drupalLogin($admin_user); + $this->drupalGet('admin/reports/hits'); + $this->assertText($access_text1, t("Found '%text' on recent hits page", array('%text' => $access_text1))); + $this->assertText($access_text2, t("Found '%text' on recent hits page", array('%text' => $access_text2))); + + // Filter the access log by page. + $edit = array(); + $edit['filter'] = 'page'; + $edit['page'] = $access_text1; + + $this->drupalPost('admin/reports/hits', $edit, t('Filter')); + + // Check if the correct log items show up. + // Use assertNoRaw and check for text within a link because the text is also + // displayed in the filter form. + $this->assertNoRaw('>' . $access_text1 . '', t("Found '%text' not on filtered by page on recent hits page", array('%text' => $access_text1))); + $this->assertText($access_text2, t("Found '%text' on filtered by page on recent hits page", array('%text' => $access_text2))); + } + +}