From: Marc L. <ma...@ma...> - 2010-02-17 23:16:26
|
Hi! Tiki has always been a great platform to collect/collaborate on data. However, for reporting, it has been historically basic. This new feature will change this situation. It was coded for a specific project but made generic and flexible where users will have a basic language to query Tiki data. I look forward to use seeing it in action :-) Best regards, M ;-) On Wed, Feb 17, 2010 at 4:55 PM, <lph...@us...> wrote: > Revision: 25325 > http://tikiwiki.svn.sourceforge.net/tikiwiki/?rev=25325&view=rev > Author: lphuberdeau > Date: 2010-02-17 21:55:51 +0000 (Wed, 17 Feb 2010) > > Log Message: > ----------- > [NEW] Admin panel to configure rating calculations > > Modified Paths: > -------------- > trunk/db/tiki.sql > trunk/lib/rating/ratinglib.php > trunk/lib/setup/prefs.php > trunk/lib/test/bootstrap.php > trunk/lib/tikiaccesslib.php > trunk/templates/tiki-admin.tpl > trunk/tiki-admin.php > > Added Paths: > ----------- > trunk/installer/schema/20100217_rating_config_tiki.sql > trunk/lib/core/lib/Math/ > trunk/lib/core/lib/Math/Formula/ > trunk/lib/core/lib/Math/Formula/Element.php > trunk/lib/core/lib/Math/Formula/Exception.php > trunk/lib/core/lib/Math/Formula/Function/ > trunk/lib/core/lib/Math/Formula/Function/Add.php > trunk/lib/core/lib/Math/Formula/Function/Mul.php > trunk/lib/core/lib/Math/Formula/Function.php > trunk/lib/core/lib/Math/Formula/Parser/ > trunk/lib/core/lib/Math/Formula/Parser/Exception.php > trunk/lib/core/lib/Math/Formula/Parser.php > trunk/lib/core/lib/Math/Formula/Runner/ > trunk/lib/core/lib/Math/Formula/Runner/Exception.php > trunk/lib/core/lib/Math/Formula/Runner.php > trunk/lib/core/lib/Math/Formula/Tokenizer.php > trunk/lib/prefs/rating.php > trunk/lib/rating/configlib.php > trunk/lib/rating/formula/ > trunk/lib/rating/formula/RatingAverage.php > trunk/lib/test/core/Math/ > trunk/lib/test/core/Math/Formula/ > trunk/lib/test/core/Math/Formula/DummyFunction/ > trunk/lib/test/core/Math/Formula/DummyFunction/FortyTwo.php > trunk/lib/test/core/Math/Formula/DummyFunction/Testop.php > trunk/lib/test/core/Math/Formula/ElementTest.php > trunk/lib/test/core/Math/Formula/ParserTest.php > trunk/lib/test/core/Math/Formula/RunnerTest.php > trunk/lib/test/core/Math/Formula/TokenizerTest.php > trunk/templates/tiki-admin-include-rating.tpl > trunk/tiki-admin_include_rating.php > > Modified: trunk/db/tiki.sql > =================================================================== > --- trunk/db/tiki.sql 2010-02-17 20:26:02 UTC (rev 25324) > +++ trunk/db/tiki.sql 2010-02-17 21:55:51 UTC (rev 25325) > @@ -3589,3 +3589,23 @@ > UNIQUE `item_attribute_uq` ( `type`, `itemId`, `attribute` ), > KEY `attribute_lookup_ix` (`attribute`, `value`) > ); > + > +DROP TABLE IF EXISTS `tiki_rating_configs`; > +CREATE TABLE `tiki_rating_configs` ( > + `ratingConfigId` INT PRIMARY KEY AUTO_INCREMENT, > + `name` VARCHAR(50) NOT NULL, > + `expiry` INT NOT NULL DEFAULT 3600, > + `formula` TEXT NOT NULL, > + `callbacks` TEXT > +); > + > +DROP TABLE IF EXISTS `tiki_rating_obtained`; > +CREATE TABLE `tiki_rating_obtained` ( > + `ratingId` INT PRIMARY KEY AUTO_INCREMENT, > + `ratingConfigId` INT NOT NULL, > + `type` VARCHAR(50) NOT NULL, > + `object` INT NOT NULL, > + `expire` INT NOT NULL, > + `value` FLOAT NOT NULL, > + UNIQUE `tiki_obtained_rating_uq` (`type`, `object`, `ratingConfigId`) > +); > > Added: trunk/installer/schema/20100217_rating_config_tiki.sql > =================================================================== > --- trunk/installer/schema/20100217_rating_config_tiki.sql (rev 0) > +++ trunk/installer/schema/20100217_rating_config_tiki.sql 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,17 @@ > +CREATE TABLE `tiki_rating_configs` ( > + `ratingConfigId` INT PRIMARY KEY AUTO_INCREMENT, > + `name` VARCHAR(50) NOT NULL, > + `expiry` INT NOT NULL DEFAULT 3600, > + `formula` TEXT NOT NULL, > + `callbacks` TEXT > +); > + > +CREATE TABLE `tiki_rating_obtained` ( > + `ratingId` INT PRIMARY KEY AUTO_INCREMENT, > + `ratingConfigId` INT NOT NULL, > + `type` VARCHAR(50) NOT NULL, > + `object` INT NOT NULL, > + `expire` INT NOT NULL, > + `value` FLOAT NOT NULL, > + UNIQUE `tiki_obtained_rating_uq` (`type`, `object`, `ratingConfigId`) > +); > > Added: trunk/lib/core/lib/Math/Formula/Element.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Element.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Element.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,93 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_Element implements ArrayAccess, Iterator, Countable > +{ > + private $type; > + private $children; > + > + function __construct( $type, array $children = array() ) { > + $this->type = $type; > + $this->children = $children; > + } > + > + function addChild( $child ) { > + $this->children[] = $child; > + } > + > + function offsetExists( $offset ) { > + return is_int( $offset ) && isset( $this->children[$offset] ); > + } > + > + function offsetGet( $offset ) { > + if( isset( $this->children[$offset] ) ) { > + return $this->children[$offset]; > + } > + } > + > + function offsetSet( $offset, $value ) { > + } > + > + function offsetUnset( $offset ) { > + } > + > + function __get( $name ) { > + foreach( $this->children as $child ) { > + if( $child instanceof Math_Formula_Element && $child->type == $name ) { > + return $child; > + } > + } > + } > + > + function getType() { > + return $this->type; > + } > + > + function current() { > + $key = key( $this->children ); > + return $this->children[$key]; > + } > + > + function next() { > + next( $this->children ); > + } > + > + function rewind() { > + reset( $this->children ); > + } > + > + function key() { > + return key( $this->children ); > + } > + > + function valid() { > + return false !== current( $this->children ); > + } > + > + function count() { > + return count( $this->children ); > + } > + > + function getExtraValues( array $allowedKeys ) { > + $extra = array(); > + > + foreach( $this->children as $child ) { > + if( $child instanceof self ) { > + if( ! in_array( $child->type, $allowedKeys ) ) { > + $extra[] = "({$child->type} ...)"; > + } > + } else { > + $extra[] = $child; > + } > + } > + > + if( count( $extra ) ) { > + return $extra; > + } > + } > +} > + > > > Property changes on: trunk/lib/core/lib/Math/Formula/Element.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/core/lib/Math/Formula/Exception.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Exception.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Exception.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,9 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_Exception extends Exception {} > + > > > Property changes on: trunk/lib/core/lib/Math/Formula/Exception.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/core/lib/Math/Formula/Function/Add.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Function/Add.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Function/Add.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,21 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +require_once 'Math/Formula/Function.php'; > +class Math_Formula_Function_Add extends Math_Formula_Function > +{ > + function evaluate( $element ) { > + $out = 0; > + > + foreach( $element as $child ) { > + $out += $this->evaluateChild( $child ); > + } > + > + return $out; > + } > +} > + > > > Property changes on: trunk/lib/core/lib/Math/Formula/Function/Add.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/core/lib/Math/Formula/Function/Mul.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Function/Mul.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Function/Mul.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,21 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +require_once 'Math/Formula/Function.php'; > +class Math_Formula_Function_Mul extends Math_Formula_Function > +{ > + function evaluate( $element ) { > + $out = 1; > + > + foreach( $element as $child ) { > + $out *= $this->evaluateChild( $child ); > + } > + > + return $out; > + } > +} > + > > > Property changes on: trunk/lib/core/lib/Math/Formula/Function/Mul.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/core/lib/Math/Formula/Function.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Function.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Function.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,28 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +abstract class Math_Formula_Function > +{ > + private $callback; > + > + function evaluateTemplate( $element, $evaluateCallback ) { > + $this->callback = $evaluateCallback; > + return $this->evaluate( $element ); > + } > + > + abstract function evaluate( $element ); > + > + protected function evaluateChild( $child ) { > + return call_user_func( $this->callback, $child ); > + } > + > + protected function error( $message ) { > + require_once 'Math/Formula/Exception.php'; > + throw new Math_Formula_Exception( $message ); > + } > +} > + > > > Property changes on: trunk/lib/core/lib/Math/Formula/Function.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/core/lib/Math/Formula/Parser/Exception.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Parser/Exception.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Parser/Exception.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,16 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +require_once 'Math/Formula/Exception.php'; > +class Math_Formula_Parser_Exception extends Math_Formula_Exception > +{ > + function __construct( $message, array $tokens, $code = null ) { > + $message = tr( '%0 near "%1"', $message, implode(' ', $tokens ) ); > + parent::__construct( $message, $code ); > + } > +} > + > > > Property changes on: trunk/lib/core/lib/Math/Formula/Parser/Exception.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/core/lib/Math/Formula/Parser.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Parser.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Parser.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,64 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_Parser > +{ > + function parse( $string ) { > + require_once 'Math/Formula/Tokenizer.php'; > + $tokenizer = new Math_Formula_Tokenizer; > + $tokens = $tokenizer->getTokens( $string ); > + > + $element = $this->getElement( $tokens ); > + > + if( ! empty( $tokens ) ) { > + require_once 'Math/Formula/Parser/Exception.php'; > + throw new Math_Formula_Parser_Exception( 'Unexpected trailing characters.', $tokens ); > + } > + > + return $element; > + } > + > + private function getElement( & $tokens ) { > + > + $first = array_shift( $tokens ); > + > + if( $first != '(' ) { > + require_once 'Math/Formula/Parser/Exception.php'; > + throw new Math_Formula_Parser_Exception( tra('Expecting "("'), $tokens ); > + } > + > + $type = array_shift( $tokens ); > + > + if( $type == '(' || $type == ')' ) { > + array_unshift( $tokens, $type ); > + require_once 'Math/Formula/Parser/Exception.php'; > + throw new Math_Formula_Parser_Exception( tr('Unexpected "%0"', $type), $tokens ); > + } > + > + require_once 'Math/Formula/Element.php'; > + $element = new Math_Formula_Element( $type ); > + > + while( ( $token = array_shift( $tokens ) ) && $token != ')' ) { > + if( $token == '(' ) { > + array_unshift( $tokens, $token ); > + $token = $this->getElement( $tokens ); > + } > + > + $element->addChild( $token ); > + } > + > + if( $token != ')' ) { > + array_unshift( $tokens, $token ); > + require_once 'Math/Formula/Parser/Exception.php'; > + throw new Math_Formula_Parser_Exception( tra('Expecting ")"'), $tokens ); > + } > + > + return $element; > + } > + > +} > + > > > Property changes on: trunk/lib/core/lib/Math/Formula/Parser.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/core/lib/Math/Formula/Runner/Exception.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Runner/Exception.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Runner/Exception.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,9 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +require_once 'Math/Formula/Exception.php'; > +class Math_Formula_Runner_Exception extends Math_Formula_Exception {} > > > Property changes on: trunk/lib/core/lib/Math/Formula/Runner/Exception.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/core/lib/Math/Formula/Runner.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Runner.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Runner.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,110 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_Runner > +{ > + private $sources; > + private $collected = array(); > + private $element; > + private $known = array(); > + private $variables = array(); > + > + function __construct( array $sources ) { > + $this->sources = $sources; > + } > + > + function setFormula( $element ) { > + $this->element = $this->getElement( $element ); > + $this->collected = array(); > + } > + > + function setVariables( array $variables ) { > + $this->variables = $variables; > + } > + > + function inspect() { > + if( $this->element ) { > + $this->inspectElement( $this->element ); > + return $this->collected; > + } else { > + require_once 'Math/Formula/Runner/Exception.php'; > + throw new Math_Formula_Runner_Exception( tra('No formula provided.') ); > + } > + } > + > + function evaluate() { > + return $this->evaluateData( $this->element ); > + } > + > + function evaluateData( $data ) { > + if( $data instanceof Math_Formula_Element ) { > + $op = $this->getOperation( $data ); > + return $op->evaluateTemplate( $data, array( $this, 'evaluateData' ) ); > + } elseif( is_numeric( $data ) ) { > + return (double) $data; > + } elseif( isset( $this->variables[$data] ) ) { > + return $this->variables[$data]; > + } else { > + require_once 'Math/Formula/Exception.php'; > + throw new Math_Formula_Exception( tr('Variable not found "%0".', $data) ); > + } > + } > + > + private function inspectElement( $element ) { > + $op = $this->getOperation( $element ); > + > + $op->evaluateTemplate( $element, array( $this, 'inspectData' ) ); > + } > + > + function inspectData( $data ) { > + if( $data instanceof Math_Formula_Element ) { > + $this->inspectElement( $data ); > + } elseif( ! is_numeric( $data ) ) { > + $this->collected[] = $data; > + } > + > + return 0; > + } > + > + private function getElement( $element ) { > + if( is_string( $element ) ) { > + require_once 'Math/Formula/Parser.php'; > + $parser = new Math_Formula_Parser; > + $element = $parser->parse( $element ); > + } > + > + return $element; > + } > + > + private function getOperation( $element ) { > + $name = $element->getType(); > + > + if( isset( $this->known[$name] ) ) { > + return $this->known[$name]; > + } > + > + require_once 'Zend/Filter/Word/DashToCamelCase.php'; > + $filter = new Zend_Filter_Word_DashToCamelCase; > + $ucname = $filter->filter( ucfirst( $name ) ); > + > + foreach( $this->sources as $prefix => $path ) { > + $class = $prefix . $ucname; > + $file = "$path/$ucname.php"; > + > + if( file_exists( $file ) ) { > + require_once $file; > + if( class_exists( $class ) ) { > + return $this->known[$name] = new $class; > + } > + } > + } > + > + require_once 'Math/Formula/Runner/Exception.php'; > + throw new Math_Formula_Runner_Exception( tr('Unknown operation "%0".', $element->getType() ) ); > + } > +} > + > > > Property changes on: trunk/lib/core/lib/Math/Formula/Runner.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/core/lib/Math/Formula/Tokenizer.php > =================================================================== > --- trunk/lib/core/lib/Math/Formula/Tokenizer.php (rev 0) > +++ trunk/lib/core/lib/Math/Formula/Tokenizer.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,47 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_Tokenizer > +{ > + function getTokens( $string ) { > + $tokens = array(); > + > + $len = strlen( $string ); > + $current = ''; > + for( $i = 0; $len > $i; ++$i ) { > + $chr = $string{$i}; > + > + $end = false; > + $extra = null; > + > + if( ctype_space( $chr ) ) { > + $end = true; > + } elseif( $chr == '(' || $chr == ')' ) { > + $extra = $chr; > + $end = true; > + } else { > + $current .= $chr; > + } > + > + if( $end && ! empty( $current ) ) { > + $tokens[] = $current; > + $current = ''; > + } > + > + if( $extra ) { > + $tokens[] = $extra; > + } > + } > + > + if( ! empty( $current ) ) { > + $tokens[] = $current; > + } > + > + return $tokens; > + } > +} > + > > > Property changes on: trunk/lib/core/lib/Math/Formula/Tokenizer.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/prefs/rating.php > =================================================================== > --- trunk/lib/prefs/rating.php (rev 0) > +++ trunk/lib/prefs/rating.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,17 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id: tiki-action_calendar.php 25076 2010-02-11 15:53:20Z changi67 $ > + > +function prefs_rating_list() { > + return array( > + 'rating_advanced' => array( > + 'name' => tra('Advanced Rating'), > + 'description' => tra('Rating system allowing for options and calculation method to be configured.'), > + 'type' => 'flag', > + ), > + ); > +} > + > > Added: trunk/lib/rating/configlib.php > =================================================================== > --- trunk/lib/rating/configlib.php (rev 0) > +++ trunk/lib/rating/configlib.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,29 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id: tiki-action_calendar.php 25076 2010-02-11 15:53:20Z changi67 $ > + > +class RatingConfigLib extends TikiDb_Bridge > +{ > + function get_configurations() { > + return $this->fetchAll( 'SELECT * FROM `tiki_rating_configs`' ); > + } > + > + function create_configuration( $name ) { > + $this->query( 'INSERT INTO `tiki_rating_configs` ( `name`, `formula` ) VALUES( ?, ? )', > + array( $name, '(rating-average (object type object-id))' ) ); > + > + return $this->lastInsertId(); > + } > + > + function update_configuration( $id, $name, $expiry, $formula ) { > + $this->query( 'UPDATE `tiki_rating_configs` SET `name` = ?, `expiry` = ?, `formula` = ? WHERE `ratingConfigId` = ?', > + array( $name, $expiry, $formula, $id ) ); > + } > +} > + > +global $ratingconfiglib; > +$ratingconfiglib = new RatingConfigLib; > + > > Added: trunk/lib/rating/formula/RatingAverage.php > =================================================================== > --- trunk/lib/rating/formula/RatingAverage.php (rev 0) > +++ trunk/lib/rating/formula/RatingAverage.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,64 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +require_once 'Math/Formula/Function.php'; > + > +class Tiki_Formula_Function_RatingAverage extends Math_Formula_Function > +{ > + private $mode = 'avg'; > + > + function evaluate( $element ) { > + $allowed = array( 'object', 'range', 'ignore', 'keep', 'revote' ); > + > + if( $extra = $element->getExtraValues( $allowed ) ) { > + $this->error( tr('Unexpected values: %0', implode( ', ', $extra ) ) ); > + } > + > + $object = $element->object; > + > + if( ! $object || count( $object ) != 2 ) { > + $this->error( tra("Object must be provided and contain two arguments: type and object") ); > + } > + > + $type = $this->evaluateChild( $object[0] ); > + $object = $this->evaluateChild( $object[1] ); > + > + $params = array(); > + > + if( $range = $element->range ) { > + if( count($range) == 1 ) { > + $params['range'] = $this->evaluateChild( $range[0] ); > + } else { > + $this->error( tra('Invalid range.') ); > + } > + } > + > + if( $revote = $element->revote ) { > + if( count($revote) == 1 ) { > + $params['revote'] = $this->evaluateChild( $revote[0] ); > + } else { > + $this->error( tra('Invalid revote period.') ); > + } > + } > + > + if( $element->ignore ) { > + $params['ignore'] = 'anonymous'; > + } > + > + if( $keep = $element->keep ) { > + if( $keep[0] == 'oldest' || $keep[0] == 'latest' ) { > + $params['keep'] = $keep[0]; > + } else { > + $this->error( tra('Expecting "keep" to be "latest" or "oldest"') ); > + } > + } > + > + global $ratinglib; require_once 'lib/rating/ratinglib.php'; > + return $ratinglib->collect( $type, $object, $this->mode, $params ); > + } > +} > + > > Modified: trunk/lib/rating/ratinglib.php > =================================================================== > --- trunk/lib/rating/ratinglib.php 2010-02-17 20:26:02 UTC (rev 25324) > +++ trunk/lib/rating/ratinglib.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -181,6 +181,31 @@ > return $this->session_to_user( session_id() ); > } > } > + > + function test_formula( $formula, $available = false ) { > + try { > + $runner = $this->get_runner(); > + $runner->setFormula( $formula ); > + $variables = $runner->inspect(); > + > + if( $available ) { > + $extra = array_diff( $variables, $available ); > + if( count($extra) > 0 ) { > + return tr( 'Unknown variables referenced: %0', implode( ', ', $extra ) ); > + } > + } > + } catch( Math_Formula_Exception $e ) { > + return $e->getMessage(); > + } > + } > + > + private function get_runner() { > + require_once 'Math/Formula/Runner.php'; > + return new Math_Formula_Runner( array( > + 'Math_Formula_Function_' => 'lib/core/lib/Math/Formula/Function', > + 'Tiki_Formula_Function_' => dirname(__FILE__) . '/formula', > + ) ); > + } > } > > global $ratinglib; $ratinglib = new RatingLib; > > Modified: trunk/lib/setup/prefs.php > =================================================================== > --- trunk/lib/setup/prefs.php 2010-02-17 20:26:02 UTC (rev 25324) > +++ trunk/lib/setup/prefs.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -1566,6 +1566,9 @@ > 'payment_paypal_business' => '', > 'payment_paypal_environment' => 'https://www.paypal.com/cgi-bin/webscr', > 'payment_paypal_ipn' => 'y', > + > + // Rating > + 'rating_advanced' => 'n', > ); > > // spellcheck > > Modified: trunk/lib/test/bootstrap.php > =================================================================== > --- trunk/lib/test/bootstrap.php 2010-02-17 20:26:02 UTC (rev 25324) > +++ trunk/lib/test/bootstrap.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -9,6 +9,10 @@ > > ini_set( 'include_path', ini_get('include_path') . PATH_SEPARATOR . "." . PATH_SEPARATOR . "../core/lib" . PATH_SEPARATOR . "../.." . PATH_SEPARATOR . "core"); > > +function tr( $string ) { > + return $string; > +} > + > function tra( $string ) { > return $string; > } > > Added: trunk/lib/test/core/Math/Formula/DummyFunction/FortyTwo.php > =================================================================== > --- trunk/lib/test/core/Math/Formula/DummyFunction/FortyTwo.php (rev 0) > +++ trunk/lib/test/core/Math/Formula/DummyFunction/FortyTwo.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,14 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_DummyFunction_FortyTwo extends Math_Formula_Function > +{ > + function evaluate( $element ) { > + return 42; > + } > +} > + > > Added: trunk/lib/test/core/Math/Formula/DummyFunction/Testop.php > =================================================================== > --- trunk/lib/test/core/Math/Formula/DummyFunction/Testop.php (rev 0) > +++ trunk/lib/test/core/Math/Formula/DummyFunction/Testop.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,25 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_DummyFunction_Testop extends Math_Formula_Function > +{ > + function evaluate( $element ) { > + $object = $element->object; > + $concat = $element->concat; > + > + if( $object && $concat && count($object) == 2 && count($concat) == 1 ) { > + $type = $this->evaluateChild( $object[0] ); > + $id = $this->evaluateChild( $object[1] ); > + $other = $this->evaluateChild( $concat[0] ); > + > + return $type . $id . $other; > + } else { > + $this->error( 'Wrong argument count for testop' ); > + } > + } > +} > + > > > Property changes on: trunk/lib/test/core/Math/Formula/DummyFunction/Testop.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/test/core/Math/Formula/ElementTest.php > =================================================================== > --- trunk/lib/test/core/Math/Formula/ElementTest.php (rev 0) > +++ trunk/lib/test/core/Math/Formula/ElementTest.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,30 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_ElementTest extends TikiTestCase > +{ > + function testGetArgument() { > + $element = new Math_Formula_Element( 'test', array( 1, 2, 3 ) ); > + $this->assertEquals( 1, $element[0] ); > + $this->assertEquals( 3, $element[2] ); > + $this->assertNull( $element[3] ); > + } > + > + function testGetKey() { > + $element = new Math_Formula_Element( 'test', array( > + 1, > + $target = new Math_Formula_Element( 'object', array( 'type', 'id' ) ), > + 2, > + new Math_Formula_Element( 'object', array( 'foobar', 'baz' ) ), > + 3, > + ) ); > + > + $this->assertSame( $target, $element->object ); > + $this->assertNull( $element->foobar ); > + } > +} > + > > > Property changes on: trunk/lib/test/core/Math/Formula/ElementTest.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/test/core/Math/Formula/ParserTest.php > =================================================================== > --- trunk/lib/test/core/Math/Formula/ParserTest.php (rev 0) > +++ trunk/lib/test/core/Math/Formula/ParserTest.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,78 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_ParserTest extends TikiTestCase > +{ > + function testEmptyElement() { > + $parser = new Math_Formula_Parser; > + > + $element = new Math_Formula_Element( 'score' ); > + > + $this->assertEquals( $element, $parser->parse( '(score)' ) ); > + } > + > + function testWithArguments() { > + $parser = new Math_Formula_Parser; > + > + $element = new Math_Formula_Element( 'object', array( 'input-type', 'input-object-id' ) ); > + $this->assertEquals( $element, $parser->parse( '(object input-type input-object-id)' ) ); > + } > + > + function testNesting() { > + $parser = new Math_Formula_Parser; > + > + $element = new Math_Formula_Element( 'score', array( > + new Math_Formula_Element( 'object', array( 'type', 'object' ) ) ) ); > + > + $this->assertEquals( $element, $parser->parse( '(score (object type object))' ) ); > + } > + > + function testMultipleElements() { > + $parser = new Math_Formula_Parser; > + > + $element = new Math_Formula_Element( 'score', array( > + new Math_Formula_Element( 'object', array( 'type', 'object' ) ), > + new Math_Formula_Element( 'range', array( 3600 ) ), > + ) ); > + > + $this->assertEquals( $element, $parser->parse( '(score (object type object) (range 3600))' ) ); > + } > + > + function testMultipleNesting() { > + $parser = new Math_Formula_Parser; > + > + $element = new Math_Formula_Element( 'score', array( > + new Math_Formula_Element( 'object', array( 'type', 'object' ) ), > + new Math_Formula_Element( 'range', array( > + new Math_Formula_Element( 'mul', array( 3600, 60 ) ), > + ) ), > + ) ); > + > + $this->assertEquals( $element, $parser->parse( '(score (object type object) (range (mul 3600 60)))' ) ); > + } > + > + function badStrings() { > + return array( > + 'noOpening' => array( 'test' ), > + 'noToken' => array( '()' ), > + 'doubles' => array( '((test))' ), > + 'trail' => array( '(test) foo' ), > + 'unfinished' => array( '(score (object type object) (range 3600)' ), > + ); > + } > + > + /** > + * @dataProvider badStrings > + * @expectedException Math_Formula_Parser_Exception > + */ > + function testBadParse( $string ) { > + $parser = new Math_Formula_Parser; > + > + $parser->parse( $string ); > + } > +} > + > > > Property changes on: trunk/lib/test/core/Math/Formula/ParserTest.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/test/core/Math/Formula/RunnerTest.php > =================================================================== > --- trunk/lib/test/core/Math/Formula/RunnerTest.php (rev 0) > +++ trunk/lib/test/core/Math/Formula/RunnerTest.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,95 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_RunnerTest extends TikiTestCase > +{ > + private $runner; > + > + function setUp() { > + $this->runner = new Math_Formula_Runner( array( > + 'Math_Formula_Function_' => realpath( dirname(__FILE__) . '/../../../../core/lib/Math/Formula/Function' ), > + 'Math_Formula_DummyFunction_' => realpath( dirname(__FILE__) . '/DummyFunction' ), > + ) ); > + } > + > + function testSimpleOperations() { > + $this->runner->setFormula( '(mul (add foobar 2) test-variable)' ); > + $required = $this->runner->inspect(); > + > + $this->assertEquals( array( 'foobar', 'test-variable' ), $required ); > + } > + > + function testSimpleOperationPreparsed() { > + $parser = new Math_Formula_Parser; > + $element = $parser->parse( '(mul (add 1 2) test)' ); > + > + $this->runner->setFormula( $element ); > + $required = $this->runner->inspect(); > + > + $this->assertEquals( array( 'test' ), $required ); > + } > + > + /** > + * @expectedException Math_Formula_Runner_Exception > + */ > + function testUnknownOperator() { > + $this->runner->setFormula( '(foobar abc)' ); > + $this->runner->inspect(); > + } > + > + /** > + * @expectedException Math_Formula_Runner_Exception > + */ > + function testNoFormulaSpecified() { > + $this->runner->inspect(); > + } > + > + function testSimpleEvaluation() { > + $this->runner->setFormula( '(add 1 2)' ); > + $this->assertEquals( 3, $this->runner->evaluate() ); > + } > + > + function testWithVariables() { > + $this->runner->setFormula( '(mul foobar 2)' ); > + $this->runner->setVariables( array( > + 'foobar' => 2.5, > + ) ); > + $this->assertEquals( 5, $this->runner->evaluate() ); > + } > + > + /** > + * @expectedException Math_Formula_Exception > + */ > + function testMissingVariable() { > + $this->runner->setFormula( '(mul foobar 2)' ); > + $this->runner->evaluate(); > + } > + > + function testSearchingForConfiguration() { > + $this->runner->setFormula( '(testop (object test 123) (concat 456))' ); > + $this->runner->setVariables( array( 'test' => 'aaa' ) ); > + > + $this->assertEquals( array( 'test' ), $this->runner->inspect() ); > + > + $this->assertEquals( 'aaa123456', $this->runner->evaluate() ); > + } > + > + /** > + * @expectedException Math_Formula_Exception > + */ > + function testInvalidData() { > + $this->runner->setFormula( '(testop (object test) (concat 456))' ); > + > + $this->runner->inspect(); > + } > + > + function testCamelCaseOperation() { > + $this->runner->setFormula( '(forty-two)' ); > + $this->assertEquals( 42, $this->runner->evaluate() ); > + } > +} > + > > > Property changes on: trunk/lib/test/core/Math/Formula/RunnerTest.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Added: trunk/lib/test/core/Math/Formula/TokenizerTest.php > =================================================================== > --- trunk/lib/test/core/Math/Formula/TokenizerTest.php (rev 0) > +++ trunk/lib/test/core/Math/Formula/TokenizerTest.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,44 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id$ > + > +class Math_Formula_TokenizerTest extends TikiTestCase > +{ > + function testSimpleToken() { > + $tokenizer = new Math_Formula_Tokenizer; > + > + $this->assertEquals( array( 'test' ), $tokenizer->getTokens( 'test' ) ); > + } > + > + function testWithParenthesis() { > + $tokenizer = new Math_Formula_Tokenizer; > + > + $this->assertEquals( array( 'test', ')' ), $tokenizer->getTokens( 'test)' ) ); > + } > + > + function testWithMultipleParenthesis() { > + $tokenizer = new Math_Formula_Tokenizer; > + > + $this->assertEquals( array( '(', 'test', ')' ), $tokenizer->getTokens( '(test)' ) ); > + } > + > + function testIgnoreSpaces() { > + $tokenizer = new Math_Formula_Tokenizer; > + > + $this->assertEquals( array( '(', 'test', ')' ), $tokenizer->getTokens( " ( test\n\t\r) " ) ); > + } > + > + function testWithMultipleWords() { > + $tokenizer = new Math_Formula_Tokenizer; > + $this->assertEquals( array( 'hello', 'world', 'foo-bar' ), $tokenizer->getTokens( 'hello world foo-bar' ) ); > + } > + > + function testWordsAfterParenthesis() { > + $tokenizer = new Math_Formula_Tokenizer; > + $this->assertEquals( array( 'hello', '(', 'world', ')', 'foo-bar' ), $tokenizer->getTokens( 'hello (world) foo-bar' ) ); > + } > +} > + > > > Property changes on: trunk/lib/test/core/Math/Formula/TokenizerTest.php > ___________________________________________________________________ > Added: svn:keywords > + Id > > Modified: trunk/lib/tikiaccesslib.php > =================================================================== > --- trunk/lib/tikiaccesslib.php 2010-02-17 20:26:02 UTC (rev 25324) > +++ trunk/lib/tikiaccesslib.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -277,6 +277,10 @@ > exit(); > } > > + function flash( $message ) { > + $this->redirect( $_SERVER['REQUEST_URI'], $message ); > + } > + > /** > * Authorizes access to Tiki RSS feeds via user/password embedded in a URL > * e.g. https://joe:secret@localhost/tiki/tiki-calendars_rss.php?ver=2 > > Added: trunk/templates/tiki-admin-include-rating.tpl > =================================================================== > --- trunk/templates/tiki-admin-include-rating.tpl (rev 0) > +++ trunk/templates/tiki-admin-include-rating.tpl 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,67 @@ > +{* $Id: tiki-admin-include-performance.tpl 25307 2010-02-17 12:45:53Z sylvieg $ *} > + > +<form class="admin" id="performance" name="performance" action="tiki-admin.php?page=rating" method="post"> > + <div class="heading input_submit_container" style="text-align: right"> > + <input type="submit" value="{tr}Apply{/tr}" /> > + <input type="reset" value="{tr}Reset{/tr}" /> > + </div> > + > + <fieldset> > + <legend>{tr}Global configuration{/tr}</legend> > + {preference name=rating_advanced} > + </fieldset> > + > + <div class="input_submit_container" style="margin-top: 5px; text-align: center"> > + <input type="submit" value="{tr}Apply{/tr}" /> > + </div> > +</form> > +<div id="rating_advanced_childcontainer"> > + {foreach from=$configurations item=config} > + <form class="config" method="post" action=""> > + <fieldset> > + <legend>{$config.name|escape}</legend> > + <input type="hidden" name="config" value="{$config.ratingConfigId|escape}"/> > + <div> > + <label for="rating_name_{$config.ratingConfigId|escape}">{tr}Name{/tr}</label> > + <input type="text" name="name" value="{$config.name|escape}" id="rating_name_{$config.ratingConfigId|escape}"/> > + </div> > + <div> > + <label for="rating_expiry_{$config.ratingConfigId|escape}">{tr}Cache duration{/tr}</label> > + <input type="text" name="expiry" value="{$config.expiry|escape}" id="rating_expiry_{$config.ratingConfigId|escape}"/> > + </div> > + <div> > + <textarea name="formula" rows="5" style="width: 100%;">{$config.formula|escape}</textarea> > + </div> > + <div class="error"></div> > + <input type="submit" name="edit" value="{tr}Save{/tr}"/> > + </fieldset> > + </form> > + {/foreach} > + <form method="post" action=""> > + <fieldset> > + <legend>{tr}Create New{/tr}</legend> > + <label for="rating_config_new">{tr}Name{/tr}</label> > + <input type="text" name="name" id="rating_config_new"/> > + <input type="submit" name="create" value="{tr}Create{/tr}"/> > + </fieldset> > + </form> > +</div> > +{jq} > +$jq('form.config').submit( function( e ) { > + return ! $jq(this).find('input[type=submit]').attr('disabled'); > +} ); > +$jq('form.config .error').hide(); > +$jq('form.config textarea').change( function( e ) { > + var text = this; > + e.preventDefault(); > + var submit = $jq(this).closest('form').find('input[type=submit]').attr('disabled', true); > + $jq.getJSON( window.location.href, { test: $jq(this).val() }, function( data ) { > + submit.attr( 'disabled', ! data.valid ); > + if( data.valid ) { > + $jq(text).closest('form').find('.error').hide(); > + } else { > + $jq(text).closest('form').find('.error').show().text( data.message ); > + } > + } ); > +} ); > +{/jq} > > Modified: trunk/templates/tiki-admin.tpl > =================================================================== > --- trunk/templates/tiki-admin.tpl 2010-02-17 20:26:02 UTC (rev 25324) > +++ trunk/templates/tiki-admin.tpl 2010-02-17 21:55:51 UTC (rev 25325) > @@ -53,7 +53,7 @@ > "calendar", "intertiki", "kaltura", "freetags", "gmap", > "i18n", "wysiwyg", "copyright", "category", "module", "look", "textarea", > "ads", "profiles", "semantic", "plugins", "webservices", > -'sefurl', 'connect', 'metrics', 'payment'))} > +'sefurl', 'connect', 'metrics', 'payment', 'rating'))} > {assign var="include" value=$smarty.get.page} > {else} > {assign var="include" value="list-sections"} > > Modified: trunk/tiki-admin.php > =================================================================== > --- trunk/tiki-admin.php 2010-02-17 20:26:02 UTC (rev 25324) > +++ trunk/tiki-admin.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -353,6 +353,11 @@ > $helpUrl = "Connect"; > $description = "Connect"; > include_once ('tiki-admin_include_connect.php'); > + } else if ($adminPage == "rating") { > + $admintitle = "Advanced Rating"; > + $helpUrl = "Advanced+Rating"; > + $description = "Advanced Rating"; > + include_once ('tiki-admin_include_rating.php'); > } else { > $helpUrl = ''; > } > > Added: trunk/tiki-admin_include_rating.php > =================================================================== > --- trunk/tiki-admin_include_rating.php (rev 0) > +++ trunk/tiki-admin_include_rating.php 2010-02-17 21:55:51 UTC (rev 25325) > @@ -0,0 +1,39 @@ > +<?php > +// (c) Copyright 2002-2010 by authors of the Tiki Wiki/CMS/Groupware Project > +// > +// All Rights Reserved. See copyright.txt for details and a complete list of authors. > +// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. > +// $Id: tiki-action_calendar.php 25076 2010-02-11 15:53:20Z changi67 $ > + > +if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) { > + header("location: index.php"); > + exit; > +} > + > +global $ratingconfiglib; require_once 'lib/rating/configlib.php'; > +global $ratinglib; require_once 'lib/rating/ratinglib.php'; > + > +if( isset($_REQUEST['test']) && $access->is_machine_request() ) { > + $message = $ratinglib->test_formula( $_REQUEST['test'], array( 'type', 'object-id' ) ); > + > + $access->output_serialized( array( > + 'valid' => empty( $message ), > + 'message' => $message, > + ) ); > + exit; > +} > + > +if( isset($_POST['create']) && ! empty( $_POST['name'] ) ) { > + $id = $ratingconfiglib->create_configuration( $_POST['name'] ); > + $access->flash( tr('New configuration created (id %0)', $id) ); > +} > + > +if( isset($_POST['edit']) ) { > + $ratingconfiglib->update_configuration( $_POST['config'], $_POST['name'], $_POST['expiry'], $_POST['formula'] ); > + $access->flash( tra('Configuration updated.') ); > +} > + > +$configurations = $ratingconfiglib->get_configurations(); > + > +$smarty->assign( 'configurations', $configurations ); > + > > > This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. > > ------------------------------------------------------------------------------ > Download Intel® Parallel Studio Eval > Try the new software tools for yourself. Speed compiling, find bugs > proactively, and fine-tune applications for parallel performance. > See why Intel Parallel Studio got high marks during beta. > http://p.sf.net/sfu/intel-sw-dev > _______________________________________________ > Tikiwiki-cvs mailing list > Tik...@li... > https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs > -- Marc Laporte http://MarcLaporte.com http://TikiWiki.org/MarcLaporte http://AvanTech.net http://OurWiki.net |