0byt3m1n1
Path:
/
data
/
applications
/
aps.bak
/
phpwiki
/
1.3.14-6
/
standard
/
htdocs
/
lib
/
plugin
/
[
Home
]
File: RateIt.php
<?php // -*-php-*- rcs_id('$Id: RateIt.php,v 1.22 2007/02/17 14:15:14 rurban Exp $'); /* Copyright 2004 $ThePhpWikiProgrammingTeam This file is part of PhpWiki. PhpWiki is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. PhpWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with PhpWiki; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * RateIt: A recommender system, based on MovieLens and suggest. * Store user ratings per pagename. The wikilens theme displays a navbar image bar * with some nice javascript magic and this plugin shows various recommendations. * * There should be two methods to store ratings: * In a SQL database as in wikilens http://dickens.cs.umn.edu/dfrankow/wikilens * * The most important fact: A page has more than one rating. There can * be (and will be!) many ratings per page (ratee): different raters * (users), in different dimensions. Are those stored per page * (ratee)? Then what if I wish to access the ratings per rater * (user)? * wikilens plans several user-centered applications like: * a) show my ratings * b) show my buddies' ratings * c) show how my ratings are like my buddies' * d) show where I agree/disagree with my buddy * e) show what this group of people agree/disagree on * * If the ratings are stored in a real DB in a table, we can index the * ratings by rater and ratee, and be confident in * performance. Currently MovieLens has 80,000 users, 7,000 items, * 10,000,000 ratings. This is an average of 1400 ratings/page if each * page were rated equally. However, they're not: the most popular * things have tens of thousands of ratings (e.g., "Pulp Fiction" has * 42,000 ratings). If ratings are stored per page, you would have to * save/read huge page metadata every time someone submits a * rating. Finally, the movie domain has an unusually small number of * items-- I'd expect a lot more in music, for example. * * For a simple rating system one can also store the rating in the page * metadata (default). * * Recommender Engines: * Recommendation/Prediction is a special field of "Data Mining" * For a list of (also free) software see * http://www.the-data-mine.com/bin/view/Software/WebIndex * - movielens: (Java Server) will be gpl'd in summer 2004 (weighted) * - suggest: is free for non-commercial use, available as compiled library * (non-weighted) * - Autoclass: simple public domain C library * - MLC++: C++ library http://www.sgi.com/tech/mlc/ * * Usage: <?plugin RateIt ?> to enable rating on this page * Note: The wikilens theme must be enabled, to enable this plugin! * Or use a sidebar based theme with the box method. * <?plugin RateIt show=ratings ?> to show my ratings * <?plugin RateIt show=buddies ?> to show my buddies * <?plugin RateIt show=ratings dimension=1 ?> * * @author: Dan Frankowski (wikilens author), Reini Urban (as plugin) * * TODO: * - fix RATING_STORAGE = WIKIPAGE * - fix smart caching * - finish mysuggest.c (external engine with data from mysql) */ require_once("lib/WikiPlugin.php"); require_once("lib/wikilens/RatingsDb.php"); class WikiPlugin_RateIt extends WikiPlugin { function getName() { return _("RateIt"); } function getDescription() { return _("Rating system. Store user ratings per page"); } function getVersion() { return preg_replace("/[Revision: $]/", '', "\$Revision: 1.22 $"); } function RatingWidgetJavascript() { global $WikiTheme; if (!empty($this->imgPrefix)) $imgPrefix = $this->imgPrefix; elseif (defined("RATEIT_IMGPREFIX")) $imgPrefix = RATEIT_IMGPREFIX; else $imgPrefix = ''; if ($imgPrefix and !$WikiTheme->_findData("images/RateIt".$imgPrefix."Nk0.png",1)) $imgPrefix = ''; $img = substr($WikiTheme->_findData("images/RateIt".$imgPrefix."Nk0.png"),0,-7); $urlprefix = WikiURL("",0,1); // TODO: check actions USE_PATH_INFO=false $js = " function displayRating(imgPrefix, ratingvalue, pred) { var cancel = imgPrefix + 'Cancel'; for (i=1; i<=10; i++) { var imgName = imgPrefix + i; var imgSrc = '".$img."'; document[imgName].title = '"._("Your rating ")."'+ratingvalue; var imgType = 'N'; if (pred) { imgType = 'R'; } else if (i<=(ratingvalue*2)) { imgType = 'O'; } document[imgName].src = imgSrc + imgType + ((i%2) ? 'k1' : 'k0') + '.png'; } //document[cancel].src = imgSrc + 'Cancel.png'; } function click(actionImg, pagename, version, imgPrefix, dimension, rating) { if (rating == 'X') { deleteRating(actionImg, pagename, dimension); displayRating(imgPrefix, 0, 0); } else { submitRating(actionImg, pagename, version, dimension, rating); displayRating(imgPrefix, rating, 0); } } function submitRating(actionImg, page, version, dimension, rating) { var myRand = Math.round(Math.random()*(1000000)); var imgSrc = '".$urlprefix."' + escape(page) + '?version=' + version + '&action=".urlencode(_("RateIt"))."&mode=add&rating=' + rating + '&dimension=' + dimension + '&nopurge=1&rand=' + myRand" .(!empty($_GET['start_debug']) ? "+'&start_debug=1'" : '')."; ".(DEBUG & _DEBUG_REMOTE ? '' : '//')."alert('submitRating(\"'+actionImg+'\", \"'+page+'\", '+version+', '+dimension+', '+rating+') => '+imgSrc); document[actionImg].src = imgSrc; } function deleteRating(actionImg, page, dimension) { var myRand = Math.round(Math.random()*(1000000)); var imgSrc = '".$urlprefix."' + escape(page) + '?action=".urlencode(_("RateIt"))."&mode=delete&dimension=' + dimension + '&nopurge=1&rand=' + myRand" .(!empty($_GET['start_debug']) ? "+'&start_debug=1'" : '')."; ".(DEBUG & _DEBUG_REMOTE ? '' : '//')."alert('deleteRating(\"'+actionImg+'\", \"'+page+'\", '+version+', '+dimension+')'); document[actionImg].src = imgSrc; } "; return JavaScript($js); } function actionImgPath() { global $WikiTheme; return $WikiTheme->_findFile("images/RateItAction.png"); } /** * Take a string and quote it sufficiently to be passed as a Javascript * string between ''s */ function _javascript_quote_string($s) { return str_replace("'", "\'", $s); } function getDefaultArguments() { return array( 'pagename' => '[pagename]', 'version' => false, 'id' => 'rateit', 'imgPrefix' => '', // '' or BStar or Star 'dimension' => false, 'small' => false, 'show' => false, 'mode' => false, ); } function head() { // early side-effects (before body) global $WikiTheme; $WikiTheme->addMoreHeaders($this->RatingWidgetJavascript()); } // Only for signed users done in template only yet. function run($dbi, $argstr, &$request, $basepage) { global $WikiTheme; //$this->_request = & $request; //$this->_dbi = & $dbi; $user = $request->getUser(); //FIXME: fails on test with DumpHtml:RateIt if (!is_object($user)) return HTML(); $this->userid = $user->getId(); if (!$this->userid) return HTML(); $args = $this->getArgs($argstr, $request); $this->dimension = $args['dimension']; $this->imgPrefix = $args['imgPrefix']; if ($this->dimension == '') { $this->dimension = 0; $args['dimension'] = 0; } if ($args['pagename']) { // Expand relative page names. $page = new WikiPageName($args['pagename'], $basepage); $args['pagename'] = $page->name; } if (empty($args['pagename'])) { return $this->error(_("no page specified")); } $this->pagename = $args['pagename']; $rdbi = RatingsDb::getTheRatingsDb(); $this->_rdbi =& $rdbi; if ($args['mode'] === 'add') { //if (!$user->isSignedIn()) return $this->error(_("You must sign in")); $actionImg = $WikiTheme->_path . $this->actionImgPath(); $rdbi->addRating($request->getArg('rating'), $this->userid, $this->pagename, $this->dimension); if (!empty($request->_is_buffering_output)) ob_end_clean(); // discard any previous output // delete the cache $page = $request->getPage(); //$page->set('_cached_html', false); $request->cacheControl('MUST-REVALIDATE'); $dbi->touch(); //fake validators without args $request->appendValidators(array('wikiname' => WIKI_NAME, 'args' => wikihash(''))); header('Content-type: image/png'); readfile($actionImg); exit(); } elseif ($args['mode'] === 'delete') { //if (!$user->isSignedIn()) return $this->error(_("You must sign in")); $actionImg = $WikiTheme->_path . $this->actionImgPath(); $rdbi->deleteRating($this->userid, $this->pagename, $this->dimension); if (!empty($request->_is_buffering_output)) ob_end_clean(); // discard any previous output // delete the cache $page = $request->getPage(); //$page->set('_cached_html', false); $request->cacheControl('MUST-REVALIDATE'); $dbi->touch(); //fake validators without args $request->appendValidators(array('wikiname' => WIKI_NAME, 'args' => wikihash(''))); header('Content-type: image/png'); readfile($actionImg); exit(); } elseif (! $args['show'] ) { return $this->RatingWidgetHtml($args['pagename'], $args['version'], $args['imgPrefix'], $args['dimension'], $args['small']); } else { //if (!$user->isSignedIn()) return $this->error(_("You must sign in")); //extract($args); $rating = $rdbi->getRating(); $html = HTML::p($this->pagename.": ". sprintf(_("Rated by %d users | Average rating %.1f stars"), $rdbi->getNumUsers($this->pagename, $this->dimension), $rdbi->getAvg($this->pagename, $this->dimension)), HTML::br()); if ($rating) { $html->pushContent(sprintf(_("Your rating was %.1f"), $rating)); } else { $pred = $rdbi->getPrediction($this->userid, $this->pagename, $this->dimension); if (is_string($pred)) $html->pushContent(sprintf(_("%s prediction for you is %s stars"), WIKI_NAME, $pred)); elseif ($pred) $html->pushContent(sprintf(_("%s prediction for you is %.1f stars"), WIKI_NAME, $pred)); } //$html->pushContent(HTML::p()); //$html->pushContent(HTML::em("(Experimental: This might be entirely bogus data)")); return $html; } } // box is used to display a fixed-width, narrow version with common header function box($args=false, $request=false, $basepage=false) { if (!$request) $request =& $GLOBALS['request']; if (!$request->_user->isSignedIn()) return; if (!isset($args)) $args = array(); $args['small'] = 1; $argstr = ''; foreach ($args as $key => $value) $argstr .= $key."=".$value; $widget = $this->run($request->_dbi, $argstr, $request, $basepage); return $this->makeBox(WikiLink(_("RateIt"),'',_("Rate It")), $widget); } /** * HTML widget display * * This needs to be put in the <body> section of the page. * * @param pagename Name of the page to rate * @param version Version of the page to rate (may be "" for current) * @param imgPrefix Prefix of the names of the images that display the rating * You can have two widgets for the same page displayed at * once iff the imgPrefix-s are different. * @param dimension Id of the dimension to rate * @param small Makes a smaller ratings widget if non-false * * Limitations: Currently this can only print the current users ratings. * And only the widget, but no value (for buddies) also. */ function RatingWidgetHtml($pagename, $version, $imgPrefix, $dimension, $small = false) { global $WikiTheme, $request; $imgId = MangleXmlIdentifier($pagename) . $imgPrefix; $actionImgName = $imgId . 'RateItAction'; $dbi =& $GLOBALS['request']->_dbi; $version = $dbi->_backend->get_latest_version($pagename); //$rdbi =& $this->_rdbi; $rdbi = RatingsDb::getTheRatingsDb(); // check if the imgPrefix icons exist. if (! $WikiTheme->_findData("images/RateIt".$imgPrefix."Nk0.png", true)) $imgPrefix = ''; // Protect against 's, though not \r or \n $reImgPrefix = $this->_javascript_quote_string($imgPrefix); $reActionImgName = $this->_javascript_quote_string($actionImgName); $rePagename = $this->_javascript_quote_string($pagename); //$dimension = $args['pagename'] . "rat"; $html = HTML::span(array("id" => $imgId)); for ($i=0; $i < 2; $i++) { $nk[$i] = $WikiTheme->_findData("images/RateIt".$imgPrefix."Nk".$i.".png"); $none[$i] = $WikiTheme->_findData("images/RateIt".$imgPrefix."Rk".$i.".png"); } $user = $request->getUser(); $userid = $user->getId(); //if (!isset($args['rating'])) $rating = $rdbi->getRating($userid, $pagename, $dimension); if (!$rating) { $pred = $rdbi->getPrediction($userid, $pagename, $dimension); } for ($i = 1; $i <= 10; $i++) { $a1 = HTML::a(array('href' => 'javascript:click(\'' . $reActionImgName . '\',\'' . $rePagename . '\',\'' . $version . '\',\'' . $reImgPrefix . '\',\'' . $dimension . '\',' . ($i/2) . ')')); $img_attr = array(); $img_attr['src'] = $nk[$i%2]; //if (!$rating and !$pred) // $img_attr['src'] = $none[$i%2]; $img_attr['name'] = $imgPrefix . $i; $img_attr['alt'] = $img_attr['name']; $img_attr['border'] = 0; $a1->pushContent(HTML::img($img_attr)); $a1->addToolTip(_("Rate the topic of this page")); $html->pushContent($a1); //This adds a space between the rating smilies: // if (($i%2) == 0) $html->pushContent(' '); } $html->pushContent(HTML::Raw(' ')); $a0 = HTML::a(array('href' => 'javascript:click(\'' . $reActionImgName . '\',\'' . $rePagename . '\',\'' . $version . '\',\'' . $reImgPrefix . '\',\'' . $dimension . '\',\'X\')')); $msg = _("Cancel rating"); $a0->pushContent(HTML::img(array('src' => $WikiTheme->getImageUrl("RateIt".$imgPrefix."Cancel"), 'name'=> $imgPrefix.'Cancel', 'border' => 0, 'alt' => $msg))); $a0->addToolTip($msg); $html->pushContent($a0); /*} elseif ($pred) { $msg = _("No opinion"); $html->pushContent(HTML::img(array('src' => $WikiTheme->getImageUrl("RateItCancelN"), 'name'=> $imgPrefix.'Cancel', 'alt' => $msg))); //$a0->addToolTip($msg); //$html->pushContent($a0); }*/ $img_attr = array(); $img_attr['src'] = $WikiTheme->_findData("images/RateItAction.png"); $img_attr['name'] = $actionImgName; $img_attr['alt'] = $img_attr['name']; //$img_attr['class'] = 'k' . $i; $img_attr['border'] = 0; $html->pushContent(HTML::img($img_attr)); // Display the current rating if there is one if ($rating) $html->pushContent(JavaScript('displayRating(\'' . $reImgPrefix . '\','.$rating .',0)')); elseif ($pred) $html->pushContent(JavaScript('displayRating(\'' . $reImgPrefix . '\','.$pred .',1)')); else $html->pushContent(JavaScript('displayRating(\'' . $reImgPrefix . '\',0,0)')); return $html; } }; // $Log: RateIt.php,v $ // Revision 1.22 2007/02/17 14:15:14 rurban // noborder for the recycle bin // // Revision 1.21 2007/01/22 23:50:48 rurban // Do not diplay if not signed in // // Revision 1.20 2006/03/04 13:57:28 rurban // rename hash for php-5.1 // // Revision 1.19 2004/11/15 16:00:01 rurban // enable RateIt imgPrefix: '' or 'Star' or 'BStar', // enable blue prediction icons, // enable buddy predictions. // // Revision 1.18 2004/11/01 10:43:59 rurban // seperate PassUser methods into seperate dir (memory usage) // fix WikiUser (old) overlarge data session // remove wikidb arg from various page class methods, use global ->_dbi instead // ... // // Revision 1.17 2004/08/05 17:31:52 rurban // more xhtml conformance fixes // // Revision 1.16 2004/08/05 17:23:54 rurban // add alt tag for xhtml conformance // // Revision 1.15 2004/07/09 12:50:50 rurban // references are declared, not enforced // // Revision 1.14 2004/07/08 20:30:07 rurban // plugin->run consistency: request as reference, added basepage. // encountered strange bug in AllPages (and the test) which destroys ->_dbi // // Revision 1.12 2004/06/30 19:59:07 dfrankow // Make changes suitable so that wikilens theme (and wikilens.org) work properly. // + Remove predictions (for now) // + Use new RatingsDb singleton. // + Change RatingWidgetHtml() to use parameters like a normal PHP function // so we can have PHP check that we're passing the right # of them. // + Change RatingWidgetHtml() to be callable static-ally // (without a plugin object) // + Remove the "RateIt" button for now, because we don't use it on wikilens.org. // Maybe if someone wants the button, there can be an arg or flag for it. // + Always show the cancel button, because UI widgets should not hide. // + Remove the "No opinion" button for now, because we don't yet store that. // This is a useful thing, tho, for the future. // // Revision 1.11 2004/06/19 10:22:41 rurban // outcomment the pear specific methods to let all pages load // // Revision 1.10 2004/06/18 14:42:17 rurban // added wikilens libs (not yet merged good enough, some work for DanFr) // // Revision 1.9 2004/06/14 11:31:39 rurban // renamed global $Theme to $WikiTheme (gforge nameclash) // inherit PageList default options from PageList // default sortby=pagename // use options in PageList_Selectable (limit, sortby, ...) // added action revert, with button at action=diff // added option regex to WikiAdminSearchReplace // // Revision 1.8 2004/06/01 15:28:01 rurban // AdminUser only ADMIN_USER not member of Administrators // some RateIt improvements by dfrankow // edit_toolbar buttons // // Revision _1.2 2004/04/29 17:55:03 dfrankow // Check in escape() changes to protect against leading spaces in pagename. // This is untested with Reini's _("RateIt") additions to this plugin. // // Revision 1.7 2004/04/21 04:29:50 rurban // write WikiURL consistently (not WikiUrl) // // Revision 1.6 2004/04/12 14:07:12 rurban // more docs // // Revision 1.5 2004/04/11 10:42:02 rurban // pgsrc/CreatePagePlugin // // Revision 1.4 2004/04/06 20:00:11 rurban // Cleanup of special PageList column types // Added support of plugin and theme specific Pagelist Types // Added support for theme specific UserPreferences // Added session support for ip-based throttling // sql table schema change: ALTER TABLE session ADD sess_ip CHAR(15); // Enhanced postgres schema // Added DB_Session_dba support // // Revision 1.3 2004/04/01 06:29:51 rurban // better wording // RateIt also for ADODB // // Revision 1.2 2004/03/31 06:22:22 rurban // shorter javascript, // added prediction buttons and display logic, // empty HTML if not signed in. // fixed deleting (empty dimension => 0) // // Revision 1.1 2004/03/30 02:38:06 rurban // RateIt support (currently no recommendation engine yet) // // For emacs users // Local Variables: // mode: php // tab-width: 8 // c-basic-offset: 4 // c-hanging-comment-ender-p: nil // indent-tabs-mode: nil // End: ?>