Commit 1a287da3 authored by Richard Mansfield's avatar Richard Mansfield

Remove use of tablerenderer for view/artefact feedback; create feedback list...

Remove use of tablerenderer for view/artefact feedback; create feedback list using templates; use artefactchooser paginator
Signed-off-by: default avatarRichard Mansfield <richardm@catalyst.net.nz>
parent 6cbbe30a
......@@ -1050,6 +1050,54 @@ abstract class ArtefactType {
public static function attached_id_list($attachmentid) {
return get_column('artefact_attachment', 'artefact', 'attachment', $attachmentid);
}
public function get_feedback($limit=10, $offset=0, $viewid, $lastpage=false) {
global $USER;
$userid = $USER->get('id');
$artefactid = $this->id;
$canedit = $USER->can_edit_artefact($this);
$count = count_records_sql('
SELECT COUNT(*)
FROM {artefact_feedback}
WHERE view = ' . $viewid . ' AND artefact = ' . $artefactid
. (!$canedit ? ' AND (public = 1 OR author = ' . $userid . ')' : ''));
if ($lastpage) { // Ignore $offset and just get the last page of feedback
$offset = (ceil($count / $limit) - 1) * $limit;
}
$feedback = get_records_sql_array('
SELECT
id, author, authorname, ctime, message, public
FROM {artefact_feedback}
WHERE view = ' . $viewid . ' AND artefact = ' . $artefactid
. (!$canedit ? ' AND (f.public = 1 OR f.author = ' . $userid . ')' : '') . '
ORDER BY id', '', $offset, $limit);
if ($feedback) {
require_once(get_config('libroot') . 'view.php');
require_once(get_config('libroot') . 'pieforms/pieform.php');
foreach ($feedback as &$f) {
if ($f->public && $canedit) {
$f->pubmessage = get_string('thisfeedbackispublic', 'view');
$f->makeprivateform = pieform(make_private_form($f->id));
}
else if (!$f->public) {
$f->pubmessage = get_string('thisfeedbackisprivate', 'view');
}
}
}
return (object) array(
'count' => $count,
'limit' => $limit,
'offset' => $offset,
'lastpage' => $lastpage,
'data' => $feedback ? $feedback : array(),
'view' => $viewid,
'artefact' => $artefactid,
'canedit' => $canedit,
'isowner' => $userid && $userid == $this->get('owner'),
);
}
}
/**
......
// The list of existing feedback.
var feedbacklist = new TableRenderer('feedbacktable', config.wwwroot + 'view/getfeedback.json.php', []);
feedbacklist.limit = 10;
feedbacklist.rowfunction = function(r, n, d) {
var td = TD(null);
td.innerHTML = r.message;
if (r.attachid && r.ownedbythisuser) {
appendChildNodes(td, DIV(null, get_string('feedbackattachmessage')));
}
var publicPrivate = null;
if (r.ispublic == 1) {
var makePrivate = null;
if (r.ownedbythisuser) {
makePrivateLink = A({'href': ''}, get_string('makeprivate'));
connect(makePrivateLink, 'onclick', function (e) {
sendjsonrequest(
'changefeedback.json.php',
r,
'POST',
function (data) {
if (!data.error) {
replaceChildNodes(makePrivateLink.parentNode, get_string('thisfeedbackisprivate'));
}
}
);
e.stop();
});
makePrivate = [' - ', makePrivateLink];
}
publicPrivate = SPAN(null, get_string('thisfeedbackispublic'), makePrivate);
}
else {
publicPrivate = get_string('thisfeedbackisprivate');
}
var attachment = null;
if (r.attachid) {
attachment = [' | ', get_string('attachment'), ': ', A({'href':config.wwwroot + 'artefact/file/download.php?file=' + r.attachid}, r.attachtitle), ' (', r.attachsize, ')'];
}
if (r.author) {
var icon = DIV({'class': 'icon'}, A({'href': config.wwwroot + 'user/view.php?id=' + r.author}, IMG({'src': config.wwwroot + 'thumb.php?type=profileicon&id=' + r.author + '&maxsize=20', 'valign': 'middle'})));
var authorname = A({'href': config.wwwroot + 'user/view.php?id=' + r.author}, r.name);
}
else {
var icon = null;
var authorname = r.name;
}
appendChildNodes(td, DIV({'class': 'details'}, icon, authorname, ' | ', r.date, ' | ', publicPrivate, attachment));
return TR({'class': 'r' + (n % 2)}, td);
};
feedbacklist.emptycontent = get_string('nopublicfeedback');
function addFeedbackSuccess() {
function addFeedbackSuccess(form, data) {
addElementClass('add_feedback_form', 'js-hidden');
$('add_feedback_form_message').innerHTML = '';
feedbacklist.doupdate();
paginator.updateResults(data);
}
function objectionSuccess() {
......@@ -108,7 +53,7 @@ addLoadEvent(function () {
if ($('toggle_watchlist_link')) {
connect('toggle_watchlist_link', 'onclick', function (e) {
e.stop();
sendjsonrequest('togglewatchlist.json.php', {'view': feedbacklist.view}, 'POST', function(data) {
sendjsonrequest('togglewatchlist.json.php', {'view': viewid}, 'POST', function(data) {
$('toggle_watchlist_link').innerHTML = data.newtext;
});
});
......
......@@ -298,4 +298,8 @@ $string['viewcopywouldexceedquota'] = 'Copying this View would exceed your file
$string['blockcopypermission'] = 'Block copy permission';
$string['blockcopypermissiondesc'] = 'If you allow other users to copy this View, you may choose how this block will be copied';
// Feedback list
$string['comment'] = 'comment';
$string['comments'] = 'comments';
?>
......@@ -2216,4 +2216,53 @@ function build_portfolio_search_html(&$data) {
$data->pagination_js = $pagination['javascript'];
}
// When feedback becomes an artefact type, move this into the feedback artefact plugin
function build_feedback_html(&$data) {
foreach ($data->data as &$item) {
$item->date = format_date(strtotime($item->ctime), 'strftimedatetime');
$item->message = clean_html(parse_bbcode($item->message));
$item->name = $item->author ? display_name($item->author) : $item->authorname;
if (!empty($item->attachment)) {
$item->attachid = $item->attachment;
$item->attachtitle = $item->title;
$item->attachsize = display_size($item->size);
if ($data->isowner) {
$item->attachmessage = get_string('feedbackattachmessage', 'view', get_string('feedbackattachdirname', 'view'));
}
}
}
$extradata = array('view' => $data->view);
if (!empty($data->artefact)) {
$data->baseurl = get_config('wwwroot') . 'view/artefact.php?view=' . $data->view . '&artefact=' . $data->artefact;
$data->jsonscript = 'view/artefactfeedback.json.php';
$extradata['artefact'] = $data->artefact;
}
else {
$data->baseurl = get_config('wwwroot') . 'view/view.php?id=' . $data->view;
$data->jsonscript = 'view/viewfeedback.json.php';
}
$smarty = smarty_core();
$smarty->assign_by_ref('data', $data->data);
$smarty->assign('canedit', $data->canedit);
$smarty->assign('baseurl', $data->baseurl);
$data->tablerows = $smarty->fetch('view/feedbacklist.tpl');
$pagination = build_pagination(array(
'id' => 'feedback_pagination',
'class' => 'center',
'url' => $data->baseurl,
'jsonscript' => $data->jsonscript,
'datatable' => 'feedbacktable',
'count' => $data->count,
'limit' => $data->limit,
'offset' => $data->offset,
'lastpage' => $data->lastpage,
'resultcounttextsingular' => get_string('comment', 'view'),
'resultcounttextplural' => get_string('comments', 'view'),
'extradata' => $extradata,
));
$data->pagination = $pagination['html'];
$data->pagination_js = $pagination['javascript'];
}
?>
......@@ -2525,6 +2525,49 @@ class View {
return false;
}
public function get_feedback($limit=10, $offset=0, $lastpage=false) {
global $USER;
$userid = $USER->get('id');
$viewid = $this->id;
$canedit = $USER->can_edit_view($this);
$count = count_records_sql('
SELECT COUNT(*)
FROM {view_feedback}
WHERE view = ' . $viewid . (!$canedit ? ' AND (public = 1 OR author = ' . $userid . ')' : ''));
if ($lastpage) { // Ignore $offset and just get the last page of feedback
$offset = (ceil($count / $limit) - 1) * $limit;
}
$feedback = get_records_sql_array('
SELECT
f.id, f.author, f.authorname, f.ctime, f.message, f.public, f.attachment, a.title, af.size
FROM {view_feedback} f
LEFT OUTER JOIN {artefact} a ON f.attachment = a.id
LEFT OUTER JOIN {artefact_file_files} af ON af.artefact = a.id
WHERE view = ' . $viewid . (!$canedit ? ' AND (f.public = 1 OR f.author = ' . $userid . ')' : '') . '
ORDER BY id', '', $offset, $limit);
if ($feedback) {
foreach ($feedback as &$f) {
if ($f->public && $canedit) {
$f->pubmessage = get_string('thisfeedbackispublic', 'view');
$f->makeprivateform = pieform(make_private_form($f->id));
}
else if (!$f->public) {
$f->pubmessage = get_string('thisfeedbackisprivate', 'view');
}
}
}
return (object) array(
'count' => $count,
'limit' => $limit,
'offset' => $offset,
'lastpage' => $lastpage,
'data' => $feedback ? $feedback : array(),
'view' => $viewid,
'canedit' => $canedit,
'isowner' => $userid && $userid == $this->get('owner'),
);
}
}
......@@ -2746,15 +2789,20 @@ function add_feedback_form_submit(Pieform $form, $values) {
db_commit();
$newlist = null;
if ($artefact) {
$goto = get_config('wwwroot') . 'view/artefact.php?artefact=' . $artefact->get('id') . '&view='.$view->get('id');
$newlist = $artefact->get_feedback(10, null, $view->get('id'), true);
}
else {
$goto = get_config('wwwroot') . 'view/view.php?id='.$view->get('id');
$newlist = $view->get_feedback(10, null, true);
}
build_feedback_html($newlist);
$form->reply(PIEFORM_OK, array(
'message' => get_string('feedbacksubmitted', 'view'),
'goto' => $goto,
'data' => $newlist,
));
}
......@@ -2765,6 +2813,34 @@ function add_feedback_form_cancel_submit(Pieform $form) {
));
}
function make_private_form($feedbackid) {
return array(
'name' => 'make_private',
'renderer' => 'oneline',
'class' => 'makeprivate',
'elements' => array(
'feedback' => array('type' => 'hidden', 'value' => $feedbackid),
'submit' => array(
'type' => 'submit',
'name' => 'make_private_submit',
'value' => get_string('makeprivate', 'view'),
),
),
);
}
function make_private_submit(Pieform $form, $values) {
global $SESSION, $view, $artefact;
if (isset($artefact) && $artefact instanceof ArtefactType) {
update_record('artefact_feedback', (object) array('public' => 0, 'id' => (int) $values['feedback']));
$SESSION->add_ok_msg(get_string('feedbackchangedtoprivate', 'view'));
redirect(get_config('wwwroot') . 'view/artefact.php?view=' . $view->get('id') . '&artefact=' . $artefact->get('id'));
}
update_record('view_feedback', (object) array('public' => 0, 'id' => (int) $values['feedback']));
$SESSION->add_ok_msg(get_string('feedbackchangedtoprivate', 'view'));
redirect(get_config('wwwroot') . 'view/view.php?id=' . $view->get('id'));
}
function objection_form() {
$form = array(
'name' => 'objection_form',
......
......@@ -797,16 +797,6 @@ function jsstrings() {
'cancel',
),
),
'feedbacklist' => array(
'view' => array(
'feedbackattachmessage',
'makeprivate',
'thisfeedbackisprivate',
'thisfeedbackispublic',
'attachment',
'nopublicfeedback',
),
),
);
}
......@@ -2736,6 +2726,9 @@ function build_pagination($params) {
$page = $params['offset'] / $params['limit'];
$last = $pages - 1;
if (!empty($params['lastpage'])) {
$page = $last;
}
$next = min($last, $page + 1);
$prev = max(0, $page - 1);
......
......@@ -100,6 +100,10 @@ body:last-child:not(:root:root) .newblockhere {
padding: 1em;
border: 1px solid #eee;
}
#feedbacktable form.makeprivate,
#feedbacktable form.makeprivate div {
display: inline;
}
#viewmenu a {
margin: 0 .5em;
}
......
......@@ -15,17 +15,19 @@
</div>
</div>
<div id="publicfeedback">
<table id="feedbacktable" class="fullwidth">
<thead>
<tr><th>{str tag="feedback" section="view"}</th></tr>
</thead>
<div class="viewfooter">
<table id="feedbacktable" class="fullwidth table">
<thead><tr><th>{str tag="feedback" section="view"}</th></tr></thead>
<tbody>
{$feedback->tablerows}
</tbody>
</table>
</div>
{$feedback->pagination}
<div id="viewmenu">
{include file="view/viewmenu.tpl"}
</div>
<div>{$addfeedbackform}</div>
<div>{$objectionform}</div>
</div>
{include file="footer.tpl"}
{foreach from=$data item=item}
<tr class="{cycle name=rows values='r0,r1'}">
<td>
{$item->message}
{if $item->attachmessage}<div>{$item->attachmessage}</div>{/if}
<div class="details">
{if $item->author}
<div class="icon"><a href="{$WWWROOT}user/view.php?id={$item->author|escape}">
<img src="{$WWWROOT}thumb.php?type=profileicon&id={$item->author|escape}&maxsize=20" valign="middle" alt="{$item->author|display_name}">
</a></div>
<a href="{$WWWROOT}user/view.php?id={$item->author|escape}">{$item->author|display_name}</a>
{else}
{$item->authorname|escape}
{/if}
| {$item->date|escape}
{if $item->pubmessage}
| {$item->pubmessage|escape}{if $item->makeprivateform}{$item->makeprivateform}{/if}
{/if}
{if $item->attachid}
| {str tag=attachment section=view}: <a href="{$WWWROOT}artefact/file/download.php?file={$item->attachid}">{$item->attachtitle|escape}</a> ({$item->attachsize|escape})
{/if}
</div>
</td>
</tr>
{/foreach}
......@@ -17,7 +17,11 @@
<div>{$releaseform}</div>
<table id="feedbacktable" class="fullwidth table">
<thead><tr><th>{str tag="feedback" section="view"}</th></tr></thead>
<tbody>
{$feedback->tablerows}
</tbody>
</table>
{$feedback->pagination}
<div id="viewmenu">
{include file="view/viewmenu.tpl"}
</div>
......
......@@ -47,9 +47,18 @@ if (!artefact_in_view($artefactid, $viewid)) {
throw new AccessDeniedException("Artefact $artefactid not in View $viewid");
}
// Feedback list pagination requires limit/offset params
$limit = param_integer('limit', 10);
$offset = param_integer('offset', 0);
require_once(get_config('docroot') . 'artefact/lib.php');
$artefact = artefact_instance_from_id($artefactid);
// Create the "make feedback private form" now if it's been submitted
if (param_variable('make_private_submit', null)) {
pieform(make_private_form(param_integer('feedback')));
}
if (!$artefact->in_view_list()) {
throw new AccessDeniedException("Artefacts of this type are only viewable within a View");
}
......@@ -90,18 +99,21 @@ $artefactpath[] = array(
// Feedback
$feedback = $artefact->get_feedback($limit, $offset, $viewid);
build_feedback_html($feedback);
$javascript = <<<EOF
feedbacklist.view = {$viewid};
feedbacklist.artefact = {$artefactid};
feedbacklist.statevars.push('view', 'artefact');
feedbacklist.updateOnLoad();
var viewid = {$viewid};
addLoadEvent(function () {
paginator = {$feedback->pagination_js}
});
EOF;
$feedbackform = pieform(add_feedback_form(false));
$objectionform = pieform(objection_form());
$smarty = smarty(
array('mahara', 'tablerenderer', 'feedbacklist'),
array('paginator', 'feedbacklist'),
array('<link rel="stylesheet" type="text/css" href="' . get_config('wwwroot') . 'theme/views.css">'),
array(),
array(
......@@ -116,6 +128,7 @@ $smarty->assign('INLINEJAVASCRIPT', $javascript);
$smarty->assign('viewid', $viewid);
$smarty->assign('viewtitle', $view->get('title'));
$smarty->assign('feedback', $feedback);
$viewowner = $view->get('owner');
if ($viewowner) {
......
......@@ -26,36 +26,27 @@
*/
define('INTERNAL', 1);
define('PUBLIC', 1);
define('JSON', 1);
require(dirname(dirname(__FILE__)) . '/init.php');
$extradata = json_decode(param_variable('extradata'));
json_headers();
$data = new StdClass;
$data->view = param_integer('view');
$data->artefact = param_integer('artefact', null);
$data->message = param_variable('message');
$data->public = param_boolean('public') ? 1 : 0;
$data->attachment = param_integer('attachment', null);
$data->author = $USER->get('id');
$data->ctime = db_format_timestamp(time());
if ($data->artefact) {
$table = 'artefact_feedback';
if (!can_view_view($extradata->view)) {
json_reply('local', get_string('noaccesstoview', 'view'));
}
else {
$table = 'view_feedback';
if (!artefact_in_view($extradata->artefact, $extradata->view)) {
json_reply('local', get_string('accessdenied', 'error'));
}
if (!insert_record($table, $data, 'id', true)) {
json_reply('local', get_string('addfeedbackfailed', 'view'));
}
$limit = param_integer('limit', 10);
$offset = param_integer('offset', 0);
require_once('activity.php');
activity_occurred('feedback', $data);
require_once(get_config('docroot') . 'artefact/lib.php');
$artefact = artefact_instance_from_id($extradata->artefact);
json_reply(false,get_string('feedbacksubmitted', 'view'));
$data = $artefact->get_feedback($limit, $offset, $extradata->view);
build_feedback_html($data);
json_reply(false, array('data' => $data));
?>
......@@ -65,8 +65,18 @@ $new = param_boolean('new');
if (!can_view_view($viewid, null, $usertoken, $mnettoken)) {
throw new AccessDeniedException(get_string('accessdenied', 'error'));
}
// Feedback list pagination requires limit/offset params
$limit = param_integer('limit', 10);
$offset = param_integer('offset', 0);
$view = new View($viewid);
// Create the "make feedback private form" now if it's been submitted
if (param_variable('make_private_submit', null)) {
pieform(make_private_form(param_integer('feedback')));
}
$group = $view->get('group');
$title = $view->get('title');
......@@ -112,13 +122,8 @@ function releaseview_submit() {
$viewbeingwatched = (int)record_exists('usr_watchlist_view', 'usr', $USER->get('id'), 'view', $viewid);
// Feedback
$javascript = <<<EOF
feedbacklist.view = {$viewid};
feedbacklist.statevars.push('view');
feedbacklist.updateOnLoad();
EOF;
$feedback = $view->get_feedback($limit, $offset);
build_feedback_html($feedback);
$anonfeedback = !$USER->is_logged_in() && ($usertoken || $viewid == get_view_from_token(get_cookie('viewaccess:'.$viewid)));
if ($USER->is_logged_in() || $anonfeedback) {
......@@ -131,7 +136,7 @@ if ($USER->is_logged_in()) {
$can_edit = $USER->can_edit_view($view) && !$submittedgroup && !$view->is_submitted();
$smarty = smarty(
array('mahara', 'tablerenderer', 'feedbacklist', 'artefact/resume/resumeshowhide.js'),
array('paginator', 'feedbacklist', 'artefact/resume/resumeshowhide.js'),
array('<link rel="stylesheet" type="text/css" href="' . get_config('wwwroot') . 'theme/views.css">'),
array(),
array(
......@@ -140,10 +145,18 @@ $smarty = smarty(
)
);
$javascript = <<<EOF
var viewid = {$viewid};
addLoadEvent(function () {
paginator = {$feedback->pagination_js}
});
EOF;
$smarty->assign('INLINEJAVASCRIPT', $javascript);
$smarty->assign('new', $new);
$smarty->assign('viewid', $viewid);
$smarty->assign('viewtitle', $view->get('title'));
$smarty->assign('feedback', $feedback);
$owner = $view->get('owner');
$smarty->assign('owner', $owner);
......
<?php
/**
* Mahara: Electronic portfolio, weblog, resume builder and social networking
* Copyright (C) 2006-2009 Catalyst IT Ltd and others; see:
* http://wiki.mahara.org/Contributors
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package mahara
* @subpackage core
* @author Catalyst IT Ltd