University of Oregon

JS Tweak to the Drupal 7 Contact Form

I tweaked our Drupal 7 Contact form today.
The goal was to allow users to select the reason for contact and potentially the subject via the URL.
We had this in place for our Drupal 6 site and the code ported over nicely.

The process was similar to the last post: JS Tweak to the Drupal 7 Menu Management Screen.

The JS consists of two parts, get the URL Var and value and update the form.
I use a stand alone function (getURLVars) to get the URL Var. Then I drop it like it’s hot (so to speak) using jQuery.

Sample URL: /contact?cid=4&subject=What%20is%20all%20this%20then.

Sample JS to try in your console:

if (typeof getURLVars !== 'function') { 
  function getURLVars(urlVar) {
    if(!urlVar){
       return false;
    }
    if(typeof (document.location.href.split('?')) === 'undefined') {
       return false; 
    }
    if (typeof (document.location.href.split('?')[1]) === 'undefined') {
       return false;
    }
    varArray = document.location.href.split('?')[1].split('&');
  
    for(var x=0; x<varArray.length; x++)
    {
      var tmp = varArray[x].split('=');
      eval(unescape(tmp[0]) + '="' + unescape(tmp[1]) + '"');
      if(urlVar == tmp[0]){
        urlVar = eval(unescape(tmp[0]) + '="' + unescape(tmp[1]) + '"');
        return urlVar;
      }
    }
    return ;
  }
}
//getURLVars('cid')

cidVal = getURLVars('cid')?getURLVars('cid'):'';
if(cidVal !== ''){
  theArray = jQuery('#edit-cid').children().map(function() {return jQuery(this).val();}).get();
  if (jQuery.inArray(cidVal, theArray) !== -1){
    //alert('yup');
    jQuery('#edit-cid').val(cidVal);
  }else{
    //alert('nope');
  }
}

subjectVal = getURLVars('subject')?getURLVars('subject'):'';
if(subjectVal !== ''){
  jQuery('#edit-subject').val(subjectVal.trim());
}

Full JS w/ Drupal.behaviors code wrapper where needed:

/**
 * JS to update the contact id based on a URL var. Ported from D6. 
 */

if (typeof getURLVars !== 'function') { 
  function getURLVars(urlVar) {
    if(!urlVar){
       return false;
    }
    if(typeof (document.location.href.split('?')) === 'undefined') {
       return false; 
    }
    if (typeof (document.location.href.split('?')[1]) === 'undefined') {
       return false;
    }
    varArray = document.location.href.split('?')[1].split('&');
  
    for(var x=0; x<varArray.length; x++)
    {
      var tmp = varArray[x].split('=');
      eval(unescape(tmp[0]) + '="' + unescape(tmp[1]) + '"');
      if(urlVar == tmp[0]){
        urlVar = eval(unescape(tmp[0]) + '="' + unescape(tmp[1]) + '"');
        return urlVar;
      }
    }
    return ;
  }
}

/** Set Defaults
 * Reason for Contact
 * if url var = cid
 * and 
 *   val is in the list of vars then jQuery('#edit-cid').val(2);
 *
 * Subject
 * Set subject
 */
 
(function () {
  Drupal.behaviors.setCid = {
    //for use on Attachment content edit screens
    attach: function(context) {
      if(typeof context !== 'undefined'){ //run only if there is a context var
        /*
          Check the type of the context var and type. Process if context = [object HTMLDocument]
          Note: when context == [object HTMLDocument] then this is page load. You may wont some script to run only on page load. Some not and some all the time.
        */
        if(typeof context == 'object' && context.toString().indexOf('HTMLDocument')!=-1){
          //Page Load
          cidVal = getURLVars('cid')?getURLVars('cid'):'';
          if(cidVal !== ''){
            theArray = jQuery('#edit-cid').children().map(function() {return jQuery(this).val();}).get();
            if (jQuery.inArray(cidVal, theArray) !== -1){
              //alert('yup');
              jQuery('#edit-cid').val(cidVal);
            }else{
              //alert('nope');
            }
          }
          
          subjectVal = getURLVars('subject')?getURLVars('subject'):'';
          if(subjectVal !== ''){
            jQuery('#edit-subject').val(subjectVal.trim());
          }
        }
      }
    }
  }
})();

Again I implemented this in a block w/ the Code Per Node (cpn) CSS and JS additions.
Here are the specifics:

Block title:
None;
Block description:
JS for Contact Page
Block body:
 
Text format:
Code input (custom type allowing html)
Pages – Only the listed pages:
contact;
CSS:
#block-block-55 {
  display: none;
  visibility: hidden;
}

*Note the specific block ID in that id selector

JS:
As pasted above

JS Tweak to the Drupal 7 Menu Management Screen

I tweaked Drupal Menu management screen today.
Previously after editing a menu item the menu page would reload and you’d be at the top of the menu list, essentially losing your place.

This was a problem for one of our admin users when digging through 390 menu items, so I thought I’d give it a quick fix.

Now when you edit a menu item and land back on the menu page, the JS below will scroll you to that last element and highlight the row.

Here’s a screenshot:

Screen Shot of menu management screen after editing a menu item

Sample JS to try in your console:

function scrollToReferrer(){
  //get the referrer URL
  referrerArr = document.referrer.split('/');
  //check if referrer was menu item edit screen
  if(referrerArr[3] == "admin" && referrerArr[5] == "menu" && (referrerArr[8] == "edit")) {
    referrerArr.pop(); //ditch edit
    //capture mlid - alt referrerArr[6]
    referrerMlid = referrerArr.pop();
    //scroll to mlid
    location.hash = "#edit-mlid"+referrerMlid+"-operations-edit";
    //slide up a "pinch" to keep the element visable (in the case of Admin Menu)    
    var x = pageXOffset, y = pageYOffset;
    scrollTo(x,y-60);
    //remove 'drag-previous' class from tr's on the page
    jQuery('tr').removeClass('drag-previous');
    //set waring class on this tr to highlight it
    jQuery("#edit-mlid"+referrerMlid+"-operations-edit").parents('tr').addClass('drag-previous');
    return true;
  }else{
    return false;
  }
}
scrollToReferrer();

And here’s the code in D7 Drupal.behaviours format as I implemented it:

(function ($) {
  Drupal.behaviors.scrollToReferrer = {
    //for use on admin/structure/menu/manage/main-menu
    attach: function(context) {
      if(typeof context !== 'undefined'){ //run only if there is a context var
        if(typeof context == 'object' && context.toString().indexOf('HTMLDocument')!=-1){ //Page Load
          //get the referrer URL
          referrerArr = document.referrer.split('/');
          //check if referrer was menu item edit screen
          if(referrerArr[3] == "admin" && referrerArr[5] == "menu" && (referrerArr[8] == "edit")) {
            referrerArr.pop(); //ditch edit
            //capture mlid - alt referrerArr[6]
            referrerMlid = referrerArr.pop();
            //scroll to mlid
            location.hash = "#edit-mlid"+referrerMlid+"-operations-edit";
            //slide up a "pinch" to keep the element visable (in the case of Admin Menu)    
            var x = pageXOffset, y = pageYOffset;
            scrollTo(x,y-60);
            //remove 'drag-previous' class from tr's on the page
            jQuery('tr').removeClass('drag-previous');
            //set waring class on this tr to highlight it
            jQuery("#edit-mlid"+referrerMlid+"-operations-edit").parents('tr').addClass('drag-previous');
            return true;
          }else{
            return false;
          }
        }//end if(typeof context == 'object' && context.toString().indexOf('HTMLDocument')!=-1){ //Page Load
      }//end typeof context !== und
    }//end function(context)
  }//end Drupal.behaviors
})(jQuery);

Now, the way to implement this correctly would be a custom module or perhaps adding the js to the themes’ .js file.

I added it via Code Per Node (cpn) to a new block that has user and page restrictions in our admin theme.
Here are the specifics:

Block title:
None;
Block description:
JS Seven Menu Page Scripts
Block body:
 
Text format:
Code input (custom type allowing html)
Pages – Only the listed pages:
admin/structure/menu/manage/main-menu;
CSS:
#block-block-53 {
    display: none;
    visibility: hidden;
    }

*Note the specific block ID in that id selector

JS:
As pasted above

A quick fix out and out.

PHP redirects if the headers have already been sent.

I ran in to a situation with some inherited code today.
The developer wanted to redirect users to a course listing page if they landed on a date/time listing that couldn’t be found.
See this code below; they check for an empty $dateTime var and create a log message before attempting to redirect the user.

if (empty($dateTime)) {
  $logger->err('DateTime with the id of '.$_REQUEST['dt_id'].' cannot be located.');
  Header( 'Location: course_list.php'); // Go back to the course list
  exit;
}

Unfortunately the headers had already been sent to the browser and the Header function call was ignored and the user only saw a partially loaded web page with no body content.

In the past I had implemented a fall-back with a meta and js redirect but I thought that could be improved upon.
Stack Overflow had some nice responses to a previous question and one of them included Redirect function that checked if the headers were already sent: headers_sent().
I liked that. So I added my fall back to that function.

