Hello,

I'd like to know if anyone has a php snippet that would show the last x number of nodes a particular user has viewed. I saw this on a site recently and really liked it, but couldn't tell what CMS it was.

I envision a block in the upper right corner that would show the last 3 pages that the user has viewed to allow for easier navigation.

Any ideas would be appreciated.

Comments

StevenPatz’s picture

it needs customised though:

http://drupal.org/node/45286

12stringblues’s picture

I don't think that is what I really need, unless I'm not understanding the code. I understand how to pull recently changed content, the most recent nodes, etc. What I need is the recently viewed pages tied to the current user that is logged in (or even a guest for that matter). I could get the last viewed pages with some javascript code to use their browsers history entries, but I would like something more internal to drupal. Anyone else have any ideas?

infojunkie’s picture

The following script implements this in a simple way (on Drupal 5.x). You can put it in a block:

<?php
global $user;
$result = db_query("SELECT DISTINCT title, path FROM {accesslog} WHERE uid = %d ORDER BY timestamp DESC LIMIT 5", $user->uid);
while ($row = db_fetch_object($result)) {
  $links[] =  l($row->title ? $row->title : t('Home'), $row->path);
}
echo theme('item_list', $links);
?>
t4him’s picture

This block works great. I want to use to display only "products" visited. It is returning "every page" visited. Any suggestions would be a great help.

Thanks in advance,

Tim "t4him" Moyers

infojunkie’s picture

The solution above is limited and relies on the statistics module which doesn't save the node type (since it logs all accesses not just node accesses). You could wrestle with the `accesslog` table to try to guess which entry was a node (e.g. it has path = node/*) and then load the nid to check its type. But that's a long and inefficient solution. Better, you could implement hook_nodeapi() and catch all 'view' operations on your node type of interest, saving them in your own table with the uid that visited. HTH.

alex’s picture

kratib

Why not just put them in session instead of database?

HillsWeb.com

infojunkie’s picture

Good idea! Even simpler.

codevoice’s picture

I expanded on this to fix a couple problems. 1) Your code would only work for users logged in. Worse, if they aren't logged in, then they see what other "Anonymous" users are doing in their history... which isn't the intended effect. 2) As someone else mentioned this wouldn't limit the results to any particular set of pages. I did this by simply checking the url against a string, which for me works great because I use pathauto, so it's pretty predictable, and still lightweight.

<?php
global $user;
$url_limiter = '%content%'; // check the url for this term, to only show certain pages in the list
if ( $user->uid == 'Anonymous' ) {
    // This is an anonymous user, so track using the hostname
    $recently_viewed_query = "SELECT DISTINCT title, path FROM `accesslog` WHERE uid = 'Anonymous' AND hostname = '" . $user->hostname . "' AND url LIKE '" . $url_limiter . "' ORDER BY timestamp DESC LIMIT 4";
} else {
    // Or if this user isn't Anonymous, then use the uid (which is more reliable)
    $recently_viewed_query = "SELECT DISTINCT title, path FROM `accesslog` WHERE uid = '" . $user->uid . "' AND url LIKE '" . $url_limiter . "' ORDER BY timestamp DESC LIMIT 4";
}
//echo $recently_viewed_query; // Sanity check!
$result = db_query($recently_viewed_query);
// Now walk through the results and display them
while ($row = db_fetch_object($result)) {
  $links[] =  l($row->title ? $row->title : t('Home'), $row->path);
}
echo 'List of links: ' . theme('item_list', $links);
?>
crbassett’s picture

Does this work with 6.x? I tried it on mine and I'm not seeming to get it to work...

Thanks!

ethosophical’s picture

You can also do this pretty easily in Views by making an "access log" view and using filters.

dhaven’s picture

Hi, can you elaborate a little more on the views and access log?
thanks
DS

ethosophical’s picture

DS,

If you're using Views, you can create a view which shows your user access log. Here's mine (keep in mind, this shows only two types of content ... your needs may vary)

$view = new view;
$view->name = 'RecentlyViewed';
$view->description = 'Shows Recently Viewed Assignments and Lessons';
$view->tag = 'recentlyviewed';
$view->view_php = '';
$view->base_table = 'accesslog';
$view->is_cacheable = FALSE;
$view->api_version = 2;
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
$handler = $view->new_display('default', 'Recently Viewed', 'default');
$handler->override_option('relationships', array(
  'uid' => array(
    'label' => 'uid',
    'required' => 0,
    'id' => 'uid',
    'table' => 'accesslog',
    'field' => 'uid',
    'relationship' => 'none',
  ),
));
$handler->override_option('fields', array(
  'title' => array(
    'label' => '',
    'display_as_link' => 1,
    'exclude' => 0,
    'id' => 'title',
    'table' => 'accesslog',
    'field' => 'title',
    'relationship' => 'none',
  ),
));
$handler->override_option('sorts', array(
  'timestamp' => array(
    'order' => 'DESC',
    'id' => 'timestamp',
    'table' => 'accesslog',
    'field' => 'timestamp',
    'relationship' => 'none',
  ),
));
$handler->override_option('filters', array(
  'path' => array(
    'operator' => 'contains',
    'value' => 'node',
    'group' => '0',
    'exposed' => FALSE,
    'expose' => array(
      'operator' => FALSE,
      'label' => '',
    ),
    'case' => 1,
    'id' => 'path',
    'table' => 'accesslog',
    'field' => 'path',
    'relationship' => 'none',
  ),
  'uid_current' => array(
    'operator' => '=',
    'value' => 1,
    'group' => '0',
    'exposed' => FALSE,
    'expose' => array(
      'operator' => FALSE,
      'label' => '',
    ),
    'id' => 'uid_current',
    'table' => 'users',
    'field' => 'uid_current',
    'relationship' => 'uid',
  ),
));
$handler->override_option('access', array(
  'type' => 'none',
));
$handler->override_option('title', 'Recently Viewed');
$handler->override_option('empty', 'No pages have been viewed since this feature has been enabled.');
$handler->override_option('empty_format', '2');
$handler->override_option('distinct', 1);
$handler->override_option('style_options', array(
  'grouping' => '',
));
$handler = $view->new_display('block', 'Block', 'block_1');
$handler->override_option('block_description', 'Recently Viewed');
$handler->override_option('block_caching', -1);
dhaven’s picture

thanks for that but would this code be an export for drupal 4? I have 5

ethosophical’s picture

This is code for Drupal 6 and the latest build of Views.

dhaven’s picture

thanks, I'll keep that for when i upgrade. Is this a new feature (draw data from access log) in 6 only?

Janice Harper’s picture

I do not think that is what I really need, unless I'm not understanding the code. I understand how to pull recently changed content, the most recent nodes, etc. What I need is the recently viewed pages tied to the current user that is logged in (or even a guest for that matter)? I could get the last viewed pages with some javascript code to use their browsers history entries, but I would like something more internal to drupal. Anyone else have any ideas?

Mieszko Czyzyk’s picture

This seems to work (make sure the statistics module is on). But despite of the distinct function that is on. We get a lot of double entrees. Any way to eliminate this so each page is being displayed only once?

noahlively’s picture

GoalGorilla,

This is the query that I got in my views setup screen:

SELECT DISTINCT(accesslog.aid) AS aid,
   accesslog.title AS accesslog_title,
   accesslog.path AS accesslog_path,
   accesslog.timestamp AS accesslog_timestamp
 FROM accesslog accesslog 
 LEFT JOIN users users_accesslog ON accesslog.uid = users_accesslog.uid
 WHERE users_accesslog.uid = ***CURRENT_USER***
   ORDER BY accesslog_timestamp DESC

As you can see, views is interjecting additional columns in the query. I don't know the way around this per se using the Views UI. However, I have expanded on code previously written in this thread and created a fully functional block to take care of this.

In addition to the code written previously, this block has a configuration form:

  • Configurable result limit
  • Configurable filter

Here is the code:

function my_module_block($op = 'list', $delta = 0, $edit = array())
{
  if ($op == 'list')
  {
    $blocks[0]['info'] = t('Recently Viewed Content');

    return $blocks;
  }
  else if ($op == 'configure')
  {
    $form['recently_viewed_record_limit'] = array(
        '#type' => 'textfield',
        '#title' => t('Result Limit'),
        '#description' => t('Number of recently viewed pages to show'),
        '#default_value' => variable_get('recently_viewed_record_limit', 4),
    );
    $form['recently_viewed_content_filter'] = array(
        '#type' => 'textfield',
        '#title' => t('URL Filter'),
        '#description' => t('Only show pages whose paths match this expression. (Use SQL <em>like</em> syntax.)'),
        '#default_value' => variable_get('recently_viewed_content_filter', ''),
    );
    return $form;
  }
  else if ($op == 'save')
  {
    variable_set('recently_viewed_record_limit', (int)$edit['recently_viewed_record_limit']);
    variable_set('recently_viewed_content_filter', $edit['recently_viewed_content_filter']);
  }
  else if ($op == 'view')
  {
    switch ($delta)
    {
      case 0:
        global $user;
        $limit = variable_get('recently_viewed_record_limit', 4);
        // check the url for this term, to only show certain pages in the list
        $url_filter = variable_get('recently_viewed_content_filter', '');
        $criteria = array();
        $where = '';


        if ( $user->uid == 'Anonymous' ) {
          // This is an anonymous user, so track using the hostname
          $criteria[] = sprintf("(uid = 'Anonymous' AND hostname = '%s')", $user->hostname);
        } else {
          // Or if this user isn't Anonymous, then use the uid (which is more reliable)
          $criteria[] = sprintf("uid = '%s'", $user->uid);
        }
        if (!empty($url_filter))
        {
          $criteria[] = sprintf(" url LIKE '%s' ", $url_filter);
        }

        if (!empty($criteria))
        {
          $where = ' WHERE ' . implode(' AND ', $criteria);
        }

        $recently_viewed_query = "SELECT DISTINCT title, path FROM `accesslog` $where ORDER BY timestamp DESC LIMIT 0, $limit";

        $result = db_query($recently_viewed_query);
        // Now walk through the results and display them
        while ($row = db_fetch_object($result)) {
          $links[] =  l($row->title ? $row->title : t('Home'), $row->path);
        }

        $block['subject'] = t('Recently Viewed Content');
        $block['content'] = theme('item_list', $links);
        break;
    }
    return $block;

  }
}

Noah Lively
Founder and CEO,
Kore Innovations
http://www.koreinnovations.io

Open Social’s picture

Hi noahlively,

This is what we need! How do I implement this block code?

Many thanks in advance!

mudman’s picture

Hi all,
This is exactly what I am looking for. I put this snippet in a block with php enable but I couldn't get any result.
I guess I didn't set the path properly. Could anyone explain how to do it?
Or I should put this snippet somewhere else instead of in a block?
Excuse me, I am quite new to Drupal. But I really need this feature on the site I am working on.
Thanks

WorldFallz’s picture

The code above is module code, not block code. Either create a little custom module, or use the custom block snippet version.

mudman’s picture

Thanks Worldfallz,
I tried that as well, but I don't know what to do with this:

//Only show pages whose paths match this expression. Use SQL 'like' syntax. Change to suit.
$url_filter = '';

i tried this, but it didn't work

$url_filter = 'WHERE path LIKE node/%';
WorldFallz’s picture

The line that creates the filter is:

$criteria[] = sprintf(" url LIKE '%s' ", $url_filter);

So, using your string, you created:

url LIKE WHERE path LIKE node/%

I'm no sql guru, but that doesn't look valid to me ;-)

mudman’s picture

Thanks for pointing that out, WorldFallz. I am a sql newbie and I am still learning, but I really like the idea of having the recently viewed item on the site I am building. What if I want to show "content/event/%" and "content/meeting/%" in the recently viewed item block? I tried copy your code into the block and change '%s' to 'node/%', it didn't work and gave me an error message.
arse error: syntax error, unexpected T_GLOBAL. could you please explain this a little bit.

