/[drupal]/contributions/modules/carbon/carbon.module
ViewVC logotype

Diff of /contributions/modules/carbon/carbon.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.20 by johnackers, Tue Jan 6 22:19:26 2009 UTC revision 1.21 by johnackers, Sat Jan 10 15:46:14 2009 UTC
# Line 1  Line 1 
1  <?php  <?php
2  /**  /**
3   * $Id: carbon.module,v 1.19 2008/07/02 13:38:03 johnackers Exp $   * $Id: carbon.module,v 1.20 2009/01/06 22:19:26 johnackers Exp $
4   *   *
5   * Drupal carbon module   * Drupal carbon module
6   *   *
# Line 254  function carbon_access($op, $node) { Line 254  function carbon_access($op, $node) {
254    return FALSE ;    return FALSE ;
255  }  }
256    
   
   
   
 /**  
  * Implementation of hook_user().  
  * Append this information to the user's profile.  
  *  
  * Note that this hook is called on user creation as well as view.  
  */  
   
 function carbon_user($op, &$edit, &$target_user) {  
   global $user ;  
   
   if ($op != 'view' || empty($target_user))  
     return ;  
   
   $map = carbon_report_base($target_user->uid, null, array());    // find out which accounts of this user can be presented  
   $output = '' ;   $org = null ;  
   if (count($map) > 0)  
   {  
     $numstamps = 0 ;  
     foreach ($map as $nid => $account)  
     {  
       $output .=  l(t("View the carbon account '!name'", array("!name" => $account->title)),"node/$account->nid", array("title" => t("This user has made his/her profile public to you")));  
       $output .= "<br/>" ;  
       $org = $account->organization ;  
       $numstamps += $account->numstamps ;  
     }  
     if (!empty($org))  
     {  
       $output .= l(t("View all of !name's carbon accounts", array("!name" => $org)), "carbon/account/$org", array("title" => t("Accounts of all users in the same group")));  
       $output .= "<br/>" ;  
     }  
     if ($numstamps > 0)  // will be zero if no stamps attached to any footprint - not good.  
     {  
       $output .=  l(t("View !name's carbon stamps", array("!name" => $target_user->name)),"carbon/stamp/$target_user->uid", array("title" => t("All the carbon stamps entered by this user attached or unattached to any carbon account")));  
       $output .= "<br/>" ;  
     }  
   }  
   else  
   {  
     $output .=  l(t("Create a carbon account for !name", array("!name" => $target_user->name)), "node/add/carbon_account", array("title" => t("The account will be owned and associated with this user")));  
     $output .= "<br/>" ;  
   }  
   if ($user->uid != $target_user->uid)  
   {  
 //    $output .= l(t("Share my account with !username", array("!username" => $target_user->name)), "carbon/share/$target_user->uid", array("title" => t("Add this user to the list of people who can see your account")));  
 //    $output .= "<br/>" ;   // functionality not yet working, so disabled.  
   }  
   $items = array();  
   
   if (strlen($output) > 0)  
     $items["carbon"] = array(  
       "title" => t("Carbon Account"),  
       "value" => $output,  
       "class" => "member");  
   
   return array(t("Carbon") => $items);  
 }  
   
257    
258  /**  /**
259   * menu hook   * menu hook
260   * @return array of menu items   * @return array of menu items
261   */   */
262  function carbon_menu($may_cache)  function carbon_menu()
263  {  {
264    global $user ;    global $user ;
265          $items = array();          $items = array();
266    
267      $items['carbon'] = array(
268        'title' => 'Carbon Account',  // does not work
269        'description' => 'Carbon help',
270    
271        'access callback' => 'user_access',
272        'access arguments' => array('access accounts'),
273    
274        'page callback' => 'carbon_help',
275        'page arguments' => array("carbon#help")
276      );
277    
278    
279      $items['carbon/%'] = array(
280        'title' => 'Carbon Account',
281        'description' => 'A consolidated view of a single account',
282    
283        'access arguments' => array('administer comments'),
284    
285        'file path' => CARBON_PATH,
286        'file' => 'carbon_view.inc',
287    
288        'page callback' => 'carbon_view_consolidated',
289        'page arguments' => array(1),
290        'type' => MENU_CALLBACK
291      );
292    
293      $items['carbon/%/view'] = array(
294        'title' => 'Overview',
295    
296        'page arguments' => array(1),
297        'weight' => -10,
298        'type' => MENU_DEFAULT_LOCAL_TASK
299      );
300    
301      $items["carbon/%/meter"] = array(
302        'title' => 'Meters',
303    
304        'file path' => CARBON_PATH,
305        'file' => 'carbon_view.inc',
306    
307        'access callback' => 'user_access',
308        'access arguments' => array('access accounts'),
309    
310          if ($may_cache) {      'page callback' => 'carbon_view_stamp',
311        'page arguments' => array(1,2),
312        'type' => MENU_LOCAL_TASK
313      );
314    
315      $items[] = array("path" => "carbon",    $items["carbon/%/travel"] = array(
316        "title" => t("Carbon"),      'title' => 'Travel and other',
317        "access" => true,  
318        "callback" => "carbon_entry",      'file path' => CARBON_PATH,
319        "type" => MENU_CALLBACK);      'file' => 'carbon_view.inc',
320    
321      $items[] = array("path" => "carbon/view",      'access callback' => 'user_access',
322        "title" => t("Consoldated carbon account view"),      'access arguments' => array('access accounts'),
323        "access" => user_access("access accounts"),  
324        "type" => MENU_SUGGESTED_ITEM);      'page callback' => 'carbon_view_stamp',
325        'page arguments' => array(1,2),
326      $items[] = array("path" => "carbon/user",      'weight' => 5,
327        "title" => t("Carbon accounts"),      'type' => MENU_LOCAL_TASK
328        "access" => user_access("access accounts"),    );
329        "type" => MENU_SUGGESTED_ITEM);  
330      $items["carbon/%/account"] = array(
331        'title' => 'Carbon footprint 2',
332        'description' => 'footprint over a specific period in GET parameters',
333    
334        'access callback' => 'user_access',
335        'access arguments' => array('access accounts'),
336    //    'access callback' => 'node_access',
337      //  'access arguments' => array('view', 2),
338    
339        'file path' => CARBON_PATH,
340        'page callback' => 'carbon_view_footprint',
341        'page arguments' => array(1),
342        'type' => MENU_CALLBACK
343      );
344    
345    
346      // add another tab to the user's profile
347    
348      $items["user/%/carbon"] = array(
349        'title' => 'Carbon',
350        'description' => 'Carbon accounts',
351        'access callback' => 'user_access',
352        'access arguments' => array('access accounts'),
353    
354        'page callback' => 'carbon_report_page',
355        'page arguments' => array(1, null),     // $uid, $group-name
356        'weight' => 10,
357        'type' => MENU_LOCAL_TASK
358      );
359    
360    
361      $items["carbon/report"] = array(
362        'title' => "Carbon account summary",
363        'description' => 'summary of accounts, parameters specified in GET parameters',
364        'access callback' => 'user_access',
365        'access arguments' => array('access accounts'),
366    
367        'page callback' => carbon_report_page,
368        'page arguments' => array(null, 2), // $uid, $group-name?
369        'type' => MENU_CALLBACK
370      );
371    
372      $items[] = array("path" => "carbon/account",  
373        "title" => t("Carbon accounts"),    $items["carbon/stamp"] = array(
374        "access" => user_access("access accounts"),      'title' => 'Carbon stamps owned by specified user',
       "type" => MENU_SUGGESTED_ITEM);  
   
     $items[] = array("path" => "carbon/source",  
       "title" => t("Carbon sources"),  
       "access" => TRUE,  
       "type" => MENU_SUGGESTED_ITEM);  
   
     $items[] = array("path" => "carbon/transfer",  
       "title" => t("Carbon transfers"),  
       "access" => TRUE,  
       "type" => MENU_SUGGESTED_ITEM);  
375    
376  /*      'access callback' => 'user_access',
377      if (arg(0) == "node" && is_numeric(arg(1))) {      'access arguments' => array('access accounts'),
       $node = node_load(arg(1));  
       if ($node->type == "carbon_account")  
       {  
         $items[] = array("path" => "node/". arg(1) ."/sheet", "title" => t("sheet"),  
           "callback" => "carbon_entry",  
           "access" => carbon_access("update", $node),  
           "weight" => 1,  
           "type" => MENU_LOCAL_TASK);  
       }  
     }  
 */  
378    
379      $items[] = array("path" => "admin/settings/carbon",      'page callback' => 'carbon_stamp_page',
380        "title" => t("Carbon"),      'page arguments' => array(2),
       "description" => t("Menu parent"),  
       "callback" => "carbon_admin_entry",  
       "access" => user_access("administer carbon nodes"),  
       "type" => MENU_NORMAL_ITEM);  
   
     $items[] = array("path" => "admin/settings/carbon/settings",  
       "title" => t("Settings"),  
       "description" => t("change options presented to users"),  
       "callback" => "drupal_get_form",  
       "callback arguments" => array("carbon_admin_settings"),  
       "access" => user_access("administer carbon nodes"),  
       "type" => MENU_NORMAL_ITEM);  
   
                 $items[] = array("path" => "admin/settings/carbon/source",  
                         "title" => t("Sources"),  
       "access" => user_access("administer carbon nodes"),  
       "description" => t("list/edit all sources of carbon emissions"),  
                         "type" => MENU_NORMAL_ITEM);  
   
   
     $items[] = array("path" => "admin/settings/carbon/source/install",  
       "title" => t("Install default carbon sources"),  
       "callback" => "drupal_get_form",  
       "callback arguments" => array("carbon_source_install"),  
       "access" => user_access("administer carbon nodes"),  
       "type" => MENU_CALLBACK);  
   
   
                 $items[] = array("path" => "admin/settings/carbon/account",  
                         "title" => t("Accounts"),  
       "access" => user_access("administer accounts"),  
       "description" => t("list/edit all accounts"),  
                         "type" => MENU_NORMAL_ITEM);  
   
                 $items[] = array("path" => "admin/settings/carbon/stamp",  
                         "title" => t("Stamps"),  
                         "access" => user_access("administer accounts"),  
       "description" => t("list/edit all carbon stamps"),  
                         "type" => MENU_NORMAL_ITEM);  
381    
382        'type' => MENU_CALLBACK
383      );
384    
385    
386      $items['carbon/share'] = array(
387        'title' => 'Carbon Account sharing',
388        'description' => 'adjust how i want to share?',
389    
390        'access callback' => 'user_access',
391        'access arguments' => array('access accounts'),
392    
393        'page callback' => 'carbon_view_consolidated',
394        'page arguments' => array($user->uid),
395    
396        'type' => MENU_CALLBACK
397      );
398    
399      // crag functionality
400    
401      $items['carbon/transfer'] = array(
402        'title' => 'Carbon trades',
403        'description' => 'movement between account',
404    
405        'access callback' => 'user_access',
406        'access arguments' => array('access accounts'),
407    
408        'page callback' => 'carbon_transfer_page',
409        'page arguments' => array(2),
410    
411        'type' => MENU_NORMAL_ITEM
412      );
413    
414      $items[] = array("path" => "admin/settings/carbon/tools",  
415        "title" => t("Tools"),    $items["carbon/edit/%"] = array(
416        "access" => user_access("administer carbon nodes"),      'title' => 'Carbon in situ edit',
417        "type" => MENU_NORMAL_ITEM);  
418        'access callback' => 'node_access',
419                  return $items;      'access arguments' => array('edit', 2),
420    }  
421        'page callback' => 'carbon_edit_node',
422        'page arguments' => array(2),
423    
424        'type' => MENU_NORMAL_ITEM
425      );
426    
427    
428      // admin type functions which are not put into the
429      // drupal menu structure. Perhaps they should
430    
431      $items["carbon/source"] = array(
432        'title' => "Carbon sources",
433        'description' => "list/edit all sources of carbon emissions",
434        'access callback' => 'carbon_access',
435        'access arguments' => array('view', 1),
436    
437        'page callback' => 'carbon_source_page',
438        'page arguments' => array("view", "carbon_source"),
439    
440        'type' => MENU_NORMAL_ITEM
441      );
442    
443    
444      $items["carbon/source/install"] = array(
445        'title' => "Install default carbon sources",
446    
447        'access callback' => "user_access",
448        'access arguments' => array("administer carbon nodes"),
449    
450        'page callback' => "carbon_source_install",
451        'page arguments' => array(),
452    
453        'type' => MENU_CALLBACK
454      );
455    
456    
457      $items["carbon/admin"] = array(
458        "title" => "Carbon Admin",
459        "description" => "Carbon menu parent",
460    
461        'access callback' => "user_access",
462        'access arguments' => array("administer carbon nodes"),
463    
464        'page callback' => "carbon_admin_entry",
465        'page arguments' => array(),
466        'type' => MENU_NORMAL_ITEM
467      );
468    
469    
470      $items["admin/settings/carbon"] = array(
471        'title' => "Carbon",
472        'description' => "Carbon module settings",
473    
474        'access callback' => "user_access",
475        'access arguments' => array("administer carbon nodes"),
476    
477        'page callback' => "drupal_get_form",
478        'page arguments' => array("carbon_admin_settings"),
479        'type' => MENU_NORMAL_ITEM
480      );
481            return $items;
482  }  }
483    
484    
# Line 434  function carbon_menu($may_cache) Line 492  function carbon_menu($may_cache)
492    
493  function carbon_block($op='list', $delta=0, $edit = array()) {  function carbon_block($op='list', $delta=0, $edit = array()) {
494          global $user ;          global $user ;
495    
496      variable_set('menu_rebuild_needed', true);  // for debugging
497    
498          // listing of blocks, such as on the admin/block page          // listing of blocks, such as on the admin/block page
499          if ($op == "list") {          if ($op == "list") {
500                  $block[0]["info"] = t(" Carbon accounts");                  $block[0]["info"] = t(" Carbon accounts");
501                  return $block;                  return $block;
502    
503          }          }
504    else if ($op == "view") {    else if ($op == "view")
505      {
506                  // you need type and uid in order to check protection          $block_size = variable_get("carbon_account_block_items",5);
507      // the ordering should be done by date rather than nid          $accountMap = new CarbonAccountMap();
508      // but it would take longer.                  $accountMap->init()->sortByRevisionDate();
509                  $sql = "SELECT  f.nid, title, protection, n.type, n.uid " .  
510                                  "FROM {carbon_account} f " .                  foreach ($accountMap->account_map as $account) {
511          "INNER JOIN {node} n ON f.nid = n.nid " .        if ($block_size-- <= 0 )  break ;
512          "ORDER BY n.nid DESC" ;                          $entry = carbon_access('view', $account) ?
513                                    l($account->title, 'carbon/'. $account->nid) : carbon_anonomize($account->title) ;
     $queryResult = db_query_range($sql, 0, variable_get("carbon_account_block_items", 10));  
   
                 while ($node = db_fetch_object($queryResult)) {  
                         $entry = carbon_access('view', $node) ?  
                                 l($node->title, 'node/'. $node->nid) : carbon_anonomize($node->title) ;  
514                          $block_content .= $entry . '<br />';                          $block_content .= $entry . '<br />';
515                  }                  }
516    
# Line 464  function carbon_block($op='list', $delta Line 520  function carbon_block($op='list', $delta
520                          return;                          return;
521                  }                  }
522                  // set up the block                  // set up the block
523                  $block["subject"] = "New carbon accounts";                  $block["subject"] = "Updated carbon accounts";
524                  $block["content"] = $block_content;                  $block["content"] = $block_content;
525                  return $block;                  return $block;
526          }          }
# Line 482  function carbon_block($op='list', $delta Line 538  function carbon_block($op='list', $delta
538          }          }
539  }  }
540    
541    /**
542     * This function hides most of an account name so users need not reveal their
543     * fullname but enough of it that their friends recognise the name.
544     *
545     * @param $name
546     * @return name which has been hideen
547     */
548    
549  function carbon_anonomize($name)  function carbon_anonomize($name)
550  {  {
551    return substr($name, 0, 5)."..." ;    return substr($name, 0, 5)."..." ;
552  }  }
553    
554    /*
555     * implementation of the (rather annoying) hook_theme
556     * These declarations are required for drupal 6
557     */
558    
559    function carbon_theme()
560    {
561      return array(
562        'carbon_source_table_view' => array(
563          'arguments' => array('node' => NULL)),
564        'carbon_account_table_view' => array(
565          'arguments' => array('node' => NULL)),
566        'carbon_stamp_view' => array(
567          'arguments' => array('node' => NULL))
568      );
569    }
570    
571    
572  /**  /**
573   * create a set of links that can be appended to a table of stamps,   * create a set of links that can be appended to a table of stamps,
574   * accounts or sources.  Drupal 5.0 API version   * accounts or sources.
575   *   *
576   * Note that $node->type, $node->organization, $node->uid, $node->name   * Note that $node->type, $node->organization, $node->uid, $node->name
577   * and other properties must be set for links to be created correctly.   * and other properties must be set for links to be created correctly.
# Line 499  function carbon_anonomize($name) Line 579  function carbon_anonomize($name)
579    
580  function carbon_link($type, $node = 0, $main = 0)  function carbon_link($type, $node = 0, $main = 0)
581  {  {
582    
583    $links = array();    $links = array();
584    
585    if ($type != "node")    if ($type != "node")
586      return $links ;      return $links ;
587    
# Line 544  function carbon_link($type, $node = 0, $ Line 625  function carbon_link($type, $node = 0, $
625  }  }
626    
627    
 function carbon_entry($a = NULL, $b = NULL, $c = NULL)  
 {  
         global $user ;  
   
   switch ($a) {  
   
   case "user" :  
     return carbon_report_page(isset($b) ? $b : $user->uid, '');  
   
   case "account" :  
     if (isset($b))  
       if ($b == "my")  
         return carbon_report_page($user->uid, '');  // is this still needed?  
       else  
         if (is_numeric($b))  
           return carbon_view_year($b, '');   // need date etc  
   
     return carbon_report_page(0, $b);  // organization  
   
   
   case "view" :  
     if (is_numeric($b))  
       return carbon_view_consolidated($b, '');      // account number  
     else  
       return(t("No account id specified"));  
   
   
   case "edit" :               // generic handler for insitu editing  
         return carbon_edit_node($b);  
   
   
 //  case "sheet" :  
   //  return carbon_account_edit($b);  
   
   case "stamp" :  
     if (isset($b))  
       if ($b == "my")  
         return carbon_stamp_page($user->uid);  
       else  
         if (is_numeric($b))  
           return carbon_stamp_page($b);  
   
     return carbon_stamp_page(0);  
   
   
   case "meter" :  
     if (isset($b))  
       if ($b == "edit")  
         return carbon_meter_table($c, null);  
       else  
         return carbon_meter_table($b, null);  
   
         return carbon_meter_table(null, $user->uid);  
   
   case "source" :  
     return carbon_source_page($b);  
   
   case "transfer" :  
     return carbon_transfer_page($b);  
   
   case "share" :  
     return carbon_share($user->uid);  
   
   case "footprint" :   // was sheet  
     include(CARBON_PATH ."/carbon_footprint.inc");  
     if (function_exists("carbon_footprint"))  
       return carbon_footprint();  
     else  
       return "function not installed";  
   
   default :  
     return carbon_help("carbon#help");  
   
   }  
 }  
   
   
 /**  
  * carbon_admin is the entry point for many commands  
  * @return form contents for this module  
  *  
  */  
   
   
 function carbon_admin_entry($a = NULL, $b = NULL, $c = NULL)  
 {  
   switch ($a) {  
   
   case "account" :  
     return carbon_report_page(0, '');  
   
   case "stamp" :  
     return carbon_stamp_page(0);  
   
   case "source" :  
     return carbon_source_page($b);  
   
   case "source" :  
     return carbon_transfer_page(0);  
   
   case "share" :  
     return carbon_share(0);  
   
   case "tools" :  
   
     if(include(CARBON_PATH ."/carbon_admin.inc"))  
     {  
       if (function_exists("carbon_admin"))  
         return carbon_admin($a, $b, $c);  
     }  
     else  
         return "carbon_admin() not accessible. The file <em>carbon_admin.inc</em> may not be present.";  
   
   case "" :  
     return t("Select option from the menu");  
   
   default :  
     return "carbon admin: option ".$a." not supported";  
   
   }  
 }  
   
