2 rcs_id('$Id: RateIt.php,v 1.3 2004-04-01 06:29:51 rurban Exp $');
4 Copyright 2004 $ThePhpWikiProgrammingTeam
6 This file is (not yet) part of PhpWiki.
8 PhpWiki is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 PhpWiki is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with PhpWiki; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 //define('RATING_STORAGE','WIKIPAGE');
24 define('RATING_STORAGE','SQL');
25 // leave undefined for internal, slow php engine.
26 //define('RATING_EXTERNAL',PHPWIKI_DIR . 'suggest.exe');
29 * RateIt: A recommender system, based on MovieLens and suggest.
30 * Store user ratings per pagename. The wikilens theme displays a navbar image bar
31 * with some nice javascript magic and this plugin shows various recommendations.
33 * There should be two methods to store ratings:
34 * In a SQL database as in wikilens http://dickens.cs.umn.edu/dfrankow/wikilens
36 * The most important fact: A page has more than one rating. There can
37 * be (and will be!) many ratings per page (ratee): different raters
38 * (users), in different dimensions. Are those stored per page
39 * (ratee)? Then what if I wish to access the ratings per rater
41 * wikilens plans several user-centered applications like:
43 * b) show my buddies' ratings
44 * c) show how my ratings are like my buddies'
45 * d) show where I agree/disagree with my buddy
46 * e) show what this group of people agree/disagree on
48 * If the ratings are stored in a real DB in a table, we can index the
49 * ratings by rater and ratee, and be confident in
50 * performance. Currently MovieLens has 80,000 users, 7,000 items,
51 * 10,000,000 ratings. This is an average of 1400 ratings/page if each
52 * page were rated equally. However, they're not: the most popular
53 * things have tens of thousands of ratings (e.g., "Pulp Fiction" has
54 * 42,000 ratings). If ratings are stored per page, you would have to
55 * save/read huge page metadata every time someone submits a
56 * rating. Finally, the movie domain has an unusually small number of
57 * items-- I'd expect a lot more in music, for example.
59 * For a simple rating system one can also store the rating in the page
62 * Usage: <?plugin RateIt ?> to enable rating on this page
63 * Note: The wikilens theme must be enabled, to enable this plugin!
64 * Or use a sidebar based theme with the box method.
65 * <?plugin RateIt show=ratings ?> to show my ratings
66 * <?plugin RateIt show=buddies ?> to show my buddies
67 * <?plugin RateIt show=ratings dimension=1 ?>
69 * @author: Dan Frankowski (wikilens author), Reini Urban (as plugin)
73 * - finish mysuggest.c (external engine with data from mysql)
74 * - add php_prediction
75 * - add the various show modes (esp. TopN queries in PHP)
79 dimension INT(4) NOT NULL,
80 raterpage INT(11) NOT NULL,
81 rateepage INT(11) NOT NULL,
82 ratingvalue FLOAT NOT NULL,
83 rateeversion INT(11) NOT NULL,
84 tstamp TIMESTAMP(14) NOT NULL,
85 PRIMARY KEY (dimension, raterpage, rateepage)
89 require_once("lib/WikiPlugin.php");
91 class WikiPlugin_RateIt
97 function getDescription() {
98 return _("Recommendation system. Store user ratings per page");
100 function getVersion() {
101 return preg_replace("/[Revision: $]/", '',
102 "\$Revision: 1.3 $");
105 function RatingWidgetJavascript() {
107 $img = substr($Theme->_findData("images/RateItNk0.png"),0,-7);
108 $urlprefix = WikiUrl("",0,1);
110 function displayRating(imgPrefix, ratingvalue, pred) {
111 var cancel = imgPrefix + 'Cancel';
112 for (i=1; i<=10; i++) {
113 var imgName = imgPrefix + i;
114 var imgSrc = '".$img."';
116 document[imgName].title = '"._("Predicted rating ")."'+ratingvalue;
118 document[imgName].title = '"._("Your rating ")."'+ratingvalue;
119 if (i<=(ratingvalue*2)) {
121 document[imgName].src = imgSrc + ((i%2) ? 'Rk1' : 'Rk0') + '.png';
123 document[imgName].src = imgSrc + ((i%2) ? 'Ok1' : 'Ok0') + '.png';
125 document[imgName].src = imgSrc + ((i%2) ? 'Nk1' : 'Nk0') + '.png';
128 if ((pred == 0) && (ratingvalue > 0))
129 document[cancel].src = imgSrc + 'Cancel.png';
131 document[cancel].src = imgSrc + 'CancelN.png';
133 function click(actionImg, pagename, version, imgPrefix, dimension, rating) {
135 deleteRating(actionImg, pagename, dimension);
136 displayRating(imgPrefix, 0, 0);
138 submitRating(actionImg, pagename, version, dimension, rating);
139 displayRating(imgPrefix, rating, 0);
142 function submitRating(actionImg, page, version, dimension, rating) {
143 var myRand = Math.round(Math.random()*(1000000));
144 var imgSrc = page + '?version=' + version + '&action=".urlencode(_("RateIt"))."&mode=add&rating=' + rating + '&dimension=' + dimension + '&nopurge=cache&rand=' + myRand;
145 //alert('submitRating(' + page + ', ' + version + ', ' + dimension + ', ' + rating + ') => '+imgSrc);
146 document[actionImg].src= imgSrc;
148 function deleteRating(actionImg, page, dimension) {
149 var myRand = Math.round(Math.random()*(1000000));
150 var imgSrc = '".$urlprefix."' + page + '?action=".urlencode(_("RateIt"))."&mode=delete&dimension=' + dimension + '&nopurge=cache&rand=' + myRand;
151 //alert('deleteRating(' + page + ', ' + version + ', ' + dimension + ')');
152 document[actionImg].src= imgSrc;
155 return JavaScript($js);
158 function actionImgPath() {
160 return $Theme->_findFile("images/RateItAction.png");
164 * Take a string and quote it sufficiently to be passed as a Javascript
167 function _javascript_quote_string($s) {
168 return str_replace("'", "\'", $s);
171 function getDefaultArguments() {
172 return array( 'pagename' => '[pagename]',
176 'dimension' => false,
183 function head() { // early side-effects (before body)
185 $Theme->addMoreHeaders($this->RatingWidgetJavascript());
188 // todo: only for signed users
189 // todo: set rating dbi for external rating database
190 function run($dbi, $argstr, $request, $basepage) {
192 $this->_request = & $request;
193 $this->_dbi = & $dbi;
194 $user = & $request->getUser();
195 $this->userid = $user->UserName();
196 $args = $this->getArgs($argstr, $request);
197 $this->dimension = $args['dimension'];
198 if ($this->dimension == '') {
199 $this->dimension = 0;
200 $args['dimension'] = 0;
202 if ($args['pagename']) {
203 // Expand relative page names.
204 $page = new WikiPageName($args['pagename'], $basepage);
205 $args['pagename'] = $page->name;
207 if (empty($args['pagename'])) {
208 return $this->error(_("no page specified"));
210 $this->pagename = $args['pagename'];
212 if (RATING_STORAGE == 'SQL') {
213 $dbi = &$this->_dbi->_backend;
214 if (isa($dbi,'WikiDB_backend_PearDB'))
215 $this->dbtype = "PearDB";
217 $this->dbtype = "ADODB";
218 $this->iter_class = "WikiDB_backend_".$this->dbtype."_generic_iter";
219 extract($dbi->_table_names);
220 if (empty($rating_tbl)) {
221 $rating_tbl = (!empty($GLOBALS['DBParams']['prefix'])
222 ? $GLOBALS['DBParams']['prefix'] : '') . 'rating';
223 $dbi->_table_names['rating_tbl'] = $rating_tbl;
227 if ($args['mode'] === 'add') {
228 if (!$user->isSignedIn())
229 return $this->error(_("You must sign in"));
231 $actionImg = $Theme->_path . $this->actionImgPath();
232 $this->addRating($request->getArg('rating'));
233 ob_end_clean(); // discard any previous output
235 $page = $request->getPage();
236 $page->set('_cached_html', false);
237 $request->cacheControl('MUST-REVALIDATE');
239 //fake validators without args
240 $request->appendValidators(array('wikiname' => WIKI_NAME,
241 'args' => hash('')));
242 header('Content-type: image/png');
243 readfile($actionImg);
245 } elseif ($args['mode'] === 'delete') {
246 if (!$user->isSignedIn())
247 return $this->error(_("You must sign in"));
249 $actionImg = $Theme->_path . $this->actionImgPath();
250 $this->deleteRating();
251 ob_end_clean(); // discard any previous output
253 $page = $request->getPage();
254 $page->set('_cached_html', false);
255 $request->cacheControl('MUST-REVALIDATE');
257 //fake validators without args
258 $request->appendValidators(array('wikiname' => WIKI_NAME,
259 'args' => hash('')));
260 header('Content-type: image/png');
261 readfile($actionImg);
263 } elseif (! $args['show'] ) {
264 // we must use the head method instead, because <body> is already printed.
265 // $Theme->addMoreHeaders($this->RatingWidgetJavascript());
266 // or we change the header in the ob_buffer.
268 //Todo: add a validator based on the users last rating mtime
269 $rating = $this->getRating();
271 static $validated = 0;
273 //$page = $request->getPage();
274 //$page->set('_cached_html', false);
275 $request->cacheControl('REVALIDATE');
279 $args['rating'] = $rating;
280 return $this->RatingWidgetHtml($args);
282 if (!$user->isSignedIn())
283 return $this->error(_("You must sign in"));
285 $rating = $this->getRating();
286 $html = HTML::p(sprintf(_("Rated by %d users | Average rating %.1f stars"),
287 $this->getNumUsers($this->pagename,$this->dimension),
288 $this->getAvg($this->pagename,$this->dimension)),
290 if ($rating !== false)
291 $html->pushContent(sprintf(_("Your rating was %.1f"),
294 $pred = $this->getPrediction($this->userid,$this->pagename,$this->dimension);
295 if (is_string($pred))
296 $html->pushContent(sprintf(_("%s prediction for you is %s stars"),
299 $html->pushContent(sprintf(_("%s prediction for you is %.1f stars"),
302 $html->pushContent(HTML::p());
303 $html->pushContent(HTML::em("(Experimental: This is entirely bogus data)"));
308 // box is used to display a fixed-width, narrow version with common header
309 function box($args=false, $request=false, $basepage=false) {
310 if (!$request) $request =& $GLOBALS['request'];
311 if (!$request->_user->isSignedIn()) return;
312 if (!isset($args)) $args = array();
315 foreach ($args as $key => $value)
316 $argstr .= $key."=".$value;
317 $widget = $this->run($request->_dbi, $argstr, $request, $basepage);
319 return $this->makeBox(WikiLink(_("RateIt"),'',_("Rate It")),
323 function addRating($rating, $userid=null, $pagename=null, $dimension=null) {
324 if (is_null($dimension)) $dimension = $this->dimension;
325 if (is_null($userid)) $userid = $this->userid;
326 if (is_null($pagename)) $pagename = $this->pagename;
327 if (RATING_STORAGE == 'SQL') {
328 $page = $this->_dbi->getPage($this->pagename);
329 $current = $page->getCurrentRevision();
330 $rateeversion = $current->getVersion();
331 $this->sql_rate($userid, $pagename, $rateeversion, $dimension, $rating);
333 $this->metadata_set_rating($userid, $pagename, $dimension, $rating);
337 function deleteRating($userid=null, $pagename=null, $dimension=null) {
338 if (is_null($dimension)) $dimension = $this->dimension;
339 if (is_null($userid)) $userid = $this->userid;
340 if (is_null($pagename)) $pagename = $this->pagename;
341 if (RATING_STORAGE == 'SQL') {
342 $this->sql_delete_rating($userid, $pagename, $dimension);
344 $this->metadata_set_rating($userid, $pagename, $dimension, -1);
348 function getRating($userid=null, $pagename=null, $dimension=null) {
349 if (is_null($dimension)) $dimension = $this->dimension;
350 if (is_null($userid)) $userid = $this->userid;
351 if (is_null($pagename)) $pagename = $this->pagename;
352 if (RATING_STORAGE == 'SQL') {
353 $ratings_iter = $this->sql_get_rating($dimension, $userid, $pagename);
354 if ($rating = $ratings_iter->next()) {
355 return $rating['ratingvalue'];
359 return $this->metadata_get_rating($userid, $pagename, $dimension);
364 // Currently we have to call the "suggest" CGI
365 // http://www-users.cs.umn.edu/~karypis/suggest/
366 // until we implement a simple recommendation engine.
367 // Note that "suggest" is only free for non-profit organizations.
368 // I am currently writing a binary CGI using suggest, which loads
370 function getPrediction($userid=null, $pagename=null, $dimension=null) {
371 if (is_null($dimension)) $dimension = $this->dimension;
372 if (is_null($userid)) $userid = $this->userid;
373 if (is_null($pagename)) $pagename = $this->pagename;
374 $dbi = &$this->_dbi->_backend;
375 if (isset($pagename))
376 $page = $dbi->_get_pageid($pagename);
379 $user = $dbi->_get_pageid($userid);
384 if (defined('RATING_EXTERNAL')) {
385 // how call suggest.exe? as CGI or natively
386 //$rating = HTML::Raw("<!--#include virtual=".RATING_ENGINE." -->");
387 $args = "-u$user -p$page -malpha"; // --top 10
388 if (isset($dimension))
389 $args .= " -d$dimension";
390 $rating = passthru(RATING_EXTERNAL . " $args");
392 $rating = $this->php_prediction($userid, $pagename, $dimension);
398 * TODO: slow item-based recommendation engine, similar to suggest RType=2.
399 * Only the SUGGEST_EstimateAlpha part
401 function php_prediction($userid=null, $pagename=null, $dimension=null) {
402 if (is_null($dimension)) $dimension = $this->dimension;
403 if (is_null($userid)) $userid = $this->userid;
404 if (is_null($pagename)) $pagename = $this->pagename;
405 if (RATING_STORAGE == 'SQL') {
413 function getNumUsers($pagename=null, $dimension=null) {
414 if (is_null($dimension)) $dimension = $this->dimension;
415 if (is_null($pagename)) $pagename = $this->pagename;
416 if (RATING_STORAGE == 'SQL') {
417 $ratings_iter = $this->sql_get_rating($dimension, null, $pagename,
419 return $ratings_iter->count();
421 $page = $this->_dbi->getPage($pagename);
422 $data = $page->get('rating');
423 if (!empty($data[$dimension]))
424 return count($data[$dimension]);
429 // TODO: metadata method
430 function getAvg($pagename=null, $dimension=null) {
431 if (is_null($dimension)) $dimension = $this->dimension;
432 if (is_null($pagename)) $pagename = $this->pagename;
433 if (RATING_STORAGE == 'SQL') {
434 $dbi = &$this->_dbi->_backend;
436 if (isset($pagename)) {
437 $raterid = $dbi->_get_pageid($pagename, true);
438 $where .= " AND raterpage=$raterid";
440 if (isset($dimension)) {
441 $where .= " AND dimension=$dimension";
443 //$dbh = &$this->_dbi;
444 extract($dbi->_table_names);
445 $query = "SELECT AVG(ratingvalue) as avg"
446 . " FROM $rating_tbl r, $page_tbl p "
447 . $where. " GROUP BY raterpage";
448 $result = $dbi->_dbh->query($query);
449 $iter = new $this->iter_class($this,$result);
450 $row = $iter->next();
460 * @param dimension The rating dimension id.
463 * If this is null (or left off), the search for ratings
464 * is not restricted by dimension.
466 * @param rater The page id of the rater, i.e. page doing the rating.
467 * This is a Wiki page id, often of a user page.
470 * If this is null (or left off), the search for ratings
471 * is not restricted by rater.
472 * TODO: Support an array
474 * @param ratee The page id of the ratee, i.e. page being rated.
475 * Example: "DudeWheresMyCar"
477 * If this is null (or left off), the search for ratings
478 * is not restricted by ratee.
479 * TODO: Support an array
481 * @param orderby An order-by clause with fields and (optionally) ASC
483 * Example: "ratingvalue DESC"
485 * If this is null (or left off), the search for ratings
486 * has no guaranteed order
488 * @param pageinfo The type of page that has its info returned (i.e.,
489 * 'pagename', 'hits', and 'pagedata') in the rows.
492 * If this is null (or left off), the info returned
493 * is for the 'ratee' page (i.e., thing being rated).
495 * @return DB iterator with results
497 function sql_get_rating($dimension=null, $rater=null, $ratee=null,
498 $orderby=null, $pageinfo = "ratee") {
499 if (empty($dimension)) $dimension=null;
500 $result = $this->_sql_get_rating_result($dimension, $rater, $ratee, $orderby, $pageinfo);
501 return new $this->iter_class($this, $result);
505 * Like get_rating(), but return a result suitable for WikiDB_PageIterator
507 function _sql_get_rating_page($dimension=null, $rater=null, $ratee=null,
508 $orderby=null, $pageinfo = "ratee") {
509 if (empty($dimension)) $dimension=null;
510 $result = $this->_sql_get_rating_result($dimension, $rater, $ratee, $orderby, $pageinfo);
512 return new $this->iter_class($this, $result);
517 * @return DB iterator with results
519 function _sql_get_rating_result($dimension=null, $rater=null, $ratee=null,
520 $orderby=null, $pageinfo = "ratee") {
521 // pageinfo must be 'rater' or 'ratee'
522 if (($pageinfo != "ratee") && ($pageinfo != "rater"))
525 $dbi = &$this->_dbi->_backend;
526 //$dbh = &$this->_dbi;
527 extract($dbi->_table_names);
528 $where = "WHERE r." . $pageinfo . "page = p.id";
529 if (isset($dimension)) {
530 $where .= " AND dimension=$dimension";
533 $raterid = $dbi->_get_pageid($rater, true);
534 $where .= " AND raterpage=$raterid";
537 $rateeid = $dbi->_get_pageid($ratee, true);
538 $where .= " AND rateepage=$rateeid";
541 if (isset($orderby)) {
542 $orderbyStr = " ORDER BY " . $orderby;
546 . " FROM $rating_tbl r, $page_tbl p "
550 $result = $dbi->_dbh->query($query);
557 * @param rater The page id of the rater, i.e. page doing the rating.
558 * This is a Wiki page id, often of a user page.
559 * @param ratee The page id of the ratee, i.e. page being rated.
560 * @param dimension The rating dimension id.
564 * @return true upon success
566 function sql_delete_rating($rater, $ratee, $dimension) {
567 //$dbh = &$this->_dbi;
568 $dbi = &$this->_dbi->_backend;
569 extract($dbi->_table_names);
572 $raterid = $dbi->_get_pageid($rater, true);
573 $rateeid = $dbi->_get_pageid($ratee, true);
574 $where = "WHERE raterpage=$raterid and rateepage=$rateeid";
575 if (isset($dimension)) {
576 $where .= " AND dimension=$dimension";
578 $dbi->_dbh->query("DELETE FROM $rating_tbl $where");
586 * @param rater The page id of the rater, i.e. page doing the rating.
587 * This is a Wiki page id, often of a user page.
588 * @param ratee The page id of the ratee, i.e. page being rated.
589 * @param rateeversion The version of the ratee page.
590 * @param dimension The rating dimension id.
591 * @param rating The rating value (a float).
595 * @return true upon success
597 //$this->userid, $this->pagename, $this->dimension, $rating);
598 function sql_rate($rater, $ratee, $rateeversion, $dimension, $rating) {
599 $dbi = &$this->_dbi->_backend;
600 extract($dbi->_table_names);
601 if (empty($rating_tbl))
602 $rating_tbl = (!empty($GLOBALS['DBParams']['prefix']) ? $GLOBALS['DBParams']['prefix'] : '') . 'rating';
605 $raterid = $dbi->_get_pageid($rater, true);
606 $rateeid = $dbi->_get_pageid($ratee, true);
607 $where = "WHERE raterpage=$raterid AND rateepage=$rateeid";
608 if (isset($dimension)) $where .= " AND dimension=$dimension";
609 $dbi->_dbh->query("DELETE FROM $rating_tbl $where");
610 // NOTE: Leave tstamp off the insert, and MySQL automatically updates it (only if MySQL is used)
611 $dbi->_dbh->query("INSERT INTO $rating_tbl (dimension, raterpage, rateepage, ratingvalue, rateeversion) VALUES ('$dimension', $raterid, $rateeid, '$rating', '$rateeversion')");
616 function metadata_get_rating($userid, $pagename, $dimension) {
617 $page = $this->_dbi->getPage($pagename);
618 $data = $page->get('rating');
619 if (!empty($data[$dimension][$userid]))
620 return (float)$data[$dimension][$userid];
625 function metadata_set_rating($userid, $pagename, $dimension, $rating = -1) {
626 $page = $this->_dbi->getPage($pagename);
627 $data = $page->get('rating');
629 unset($data[$dimension][$userid]);
631 if (empty($data[$dimension][$userid]))
632 $data[$dimension] = array($userid => (float)$rating);
634 $data[$dimension][$userid] = $rating;
636 $page->set('rating',$data);
640 * HTML widget display
642 * This needs to be put in the <body> section of the page.
644 * @param pagename Name of the page to rate
645 * @param version Version of the page to rate (may be "" for current)
646 * @param imgPrefix Prefix of the names of the images that display the rating
647 * You can have two widgets for the same page displayed at
648 * once iff the imgPrefix-s are different.
649 * @param dimension Id of the dimension to rate
650 * @param small Makes a smaller ratings widget if non-false
652 * Limitations: Currently this can only print the current users ratings.
653 * And only the widget, but no value (for buddies) also.
655 function RatingWidgetHtml($args) {
656 global $Theme, $request;
658 if (!$request->_user->isSignedIn()) return;
659 $imgPrefix = $pagename . $imgPrefix;
660 $actionImgName = $imgPrefix . 'RateItAction';
661 $dbi =& $GLOBALS['request']->getDbh();
662 $version = $dbi->_backend->get_latest_version($pagename);
664 // Protect against 's, though not \r or \n
665 $reImgPrefix = $this->_javascript_quote_string($imgPrefix);
666 $reActionImgName = $this->_javascript_quote_string($actionImgName);
667 $rePagename = $this->_javascript_quote_string($pagename);
668 //$dimension = $args['pagename'] . "rat";
670 $html = HTML::span(array("id" => $id));
671 for ($i=0; $i < 2; $i++) {
672 $nk[$i] = $Theme->_findData("images/RateItNk$i.png");
673 $none[$i] = $Theme->_findData("images/RateItRk$i.png");
676 $html->pushContent(Button(_("RateIt"),_("RateIt"),$pagename));
677 $html->pushContent(HTML::raw(' '));
681 $user = $request->getUser();
682 $userid = $user->getId();
683 if (!isset($args['rating']))
684 $rating = $this->getRating($userid, $pagename, $dimension);
686 $pred = $this->getPrediction($userid,$pagename,$dimension);
688 for ($i = 1; $i <= 10; $i++) {
689 $a1 = HTML::a(array('href' => 'javascript:click(\'' . $reActionImgName . '\',\'' . $rePagename . '\',\'' . $version . '\',\'' . $reImgPrefix . '\',\'' . $dimension . '\',' . ($i/2) . ')'));
691 $img_attr['src'] = $nk[$i%2];
692 if (!$rating and !$pred)
693 $img_attr['src'] = $none[$i%2];
694 $img_attr['name'] = $imgPrefix . $i;
695 $img_attr['border'] = 0;
696 $a1->pushContent(HTML::img($img_attr));
697 $a1->addToolTip(_("Rate the topic of this page"));
698 $html->pushContent($a1);
699 //This adds a space between the rating smilies:
700 // if (($i%2) == 0) $html->pushContent(' ');
702 $html->pushContent(HTML::Raw(' '));
703 $a0 = HTML::a(array('href' => 'javascript:click(\'' . $reActionImgName . '\',\'' . $rePagename . '\',\'' . $version . '\',\'' . $reImgPrefix . '\',\'' . $dimension . '\',\'X\')'));
705 $msg = _("Cancel rating");
706 $a0->pushContent(HTML::img(array('src' => $Theme->getImageUrl("RateItCancel"),
707 'name'=> $imgPrefix.'Cancel',
709 $a0->addToolTip($msg);
710 $html->pushContent($a0);
712 $msg = _("No opinion");
713 $html->pushContent(HTML::img(array('src' => $Theme->getImageUrl("RateItCancelN"),
714 'name'=> $imgPrefix.'Cancel',
716 //$a0->addToolTip($msg);
717 //$html->pushContent($a0);
720 $img_attr['src'] = $Theme->_findData("images/RateItAction.png");
721 $img_attr['name'] = $actionImgName;
722 //$img_attr['class'] = 'k' . $i;
723 $img_attr['border'] = 0;
724 $html->pushContent(HTML::img($img_attr));
725 // Display the current rating if there is one
727 $html->pushContent(JavaScript('displayRating(\'' . $reImgPrefix . '\','.$rating .',0)'));
729 $html->pushContent(JavaScript('displayRating(\'' . $reImgPrefix . '\','.$pred .',1)'));
731 $html->pushContent(JavaScript('displayRating(\'' . $reImgPrefix . '\',0,0)'));
738 // $Log: not supported by cvs2svn $
739 // Revision 1.2 2004/03/31 06:22:22 rurban
740 // shorter javascript,
741 // added prediction buttons and display logic,
742 // empty HTML if not signed in.
743 // fixed deleting (empty dimension => 0)
745 // Revision 1.1 2004/03/30 02:38:06 rurban
746 // RateIt support (currently no recommendation engine yet)
754 // c-hanging-comment-ender-p: nil
755 // indent-tabs-mode: nil