2 rcs_id('$Id: RateIt.php,v 1.8 2004-06-01 15:28:01 rurban Exp $');
4 Copyright 2004 $ThePhpWikiProgrammingTeam
6 This file is 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'); // not fully supported yet
24 define('RATING_STORAGE','SQL'); // only for mysql yet.
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 * Recommender Engines:
63 * Recommendation/Prediction is a special field of "Data Mining"
64 * For a list of (also free) software see
65 * http://www.the-data-mine.com/bin/view/Software/WebIndex
66 * - movielens: (Java Server) will be gpl'd in summer 2004 (weighted)
67 * - suggest: is free for non-commercial use, available as compiled library
69 * - Autoclass: simple public domain C library
70 * - MLC++: C++ library http://www.sgi.com/tech/mlc/
72 * Usage: <?plugin RateIt ?> to enable rating on this page
73 * Note: The wikilens theme must be enabled, to enable this plugin!
74 * Or use a sidebar based theme with the box method.
75 * <?plugin RateIt show=ratings ?> to show my ratings
76 * <?plugin RateIt show=buddies ?> to show my buddies
77 * <?plugin RateIt show=ratings dimension=1 ?>
79 * @author: Dan Frankowski (wikilens author), Reini Urban (as plugin)
82 * - fix RATING_STORAGE = WIKIPAGE
84 * - finish mysuggest.c (external engine with data from mysql)
85 * - add php_prediction
86 * - add the various show modes (esp. TopN queries in PHP)
90 dimension INT(4) NOT NULL,
91 raterpage INT(11) NOT NULL,
92 rateepage INT(11) NOT NULL,
93 ratingvalue FLOAT NOT NULL,
94 rateeversion INT(11) NOT NULL,
95 tstamp TIMESTAMP(14) NOT NULL,
96 PRIMARY KEY (dimension, raterpage, rateepage)
100 require_once("lib/WikiPlugin.php");
102 class WikiPlugin_RateIt
108 function getDescription() {
109 return _("Rating system. Store user ratings per page");
111 function getVersion() {
112 return preg_replace("/[Revision: $]/", '',
113 "\$Revision: 1.8 $");
116 function RatingWidgetJavascript() {
118 $img = substr($Theme->_findData("images/RateItNk0.png"),0,-7);
119 $urlprefix = WikiURL("",0,1);
121 function displayRating(imgPrefix, ratingvalue, pred) {
122 var cancel = imgPrefix + 'Cancel';
123 for (i=1; i<=10; i++) {
124 var imgName = imgPrefix + i;
125 var imgSrc = '".$img."';
127 document[imgName].title = '"._("Predicted rating ")."'+ratingvalue;
129 document[imgName].title = '"._("Your rating ")."'+ratingvalue;
130 if (i<=(ratingvalue*2)) {
132 document[imgName].src = imgSrc + ((i%2) ? 'Rk1' : 'Rk0') + '.png';
134 document[imgName].src = imgSrc + ((i%2) ? 'Ok1' : 'Ok0') + '.png';
136 document[imgName].src = imgSrc + ((i%2) ? 'Nk1' : 'Nk0') + '.png';
139 if ((pred == 0) && (ratingvalue > 0))
140 document[cancel].src = imgSrc + 'Cancel.png';
142 document[cancel].src = imgSrc + 'CancelN.png';
144 function click(actionImg, pagename, version, imgPrefix, dimension, rating) {
146 deleteRating(actionImg, pagename, dimension);
147 displayRating(imgPrefix, 0, 0);
149 submitRating(actionImg, pagename, version, dimension, rating);
150 displayRating(imgPrefix, rating, 0);
153 function submitRating(actionImg, page, version, dimension, rating) {
154 var myRand = Math.round(Math.random()*(1000000));
155 var imgSrc = escape(page) + '?version=' + version + '&action=".urlencode(_("RateIt"))."&mode=add&rating=' + rating + '&dimension=' + dimension + '&nopurge=cache&rand=' + myRand;
156 //alert('submitRating(' + page + ', ' + version + ', ' + dimension + ', ' + rating + ') => '+imgSrc);
157 document[actionImg].src= imgSrc;
159 function deleteRating(actionImg, page, dimension) {
160 var myRand = Math.round(Math.random()*(1000000));
161 var imgSrc = '".$urlprefix."' + escape(page) + '?action=".urlencode(_("RateIt"))."&mode=delete&dimension=' + dimension + '&nopurge=cache&rand=' + myRand;
162 //alert('deleteRating(' + page + ', ' + version + ', ' + dimension + ')');
163 document[actionImg].src= imgSrc;
166 return JavaScript($js);
169 function actionImgPath() {
171 return $Theme->_findFile("images/RateItAction.png");
175 * Take a string and quote it sufficiently to be passed as a Javascript
178 function _javascript_quote_string($s) {
179 return str_replace("'", "\'", $s);
182 function getDefaultArguments() {
183 return array( 'pagename' => '[pagename]',
187 'dimension' => false,
194 function head() { // early side-effects (before body)
196 $Theme->addMoreHeaders($this->RatingWidgetJavascript());
199 // todo: only for signed users
200 // todo: set rating dbi for external rating database
201 function run($dbi, $argstr, $request, $basepage) {
203 $this->_request = & $request;
204 $this->_dbi = & $dbi;
205 $user = & $request->getUser();
206 $this->userid = $user->UserName();
207 $args = $this->getArgs($argstr, $request);
208 $this->dimension = $args['dimension'];
209 if ($this->dimension == '') {
210 $this->dimension = 0;
211 $args['dimension'] = 0;
213 if ($args['pagename']) {
214 // Expand relative page names.
215 $page = new WikiPageName($args['pagename'], $basepage);
216 $args['pagename'] = $page->name;
218 if (empty($args['pagename'])) {
219 return $this->error(_("no page specified"));
221 $this->pagename = $args['pagename'];
223 if (RATING_STORAGE == 'SQL') {
224 $dbi = &$this->_dbi->_backend;
225 if (isa($dbi,'WikiDB_backend_PearDB'))
226 $this->dbtype = "PearDB";
228 $this->dbtype = "ADODB";
229 $this->iter_class = "WikiDB_backend_".$this->dbtype."_generic_iter";
230 extract($dbi->_table_names);
231 if (empty($rating_tbl)) {
232 $rating_tbl = (!empty($GLOBALS['DBParams']['prefix'])
233 ? $GLOBALS['DBParams']['prefix'] : '') . 'rating';
234 $dbi->_table_names['rating_tbl'] = $rating_tbl;
238 if ($args['mode'] === 'add') {
239 if (!$user->isSignedIn())
240 return $this->error(_("You must sign in"));
242 $actionImg = $Theme->_path . $this->actionImgPath();
243 $this->addRating($request->getArg('rating'));
244 ob_end_clean(); // discard any previous output
246 $page = $request->getPage();
247 $page->set('_cached_html', false);
248 $request->cacheControl('MUST-REVALIDATE');
250 //fake validators without args
251 $request->appendValidators(array('wikiname' => WIKI_NAME,
252 'args' => hash('')));
253 header('Content-type: image/png');
254 readfile($actionImg);
256 } elseif ($args['mode'] === 'delete') {
257 if (!$user->isSignedIn())
258 return $this->error(_("You must sign in"));
260 $actionImg = $Theme->_path . $this->actionImgPath();
261 $this->deleteRating();
262 ob_end_clean(); // discard any previous output
264 $page = $request->getPage();
265 $page->set('_cached_html', false);
266 $request->cacheControl('MUST-REVALIDATE');
268 //fake validators without args
269 $request->appendValidators(array('wikiname' => WIKI_NAME,
270 'args' => hash('')));
271 header('Content-type: image/png');
272 readfile($actionImg);
274 } elseif (! $args['show'] ) {
275 // we must use the head method instead, because <body> is already printed.
276 // $Theme->addMoreHeaders($this->RatingWidgetJavascript());
277 // or we change the header in the ob_buffer.
279 //Todo: add a validator based on the users last rating mtime
280 $rating = $this->getRating();
282 static $validated = 0;
284 //$page = $request->getPage();
285 //$page->set('_cached_html', false);
286 $request->cacheControl('REVALIDATE');
290 $args['rating'] = $rating;
291 return $this->RatingWidgetHtml($args);
293 if (!$user->isSignedIn())
294 return $this->error(_("You must sign in"));
296 $rating = $this->getRating();
297 $html = HTML::p(sprintf(_("Rated by %d users | Average rating %.1f stars"),
298 $this->getNumUsers($this->pagename,$this->dimension),
299 $this->getAvg($this->pagename,$this->dimension)),
301 if ($rating !== false)
302 $html->pushContent(sprintf(_("Your rating was %.1f"),
305 $pred = $this->getPrediction($this->userid,$this->pagename,$this->dimension);
306 if (is_string($pred))
307 $html->pushContent(sprintf(_("%s prediction for you is %s stars"),
310 $html->pushContent(sprintf(_("%s prediction for you is %.1f stars"),
313 $html->pushContent(HTML::p());
314 $html->pushContent(HTML::em("(Experimental: This is entirely bogus data)"));
319 // box is used to display a fixed-width, narrow version with common header
320 function box($args=false, $request=false, $basepage=false) {
321 if (!$request) $request =& $GLOBALS['request'];
322 if (!$request->_user->isSignedIn()) return;
323 if (!isset($args)) $args = array();
326 foreach ($args as $key => $value)
327 $argstr .= $key."=".$value;
328 $widget = $this->run($request->_dbi, $argstr, $request, $basepage);
330 return $this->makeBox(WikiLink(_("RateIt"),'',_("Rate It")),
334 function addRating($rating, $userid=null, $pagename=null, $dimension=null) {
335 if (is_null($dimension)) $dimension = $this->dimension;
336 if (is_null($userid)) $userid = $this->userid;
337 if (is_null($pagename)) $pagename = $this->pagename;
338 if (RATING_STORAGE == 'SQL') {
339 $page = $this->_dbi->getPage($this->pagename);
340 $current = $page->getCurrentRevision();
341 $rateeversion = $current->getVersion();
342 $this->sql_rate($userid, $pagename, $rateeversion, $dimension, $rating);
344 $this->metadata_set_rating($userid, $pagename, $dimension, $rating);
348 function deleteRating($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 $this->sql_delete_rating($userid, $pagename, $dimension);
355 $this->metadata_set_rating($userid, $pagename, $dimension, -1);
359 function getRating($userid=null, $pagename=null, $dimension=null) {
360 if (is_null($dimension)) $dimension = $this->dimension;
361 if (is_null($userid)) $userid = $this->userid;
362 if (is_null($pagename)) $pagename = $this->pagename;
363 if (RATING_STORAGE == 'SQL') {
364 $ratings_iter = $this->sql_get_rating($dimension, $userid, $pagename);
365 if ($rating = $ratings_iter->next()) {
366 return $rating['ratingvalue'];
370 return $this->metadata_get_rating($userid, $pagename, $dimension);
374 function getUsersRated($dimension=null, $orderby = null) {
375 if (is_null($dimension)) $dimension = $this->dimension;
376 if (is_null($userid)) $userid = $this->userid;
377 if (is_null($pagename)) $pagename = $this->pagename;
378 if (RATING_STORAGE == 'SQL') {
379 $ratings_iter = $this->sql_get_users_rated($dimension, $orderby);
380 if ($rating = $ratings_iter->next()) {
381 return $rating['ratingvalue'];
385 return $this->metadata_get_users_rated($dimension, $orderby);
390 // Currently we have to call the "suggest" CGI
391 // http://www-users.cs.umn.edu/~karypis/suggest/
392 // until we implement a simple recommendation engine.
393 // Note that "suggest" is only free for non-profit organizations.
394 // I am currently writing a binary CGI using suggest, which loads
396 function getPrediction($userid=null, $pagename=null, $dimension=null) {
397 if (is_null($dimension)) $dimension = $this->dimension;
398 if (is_null($userid)) $userid = $this->userid;
399 if (is_null($pagename)) $pagename = $this->pagename;
400 $dbi = &$this->_dbi->_backend;
401 if (isset($pagename))
402 $page = $dbi->_get_pageid($pagename);
405 $user = $dbi->_get_pageid($userid);
410 if (defined('RATING_EXTERNAL')) {
411 // how call suggest.exe? as CGI or natively
412 //$rating = HTML::Raw("<!--#include virtual=".RATING_ENGINE." -->");
413 $args = "-u$user -p$page -malpha"; // --top 10
414 if (isset($dimension))
415 $args .= " -d$dimension";
416 $rating = passthru(RATING_EXTERNAL . " $args");
418 $rating = $this->php_prediction($userid, $pagename, $dimension);
424 * TODO: slow item-based recommendation engine, similar to suggest RType=2.
425 * Only the SUGGEST_EstimateAlpha part
427 function php_prediction($userid=null, $pagename=null, $dimension=null) {
428 if (is_null($dimension)) $dimension = $this->dimension;
429 if (is_null($userid)) $userid = $this->userid;
430 if (is_null($pagename)) $pagename = $this->pagename;
431 if (RATING_STORAGE == 'SQL') {
439 function getNumUsers($pagename=null, $dimension=null) {
440 if (is_null($dimension)) $dimension = $this->dimension;
441 if (is_null($pagename)) $pagename = $this->pagename;
442 if (RATING_STORAGE == 'SQL') {
443 $ratings_iter = $this->sql_get_rating($dimension, null, $pagename,
445 return $ratings_iter->count();
447 $page = $this->_dbi->getPage($pagename);
448 $data = $page->get('rating');
449 if (!empty($data[$dimension]))
450 return count($data[$dimension]);
455 // TODO: metadata method
456 function getAvg($pagename=null, $dimension=null) {
457 if (is_null($dimension)) $dimension = $this->dimension;
458 if (is_null($pagename)) $pagename = $this->pagename;
459 if (RATING_STORAGE == 'SQL') {
460 $dbi = &$this->_dbi->_backend;
462 if (isset($pagename)) {
463 $raterid = $dbi->_get_pageid($pagename, true);
464 $where .= " AND raterpage=$raterid";
466 if (isset($dimension)) {
467 $where .= " AND dimension=$dimension";
469 //$dbh = &$this->_dbi;
470 extract($dbi->_table_names);
471 $query = "SELECT AVG(ratingvalue) as avg"
472 . " FROM $rating_tbl r, $page_tbl p "
473 . $where. " GROUP BY raterpage";
474 $result = $dbi->_dbh->query($query);
475 $iter = new $this->iter_class($this,$result);
476 $row = $iter->next();
486 * @param dimension The rating dimension id.
489 * If this is null (or left off), the search for ratings
490 * is not restricted by dimension.
492 * @param rater The page id of the rater, i.e. page doing the rating.
493 * This is a Wiki page id, often of a user page.
496 * If this is null (or left off), the search for ratings
497 * is not restricted by rater.
498 * TODO: Support an array
500 * @param ratee The page id of the ratee, i.e. page being rated.
501 * Example: "DudeWheresMyCar"
503 * If this is null (or left off), the search for ratings
504 * is not restricted by ratee.
505 * TODO: Support an array
507 * @param orderby An order-by clause with fields and (optionally) ASC
509 * Example: "ratingvalue DESC"
511 * If this is null (or left off), the search for ratings
512 * has no guaranteed order
514 * @param pageinfo The type of page that has its info returned (i.e.,
515 * 'pagename', 'hits', and 'pagedata') in the rows.
518 * If this is null (or left off), the info returned
519 * is for the 'ratee' page (i.e., thing being rated).
521 * @return DB iterator with results
523 function sql_get_rating($dimension=null, $rater=null, $ratee=null,
524 $orderby=null, $pageinfo = "ratee") {
525 if (empty($dimension)) $dimension=null;
526 $result = $this->_sql_get_rating_result($dimension, $rater, $ratee, $orderby, $pageinfo);
527 return new $this->iter_class($this, $result);
530 function sql_get_users_rated($dimension=null, $orderby=null) {
531 if (empty($dimension)) $dimension=null;
532 $result = $this->_sql_get_rating_result($dimension, null, null, $orderby, "rater");
533 return new $this->iter_class($this, $result);
538 * @return result ressource, suitable to the iterator
540 function _sql_get_rating_result($dimension=null, $rater=null, $ratee=null,
541 $orderby=null, $pageinfo = "ratee") {
542 // pageinfo must be 'rater' or 'ratee'
543 if (($pageinfo != "ratee") && ($pageinfo != "rater"))
546 $dbi = &$this->_dbi->_backend;
547 //$dbh = &$this->_dbi;
548 extract($dbi->_table_names);
549 $where = "WHERE r." . $pageinfo . "page = p.id";
550 if (isset($dimension)) {
551 $where .= " AND dimension=$dimension";
554 $raterid = $dbi->_get_pageid($rater, true);
555 $where .= " AND raterpage=$raterid";
558 $rateeid = $dbi->_get_pageid($ratee, true);
559 $where .= " AND rateepage=$rateeid";
562 if (isset($orderby)) {
563 $orderbyStr = " ORDER BY " . $orderby;
565 if (isset($rater) or isset($ratee)) $what = '*';
566 // same as _get_users_rated_result()
567 else $what = 'DISTINCT p.pagename';
569 $query = "SELECT $what"
570 . " FROM $rating_tbl r, $page_tbl p "
574 $result = $dbi->_dbh->query($query);
581 * @param rater The page id of the rater, i.e. page doing the rating.
582 * This is a Wiki page id, often of a user page.
583 * @param ratee The page id of the ratee, i.e. page being rated.
584 * @param dimension The rating dimension id.
588 * @return true upon success
590 function sql_delete_rating($rater, $ratee, $dimension) {
591 //$dbh = &$this->_dbi;
592 $dbi = &$this->_dbi->_backend;
593 extract($dbi->_table_names);
596 $raterid = $dbi->_get_pageid($rater, true);
597 $rateeid = $dbi->_get_pageid($ratee, true);
598 $where = "WHERE raterpage=$raterid and rateepage=$rateeid";
599 if (isset($dimension)) {
600 $where .= " AND dimension=$dimension";
602 $dbi->_dbh->query("DELETE FROM $rating_tbl $where");
610 * @param rater The page id of the rater, i.e. page doing the rating.
611 * This is a Wiki page id, often of a user page.
612 * @param ratee The page id of the ratee, i.e. page being rated.
613 * @param rateeversion The version of the ratee page.
614 * @param dimension The rating dimension id.
615 * @param rating The rating value (a float).
619 * @return true upon success
621 // ($this->userid, $this->pagename, $this->dimension, $rating);
622 function sql_rate($rater, $ratee, $rateeversion, $dimension, $rating) {
623 $dbi = &$this->_dbi->_backend;
624 extract($dbi->_table_names);
625 if (empty($rating_tbl))
626 $rating_tbl = $this->_dbi->getParam('prefix') . 'rating';
629 $raterid = $dbi->_get_pageid($rater, true);
630 $rateeid = $dbi->_get_pageid($ratee, true);
633 $where = "WHERE raterpage='$raterid' AND rateepage='$rateeid'";
634 if (isset($dimension)) $where .= " AND dimension='$dimension'";
635 // atomic transaction:
636 $dbi->_dbh->query("UPDATE $rating_tbl SET ratingvalue='$rating', rateeversion='$rateeversion' $where");
639 $dbi->_dbh->query("DELETE FROM $rating_tbl $where");
640 // NOTE: Leave tstamp off the insert, and MySQL automatically updates it (only if MySQL is used)
641 $dbi->_dbh->query("INSERT INTO $rating_tbl (dimension, raterpage, rateepage, ratingvalue, rateeversion) VALUES ('$dimension', $raterid, $rateeid, '$rating', '$rateeversion')");
647 function metadata_get_rating($userid, $pagename, $dimension) {
648 $page = $this->_dbi->getPage($pagename);
649 $data = $page->get('rating');
650 if (!empty($data[$dimension][$userid]))
651 return (float)$data[$dimension][$userid];
656 function metadata_set_rating($userid, $pagename, $dimension, $rating = -1) {
657 $page = $this->_dbi->getPage($pagename);
658 $data = $page->get('rating');
660 unset($data[$dimension][$userid]);
662 if (empty($data[$dimension][$userid]))
663 $data[$dimension] = array($userid => (float)$rating);
665 $data[$dimension][$userid] = $rating;
667 $page->set('rating',$data);
671 * HTML widget display
673 * This needs to be put in the <body> section of the page.
675 * @param pagename Name of the page to rate
676 * @param version Version of the page to rate (may be "" for current)
677 * @param imgPrefix Prefix of the names of the images that display the rating
678 * You can have two widgets for the same page displayed at
679 * once iff the imgPrefix-s are different.
680 * @param dimension Id of the dimension to rate
681 * @param small Makes a smaller ratings widget if non-false
683 * Limitations: Currently this can only print the current users ratings.
684 * And only the widget, but no value (for buddies) also.
686 function RatingWidgetHtml($args) {
687 global $Theme, $request;
689 if (!$request->_user->isSignedIn()) return;
690 $imgPrefix = $pagename . $imgPrefix;
691 $actionImgName = $imgPrefix . 'RateItAction';
692 $dbi =& $GLOBALS['request']->getDbh();
693 $version = $dbi->_backend->get_latest_version($pagename);
695 // Protect against 's, though not \r or \n
696 $reImgPrefix = $this->_javascript_quote_string($imgPrefix);
697 $reActionImgName = $this->_javascript_quote_string($actionImgName);
698 $rePagename = $this->_javascript_quote_string($pagename);
699 //$dimension = $args['pagename'] . "rat";
701 $html = HTML::span(array("id" => $id));
702 for ($i=0; $i < 2; $i++) {
703 $nk[$i] = $Theme->_findData("images/RateItNk$i.png");
704 $none[$i] = $Theme->_findData("images/RateItRk$i.png");
707 $html->pushContent(Button(_("RateIt"),_("RateIt"), $pagename));
708 $html->pushContent(HTML::raw(' '));
712 $user = $request->getUser();
713 $userid = $user->getId();
714 if (!isset($args['rating']))
715 $rating = $this->getRating($userid, $pagename, $dimension);
717 $pred = $this->getPrediction($userid,$pagename,$dimension);
719 for ($i = 1; $i <= 10; $i++) {
720 $a1 = HTML::a(array('href' => 'javascript:click(\'' . $reActionImgName . '\',\'' . $rePagename . '\',\'' . $version . '\',\'' . $reImgPrefix . '\',\'' . $dimension . '\',' . ($i/2) . ')'));
722 $img_attr['src'] = $nk[$i%2];
723 if (!$rating and !$pred)
724 $img_attr['src'] = $none[$i%2];
725 $img_attr['name'] = $imgPrefix . $i;
726 $img_attr['border'] = 0;
727 $a1->pushContent(HTML::img($img_attr));
728 $a1->addToolTip(_("Rate the topic of this page"));
729 $html->pushContent($a1);
730 //This adds a space between the rating smilies:
731 // if (($i%2) == 0) $html->pushContent(' ');
733 $html->pushContent(HTML::Raw(' '));
734 $a0 = HTML::a(array('href' => 'javascript:click(\'' . $reActionImgName . '\',\'' . $rePagename . '\',\'' . $version . '\',\'' . $reImgPrefix . '\',\'' . $dimension . '\',\'X\')'));
736 $msg = _("Cancel rating");
737 $a0->pushContent(HTML::img(array('src' => $Theme->getImageUrl("RateItCancel"),
738 'name'=> $imgPrefix.'Cancel',
740 $a0->addToolTip($msg);
741 $html->pushContent($a0);
743 $msg = _("No opinion");
744 $html->pushContent(HTML::img(array('src' => $Theme->getImageUrl("RateItCancelN"),
745 'name'=> $imgPrefix.'Cancel',
747 //$a0->addToolTip($msg);
748 //$html->pushContent($a0);
751 $img_attr['src'] = $Theme->_findData("images/RateItAction.png");
752 $img_attr['name'] = $actionImgName;
753 //$img_attr['class'] = 'k' . $i;
754 $img_attr['border'] = 0;
755 $html->pushContent(HTML::img($img_attr));
756 // Display the current rating if there is one
758 $html->pushContent(JavaScript('displayRating(\'' . $reImgPrefix . '\','.$rating .',0)'));
760 $html->pushContent(JavaScript('displayRating(\'' . $reImgPrefix . '\','.$pred .',1)'));
762 $html->pushContent(JavaScript('displayRating(\'' . $reImgPrefix . '\',0,0)'));
769 // $Log: not supported by cvs2svn $
770 // Revision _1.2 2004/04/29 17:55:03 dfrankow
771 // Check in escape() changes to protect against leading spaces in pagename.
772 // This is untested with Reini's _("RateIt") additions to this plugin.
774 // Revision 1.7 2004/04/21 04:29:50 rurban
775 // write WikiURL consistently (not WikiUrl)
777 // Revision 1.6 2004/04/12 14:07:12 rurban
780 // Revision 1.5 2004/04/11 10:42:02 rurban
781 // pgsrc/CreatePagePlugin
783 // Revision 1.4 2004/04/06 20:00:11 rurban
784 // Cleanup of special PageList column types
785 // Added support of plugin and theme specific Pagelist Types
786 // Added support for theme specific UserPreferences
787 // Added session support for ip-based throttling
788 // sql table schema change: ALTER TABLE session ADD sess_ip CHAR(15);
789 // Enhanced postgres schema
790 // Added DB_Session_dba support
792 // Revision 1.3 2004/04/01 06:29:51 rurban
794 // RateIt also for ADODB
796 // Revision 1.2 2004/03/31 06:22:22 rurban
797 // shorter javascript,
798 // added prediction buttons and display logic,
799 // empty HTML if not signed in.
800 // fixed deleting (empty dimension => 0)
802 // Revision 1.1 2004/03/30 02:38:06 rurban
803 // RateIt support (currently no recommendation engine yet)
811 // c-hanging-comment-ender-p: nil
812 // indent-tabs-mode: nil