Next I swapped that Header function call with the Redirect function call:

if (empty($dateTime)) {
  $logger->err('DateTime with the id of '.$_REQUEST['dt_id'].' cannot be located.');
  Redirect('course_list.php'); // Go back to the course list
}

Problem solved.

Here’s the function (note: it’s wrapped in a function_exists() call to make sure I don’t declare this twice at any point):

<?php
if(!function_exists('Redirect')){
  function Redirect($url, $code = 302) //from http://stackoverflow.com/questions/768431/how-to-make-a-redirect-in-php
  {
    #echo 'redirecting';
    if (strncmp('cli', PHP_SAPI, 3) !== 0)
    {
      #echo 'cli';
      if (headers_sent() !== true)
      {
        if (strlen(session_id()) > 0) // if using sessions
        {
            session_regenerate_id(true); // avoids session fixation attacks
            session_write_close(); // avoids having sessions lock other requests
        }
        if (strncmp('cgi', PHP_SAPI, 3) === 0)
        {
            header(sprintf('Status: %03u', $code), true, $code);
        }
      header('Location: ' . $url, true, (preg_match('~^30[1237]$~', $code) > 0) ? $code : 302);
      } else { //headers have already been sent. Use Meta refresh with js location.href fallback
      ?>
        <em>This page has moved.</em><br />
        If you are not redirected automatically please follow this link: <a href=\"<?php echo $url; ?>"><?php echo $url; ?></a>.
        <meta http-equiv="Refresh" content="1;
        URL=<?php echo $url; ?>">
        <script language=javascript>
        setTimeout("location.href='<?php echo $url; ?>'",1);
        </script>
        <?php
      }//end if headers_sent

      exit();
    }
  }
}
#usage:
#Redirect('course_list.php');
?>

Protected: Selling a home…

This content is password protected. To view it please enter your password below:

Updating select lists in bulk – w/ jquery, drupal

So I wrote this script 1 year and 1/2 ago about bulk label operations (Drupal 7 CCK – Creating ‘Label Bulk Operations’). Well the updateLabels() function from that code came in handy today.
I needed to update dozens of CCK formats from various defaults to editable. If you’ve used editable fields you know it can come in handy but if you have a lot of fields you’ll be in a carpel tunnel conundrum if you try to change then all.
So I pulled up the updateLabels() function opened the Firebug console and set all the various defaults to editable. And then back again. (more…)

Concatenate fields in a Fillable form

Recently someone referenced an old blog post: use data from one field to populate other fields in an acrobat fill-in forms and asked me how to concatenate multiple field values into one.

So I put together a sample pdf.

(more…)

Drush enable all inactive modules

I’ve been using make files a lot lately and when ever I’m ready to release an updated file I like to verify that all the project versions are up to date.
To leverage Drupal’s update functionality each project needs to be enabled. So I typically generate a new site from the make file and enable all the projects with drush. The key to that is the combination of commands:

drush en `drush pm-list --status="disabled,not installed" --pipe`

Here I’m enabling a list of module; specifically drush’s list of disabled and not installed modules.

drush pm-list --status="disabled,not installed"

That code gets you part of the way there. The addition of this:

--pipe

makes it a script-able list. Then when I surround that with back-ticks and feed it to

drush en `...`

I’m in business. (more…)

Drupal 6 – Contact Override custom module

Just created a quick and dirty custom module: Contact Override.
I posted it on bitbucket: bitbucket.org/_vid/contact_override

 

Nothing too exciting, just an override to the D6 contact form. It appends the senders email into the body of the email.

 

We had noticed that while emails from the Drupal contact form contained the senders email in the reply field that email address was often lost when forwarding the message.

Here’s the readme: (more…)

Reponsive design and the iPad viewport

I had been noticing that the iPad’s viewport in webkit browsers was odd but I couldn’t put my finger on it.

So I opened Safari on the iPad and ran some JS in firebug lite; the result: 768×504 in landscape and 768×928 in portrait mode.
Not what I expected at all.

The code I ran in firebug lite was:

alert(jQuery(window).width() + 'x ' + jQuery(window).height());

(prepend javascript: for use in the URL bar)

Bottom line: the viewport width is 768px in both portrait and landscape mode. That’s a little odd.
(more…)

Working on a file in two git branches at one time

I have a file in two different branches of a project that is so drastically different (hundreds upon hundreds of changes) that merging was becoming unreasonable. I’ll call that file CONVOLUTED.module.
I wanted to go old-school and compare the files side by side. Cutting and pasting changes with regular commits so I could test the incremental changes as I manually merged the files.
I figured there are several options to do this.
(more…)