EGLUG contributed Drupal modules

BidiModule

<?php // -*-php-*-
/*
 * Original module by Alaa Abd El Fatah.
 * Mohammed Sameer: My Modifications Copyright (c) 2004 Mohammed Sameer, 
 *                  under the GNU GPL v2 or later.
 * Mohammed Sameer: 2004 10 30
 *                  * Ported to drupal 4.5 API.
 *
 * 04/04/2005 Ported to drupal 4.6 API by Amr Mostafa
 */

setlocale(LC_ALL"ar_EG.UTF-8");


/**
 * Implementation of hook_help()
 */
function bidi_help($section) {
  
$output "";

  switch (
$section) {
    case 
'admin/modules#description':
      
$output t("Automaticaly sets line direction");
      break;
  }

  return 
$output;
}

/**
 * Implementation of hook_filter_tips()
 */
function bidi_filter_tips($delta$format$long false) {
  if (
$long) {
    return 
t("You may write mixed Arabic and English freely, line direction will be computed automaticaly");
  }
  else {
    return 
t("You may write mixed Arabic and English freely, line direction will be computed automaticaly");
  }
}

/**
 * Implementation of hook_filter()
 */
function bidi_filter($op$delta 0$format = -1$text "") {
  switch (
$op) {
    case 
'list':
      return array(
=> t('BiDi'));
    case 
'description':
      return 
t("Bidi filter");
    case 
"process":
        return 
_bidi_filter_process($text);
    default:
        return 
$text;
  }
}

/**
 * Determine text language (arabic or english).
 * This function needs to be split into a single include file so it can be used on theme and other places.
 *
 * @param $text
 *   The text needs to be.
 * @return
 *   right2left or left2right depending on the detected language.
 */
function _get_dir($text) {
  
$text strtolower($text);
  
$len strlen($text);
  
  
$arabic 0;
  
$english 0;
  
  
// constants
  
$a ord('a');
  
$z ord('z');
  
  
// arabic UTF-8 letters have one of these values in the 1st byte
  
$ar1 0xd8;
  
$ar2 0xd9;
  
  
// this calculates the dominant language in the text, most bidi implementations just use the first letter (which may be more efficient)
  
for ($i 0$i $len; ++$i) {
    
$bin ord($text[$i]);
    
    if (
$bin == $ar1 || $bin == $ar2) {
      ++
$arabic;
      ++
$i;
    }
    else if (
$bin >= $a && $bin <= $z) {
      ++
$english;
    }
  }
  
  
// should this return a bool instead?
  
if ($english || $arabic) {
    if (
$english $arabic)    
      return 
' dir="LTR" ';
    else
      return 
' dir="RTL" ';
  }
  else {
    return 
"";
  }
}

/**
 * Sets HTML text line directions (rtl or ltr) as needed.
 *
 * @param $text
 *   The HTML text needs to be processed.
 *
 * @return
 *   The processed result, with line directions are set using css
 *   classes "righ2left" and "left2right".
 */
function _bidi_filter_process($text) {
  
$output "";
  
  
$len strlen($text);
  
$i 0;
  
  while (
$i $len) {
    
// find next tag
    
$e strpos(substr($text$i), "<");
    if (
$e === FALSE) {
      break;
    }
    
    
$output .= substr($text$i$e);
    
    
$i += $e;
    
    
// find end of next tag
    
$e strcspn(substr($text$i), "/> ");
    
$tag substr($text$i+1$e-1);
    
    
$output .= substr($text$i$e);
    
$i += $e;
    
    switch (
$tag) {
      
// single tag entity
      
case "img":
      case 
"br":
      case 
"hr":
        continue;
        break;
      
      
//nested cases
      
case "ul":
      case 
"ol":
      case 
"dl":
      case 
"div":
        
$e 0;
        
// stack counter
        
$j 1;
        
        
// if $j = 0 then stack is empty
        
while ($j 0) {
          
$e += strpos(substr($text$i+$e) , $tag);
          
          
// yeah I know I'm assuming certain sizes for tags, sue me
          // new nested block
          
if ($text[$i+$e-1] == '<') {
            ++
$j;
          }
          
// end of block
          
else if ($text[$i+$e-1] == '/') {
            --
$j;
          }
          
// skip current tag
          
$e += 2;
        }
        
        
$content substr($text$i+1$e); 
        
$output .= _get_dir(strip_tags($content));
        
$e += strlen($tag);
        
$output.= substr($text$i$e);
        
$i += $e;
        break;
      
      
// block elements these get out attention
      
case "p":
      case 
"blockquote":
      case 
"pre":
      case 
"h1":
      case 
"h2":
      case 
"h3":
      case 
"h4":
      case 
"h5":
      case 
"h6":
      
      
// nested lists are proken better think of a fix
      
case "li":
        
$close_tag "</".$tag.">";
        
// breaks on extra whitespaces, maybe I should use regexps instead
        
$e strpos(substr($text$i), $close_tag);
        
$content substr($text$i+1$e-1);
        
// calculate directions
        
$output.= _get_dir(strip_tags($content));
        
$e += strlen($close_tag);
        
$output.= substr($text$i$e);
        
$i += $e;
        break;
      
      
// non block tags we are not handeling
      
default:
        
$close_tag "</".$tag.">";
        
$e strpos(substr($text$i), $close_tag) + strlen($close_tag);
        
$output.= substr($text$i$e);
        
$i += $e;
      }
  }
  
  
// any missing content
  
$output .= substr($text$i);
  
  return 
$output;
}

?>

Captcha module patch, to support those with textish browsers

This is a patch I made for drupal's captcha module.

Since the captcha can only be read using graphical browsers, I thought of this idea to enable users with textish browsers to read the captcha code too.

It's not secure though. Since the captcha code gets printed in a HTML tag (that's how textish browsers can read it), a script can read the code and bypass your captcha protection. I don't recommend applying the patch if you are going to use captcha in anything other than fighting spam comments :).

captcha.module.patch



--- captcha.module.orig	2005-01-19 17:00:08.000000000 +0200
+++ captcha.module	2005-03-05 00:12:03.921875000 +0200
@@ -83,9 +83,10 @@
   if (_captcha_istrue("captcha_user_register") && !$newuser->uid && !$user->uid)
   switch ($type) {
     case t("register"):
-    // Add two items to the resigtration form.
+    $string = _captcha_code();
     
-    $output .= form_item("", '<img src="/'.url('captcha/image/'.time()).'" alt="Captcha Image: you will need to recognize the text in it."/>');
+    // Add two items to the resigtration form.
+    $output .= form_item("", '<img src="/'.url('captcha/image/'.time()).'" alt="Captcha: '.$string.'"/>');
     $output .= form_textfield(t('Word'), 'captchaword', NULL, 15, 15, 'Please type in the letters/numbers that are shown in the image above.', NULL, TRUE);
 
     return array(array('title' => t('Verify Registration'), 'data'=>$output));
@@ -137,7 +138,8 @@
       
     case 'form':
       if (sess_read('captcha_comment_correct')!='ok') {
-        $output .= form_item("", '<img src="/'.url('captcha/image/'.time()).'" alt="Captcha Image: you will need to recognize the text in it."/>');
+	$string = _captcha_code();
+        $output .= form_item("", '<img src="/'.url('captcha/image/'.time()).'" alt="Captcha: '.$string.'"/>');
         $output .= form_textfield(t('Word'), 'captchaword', NULL, 15, 15, 'Please type in the letters/numbers that are shown in the image above.', NULL, TRUE);
         return form_group(t('Verify comment authorship'), $output);
       } else return NULL;
@@ -206,7 +208,7 @@
     
     header('Content-type: image/png');
 
-    $string = _captcha_code();
+    $string = sess_read('captcha');
     
     //set up image, the first number is the width and the second is the height
     $im = imagecreatetruecolor(120, 20);

DiffModule

I'm stil having some problems with the wiki formatting breaking, But i think I'll dump the diff as it is without applying filters.
It's in the unified diff format and not colored! It's inline diff, The underlined lines are new, The striked ones are deleted.
I gave the admins and mods the permission to access it until I fix all of the remaining things.
Todo:
* diff to previous/next/beginning.
* Admin interface to choose unified or inline diff.
* Find a way to highlight.
* Try to write the diff code instead of using pear. <?php
// $Id: diff.module,v 1.24 2008/03/05 21:03:17 weitzman Exp $

/**
 * Number of items on one page of the revision list.
 */
define('REVISION_LIST_SIZE'50);

/**
 * Implementation of hook_help().
 */
function diff_help($section) {
  switch (
$section) {
    case 
'admin/help#diff':
      
$output '<p>'t('The diff module overwrites the normal revisions view. The revisions table is enhanced with a possibility to view the difference between two node revisions. Users with the %view_revisions permission will also be able to view the changes between any two selected revisions. You may disable this for individual content types on the content type configuration page. This module also provides a nifty %preview_changes button while editing a post.', array('%preview_changes' => t('Preview changes'), '%view_revisions' => t('view revisions'))).'</p>';
      return 
$output;
  }
}

/**
 * Implementation of hook_requirements().
 * Checks if the diff modules is loaded after the node module in the hook ordering.
 */
function diff_requirements($phase) {
  
// Don't check when installing
  
if ($phase == 'install') {
    return;
  }
  
$modules array_keys(module_list());
  if (
array_search('diff'$modules) <= array_search('node'$modules)) {
    
diff_autoadjust();
  }
}

/**
 * Implementation of hook_menu()
 * The menu path 'node/$nid/revisions' is overriden with 'diff_diffs'.
 */
function diff_menu($may_cache) {
  
$items = array();
  if (!
$may_cache) {
    if (
arg(0) == 'node' && is_numeric(arg(1))) {
      
$node node_load(arg(1));
      if (
$node->nid) {
        
$revisions_access = (user_access('view revisions') || user_access('administer nodes')) && 
                            
node_access('view'$node) && 
                            
db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d'arg(1))) > 1;
        
$items[] = array(
          
'path' => 'node/'arg(1) .'/revisions''title' => t('Revisions'),
          
'callback' => 'diff_diffs',
          
'access' => $revisions_access,
          
'weight' => 4,
          
'type' => MENU_LOCAL_TASK,
        );
      }
    }
  }
  return 
$items;
}

/**
 * Adjust the module weights for diff to load after node module.
 */
function diff_autoadjust() {
  
$modules array_keys(module_list());
  if (
array_search('diff'$modules) <= array_search('node'$modules)) {
    
module_load_install('diff');
    
diff_set_weight();
  }
}

/**
 * Menu callback for diff related activities.
 */
function diff_diffs() {
  if (
is_numeric(arg(1)) && arg(2) == 'revisions') {
    
$op arg(3) ? arg(3) : 'overview';
    switch (
$op) {
      case 
'overview':
        
$node node_load(arg(1));
        if ((
user_access('view revisions') || user_access('administer nodes')) && node_access('view'$node)) {
          return 
diff_diffs_overview($node);
        }
        
drupal_access_denied();
        return;

      case 
'view':
        if (
is_numeric(arg(4)) && is_numeric(arg(5))) {
          
$node node_load(arg(1));
          if (
$node->nid) {
            if ((
user_access('view revisions') || user_access('administer nodes')) && node_access('view'$node)) {
              
drupal_set_title(t('Diff for %title', array('%title' => $node->title)));
              return 
diff_diffs_show($nodearg(4), arg(5));
            }
            
drupal_access_denied();
            return;
          }
        }
        break;

      default:
        
// A view, revert or delete operation from the orignial node module,
        // so call the original node module to handle this.
        
return node_revisions();
        break;
    }
  }
  
drupal_not_found();
}

/**
 * Generate an overview table of older revisions of a node and provide 
 * an input form to select two revisions for a comparison.
 */
function diff_diffs_overview(&$node) {
  
$output '';

  
drupal_set_title(t('Revisions for %title', array('%title' => $node->title)));
  
$output .= drupal_get_form('diff_node_revisions'$node);

  return 
$output;
}

/**
 * Input form to select two revisions.
 *
 * @param $node
 *   Node whose revisions are displayed for selection.
 */
function diff_node_revisions(&$node) {
  global 
$form_values;
  
$form = array();

  
$form['nid'] = array(
    
'#type' => 'hidden',
    
'#value' => $node->nid,
  );

  
$revision_list node_revision_list($node);

  if (
count($revision_list) > REVISION_LIST_SIZE) {
    
// If the list of revisions is longer than the number shown on one page split the array.
    
$page = isset($_GET['page']) ? $_GET['page'] : '0';
    
$revision_chunks array_chunk(node_revision_list($node), REVISION_LIST_SIZE);
    
$revisions $revision_chunks[$page];
    
// Set up global pager variables as would 'pager_query' do.
    // These variables are then used in the theme('pager') call later.
    
global $pager_page_array$pager_total$pager_total_items;
    
$pager_total_items[0] = count($revision_list);
    
$pager_total[0] = ceil(count($revision_list) / REVISION_LIST_SIZE);
    
$pager_page_array[0] = max(0min($page, ((int)$pager_total[0]) - 1));
  } else {
    
$revisions $revision_list;
  }

  
$revert_permission FALSE;
  if ((
user_access('revert revisions') || user_access('administer nodes')) && node_access('update'$node)) {
    
$revert_permission TRUE;
  }
  
$delete_permission FALSE;
  if (
user_access('administer nodes')) {
    
$delete_permission TRUE;
  }

  foreach (
$revisions as $revision) {
    
$operations = array();
    
$revision_ids[$revision->vid] = '';

    if (
$revision->current_vid 0) {
      
$form['info'][$revision->vid] = array(
        
'#value' => t('!date by !username', array(
          
'!date' => l(format_date($revision->timestamp'small'), "node/$node->nid"), 
          
'!username' => theme('username'$revision)))
        . ((
$revision->log != '') ? '<p class="revision-log">'filter_xss($revision->log) .'</p>' ''),
      );
    }
    else {
      
$form['info'][$revision->vid] = array(
        
'#value' => t('!date by !username', array(
          
'!date' => l(format_date($revision->timestamp'small'), "node/$node->nid/revisions/$revision->vid/view"), 
          
'!username' => theme('username'$revision)))
        . ((
$revision->log != '') ? '<p class="revision-log">'filter_xss($revision->log) .'</p>' '')
      );
      if (
$revert_permission) {
        
$operations[] = array('#value' => l(t('revert'), "node/$node->nid/revisions/$revision->vid/revert"));
      }
      if (
$delete_permission) {
        
$operations[] = array('#value' => l(t('delete'), "node/$node->nid/revisions/$revision->vid/delete"));
      }
      
// Set a dummy, even if the user has no permission for the other
      // operations, so that we can check if the operations array is
      // empty to know if the row denotes the current revision.
      
$operations[] = array();
    }
    
$form['operations'][$revision->vid] = $operations;

  }
  
$new_vid key($revision_ids);
  
next($revision_ids);
  
$old_vid key($revision_ids);
  
$form['diff']['old'] = array(
    
'#type' => 'radios'
    
'#options' => $revision_ids,
    
'#default_value' => $old_vid
  
);
  
$form['diff']['new'] = array(
    
'#type' => 'radios'
    
'#options' => $revision_ids,
    
'#default_value' => $new_vid
  
);
  
$form['submit'] = array('#type' => 'submit''#value' => t('Show diff'));  

  if (
count($revision_list) > REVISION_LIST_SIZE) {
    
$form['#suffix'] = theme('pager'NULLREVISION_LIST_SIZE0);
  }

  return 
$form;
}

/**
 * Theme function to display the revisions formular with means to select
 * two revisions.
 */
function theme_diff_node_revisions($form) {
  
// Overview table:
  
$header = array(
    
t('Revision'),
    array(
'data' => drupal_render($form['submit']), 'colspan' => 2),
    array(
'data' => t('Operations'), 'colspan' => 2)
  );
  if (isset(
$form['info']) && is_array($form['info'])) {
    foreach (
element_children($form['info']) as $key) {
      
$row = array();
      if (isset(
$form['operations'][$key][0])) {
        
// Note: even if the commands for revert and delete are not permitted,
        // the array is not empty since we set a dummy in this case.
        
$row[] = drupal_render($form['info'][$key]);
        
$row[] = drupal_render($form['diff']['old'][$key]);
        
$row[] = drupal_render($form['diff']['new'][$key]);
        
$row[] = drupal_render($form['operations'][$key][0]);
        
$row[] = drupal_render($form['operations'][$key][1]);
        
$rows[] = $row;
      }
      else {
        
// its the current revision (no commands to revert or delete)
        
$row[] = array('data' => drupal_render($form['info'][$key]), 'class' => 'revision-current');
        
$row[] = array('data' => drupal_render($form['diff']['old'][$key]), 'class' => 'revision-current');
        
$row[] = array('data' => drupal_render($form['diff']['new'][$key]), 'class' => 'revision-current');
        
$row[] = array('data' => theme('placeholder't('current revision')), 'class' => 'revision-current''colspan' => '2');
        
$rows[] = array(
          
'data' => $row,
          
'class' => 'error',
        );
      }
    }
  }
  
$output .= theme('table'$header$rows);
  
$output .= drupal_render($form);
  return 
$output;
}

/**
 * Submit code for input form to select two revisions.
 */
function diff_node_revisions_submit($form_id$form_values) {
  
// the ids are ordered so the old revision is always on the left
  
$old_vid min($form_values['old'], $form_values['new']);
  
$new_vid max($form_values['old'], $form_values['new']);
  return 
'node/'.$form_values['nid'].'/revisions/view/'.$old_vid.'/'.$new_vid;
}

/**
 * Validation for input form to select two revisions.
 */
function diff_node_revisions_validate($form_id$form_values) {
  
$old_vid $form_values['old'];
  
$new_vid $form_values['new'];
  if (
$old_vid==$new_vid || !$old_vid || !$new_vid) {
    
form_set_error('diff't('Select different revisions to compare.'));
  }
}

/**
 * Create output string for a comparison of 'node' between
 * versions 'old_vid' and 'new_vid'.
 *
 * @param $node
 *   Node on which to perform comparison
 * @param $old_vid
 *   Version ID of the old revision.
 * @param $new_vid
 *   Version ID of the new revision.
 */
function diff_diffs_show(&$node$old_vid$new_vid) {

  
$lame_revisions node_revision_list($node);
  foreach (
$lame_revisions as $revision) {
    
$node_revisions[$revision->vid] = $revision;
  }

  
$old_node node_load($node->nid$old_vid);
  
$new_node node_load($node->nid$new_vid);

  
// Generate table header (date, username, logmessage).
  
$old_header t('!date by !username', array(
    
'!date' => l(format_date($old_node->revision_timestamp), "node/$node->nid/revisions/$old_node->vid/view"), 
    
'!username' => theme('username'$node_revisions[$old_vid]),
  ));
  
$new_header t('!date by !username', array(
    
'!date' => l(format_date($new_node->revision_timestamp), "node/$node->nid/revisions/$new_node->vid/view"), 
    
'!username' => theme('username'$node_revisions[$new_vid]),
  ));

  
$old_log $old_node->log != '' '<p class="revision-log">'filter_xss($old_node->log) .'</p>' '';
  
$new_log $new_node->log != '' '<p class="revision-log">'filter_xss($new_node->log) .'</p>' '';

  
// Generate previous diff/next diff links.
  
$next_vid _diff_get_next_vid($node_revisions$new_vid);
  if (
$next_vid) {
    
$next_link l(t('next diff >'), 'node/'$node->nid .'/revisions/view/'$new_vid .'/'$next_vid);
  }
  else {
    
$next_link '';
  }
  
$prev_vid _diff_get_previous_vid($node_revisions$old_vid);
  if (
$prev_vid) {
    
$prev_link l(t('< previous diff'), 'node/'$node->nid .'/revisions/view/'$prev_vid .'/'$old_vid);
  }
  else {
    
$prev_link '';
  }

  
$cols = array(
    array(
      array(
        
'class' => 'diff-marker',
      ),
      array(
        
'class' => 'diff-content',
      ),
      array(
        
'class' => 'diff-marker',
      ),
      array(
        
'class' => 'diff-content',
      ),
    ),
  );
  
$header = array(
    array(
      
'data' => $old_header,
      
'colspan' => 2
    
),
    array(
      
'data' => $new_header,
      
'colspan' => 2
    
)
  );
  
$rows = array();
  if (
$old_log || $new_log) {
    
$rows[] = array(
      array(
        
'data' => $old_log,
        
'colspan' => 2
      
),
      array(
        
'data' => $new_log,
        
'colspan' => 2
      
)
    );
  }
  
$rows[] = array(
    array(
      
'data' => $prev_link,
      
'class' => 'diff-prevlink',
      
'colspan' => 2
    
),
    array(
      
'data' => $next_link,
      
'class' => 'diff-nextlink',
      
'colspan' => 2
    
)
  );
  
$rows array_merge($rows_diff_body_rows($old_node$new_node));
  
$output theme('diff_table'$header$rows, array('class' => 'diff'), NULL$cols);

  if (
$node->vid == $new_vid) {
    
$output .= '<div class="diff-section-title">'t('Current revision:') .'</div>';
  }
  else {
    
$output .= '<div class="diff-section-title">'t('Revision of !new_date:', array('!new_date' => format_date($new_node->revision_timestamp))) .'</div>';
  }
  
// Don't include node links (final argument) when viewing the diff.
  
$output .= node_view($new_nodeFALSEFALSEFALSE);
  return 
$output;
}

/**
 * Creates an array of rows which represent a diff between $old_node and $new_node.
 * The rows can be used via theme('diff_table') to be displayed.
 *
 * @param $old_node
 *   Node for comparison which will be displayed on the left side.
 * @param $new_node
 *   Node for comparison which will be displayed on the right side.
 */
function _diff_body_rows(&$old_node, &$new_node) {
  
drupal_add_css(drupal_get_path('module''diff') .'/diff.css''module''all'FALSE);
  include_once(
'DiffEngine.php');
  include_once(
'node.inc');
  if (
module_exists('taxonomy')) {
    include_once(
'taxonomy.inc');
  }
  if (
module_exists('upload')) {
    include_once(
'upload.inc');
  }
  if (
module_exists('content')) {
    include_once(
'cck.inc');
  }

  
$rows = array();
  
$any_visible_change false;
  
$node_diffs module_invoke_all('diff'$old_node$new_node);
  
  
// We start off assuming all form elements are in the correct order.
  
$node_diffs['#sorted'] = TRUE;
  
  
// Recurse through all child elements.
  
$count 0;
  foreach(
element_children($node_diffs) as $key) {
    
// Assign a decimal placeholder weight to preserve original array order.
    
if (!isset($node_diffs[$key]['#weight'])) {
      
$node_diffs[$key]['#weight'] = $count/1000;
    }
    else {
      
// If one of the child elements has a weight then we will need to sort
      // later.
      
unset($node_diffs['#sorted']);
    }
    
$count++;
  }
  
  
// One of the children has a #weight.
  
if (!isset($node_diffs['#sorted'])) {
    
uasort($node_diffs"_element_sort");
  }
  
  foreach(
$node_diffs as $node_diff) {
    
$diff = new Diff($node_diff['#old'], $node_diff['#new']);
    
$formatter = new DrupalDiffFormatter();
    if (isset(
$node_diff['#format'])) {
      
$formatter->show_header $node_diff['#format']['show_header'];
    }
    
$diff_rows $formatter->format($diff);
    if (
count($diff_rows)) {
      
$rows[] = array(
        array(
          
'data' => t('Changes to %name', array('%name' => $node_diff['#name'])),
          
'class' => 'diff-section-title',
          
'colspan' => 4
        
)
      );
      
$rows array_merge($rows$diff_rows);
      
$any_visible_change true;
    }
  }
  if (!
$any_visible_change) {
    
$rows[] = array(
      array(
        
'data' => t(