431 lines
12 KiB
Perl
431 lines
12 KiB
Perl
|
|
# This code was forked from the LiveJournal project owned and operated
|
||
|
|
# by Live Journal, Inc. The code has been modified and expanded by
|
||
|
|
# Dreamwidth Studios, LLC. These files were originally licensed under
|
||
|
|
# the terms of the license supplied by Live Journal, Inc, which can
|
||
|
|
# currently be found at:
|
||
|
|
#
|
||
|
|
# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
|
||
|
|
#
|
||
|
|
# In accordance with the original license, this code and all its
|
||
|
|
# modifications are provided under the GNU General Public License.
|
||
|
|
# A copy of that license can be found in the LICENSE file included as
|
||
|
|
# part of this distribution.
|
||
|
|
|
||
|
|
package LJ::Poll::Question;
|
||
|
|
use strict;
|
||
|
|
use Carp qw (croak);
|
||
|
|
|
||
|
|
sub new {
|
||
|
|
my ( $class, $poll, $pollqid ) = @_;
|
||
|
|
|
||
|
|
my $self = {
|
||
|
|
poll => $poll,
|
||
|
|
pollqid => $pollqid,
|
||
|
|
};
|
||
|
|
|
||
|
|
bless $self, $class;
|
||
|
|
return $self;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub new_from_row {
|
||
|
|
my ( $class, $row ) = @_;
|
||
|
|
|
||
|
|
my $pollid = $row->{pollid};
|
||
|
|
my $pollqid = $row->{pollqid};
|
||
|
|
|
||
|
|
my $poll;
|
||
|
|
$poll = LJ::Poll->new($pollid) if $pollid;
|
||
|
|
|
||
|
|
my $question = __PACKAGE__->new( $poll, $pollqid );
|
||
|
|
$question->absorb_row($row);
|
||
|
|
|
||
|
|
return $question;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub absorb_row {
|
||
|
|
my ( $self, $row ) = @_;
|
||
|
|
|
||
|
|
# items is optional, used for caching
|
||
|
|
$self->{$_} = $row->{$_} foreach qw (sortorder type opts qtext items);
|
||
|
|
$self->{_loaded} = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub _load {
|
||
|
|
my $self = shift;
|
||
|
|
return if $self->{_loaded};
|
||
|
|
|
||
|
|
croak "_load called on a LJ::Poll::Question object with no pollid"
|
||
|
|
unless $self->pollid;
|
||
|
|
croak "_load called on a LJ::Poll::Question object with no pollqid"
|
||
|
|
unless $self->pollqid;
|
||
|
|
|
||
|
|
my $sth = $self->poll->journal->prepare(
|
||
|
|
'SELECT * FROM pollquestion2 WHERE pollid=? AND pollqid=? and journalid=?');
|
||
|
|
$sth->execute( $self->pollid, $self->pollqid, $self->poll->journalid );
|
||
|
|
|
||
|
|
$self->absorb_row( $sth->fetchrow_hashref );
|
||
|
|
}
|
||
|
|
|
||
|
|
# returns the question rendered for previewing
|
||
|
|
sub preview_as_html {
|
||
|
|
my $self = shift;
|
||
|
|
my $ret = '';
|
||
|
|
|
||
|
|
my $type = $self->type;
|
||
|
|
my $opts = $self->opts;
|
||
|
|
|
||
|
|
my $qtext = $self->qtext;
|
||
|
|
if ($qtext) {
|
||
|
|
LJ::Poll->clean_poll( \$qtext );
|
||
|
|
$ret .= "<p>$qtext</p>\n";
|
||
|
|
}
|
||
|
|
if ( $type eq 'check' ) {
|
||
|
|
my ( $mincheck, $maxcheck ) = split( m!/!, $opts );
|
||
|
|
$mincheck ||= 0;
|
||
|
|
$maxcheck ||= 255;
|
||
|
|
|
||
|
|
if ( $mincheck > 0 && $mincheck eq $maxcheck ) {
|
||
|
|
$ret .= "<i>"
|
||
|
|
. LJ::Lang::ml( "poll.checkexact2", { options => $mincheck } )
|
||
|
|
. "</i><br />\n";
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
if ( $mincheck > 0 ) {
|
||
|
|
$ret .= "<i>"
|
||
|
|
. LJ::Lang::ml( "poll.checkmin2", { options => $mincheck } )
|
||
|
|
. "</i><br />\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $maxcheck < 255 ) {
|
||
|
|
$ret .= "<i>"
|
||
|
|
. LJ::Lang::ml( "poll.checkmax2", { options => $maxcheck } )
|
||
|
|
. "</i><br />\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$ret .= "<div style='margin: 10px 0 10px 5%'>";
|
||
|
|
|
||
|
|
# text questions
|
||
|
|
if ( $type eq 'text' ) {
|
||
|
|
my ( $size, $max ) = split( m!/!, $opts );
|
||
|
|
$ret .= LJ::html_text( { 'size' => $size, 'maxlength' => $max } );
|
||
|
|
|
||
|
|
# scale questions
|
||
|
|
}
|
||
|
|
elsif ( $type eq 'scale' ) {
|
||
|
|
my ( $from, $to, $by, $lowlabel, $highlabel ) = split( m!/!, $opts );
|
||
|
|
$by ||= 1;
|
||
|
|
my $count = int( ( $to - $from ) / $by ) + 1;
|
||
|
|
my $do_radios = ( $count <= 11 );
|
||
|
|
|
||
|
|
if ($do_radios) {
|
||
|
|
|
||
|
|
# few opts, display radios
|
||
|
|
my @all_values;
|
||
|
|
for ( my $at = $from ; $at <= $to ; $at += $by ) {
|
||
|
|
push @all_values, $at;
|
||
|
|
}
|
||
|
|
$ret .= DW::Template->template_string(
|
||
|
|
'poll/scale_radio.tt',
|
||
|
|
{
|
||
|
|
lowlabel => $lowlabel,
|
||
|
|
highlabel => $highlabel,
|
||
|
|
selectedanswer => '',
|
||
|
|
pollid => '',
|
||
|
|
qid => '',
|
||
|
|
values => \@all_values,
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
# many opts, display select
|
||
|
|
my @optlist = ( '', ' ' );
|
||
|
|
push @optlist, ( $from, $from . " " . $lowlabel );
|
||
|
|
|
||
|
|
my $at = 0;
|
||
|
|
for ( $at = $from + $by ; $at <= $to - $by ; $at += $by ) {
|
||
|
|
push @optlist, ( '', $at );
|
||
|
|
}
|
||
|
|
|
||
|
|
push @optlist, ( $at, $at . " " . $highlabel );
|
||
|
|
|
||
|
|
$ret .= LJ::html_select( {}, @optlist );
|
||
|
|
}
|
||
|
|
|
||
|
|
# questions with items
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
# drop-down list
|
||
|
|
if ( $type eq 'drop' ) {
|
||
|
|
my @optlist = ( '', '' );
|
||
|
|
foreach my $it ( $self->items ) {
|
||
|
|
LJ::Poll->clean_poll( \$it->{item} );
|
||
|
|
push @optlist, ( '', $it->{item} );
|
||
|
|
}
|
||
|
|
$ret .= LJ::html_select( {}, @optlist );
|
||
|
|
|
||
|
|
# radio or checkbox
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
foreach my $it ( $self->items ) {
|
||
|
|
LJ::Poll->clean_poll( \$it->{item} );
|
||
|
|
$ret .= LJ::html_check( { 'type' => $self->type } ) . "$it->{item}<br />\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$ret .= "</div>";
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub items {
|
||
|
|
my $self = shift;
|
||
|
|
|
||
|
|
return @{ $self->{items} } if $self->{items};
|
||
|
|
|
||
|
|
my $sth = $self->poll->journal->prepare( 'SELECT pollid, pollqid, pollitid, sortorder, item '
|
||
|
|
. 'FROM pollitem2 WHERE pollid=? AND pollqid=? AND journalid=?' );
|
||
|
|
$sth->execute( $self->pollid, $self->pollqid, $self->poll->journalid );
|
||
|
|
|
||
|
|
die $sth->errstr if $sth->err;
|
||
|
|
|
||
|
|
my @items;
|
||
|
|
|
||
|
|
while ( my $row = $sth->fetchrow_hashref ) {
|
||
|
|
my $item = {};
|
||
|
|
$item->{$_} = $row->{$_} foreach qw(pollitid sortorder item pollid pollqid);
|
||
|
|
push @items, $item;
|
||
|
|
}
|
||
|
|
|
||
|
|
@items = sort { $a->{sortorder} <=> $b->{sortorder} } @items;
|
||
|
|
|
||
|
|
$self->{items} = \@items;
|
||
|
|
|
||
|
|
return @items;
|
||
|
|
}
|
||
|
|
|
||
|
|
# accessors
|
||
|
|
sub poll {
|
||
|
|
my $self = shift;
|
||
|
|
return $self->{poll};
|
||
|
|
}
|
||
|
|
|
||
|
|
sub pollid {
|
||
|
|
my $self = shift;
|
||
|
|
return $self->poll->pollid;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub pollqid {
|
||
|
|
my $self = shift;
|
||
|
|
return $self->{pollqid};
|
||
|
|
}
|
||
|
|
|
||
|
|
sub sortorder {
|
||
|
|
my $self = shift;
|
||
|
|
$self->_load;
|
||
|
|
return $self->{sortorder};
|
||
|
|
}
|
||
|
|
|
||
|
|
sub type {
|
||
|
|
my $self = shift;
|
||
|
|
$self->_load;
|
||
|
|
return $self->{type};
|
||
|
|
}
|
||
|
|
|
||
|
|
sub opts {
|
||
|
|
my $self = shift;
|
||
|
|
$self->_load;
|
||
|
|
return $self->{opts} || '';
|
||
|
|
}
|
||
|
|
*text = \&qtext;
|
||
|
|
|
||
|
|
sub qtext {
|
||
|
|
my $self = shift;
|
||
|
|
$self->_load;
|
||
|
|
return $self->{qtext};
|
||
|
|
}
|
||
|
|
|
||
|
|
# Count answers pages
|
||
|
|
sub answers_pages {
|
||
|
|
my $self = shift;
|
||
|
|
my $jid = shift;
|
||
|
|
|
||
|
|
my $pagesize = shift || 2000;
|
||
|
|
|
||
|
|
my $pages = 0;
|
||
|
|
|
||
|
|
# Get results count
|
||
|
|
my $sth = $self->poll->journal->prepare( "SELECT COUNT(*) as count FROM pollresult2"
|
||
|
|
. " WHERE pollid=? AND pollqid=? AND journalid=?" );
|
||
|
|
$sth->execute( $self->pollid, $self->pollqid, $jid );
|
||
|
|
die $sth->errstr if $sth->err;
|
||
|
|
$_ = $sth->fetchrow_hashref;
|
||
|
|
my $count = $_->{count};
|
||
|
|
$pages = 1 + int( ( $count - 1 ) / $pagesize );
|
||
|
|
die $sth->errstr if $sth->err;
|
||
|
|
|
||
|
|
return $pages;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub answers_as_html {
|
||
|
|
my $self = shift;
|
||
|
|
my $jid = shift;
|
||
|
|
my $isanon = shift;
|
||
|
|
|
||
|
|
my $page = shift || 1;
|
||
|
|
my $pagesize = shift || 2000;
|
||
|
|
|
||
|
|
my $pages = shift || $self->answers_pages( $jid, $pagesize );
|
||
|
|
|
||
|
|
my $ret = '';
|
||
|
|
|
||
|
|
my $LIMIT = $pagesize * ( $page - 1 ) . "," . $pagesize;
|
||
|
|
|
||
|
|
my $uid_map = {};
|
||
|
|
if ( $isanon eq "yes" ) {
|
||
|
|
if ( $self->{_uids} ) {
|
||
|
|
$uid_map = $self->{_uids};
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
# get user list
|
||
|
|
my $uids = $self->poll->journal->selectcol_arrayref(
|
||
|
|
"SELECT userid from pollsubmission2 WHERE pollid=? AND journalid=?",
|
||
|
|
undef, $self->pollid, $jid );
|
||
|
|
my $i = 0;
|
||
|
|
$uid_map = { map { $_ => ++$i } @{ $uids || [] } };
|
||
|
|
$self->{_uids} = $uid_map;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Get data
|
||
|
|
my $sth =
|
||
|
|
$self->poll->journal->prepare( "SELECT pr.value, ps.datesubmit, pr.userid "
|
||
|
|
. "FROM pollresult2 pr, pollsubmission2 ps "
|
||
|
|
. "WHERE pr.pollid=? AND pollqid=? "
|
||
|
|
. "AND ps.pollid=pr.pollid AND ps.userid=pr.userid "
|
||
|
|
. "AND ps.journalid=? "
|
||
|
|
. "ORDER BY ps.datesubmit "
|
||
|
|
. "LIMIT $LIMIT" );
|
||
|
|
$sth->execute( $self->pollid, $self->pollqid, $jid );
|
||
|
|
die $sth->errstr if $sth->err;
|
||
|
|
|
||
|
|
my ( $pollid, $pollqid ) = ( $self->pollid, $self->pollqid );
|
||
|
|
|
||
|
|
my @res;
|
||
|
|
push @res, $_ while $_ = $sth->fetchrow_hashref;
|
||
|
|
@res = sort { $a->{datesubmit} cmp $b->{datesubmit} } @res;
|
||
|
|
|
||
|
|
foreach my $res (@res) {
|
||
|
|
my ( $userid, $value ) = ( $res->{userid}, $res->{value}, $res->{pollqid} );
|
||
|
|
my @items = $self->items;
|
||
|
|
|
||
|
|
my %it;
|
||
|
|
$it{ $_->{pollitid} } = $_->{item} foreach @items;
|
||
|
|
|
||
|
|
my $u = LJ::load_userid($userid) or die "Invalid userid $userid";
|
||
|
|
|
||
|
|
## some question types need translation; type 'text' doesn't.
|
||
|
|
if ( $self->type eq "radio" || $self->type eq "drop" ) {
|
||
|
|
$value = $it{$value};
|
||
|
|
}
|
||
|
|
elsif ( $self->type eq "check" ) {
|
||
|
|
$value = join( ", ", map { $it{$_} } split( /,/, $value ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
LJ::Poll->clean_poll( \$value );
|
||
|
|
my $user_display =
|
||
|
|
$isanon eq "yes" ? "User <b>#" . $uid_map->{$userid} . "</b>" : $u->ljuser_display;
|
||
|
|
|
||
|
|
$ret .= "<div>" . $user_display . " -- $value</div>\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
#returns how a user answered this question
|
||
|
|
sub user_answer_as_html {
|
||
|
|
my $self = shift;
|
||
|
|
my $userid = shift;
|
||
|
|
my $isanon = shift;
|
||
|
|
|
||
|
|
my $ret = '';
|
||
|
|
|
||
|
|
# Get data
|
||
|
|
my $sth = $self->poll->journal->prepare(
|
||
|
|
"SELECT value FROM pollresult2 " . "WHERE pollid=? AND pollqid=? AND userid=? " );
|
||
|
|
|
||
|
|
$sth->execute( $self->pollid, $self->pollqid, $userid );
|
||
|
|
die $sth->errstr if $sth->err;
|
||
|
|
|
||
|
|
my ( $pollid, $pollqid ) = ( $self->pollid, $self->pollqid );
|
||
|
|
|
||
|
|
my $qtext = $self->qtext;
|
||
|
|
my @res;
|
||
|
|
push @res, $_ while $_ = $sth->fetchrow_hashref;
|
||
|
|
|
||
|
|
foreach my $res (@res) {
|
||
|
|
my $value = $res->{value};
|
||
|
|
my @items = $self->items;
|
||
|
|
|
||
|
|
my %it;
|
||
|
|
$it{ $_->{pollitid} } = $_->{item} foreach @items;
|
||
|
|
|
||
|
|
# some question types need translation; type 'text' doesn't.
|
||
|
|
if ( $self->type eq "radio" || $self->type eq "drop" ) {
|
||
|
|
$value = $it{$value};
|
||
|
|
}
|
||
|
|
elsif ( $self->type eq "check" ) {
|
||
|
|
$value = join( ", ", map { $it{$_} } split( /,/, $value ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
LJ::Poll->clean_poll( \$value );
|
||
|
|
LJ::Poll->clean_poll( \$qtext );
|
||
|
|
|
||
|
|
$ret .= '<b>' . $qtext . "</b> -- " . $value . "<br/>\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub paging_bar_as_html {
|
||
|
|
my $self = shift;
|
||
|
|
|
||
|
|
my $page = shift || 1;
|
||
|
|
my $pages = shift || 1;
|
||
|
|
my $pagesize = shift || 2000;
|
||
|
|
|
||
|
|
my ( $jid, $pollid, $pollqid, %opts ) = @_;
|
||
|
|
|
||
|
|
my $href_opts = sub {
|
||
|
|
my $page = shift;
|
||
|
|
|
||
|
|
# FIXME: this is a quick hack to disable the paging JS on /poll/index since it doesn't work
|
||
|
|
# better fix will await another look at that whole area
|
||
|
|
return
|
||
|
|
( $opts{no_class} ? "" : " class='LJ_PollAnswerLink'" )
|
||
|
|
. " lj_pollid='$pollid'"
|
||
|
|
. " lj_qid='$pollqid'"
|
||
|
|
. " lj_posterid='$jid'"
|
||
|
|
. " lj_page='$page'"
|
||
|
|
. " lj_pagesize='$pagesize'";
|
||
|
|
};
|
||
|
|
|
||
|
|
return LJ::paging_bar( $page, $pages, { href_opts => $href_opts } );
|
||
|
|
}
|
||
|
|
|
||
|
|
sub answers {
|
||
|
|
my $self = shift;
|
||
|
|
|
||
|
|
my $ret = '';
|
||
|
|
my $sth = $self->poll->journal->prepare(
|
||
|
|
"SELECT userid, pollqid, value FROM pollresult2 " . "WHERE pollid=? AND pollqid=?" );
|
||
|
|
$sth->execute( $self->pollid, $self->pollqid );
|
||
|
|
die $sth->errstr if $sth->err;
|
||
|
|
|
||
|
|
my @res;
|
||
|
|
push @res, $_ while $_ = $sth->fetchrow_hashref;
|
||
|
|
|
||
|
|
return @res;
|
||
|
|
}
|
||
|
|
|
||
|
|
1;
|