AJAX loaded site-wide menu and login problems

AJAX loaded site-wide menu and login problems

by Chris Price -
Number of replies: 2
I think I've figured this out on my own, but leaving here in case anyone has any thoughts. Will post a followup if my idea works.

---------------------------------

I'm currently working on a Moodle site that uses a couple of WordPress installations as the main portal and the actual course catalog. In order to integrate these slightly with Moodle, I've included a small "student menu" into the main menu of the site. The contents of this menu are loaded via jQuery.load() to avoid any conflicts between WordPress and Moodle. Altogether, this simulates an integrated login for students across the entire site (Moodle is the only area that really requires login).

The student menu requires Moodle's config file and checks for the presence of $USER->id... well here's the code:

<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
</head>

<?php
function ajax_request() {
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
}
?>

<?php if (!ajax_request()){die("Direct access not allowed.");}?>

<?php
require_once('../includes/vars.php'); //contains system-wide paths
require_once( 'path/to/Moodle/config.php'); //Moode config
global $USER;
//DEFINE FUNCTIONS

if($USER->id) :

$userID = $USER-> id;
$grav_mail = $USER-> email;
$firstname = $USER-> firstname;
$themeURL = $CFG->themewww .'/'. current_theme();

else :
$userID = 0;
$grav_mail = 'guest';
$firstname = 'Guest';
$themeURL = $CFG->themewww .'/'. current_theme();

endif;

//Move this into functions.php
function print_gravatar($size, $mail, $name, $user, $themeURL){
$defaultGravatar = $themeURL . '/images/daavatar' . $size . '.png';
$grav_url = "http://www.gravatar.com/avatar/" . md5(strtolower($mail)) . "?default=".urlencode($defaultGravatar)."&size=".$size;

echo '<img id="gravatar_' . $user . '" class="gravatar" src=" '. $grav_url .'" alt="' . $name . '\'s Gravatar" title="' . $name . '\'s Gravatar" height="'. $size .'" width="'. $size .'"/>';

}
?>

<?php
//START NOT LOGGED IN MENU------------------------------------------------------
if ($userID == 0) :
?>

<div class="avatar glass notloggedin"><?php print_gravatar(35, $grav_mail, $firstname, $userID, $themeURL); ?></div>
<ul>

<li><?php echo user_login_string();?></li>

</ul>

<ul>
LIST OF LINKS
</ul>

<?php
//START LOGGED IN MENU-----------------------------------------------------------
elseif ($userID != 0) :

?>

<div class="avatar glass loggedin"><?php print_gravatar(35, $grav_mail, $firstname, $userID, $themeURL); ?></div>
<ul>

<li><?php echo user_login_string();?></li>

</ul>

<ul>
LIST OF LINKS
</ul>

//List user's courses for jQuery access, but hide them from view
<style type="text/css">
.dimmed{text-decoration:line-through;}
#courseTestSet{display: none;}
</style>
<?php

require_once($CFG->dirroot.'/user/profile/lib.php');

$id = optional_param('id', 0, PARAM_INT); // user id
$course = optional_param('course', SITEID, PARAM_INT); // course id (defaults to Site)


$mycourses = get_my_courses($USER->id, null, null, false, 21);
$shown=0;
$viewlink = '<div id="courseTestSet">';
foreach ($mycourses as $mycourse) {
if ($mycourse->category) {
if ($mycourse->id){
$cssid = 'cs'.$mycourse->id; //output looks like cs25
$class = '';
if ($mycourse->visible == 0) {
// get_my_courses will filter courses $USER cannot see
// if we get one with visible 0 it just means it's hidden
// ... but not from $USER
$class .= ' dimmed';
}
$viewlink .= "<a href=\"{$CFG->wwwroot}/course/view.php?id={$mycourse->id}\" class=\"$class\" id=\"$cssid\">"
. format_string($mycourse->fullname) . "</a> ";
}
else {
$viewlink .= format_string($mycourse->fullname) . ", ";
}
}
$shown++;
if($shown==20) {
$viewlink.= "</div>";
break;
}
}
echo $viewlink;
?>
<?php
//END LOGGED IN IF STATEMENT-----------------------------------------------------
endif; ?>
<div class="clear"></div>

-----------------------------------------------------------------------

As you see, it isn't doing anything really fancy - and it works well across Moodle and WordPress - until the user's session expires. At session expiration, in place of the student menu (presumably because Moodle config is included), the "A server error that affects your login session was detected" (cookies have a unique prefix & database sessions are turned on) page loads into the student menu area and completely messes everything up.

To make matters worse, the meta-refresh on that page redirects the user to the URL of the student menu script in the main window, and since the request wasn't made via jQuery.load(), the page simply says "Direct access not allowed." by design.

Since the student menu is a global object, it doesn't matter which page on the site is visited, if the "Continue" link on the session error page isn't clicked (good luck catching it with the meta-refresh) the whole process starts again and it is a never-ending loop back to the student menu's URL.

How/where can I change the session error behavior so that the "server error" page doesn't load and instead, the student menu loads as normal - just showing that the student isn't logged in? As mentioned before, I'm sure it isn't really a server session error - but instead an expired Moodle session.
Average of ratings: -
In reply to Chris Price

Re: AJAX loaded site-wide menu and login problems

by Chris Price -
Not the most elegant solution in the world, but with a couple of test runs with 5 minute session expiration, the user experience is better, but still not ideal.


<?php
require_once('../includes/vars.php'); //sitewide paths
$pageURL = 'http://' . $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
$referrer = $_SERVER['HTTP_REFERER'];

if ($referrer == $pageURL){

header( 'Location: ' . DAA_SCHOOLPATH . '/login/index.php');


}

?>

<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
</head>

<?php
require_once( '../../academy/config.php'); //Moodle Config
global $USER;
?>

<?php
function ajax_request() {
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
}
?>

<?php if (!ajax_request()){die('Direct access not allowed.');}?>

---------//remaining parts unchanged//---------

With this, in areas that are controlled by WordPress, when the session expires the student menu's call to Moodle's user_login_string(); shows the user as being logged out. On Moodle controlled pages, the student menu catches itself referring from itself and shoots the user to the login page where Moodle catches and displays the "a server error...." page with the continue link and forwards once again to the login page. Very confusing chain of events there, but it doesn't result in a never-ending loop.

Still open to suggestions if anyone is following what's happening. I'd like to totally bypass the "a server error" page and simply show the login page.
In reply to Chris Price

Re: AJAX loaded site-wide menu and login problems

by Chris Price -
By this point, I've added an AJAX login form to the above menu for when users are not signed in. It works well, but still experiencing the "A server error that affects your login status was detected" page load - and now it is twice as bad with having to include config.php in both the ajax login script and the global menu as well.

What does Moodle do when it detects whatever is happening (I'm still sure it is just a session timeout)? I need to just do it automatically without showing the error page.