Index: branches/RC/core/units/general/cat_tag_processor.php =================================================================== diff -u -N -r10712 -r10870 --- branches/RC/core/units/general/cat_tag_processor.php (.../cat_tag_processor.php) (revision 10712) +++ branches/RC/core/units/general/cat_tag_processor.php (.../cat_tag_processor.php) (revision 10870) @@ -404,40 +404,13 @@ */ function VotesIndicator($params) { - $blocks_params = $this->prepareTagParams($params); - $blocks_params['name'] = $params['render_as']; - $object =& $this->getObject($params); /* @var $object kDBItem */ - if (false && $this->Application->ConfigValue('UseFloatRating')) { - $rating = $object->GetDBField('CachedRating'); - $float_rating = floor(($rating - floor($rating)) / 0.25) * 0.25; - } - else { - $rating = round( $object->GetDBField('CachedRating') ); - $float_rating = 0; - } + $rating_helper =& $this->Application->recallObject('RatingHelper'); + /* @var $rating_helper RatingHelper */ - $i = 1; - $ret = ''; - while ($i <= 5) { - $blocks_params['number'] = $i; - $blocks_params['active'] = $rating >= $i ? 1 : 0; - - if ($rating > $i - 1 && $rating < $i && $float_rating > 0) { - $blocks_params['active'] = 1; - $blocks_params['float_rating'] = $float_rating; - } - else { - $blocks_params['float_rating'] = ''; - } - - $ret .= trim($this->Application->ParseBlock($blocks_params)); - $i++; - } - - return $ret; + return $rating_helper->ratingBar($object); } function RelevanceIndicator($params) Index: branches/RC/core/units/general/helpers/rating_helper.php =================================================================== diff -u -N --- branches/RC/core/units/general/helpers/rating_helper.php (revision 0) +++ branches/RC/core/units/general/helpers/rating_helper.php (revision 10870) @@ -0,0 +1,165 @@ + 'lu_CurrentRating', + 'vote_title' => 'lu_VoteTitle', + 'vote_count' => 'lu_VoteCount', + 'invalid_rating' => 'lu_InvalidRating', + 'already_voted' => 'lu_AlreadyVoted', + 'thanks_for_voting' => 'lu_ThanksForVoting', + ); + + /** + * Draws rating bar for a given category item + * + * @param kDBItem $object + * @param bool $show_div + * @param string $additional_msg + * @return string + */ + function ratingBar(&$object, $show_div = true, $additional_msg = '') + { + $perm_prefix = $this->Application->getUnitOption($object->Prefix, 'PermItemPrefix'); + $static = !$this->Application->CheckPermission($perm_prefix . '.RATE', 0, $object->GetDBField('CategoryId')); + + $total_votes = $object->GetDBField('CachedVotesQty'); + $total_rating = $object->GetDBField('CachedRating') * $total_votes; + + $spam_helper =& $this->Application->recallObject('SpamHelper'); + /* @var $spam_helper SpamHelper */ + + $config_mapping = $this->Application->getUnitOption($object->Prefix, 'ConfigMapping'); + $review_settings = $config_mapping['RatingDelayValue'].':'.$config_mapping['RatingDelayInterval']; + $spam_helper->InitHelper($object->GetDBField('ResourceId'), 'Rating', $review_settings); + + $user_voted = $spam_helper->InSpamControl(); + + // now draw the rating bar + $rating_width = $total_votes ? @number_format($total_rating / $total_votes, 2) * $this->ratingUnitWidth : 0; + $rating1 = $total_votes ? @number_format($total_rating / $total_votes, 1) : 0; + $rating2 = $total_votes ? @number_format($total_rating / $total_votes, 2) : 0; + + $rater = ' +

' . + $this->_replaceInPhrase('vote_title', Array(''.$rating1.'', $this->ratingMaximal)) . ' ('. $this->_replaceInPhrase('vote_count', Array($total_votes)) . ') ' . $additional_msg . ' +

'; + + if ($show_div) { + // adds div around rating stars (when drawing rating first time) + $rater = '
' . $rater . '
'; + } + + return $rater; + } + + /** + * Saves user's vote, when allowed + * + * @param kDBItem $object + * @return string + */ + function makeVote(&$object) + { + $spam_helper =& $this->Application->recallObject('SpamHelper'); + /* @var $spam_helper SpamHelper */ + + $config_mapping = $this->Application->getUnitOption($object->Prefix, 'ConfigMapping'); + $review_settings = $config_mapping['RatingDelayValue'].':'.$config_mapping['RatingDelayInterval']; + $spam_helper->InitHelper($object->GetDBField('ResourceId'), 'Rating', $review_settings); + + if (!$object->isLoaded() || $spam_helper->InSpamControl()) { + return '@err:' . $this->_replaceInPhrase('already_voted'); + } + + $perm_prefix = $this->Application->getUnitOption($object->Prefix, 'PermItemPrefix'); + $can_rate = $this->Application->CheckPermission($perm_prefix . '.RATE', 0, $object->GetDBField('CategoryId')); + $rating = (int)$this->Application->GetVar('rating'); // not numeric rating is from GoogleBot :( + + if (($rating <= 0) || ($rating > $this->ratingMaximal) || !$can_rate) { + return '@err:' . $this->_replaceInPhrase('invalid_rating'); + } + + // save current rating + $fields_hash = Array ( + 'ItemId' => $object->GetID(), + 'RatingValue' => $rating, + 'IPAddress' => $_SERVER['REMOTE_ADDR'], + 'CreatedOn' => adodb_mktime(), + ); + $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'ItemRating'); + + // recalculate average rating + $votes_count = $object->GetDBField('CachedVotesQty'); + $avg_rating = $object->GetDBField('CachedRating'); + + $avg_rating = round((($votes_count * $avg_rating) + $rating) / ($votes_count + 1), 2); + $object->SetDBField('CachedRating', "$avg_rating"); + $object->Update(); + + $sql = 'UPDATE '.$object->TableName.' + SET CachedVotesQty = CachedVotesQty + 1 + WHERE '.$object->IDField.' = '.$object->GetID(); + $this->Conn->Query($sql); + + $object->SetDBField('CachedVotesQty', $object->GetDBField('CachedVotesQty') + 1); // for using in template + + // prevent user from voting too quickly + $spam_helper->AddToSpamControl(); + + return $this->ratingBar($object, false, '' . $this->_replaceInPhrase('thanks_for_voting') . ''); + } + + /** + * Performs sprintf on phrase translation using given variables + * + * @param string $phrase + * @param Array $arguments + * @return string + */ + function _replaceInPhrase($phrase, $arguments = Array ()) + { + $value = $this->Application->Phrase($this->_phrases[$phrase]); + + if ($arguments) { + return vsprintf($value, $arguments); + } + + return $value; + } + } \ No newline at end of file Index: branches/RC/themes/default2007/platform/elements/html_head.tpl =================================================================== diff -u -N -r10362 -r10870 --- branches/RC/themes/default2007/platform/elements/html_head.tpl (.../html_head.tpl) (revision 10362) +++ branches/RC/themes/default2007/platform/elements/html_head.tpl (.../html_head.tpl) (revision 10870) @@ -6,13 +6,13 @@ - " /> + " /> " /> - " /> + " /> " /> @@ -25,14 +25,20 @@ - + inc/lbox/lightbox.css" type="text/css" media="screen" /> + inc/styles.css" type="text/css" /> - - + + + + - - - + + + + \ No newline at end of file Index: branches/RC/core/units/general/cat_event_handler.php =================================================================== diff -u -N -r10796 -r10870 --- branches/RC/core/units/general/cat_event_handler.php (.../cat_event_handler.php) (revision 10796) +++ branches/RC/core/units/general/cat_event_handler.php (.../cat_event_handler.php) (revision 10870) @@ -19,10 +19,9 @@ 'OnBeforeDeleteOriginal' => Array('self' => 'edit|advanced:approve'), 'OnDownloadFile' => Array('self' => 'view'), - 'OnCancelAction' => Array('self' => true), - 'OnItemBuild' => Array('self' => true), + 'OnMakeVote' => Array ('self' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); @@ -1118,7 +1117,15 @@ } foreach ($field_list as $field) { - $config_elem = $search_config[ $search_config_map[$field] ]; + + if (!array_key_exists($field, $search_config_map)) { + $map_key = $search_config_map[$items_table . '.' . $field]; + } + else { + $map_key = $search_config_map[$field]; + } + + $config_elem = $search_config[ $map_key ]; $weight = $config_elem['Priority']; $revelance_parts[] = 'IF('.$field.' LIKE "%'.implode(' ', $positive_words).'%", '.$weight_sum.', 0)'; foreach ($positive_words as $keyword) { @@ -2432,6 +2439,31 @@ $filename = $object->GetField($field, 'full_path'); $file_helper->DownloadFile($filename); } + + /** + * Saves user's vote + * + * @param kEvent $event + */ + function OnMakeVote(&$event) + { + $event->status = erSTOP; + + if ($this->Application->GetVar('ajax') != 'yes') { + // this is supposed to call from AJAX only + return ; + } + + $rating_helper =& $this->Application->recallObject('RatingHelper'); + /* @var $rating_helper RatingHelper */ + + $object =& $event->getObject( Array ('skip_autoload' => true) ); + /* @var $object kDBItem */ + + $object->Load( $this->Application->GetVar('id') ); + + echo $rating_helper->makeVote($object); + } } ?> \ No newline at end of file Index: branches/RC/themes/default2007/platform/img/starrating.gif =================================================================== diff -u -N Binary files differ Index: branches/RC/core/kernel/db/db_tag_processor.php =================================================================== diff -u -N -r10859 -r10870 --- branches/RC/core/kernel/db/db_tag_processor.php (.../db_tag_processor.php) (revision 10859) +++ branches/RC/core/kernel/db/db_tag_processor.php (.../db_tag_processor.php) (revision 10870) @@ -1408,6 +1408,20 @@ return $ret; } + /** + * Checks, that grid has icons defined and they should be shown + * + * @param Array $params + * @return bool + */ + function UseItemIcons($params) + { + $object =& $this->getObject($params); + + $grids = $this->Application->getUnitOption($this->Prefix, 'Grids'); + return array_key_exists('Icons', $grids[ $params['grid'] ]); + } + function ItemIcon($params) { $object =& $this->getObject($params); Index: branches/RC/core/units/general/helpers/helpers_config.php =================================================================== diff -u -N -r10155 -r10870 --- branches/RC/core/units/general/helpers/helpers_config.php (.../helpers_config.php) (revision 10155) +++ branches/RC/core/units/general/helpers/helpers_config.php (.../helpers_config.php) (revision 10870) @@ -26,5 +26,6 @@ Array('pseudo'=>'CategoryHelper','class'=>'CategoryHelper','file'=>'category_helper.php','build_event'=>'','require_classes'=>'kHelper'), Array('pseudo'=>'CSVHelper','class'=>'kCSVHelper','file'=>'csv_helper.php','build_event'=>'','require_classes'=>'kHelper'), Array('pseudo'=>'ChartHelper','class'=>'kChartHelper','file'=>'chart_helper.php','build_event'=>'','require_classes'=>'kHelper'), + Array('pseudo'=>'RatingHelper','class'=>'RatingHelper','file'=>'rating_helper.php','build_event'=>'','require_classes'=>'kHelper'), ), ); \ No newline at end of file Index: branches/RC/themes/default2007/platform/inc/styles.css =================================================================== diff -u -N -r10833 -r10870 --- branches/RC/themes/default2007/platform/inc/styles.css (.../styles.css) (revision 10833) +++ branches/RC/themes/default2007/platform/inc/styles.css (.../styles.css) (revision 10870) @@ -508,6 +508,89 @@ vertical-align:top; } +/* --- Item Raring Styles --- */ +.ratingblock, .ratingblock div { + display: block; +} + +.unit-rating { /* the UL */ + list-style: none; + margin: 0px; + padding: 0px; + height: 30px; + position: relative; + background: url('../img/starrating.gif') top left repeat-x; +} + +.unit-rating li { + text-indent: -90000px; + padding: 0px; + margin: 0px; + /*\*/ + float: left; + /* */ +} + +.unit-rating li a { + outline: none; + display: block; + width: 30px; + height: 30px; + text-decoration: none; + text-indent: -9000px; + z-index: 20; + position: absolute; + padding: 0px; +} + +.unit-rating li a:hover { + background: url('../img/starrating.gif') left center; + z-index: 2; + left: 0px; +} + +.unit-rating a.r1-unit{left: 0px;} +.unit-rating a.r1-unit:hover{width:30px;} +.unit-rating a.r2-unit{left:30px;} +.unit-rating a.r2-unit:hover{width: 60px;} +.unit-rating a.r3-unit{left: 60px;} +.unit-rating a.r3-unit:hover{width: 90px;} +.unit-rating a.r4-unit{left: 90px;} +.unit-rating a.r4-unit:hover{width: 120px;} +.unit-rating a.r5-unit{left: 120px;} +.unit-rating a.r5-unit:hover{width: 150px;} +.unit-rating a.r6-unit{left: 150px;} +.unit-rating a.r6-unit:hover{width: 180px;} +.unit-rating a.r7-unit{left: 180px;} +.unit-rating a.r7-unit:hover{width: 210px;} +.unit-rating a.r8-unit{left: 210px;} +.unit-rating a.r8-unit:hover{width: 240px;} +.unit-rating a.r9-unit{left: 240px;} +.unit-rating a.r9-unit:hover{width: 270px;} +.unit-rating a.r10-unit{left: 270px;} +.unit-rating a.r10-unit:hover{width: 300px;} + +.unit-rating li.current-rating { + background: url('../img/starrating.gif') left bottom; + position: absolute; + height: 30px; + display: block; + text-indent: -9000px; + z-index: 1; +} + +.voted { + color: #999; +} + +.thanks { + color: #36AA3D; +} + +.static { + color: #5D3126; +} + /* --- Unprocessed Styles --- */ .text-title { font-size: 12px; @@ -544,4 +627,4 @@ .information-background { background-color: #F6F6FF; -} \ No newline at end of file +} Index: branches/RC/themes/default2007/platform/inc/ajax.js =================================================================== diff -u -N --- branches/RC/themes/default2007/platform/inc/ajax.js (revision 0) +++ branches/RC/themes/default2007/platform/inc/ajax.js (revision 10870) @@ -0,0 +1,240 @@ +function preg_print_pre(obj, reg) +{ + if (!reg) reg = /.*/; + var p = '' + for (var prop in obj) { + if (prop.match(reg) ) { + p += prop + ': '+obj[prop] + '\n' + } + } + alert(p) +} + + +// Main AJAX classs +function Request() {} + +Request.timeout = 60000; //60 seconds +Request.method = 'GET'; +Request.headers = new Array(); +Request.params = null; + +Request.makeRequest = function(p_url, p_busyReq, p_progId, p_successCallBack, p_errorCallBack, p_pass, p_object) { + //p_url: the web service url + //p_busyReq: is a request for this object currently in progress? + //p_progId: element id where progress HTML should be shown + //p_successCallBack: callback function for successful response + //p_errorCallBack: callback function for erroneous response + //p_pass: string of params to pass to callback functions + //p_object: object of params to pass to callback functions + + if (p_busyReq) return; + var req = Request.getRequest(); + if (req != null) { + p_busyReq = true; + Request.showProgress(p_progId); + req.onreadystatechange = function() { + if (req.readyState == 4) { + p_busyReq = false; + window.clearTimeout(toId); + try { + if (req.status == 200) { + // preg_print_pre(req) + p_successCallBack(req, p_pass, p_object); + } else { + p_errorCallBack(req, p_pass, p_object); + } + Request.hideProgress(p_progId); + } + catch (e) { +// alert('AJAX error') + } + } + } + var $ajax_mark = (p_url.indexOf('?') ? '&' : '?') + 'ajax=yes'; + req.open(Request.method, p_url + $ajax_mark, true); + + if (Request.method == 'POST') { + Request.headers['Content-type'] = 'application/x-www-form-urlencoded'; + Request.headers['referer'] = p_url; + } + else { + Request.headers['If-Modified-Since'] = 'Sat, 1 Jan 2000 00:00:00 GMT'; + } + + Request.sendHeaders(req); + if (Request.method == 'POST') { + req.send(Request.params); + Request.method = 'GET'; // restore method back to GET + } + else { + req.send(null); + } + + var toId = window.setTimeout( function() {if (p_busyReq) req.abort();}, Request.timeout ); + } +} + +Request.processRedirect = function($request) { + var $match_redirect = new RegExp('^#redirect#(.*)').exec($request.responseText); + if ($match_redirect != null) { + // redirect to external template requested + window.location.href = $match_redirect[1]; + return true; + } + return false; +} +Request.sendHeaders = function($request) { + for (var $header_name in Request.headers) { + if (typeof Request.headers[$header_name] == 'function') { + continue; + } + $request.setRequestHeader($header_name, Request.headers[$header_name]); + } + Request.headers = new Array(); // reset header afterwards +} + +Request.getRequest = function() { + var xmlHttp; + try { xmlHttp = new ActiveXObject('MSXML2.XMLHTTP'); return xmlHttp; } catch (e) {} + try { xmlHttp = new ActiveXObject('Microsoft.XMLHTTP'); return xmlHttp; } catch (e) {} + try { xmlHttp = new XMLHttpRequest(); return xmlHttp; } catch(e) {} + return null; +} + +Request.showProgress = function(p_id) { + if (p_id != '') { + Request.setOpacity(20, p_id); + + if (!document.getElementById(p_id + '_progress')) { + document.body.appendChild(Request.getProgressObject(p_id)); + } + else { + var $progress_div = document.getElementById(p_id + '_progress'); + $progress_div.style.top = getRealTop(p_id) + 'px'; + $progress_div.style.height = document.getElementById(p_id).clientHeight; + $progress_div.style.display = 'block'; + } +// document.getElementById(p_id).innerHTML = Request.getProgressHtml(); + } +} + +Request.hideProgress = function(p_id) { + if (p_id != '') { + document.getElementById(p_id + '_progress').style.display = 'none'; + Request.setOpacity(100, p_id); + } +} + +Request.setOpacity = function (opacity, id) { + var elem = typeof(id)=='string' ? document.getElementById(id) : id; + var object = elem.style; + object.opacity = (opacity / 100); + object.MozOpacity = (opacity / 100); + object.KhtmlOpacity = (opacity / 100); + object.filter = "alpha(opacity=" + opacity + ")"; +} + +Request.getProgressHtml = function() { + return "

" + Request.progressText + "
" + Request.progressText + "

"; +} + +Request.getProgressObject = function($id) { + var $div = document.createElement('DIV'); + var $parent_div = document.getElementById($id); + + $div.id = $id + '_progress'; + + $div.style.width = $parent_div.clientWidth + 'px'; + $div.style.height = '150px'; // default height if div is empty (first ajax request for div) + $div.style.left = getRealLeft($parent_div) + 'px'; + $div.style.top = getRealTop($parent_div) + 'px'; + $div.style.position = 'absolute'; + + /*$div.style.border = '1px solid green'; + $div.style.backgroundColor = '#FF0000';*/ + + $div.innerHTML = '
'+Request.progressText+'
'+escape(Request.progressText)+'
'; + return $div; +} + +Request.getErrorHtml = function(p_req) { + //TODO: implement accepted way to handle request error + return '[status: ' + p_req.status + '; status_text: ' + p_req.statusText + '; responce_text: ' + p_req.responseText + ']'; +} + +Request.serializeForm = function(theform) { + if (typeof(theform) == 'string') { + theform = document.getElementById(theform); + } + + var els = theform.elements; + var len = els.length; + var queryString = ''; + + Request.addField = function(name, value) { + if (queryString.length > 0) queryString += '&'; + queryString += encodeURIComponent(name) + '=' + encodeURIComponent(value); + }; + + for (var i = 0; i= 0) { + Request.addField(el.name, el.options[el.selectedIndex].value); + } + break; + + case 'select-multiple': + for (var j = 0; j < el.options.length; j++) { + if (!el.options[j].selected) continue; + Request.addField(el.name, el.options[j].value); + } + break; + + case 'checkbox': + case 'radio': + if (!el.checked) continue; + Request.addField(el.name,el.value); + break; + } + } + return queryString; +}; + +function RatingManager ($url) { + this.Url = $url; + this.BusyRequest = false; +} + +RatingManager.prototype.makeVote = function ($vote, $prefix, $id) { + var $url = this.Url.replace('#PREFIX#', $prefix).replace('#VOTE#', $vote).replace('#ID#', $id); + + Request.makeRequest($url, this.BusyRequest, '', this.successCallback, this.errorCallback, [$vote, $prefix, $id], this); +} + +RatingManager.prototype.successCallback = function ($request, $params, $object) { + var response = $request.responseText; + + if (response.substring(0, 5) == '@err:') { + alert(response.substring(5)); + return ; + } + + document.getElementById('page_rating_' + $params[2]).innerHTML = response; +} + + +RatingManager.prototype.errorCallback = function($request, $params, $object) { + alert('AJAX Error; class: RatingManager; ' + Request.getErrorHtml($request)); +} \ No newline at end of file