WorldFallz’s picture

This is only a guess, but try:

$url_filter = 'node';

mudman’s picture

i still couldn't get it to work. But that's alright. Maybe I could when I learn a bit more sql. Thanks!

WorldFallz’s picture

Couple more guesses:

$url_filter = 'node%';

$url_filter = '%node%';

minus’s picture

did you have any luck with this? it`s a really nice feature.

--------------------------
http://www.mortenn.no

WorldFallz’s picture

Awesome snippet! Posted to the block snippets section altered to be a self contained custom block: http://drupal.org/node/533358 so it doesn't require a module.

Open Social’s picture

After changing 'accesslog' into the right table with prefix (XXX_accesslog) we got the Block working!

WorldFallz’s picture

That would be true for all snippets if you're using prefixed tables.

Open Social’s picture

On line 64 of statics.module data is written to the accesslog.

Why do we use referer_uri() for the URL instead of request_uri? This makes no sense for the recent visited pages....

saurabhcms’s picture

Hello,

First you have to enable a statistics module at admin/builld/modules.
You can use the views to make a block and choose the sort criteria,Node statistics: Most recent view and choose the filter and filters according to your need.

Thanks,
Saurabh.

BigMike’s picture

Saurabh,

I've been searching around for a statistics module and I cannot decide which one is right for this. Could you please recommend a statistics module that I should use with the described view?

Thank you in advance,
Mike

Heine’s picture

You should use db_query placeholders, not attempt to emulate them with sprintf. $url_filter will not be properly escaped in the example.

izmeez’s picture

subscribing

minus’s picture

Would be a nice contribution I think --> http://drupal.org/node/691996#comment-2507098

--------------------------
http://www.mortenn.no

pgorecki’s picture

You can try my module for displaying recently read nodes (with configurable node types). It's currently in a sandbox:
http://drupal.org/project/recently_read

MakeOnlineShop’s picture

Easiest way to show LATEST VIEWED NODES list ?

Hi,

Can you tell me what is the easiest way to show the latest viewed nodes list now ?

I want to use it for to show customers latest viewed products on Ubercart shop.

Thanks a lot.

BigMike’s picture

@make-online-shop:

The Recently Read module posted above sounds promising and I'd recommend you try that first. I was able to get a Recently Viewed block on my Ubercart site using Flag, Rules, and View modules as per Jan Tomka's walk-through discussed here: http://jan.tomka.name/blog/list-recently-viewed-nodes-drupal

Here is a sample of Jan's list of recently viewed nodes operating on my Ubercart site: http://www.marlincrawler.com Click around on a few products and you'll see a block beneath my upsell block (Related products) that shows Recently Viewed Products.

Jan's method involves three modules and a bit of setup work, so I'd try the Recently Read module first to see if that will work for you.

Good luck,
BigMike

BigMike’s picture

Holy smokes, earlier today I updated my Views for the new critical security issue, and just now noticed that my Recently Viewed Products is no longer working!! >_< CRAP! Now I gotta find out what changed with Views!! :-(

MakeOnlineShop’s picture

Recently viewed products works with Ubercart but not for anonymous when CACHE enabled

Hi,

Thank you, the recently viewed module works with Ubercart for logged users but no pictures of the products are shown and it seems not to work with Anonymous users when CACHE is enabled.

Any other idea ?

Thanks.

BigMike’s picture

I just updated to Views 6.x-2.16 and my "Recently Viewed Products" view was automagically restored to its former glory! WHEW!

MakeOnlineShop’s picture

Sorry offtopic, but your small cart in the menu is so cute :-) How did you do it and does it work for unlogged users because it is an Ajax cart ? Would like to have the same !

And I didn't see your recently viewed products on the shop ?

BigMike’s picture

make-online-shop,

I got the problem fixed. It was an issue with Views 6.x-2.14. Please check my site again and you will see a Recently Viewed Products block appear at the bottom of any product page once you have viewed more than 1 product.

Thanks!
Mike