628    
629  function carbon_share()  function carbon_share()
630  {  {
# Line 777  function carbon_highlighted_link($node, Line 736  function carbon_highlighted_link($node,
736    if (!$access)    if (!$access)
737      return "" ;      return "" ;
738    
739    $html = "<a title='".$title."' href='".check_url(url($path, $query, null, false))."'><span style= 'font-size : -1; color: #FFF ; background-color: ".$color.";'>".$text."</span></a>";    $html = "<a title='".$title."' href='".check_url(url($path, array('query'=>$query)))."'><span style= 'font-size : -1; color: #FFF ; background-color: ".$color.";'>".$text."</span></a>";
740    return $html ;    return $html ;
741  }  }
742    
# Line 794  function carbon_format_add_new_op($node, Line 753  function carbon_format_add_new_op($node,
753  }  }
754    
755    
 function dump_array($title, $ar)  
 {  
         echo("<h1>".$title."</h1>");  
         if ($ar == null)  
         {  
                 echo "null"; return ;  
         }  
   
         foreach ($ar as $entry)  
         {  
                 echo "[".$entry."]" ;  
         }  
         echo("<br/>");  
 }  
   
   
756  /*  /*
757   * hm probably not necessary to define this class   * hm probably not necessary to define this class
758   */   */
# Line 820  class Node Line 763  class Node
763  }  }
764    
765    
766    //  Shared date functions
767    
768    
769    /**
770     * Format a date range. The main purpose of this is to show a
771     * date range using as few as characters as possible so that
772     * it takes little space on the screen without being ambiguous.
773     */
774    
775    function _format_date_range($firstdate, $lastdate)
776    {
777      $y1 = _format_date($firstdate, "custom", "y"); $y2 = _format_date($lastdate, "custom", "y");
778      $m1 = _format_date($firstdate, "custom", "m"); $m2 = _format_date($lastdate, "custom", "m");
779    
780      if ($y1 == $y2)
781      {
782        if ($m1 == $m2)
783          return _format_date($firstdate,"custom", "M Y");
784    
785        return _format_date($firstdate,"custom", "M")." to "._format_date($lastdate,"custom", "M Y");
786      }
787      return _format_date($firstdate,"custom", "M Y")." to "._format_date($lastdate,"custom", "M Y");
788    }
789    
790    
791    
792    /**
793     * Bypass Drupal's format_date() which deals only with offsets and
794     * appears to not understand daylight savings time.
795     *
796     * It is really important that dates are handled consistently.
797     * Otherwise you end up accepting dates with one offset e.g. BST
798     * and displaying them with another e.g. GMT which is what happened
799     * with early software versions.
800     *
801     * @param $timestamp
802     * @param $type
803     * @return unknown_type
804     */
805    
806    
807    function _format_date($timestamp, $type = 'medium', $format = '')
808    {
809      // lifted formatting options from drupal code for consistency
810      switch ($type) {
811        case 'small':
812          $format = variable_get('date_format_short', 'm/d/Y - H:i');
813          break;
814        case 'large':
815          $format = variable_get('date_format_long', 'l, F j, Y - H:i');
816          break;
817        case 'custom':
818          // No change to format
819          break;
820        case 'medium':
821        default:
822          $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
823      }
824      return date($format, $timestamp);
825    }
826    
827    /**
828     * This class is used to convert between dates embedded in URLs
829     * and the unix timestamp.
830     *
831     * @author johna
832     *
833     */
834    
835    
836    class CarbonDate
837    {
838      /*
839       * return a unix timestamp from a string
840       */
841            function fromYYYYMMDD($s) // static removed
842      {
843        return mktime(0, 0, 0, substr($s,4,2), substr($s,6,2), substr($s,0,4));
844      }
845    
846      /*
847       * return a string from a unix timestamp
848       */
849    
850      function asYYYYMMDD($time) // static removed
851      {
852        $y = _format_date($time, 'custom', 'Y');
853        $m = _format_date($time, 'custom', 'm');
854        $d = _format_date($time, 'custom', 'd');
855        return sprintf("%04d%02d%02d", $y, $m, $d);
856      }
857    }
858    
859    
860    /**
861     * Add whole years onto a date
862     *
863     * @param unix timestamp (secs from 1970)
864     * @param integer number of months
865     * @return the new date (secs from 1970)
866     */
867    
868    function _add_months($date, $num)
869    {
870        return strtotime($num." month", $date);
871    }
872    
873    
874    
875    
876  ?>  ?>

Legend:
Removed from v.1.20  
changed lines
  Added in v.1.21

  ViewVC Help
Powered by ViewVC 1.1.3