Commit c0244b6d authored by Robert Lyon's avatar Robert Lyon

Bug 1316917: New approach to star rating using bootstrap glyphs

This is a slimmer / stripped down system for doing jquery ratings.

Instead of relying on styling radio buttons it relies on bootstrap
glyphs and a hidden input field.

The code is also controlled by a pieform element

See lib/form/elements/ratings.php for more info about that part

It also has some new settings in the Extensions -> artefact -> comment
config form. They include settign the colour for the star icon, or
using a different icon, eg hearts/thumbs up, and the number of ratings
to show (3 - 12)

behatnotneeded

Change-Id: Ibf529efcb9a665c9f303242ed12d0c7b3dee2356
Signed-off-by: Robert Lyon's avatarRobert Lyon <robertl@catalyst.net.nz>
(cherry picked from commit d1bf622a)
parent 247cb6b9
......@@ -57,10 +57,8 @@ $elements['message'] = array(
);
if (get_config_plugin('artefact', 'comment', 'commentratings')) {
$elements['rating'] = array(
'type' => 'radio',
'type' => 'ratings',
'title' => get_string('rating', 'artefact.comment'),
'options' => array('1' => '', '2' => '', '3' => '', '4' => '', '5' => ''),
'class' => 'star',
'defaultvalue' => $comment->get('rating'),
);
}
......
......@@ -29,7 +29,6 @@ $string['commentmadepublic'] = "Comment made public";
$string['commentdeletedauthornotification'] = "Your comment on %s was deleted:\n%s";
$string['commentdeletednotificationsubject'] = 'Comment on %s deleted';
$string['commentnotinview'] = 'Comment %d not in page %d';
$string['commentratings'] = 'Enable comment ratings';
$string['commentremoved'] = 'Comment removed';
$string['commentremovedbyauthor'] = 'Comment removed by the author';
$string['commentremovedbyowner'] = 'Comment removed by the owner';
......@@ -112,3 +111,15 @@ To see %s online, follow this link:
$string['artefactdefaultpermissions'] = 'Default comment permission';
$string['artefactdefaultpermissionsdescription'] = 'The selected artefact types will have comments enabled on creation. Users can override these settings for individual artefacts.';
// Extension config form
$string['commentratings'] = 'Enable comment ratings';
$string['ratingicons'] = 'Icon to use to display ratings';
$string['ratinglength'] = 'Number of rating choices';
$string['ratingcolour'] = 'Colour';
$string['ratingcolourdesc'] = 'The colour to display the rating choices in. A chosen rating will display the icon in the solid colour, and an unchosen one will display the colour in the icon outline.';
$string['star'] = 'Star';
$string['heart'] = 'Heart';
$string['thumbsup'] = 'Thumbs up';
$string['ok'] = 'Tick';
$string['ratingexample'] = 'Generated example';
......@@ -2,7 +2,7 @@
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Comment ratings</h3>
<p>Turn this on to enable 5-star ratings on artefacts and pages.</p>
<p>Note that ratings cannot be on their own and require either a comment or
a file attachment.</p>
<p>Turn this on to enable ratings on artefacts and pages.</p>
<p>You can choose a rating scale from 3 to 12 items, the icon, and the icon colour to use. </p>
<p>If you change the number of rating choices, please note that the ratings themselves will not be recalculated to fit that new scale.</p>
<p>Note that ratings cannot be on their own and require either a comment or a file attachment.</p>
......@@ -15,7 +15,8 @@ require_once('activity.php');
require_once('license.php');
define('MIN_RATING', 1);
define('MAX_RATING', 5);
$maxrating = get_config_plugin('artefact', 'comment', 'ratinglength');
define('MAX_RATING', $maxrating ? $maxrating : 5);
function valid_rating($ratingstr) {
if (empty($ratingstr)) {
......@@ -870,6 +871,10 @@ class ArtefactTypeComment extends ArtefactType {
$smarty->assign('viewid', $data->view);
$smarty->assign('baseurl', $data->baseurl);
$smarty->assign('onview', $onview);
$icon = get_config_plugin('artefact', 'comment', 'ratingicon');
$smarty->assign('star', $icon ? $icon : 'star');
$colour = get_config_plugin('artefact', 'comment', 'ratingcolour');
$smarty->assign('colour', $colour ? $colour : '#DBB80E');
$data->tablerows = $smarty->fetch('artefact:comment:commentlist.tpl');
......@@ -934,10 +939,8 @@ class ArtefactTypeComment extends ArtefactType {
);
if (get_config_plugin('artefact', 'comment', 'commentratings')) {
$form['elements']['rating'] = array(
'type' => 'radio',
'type' => 'ratings',
'title' => get_string('rating', 'artefact.comment'),
'options' => array('1' => '', '2' => '', '3' => '', '4' => '', '5' => ''),
'class' => 'star',
);
}
$form['elements']['ispublic'] = array(
......@@ -1081,6 +1084,10 @@ class ArtefactTypeComment extends ArtefactType {
}
public static function get_config_options() {
$length = get_config_plugin('artefact', 'comment', 'ratinglength');
$length = empty($length) ? 5 : $length;
$colour = get_config_plugin('artefact', 'comment', 'ratingcolour');
$colour = empty($colour) ? '#DBB80E' : $colour;
$elements = array(
'commentratings' => array(
'type' => 'switchbox',
......@@ -1088,6 +1095,48 @@ class ArtefactTypeComment extends ArtefactType {
'defaultvalue' => get_config_plugin('artefact', 'comment', 'commentratings'),
'help' => true,
),
'ratingicon' => array(
'type' => 'select',
'title' => get_string('ratingicons', 'artefact.comment'),
'defaultvalue' => get_config_plugin('artefact', 'comment', 'ratingicon'),
'options' => array(
'star' => get_string('star', 'artefact.comment'),
'heart' => get_string('heart', 'artefact.comment'),
'thumbs-up' => get_string('thumbsup', 'artefact.comment'),
'check-circle' => get_string('ok', 'artefact.comment'),
),
),
'ratinglength' => array(
'type' => 'select',
'title' => get_string('ratinglength', 'artefact.comment'),
'defaultvalue' => $length,
'options' => array(
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
'8' => '8',
'9' => '9',
'10' => '10',
'11' => '11',
'12' => '12',
),
),
'ratingcolour' => array(
'type' => 'color',
'title' => get_string('ratingcolour', 'artefact.comment'),
'defaultvalue' => $colour,
'description' => get_string('ratingcolourdesc', 'artefact.comment'),
),
'ratingexample' => array(
'type' => 'ratings',
'title' => get_string('ratingexample', 'artefact.comment'),
'readonly' => true,
'defaultvalue' => ceil($length / 2),
'iconempty' => true,
'officon' => 'dummy',
),
);
return array(
'name' => 'commentconfig',
......@@ -1098,7 +1147,9 @@ class ArtefactTypeComment extends ArtefactType {
}
public static function save_config_options($form, $values) {
foreach (array('commentratings') as $settingname) {
$valid = array('commentratings', 'ratingicon', 'ratinglength',
'ratingcolour');
foreach ($valid as $settingname) {
set_config_plugin('artefact', 'comment', $settingname, $values[$settingname]);
}
}
......
jquery.rating.js
=================
Website: http://code.google.com/p/jquery-star-rating-plugin/
Version: 3.14
Changes:
* Added a pager hook for reskining
* Changed name and path of images in theme/raw/style/jquery.rating.css
* Remove the $.browser check for IE8 & older (because jQuery no longer ships it by default, and
we no longer support IE8.)
\ No newline at end of file
// Customised system based on the proof of concept workings of
// https://github.com/robertlabrie/bootstrap-star-rating
(function ( $ ) {
$.fn.rating = function( method, options ) {
method = method || 'create';
// This is the easiest way to have default options.
var settings = $.extend({
// These are the defaults.
limit: 5,
value: 0,
glyph: "icon-star",
offglyph: "icon-ban",
emptyglyph: false,
coloroff: "gray",
coloron: "gold",
size: "1.0em",
padding: "0 3px",
cursor: "default",
readonly: false,
onClick: function () {},
endofarray: "idontmatter"
}, options );
var style = "";
style = style + "font-size:" + settings.size + "; ";
style = style + "color:" + settings.coloroff + "; ";
style = style + "cursor:" + settings.cursor + "; ";
style = style + "padding:" + settings.padding + "; ";
if (method == 'create') {
// Initialize the data-rating property
this.each(function() {
attr = $(this).attr('data-rating');
if (attr === undefined || attr === false) { $(this).attr('data-rating',settings.value); }
});
// Add 'no rating' glyph
this.append('<span data-value="0" class="ratingicon icon ' + settings.offglyph + '" style="' + style + '" aria-hidden="true"></span>');
// Loop through the glyphs
for (var i = 0; i < settings.limit; i++) {
this.append('<span data-value="' + (i+1) + '" class="ratingicon icon ' + settings.glyph + '" style="' + style + '" aria-hidden="true"></span>');
}
// Paint the glyphs
this.each(function() { paint($(this)); });
}
if (method == 'set') {
this.attr('data-rating',options);
this.each(function() { paint($(this)); });
}
if (method == 'get') {
return this.attr('data-rating');
}
// Register the click events
this.find("span.ratingicon").click(function() {
if (settings.readonly !== true) {
rating = $(this).attr('data-value')
$(this).parent().attr('data-rating',rating);
paint($(this).parent());
settings.onClick.call( $(this).parent() );
}
});
function paint(div) {
rating = parseInt(div.attr('data-rating'));
// If there is an input in the div lets set it's value
div.find("input").val(rating);
div.find("span.ratingicon").each(function() {
// Now paint the glyphs
var rating = parseInt($(this).parent().attr('data-rating'));
var value = parseInt($(this).attr('data-value'));
if (value > rating || (value == 0 && rating > 0)) {
$(this).css('color',settings.coloroff);
if (settings.emptyglyph) {
if ($(this).hasClass(settings.glyph)) {
// need to add the '-o' before the end for thumbs
if (settings.glyph == 'icon-thumbs-up') {
$(this).removeClass(settings.glyph).addClass('icon-thumbs-o-up');
}
else {
$(this).removeClass(settings.glyph).addClass(settings.glyph + '-o');
}
}
}
}
else {
$(this).css('color',settings.coloron);
}
});
}
};
}( jQuery ));
This diff is collapsed.
......@@ -24,7 +24,7 @@ function pieform_element_color(Pieform $form, $element) {
$result = '';
$name = Pieform::hsc($element['name']);
$baseid = Pieform::hsc($form->get_name() . '_' . $element['name']);
$value = Pieform::hsc($element['defaultvalue']);
$value = Pieform::hsc($form->get_value($element));
$transparent = (!empty($element['options']['transparent']) && $element['options']['transparent'] == true);
// Transparency optional control
......@@ -103,6 +103,10 @@ function pieform_element_color_get_value(Pieform $form, $element) {
return $color;
}
if (isset($element['defaultvalue'])) {
return $element['defaultvalue'];
}
return 'transparent';
}
......@@ -112,10 +116,24 @@ function pieform_element_color_get_value(Pieform $form, $element) {
* @param array $element The element to get <head> code for
* @return array An array of HTML elements to go in the <head>
*/
function pieform_element_color_get_headdata($element) {
$libfile = get_config('wwwroot') . 'js/jscolor/jscolor.min.js';
$result = array(
'<script type="application/javascript" src="' . $libfile . '"></script>'
function pieform_element_color_get_headdata($element, Pieform $form) {
$libfile = get_config('wwwroot') . 'js/jscolor/jscolor.js';
$name = $form->get_property('name') . '_' . $element['name'];
$result = '<script type="application/javascript">';
$result .= "var initjscolor = false; \n";
$result .= "PieformManager.connect('onload', null, function() {\n";
$result .= " jQuery('document').ready(function($) { \n";
$result .= " if (initjscolor === true) { \n";
$result .= " // rewire up the picker to show up\n";
$result .= " var jsc = new jscolor('" . $name . "'); \n";
$result .= " } \n";
$result .= " // only after initial page load\n";
$result .= " initjscolor = true; \n";
$result .= " }); \n";
$result .= "});</script>";
$results = array(
'<script type="application/javascript" src="' . $libfile . '"></script>',
$result
);
return $result;
return $results;
}
<?php
/**
*
* @package mahara
* @subpackage artefact
* @author Robert Lyon <rlyon@catalyst.net.nz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
// To use in forms:
// 'ELEMENTNAME' => array(
// 'type' => 'ratings',
// 'title' => TEXT,
// 'readonly' => Set to true if you only want to display the rating and not have it manipulated.
// 'colouron' => The colour to use for the selected ratings, either #hex string or 'default'.
// 'colouroff' => The colour to use for the non selected ratings, either #hex string or 'default'.
// 'limit' => The number of stars, default 5.
// 'icon' => The type of icon to use for the rating points, default 'star'.
// 'iconempty' => To display the empty icon rather than the icon greyed out. Note the icon needs to have a '-o' eqiuvalent
// 'officon' => The type of icon to use for the 'no rating' point, default 'ban-circle'.
// 'onclick' => JS function for manipulation.
// ),
defined('INTERNAL') || die();
/**
* jQuery Rating selector element
*
* @param array $element The element to render
* @param Pieform $form The form to render the element for
* @return string The HTML for the element
*/
function pieform_element_ratings(Pieform $form, $element) {
$wwwroot = get_config('wwwroot');
$smarty = smarty_core();
$smarty->left_delimiter = '{{';
$smarty->right_delimiter = '}}';
$value = $form->get_value($element);
if (is_null($value) || !is_int($value)) {
$value = !empty($element['defaultvalue']) ? $element['defaultvalue'] : 0;
}
$limit = 5;
if (!empty(get_config_plugin('artefact', 'comment', 'ratinglength'))) {
$limit = get_config_plugin('artefact', 'comment', 'ratinglength');
}
$limit = (!empty($element['limit']) && is_int($element['limit'])) ? $element['limit'] : $limit;
$smarty->assign('id', $form->get_name() . '_' . $element['id']);
$smarty->assign('name', $element['name']);
$smarty->assign('value', $value);
$smarty->assign('readonly', !empty($element['readonly']) ? true : false);
$defaultcolour = '#DBB80E';
if (!empty(get_config_plugin('artefact', 'comment', 'ratingcolour'))) {
$defaultcolour = get_config_plugin('artefact', 'comment', 'ratingcolour');
}
$smarty->assign('iconempty', !empty($element['iconempty']) ? 1 : 0);
$colouron = (empty($element['colouron']) || $element['colouron'] == 'default') ? $defaultcolour : $element['colouron'];
$colouroff = (empty($element['colouroff']) || $element['colouroff'] == 'default') ? '#AAAAAA' : $element['colouroff'];
$smarty->assign('colouron', $colouron);
if (!empty($element['iconempty'])) {
$colouroff = $colouron;
}
$smarty->assign('colouroff', $colouroff);
$smarty->assign('limit', (int) $limit);
$defaulticon = 'star';
if (!empty(get_config_plugin('artefact', 'comment', 'ratingicon'))) {
$defaulticon = get_config_plugin('artefact', 'comment', 'ratingicon');
}
$smarty->assign('icon', (empty($element['icon']) || $element['icon'] == 'default') ? $defaulticon : $element['icon']);
$smarty->assign('officon', (empty($element['officon']) || $element['officon'] == 'default') ? 'ban' : $element['officon']);
$smarty->assign('onclick', (!empty($element['onclick'])) ? $element['onclick'] : false);
return $smarty->fetch('form/ratings.tpl');
}
/**
* Returns code to go in <head> for the given ratings instance
*
* @param array $element The element to get <head> code for
* @return array An array of HTML elements to go in the <head>
*/
function pieform_element_ratings_get_headdata($element) {
$libfile = get_config('wwwroot') . 'js/bootstrap-ratings.js';
$result = array(
'<script type="application/javascript" src="' . $libfile . '"></script>'
);
return $result;
}
\ No newline at end of file
......@@ -999,7 +999,13 @@ function get_config_plugin($plugintype, $pluginname, $key) {
else {
// To minimize database calls, get all the records for this plugin from the database at once.
$records = get_records_array($plugintype . '_config', 'plugin', $pluginname, 'field');
try {
$records = get_records_array($plugintype . '_config', 'plugin', $pluginname, 'field');
}
catch (SQLException $e) {
// Db might not exist yet on install
return null;
}
if (!empty($records)) {
foreach ($records as $record) {
$storeconfigname = "plugin_{$typename}_{$record->field}";
......
......@@ -1778,7 +1778,7 @@ function pieform_get_headdata() {/*{{{*/
foreach ($form->get_elements() as $element) {
$function = 'pieform_element_' . $element['type'] . '_get_headdata';
if (function_exists($function)) {
$elems = $function($element);
$elems = $function($element, $form);
$htmlelements = array_merge($htmlelements, $elems);
}
}
......
......@@ -30,11 +30,7 @@
<span class="star-comment-rating">
{for i $item->ratingdata->min_rating $item->ratingdata->max_rating}
{if !$item->ratingdata->export}
<input name="star{$item->id}" type="radio" class="star" {if $i === $item->ratingdata->value} checked="checked" {/if} disabled="disabled" />
{else}
<div class="star-rating star star-rating-applied star-rating-readonly{if $i <= $item->ratingdata->value} star-rating-on{/if}"><a>&nbsp;</a></div>
{/if}
<div class="star-rating star star-rating-applied star-rating-readonly {$star}-rating{if $i <= $item->ratingdata->value}-on{else}-off{/if}"><a {if $colour}style="color: {$colour}"{/if}>&nbsp;</a></div>
{/for}
</span>
{/if}
......
......@@ -54,14 +54,52 @@
@extend .icon-star;
}
}
a {
@extend .icon-star-o;
color: #dbb80e;
&.star-rating-off {
a {
@extend .icon-star-o;
color: #bebebe;
}
}
&.heart-rating-hover,
&.heart-rating-on {
a {
@extend .icon-heart;
}
}
&.heart-rating-off {
a {
@extend .icon-heart-o;
color: #bebebe;
}
}
&.thumbs-up-rating-hover,
&.thumbs-up-rating-on {
a {
@extend .icon-thumbs-up;
}
}
&.thumbs-up-rating-off {
a {
@extend .icon-thumbs-o-up;
color: #bebebe;
}
}
&.check-circle-rating-hover,
&.check-circle-rating-on {
a {
@extend .icon-check-circle;
}
}
&.check-circle-rating-off {
a {
@extend .icon-check-circle-o;
color: #bebebe;
}
}
}
.star-rating-readonly {
width: 14px;
width: 15px;
a {
font-size: 15px;
&:hover {
......@@ -72,6 +110,14 @@
.star-comment-rating {
float:right;
margin-top:-10px;
margin: -10px 40% 0 0;
padding: 10px 0 0 10px;
}
.ratingicon:hover {
color: #417005 !important;
}
#pluginconfig_ratingexample_container > div,
#add_feedback_form_rating_container > div {
display: inline;
}
\ No newline at end of file
<div id="{{$id}}-container" data-rating="{{$value}}">{{if !$readonly}}<input type=hidden name="rating" id="{{$id}}">{{/if}}</div>
<script type="application/javascript">
jQuery(document).ready(function() {
jQuery("#{{$id}}-container").rating('create', {
coloron:'{{$colouron}}',
coloroff:'{{$colouroff}}',
glyph:'icon-{{$icon}}',
offglyph:'icon-{{$officon}}',
emptyglyph: {{$iconempty}},
limit: {{$limit}},
{{if $onclick}}
onClick: {{$onclick}},
{{/if}}
{{if $readonly}}
readonly: true,
{{/if}}
});
});
</script>
......@@ -39,7 +39,6 @@
{/foreach}
<script type="application/javascript" src="{$WWWROOT}/lib/bootstrap/assets/javascripts/bootstrap.js?v={$CACHEVERSION}"></script>
<script type="application/javascript" src="{$WWWROOT}/js/javascript-templates/js/tmpl.min.js?v={$CACHEVERSION}"></script>
<script type="application/javascript" src="{$WWWROOT}js/jquery.rating.js?v={$CACHEVERSION}"></script>
<script type="application/javascript" src="{theme_url filename='js/masonry.min.js'}?v={$CACHEVERSION}"></script>
<script type="application/javascript" src="{$WWWROOT}/js/select2/select2.full.js?v={$CACHEVERSION}"></script>
<script type="application/javascript" src="{theme_url filename='js/pieform.js'}?v={$CACHEVERSION}"></script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment