-Subproject commit 23749c61b65d441b631e791005277ec1c69f4c83
+Subproject commit cb22cf2a3b34e4f263dcfb6b51396111f8d6b785
if (!$survey->canSee(S::user())) {
return PL_FORBIDDEN;
}
+ if (Post::has('vote')) {
+ $answers = Post::v('qid');
+ $vote = $survey->vote(S::user(), $answers);
+ if (is_null($vote)) {
+ $page->kill("Tu n'as pas le droit de voter à ce sondage.");
+ } else if ($vote->inError()) {
+ $page->trigError("Certaines réponses sont invalides et doivent être corrigées");
+ } else {
+ $vote->insert(true);
+ $page->trigSuccess("Ton vote a été enregistré");
+ }
+ }
$page->assign('survey', $survey);
}
}
--- /dev/null
+<?php
+/***************************************************************************
+ * Copyright (C) 2003-2010 Polytechnique.org *
+ * http://opensource.polytechnique.org/ *
+ * *
+ * 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 2 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, write to the Free Software *
+ * Foundation, Inc., *
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ ***************************************************************************/
+
+class SurveyVote extends PlDBTableEntry
+{
+ protected $survey;
+ protected $user;
+
+ private $answers = array();
+ private $fetchAnswers;
+
+ public function __construct(Survey $survey, User $user)
+ {
+ parent::__construct('survey_votes');
+ $this->survey = $survey;
+ $this->user = $user;
+ $this->sid = $survey->id;
+ }
+
+ protected function postSave()
+ {
+ Platal::assert(!is_null($this->vid), "Cannot process a vote without its identifier");
+ XDB::execute("REPLACE INTO survey_voters (sid, uid, vid)
+ VALUES ({?}, {?}, {?})",
+ $this->survey->id, $this->user->id(),
+ $this->survey->flags->hasFlag('anonymous') ? null : $this->vid);
+
+ /* Save answers */
+ $selector = new SurveyAnswer($this);
+ $selector->delete();
+
+ $answers = array();
+ foreach ($this->answers as $key=>$answer) {
+ if (!is_null($answer)) {
+ $answer->vid = $this->vid;
+ $answers[] = $answer;
+ }
+ }
+ PlDBTableEntry::insertBatch($answers);
+ return true;
+ }
+
+ protected function postFetch()
+ {
+ $selector = new SurveyAnswer($this);
+ foreach ($selector as $answer) {
+ $question = $this->survey->questionForId($answer->qid);
+ $this->answers[$answer->qid] = $answer;
+ }
+ return true;
+ }
+
+ public function inError()
+ {
+ foreach ($this->answers as $answer) {
+ if ($answer->inError !== false) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function getAnswer(SurveyQuestion $question)
+ {
+ if (!isset($this->answers[$question->qid])) {
+ $val = new SurveyAnswer($this);
+ $val->qid = $question->qid;
+ $this->answers[$question->qid] = $val;
+ }
+ return $this->answers[$question->qid];
+ }
+
+ public function export()
+ {
+ $export = array();
+ foreach ($this->answers as $qid=>$answer) {
+ $export[$qid] = $answer->export();
+ }
+ return $export;
+ }
+
+ public static function getVote(Survey $survey, User $user, $fetchAnswers = true)
+ {
+ $vid = XDB::query('SELECT vid
+ FROM survey_voters
+ WHERE sid = {?} AND uid = {?}',
+ $survey->id, $user->id());
+ if ($vid->numRows() == 0) {
+ $vote = new SurveyVote($survey, $user);
+ $vote->fetchAnswers = $fetchAnswers;
+ return $vote;
+ }
+ $vid = $vid->fetchOneCell();
+ if (is_null($vid)) {
+ /* User already vote, but survey is anonymous and the vote
+ * cannot be changed
+ */
+ return null;
+ }
+ $vote = new SurveyVote($survey, $user);
+ $vote->vid = $vid;
+ $vote->fetchAnswers = $fetchAnswers;
+ $vote->fetch();
+ return $vote;
+ }
+}
+
+class SurveyAnswer extends PlDBTableEntry
+{
+ public $inError = false;
+ public $vote;
+
+ public function __construct(SurveyVote $vote)
+ {
+ parent::__construct('survey_vote_answers');
+ $this->registerFieldFormatter('answer', 'JSonFieldFormatter');
+ $this->vote = $vote;
+ if (!is_null($vote->vid)) {
+ $this->vid = $vote->vid;
+ }
+ }
+
+ protected function preSave()
+ {
+ Platal::assert(!$this->inError, "Cannot save an invalid answer");
+ $this->sid = $this->vote->sid;
+ $this->vid = $this->vote->vid;
+ return true;
+ }
+
+ public function export()
+ {
+ $export = array();
+ if (!is_null($this->answer)) {
+ $export['value'] = $this->answer->export();
+ }
+ if ($this->inError !== false) {
+ $export['error'] = $this->inError;
+ }
+ return $export;
+ }
+}
+
+// vim:set et sw=4 sts=4 ts=4 foldmethod=marker enc=utf-8:
+?>
--- /dev/null
+<?php
+/***************************************************************************
+ * Copyright (C) 2003-2010 Polytechnique.org *
+ * http://opensource.polytechnique.org/ *
+ * *
+ * 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 2 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, write to the Free Software *
+ * Foundation, Inc., *
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ ***************************************************************************/
+
+interface SurveyQuestionContainer
+{
+ public function addQuestion(SurveyQuestion $question, $pos = null);
+ public function newQuestion($type, $pos = null);
+ public function reassignQuestionIds();
+}
+
+class SurveyQuestion extends PlDBTableEntry
+{
+ protected $survey;
+ protected $parentQuestion;
+
+ public function __construct(Survey $survey)
+ {
+ parent::__construct('survey_questions');
+ $this->registerFieldFormatter('parameters', 'JSonFieldFormatter');
+ $this->survey = $survey;
+ }
+
+ public function typedInstance()
+ {
+ $instance = self::instanceForType($this->survey, $this->type);
+ $instance->copy($this);
+ return $instance;
+ }
+
+ public static function instanceForType(Survey $survey, $type)
+ {
+ require_once dirname(__FILE__) . '/' . $type . '.inc.php';
+ $class = 'SurveyQuestion' . $type;
+ return new $class($survey);
+ }
+
+ public function voteTemplate()
+ {
+ return 'survey/question.' . $this->type . '.tpl';
+ }
+
+ public function editTemplate()
+ {
+ return 'survey/edit.' . $this->type . '.tpl';
+ }
+
+ protected function buildAnswer(SurveyAnswer $answer, PlDict $answers)
+ {
+ Platal::assert(false, "This should not happen");
+ }
+
+ public function vote(SurveyVote $vote, PlDict $answers)
+ {
+ if ($this->flags->hasFlag('noanswer')) {
+ if ($answers->has($this->qid)) {
+ throw new Exception("Des réponses ont été données à une question n'en attendant pas");
+ }
+ return null;
+ }
+ $answer = $vote->getAnswer($this);
+ if (is_null($answer)) {
+ return null;
+ }
+ if (!$this->buildAnswer($answer, $answers)) {
+ return $answer;
+ }
+ if ($this->flags->hasFlag('mandatory') && is_null($answer->answer)) {
+ $answer->inError = 'Tu dois répondre à cette question';
+ }
+ return $answer;
+ }
+}
+
+class SurveyQuestionGroup extends SurveyQuestion implements SurveyQuestionContainer
+{
+ public $children = array();
+
+ public function __construct(Survey $survey)
+ {
+ parent::__construct($survey);
+ }
+
+ public function addQuestion(SurveyQuestion $question, $pos = null)
+ {
+ $question->parentQuestion = $this;
+ if (is_null($pos)) {
+ $this->children[] = $question;
+ } else {
+ array_splice($this->children, $pos, 0, $question);
+ }
+ }
+
+ public function newQuestion($type, $pos = null)
+ {
+ $question = SurveyQuestion::instanceForType($this->survey, $type);
+ $this->addQuestion($question, $pos);
+ return $question;
+ }
+
+ public function reassignQuestionIds()
+ {
+ $id = $this->qid + 1;
+ foreach ($this->children as $question) {
+ $question->qid = $id;
+ if ($question instanceof SurveyQuestionContainer) {
+ $id = $question->reassignQuestionIds();
+ } else {
+ $id++;
+ }
+ }
+ return $id;
+ }
+
+ protected function postSave()
+ {
+ foreach ($this->children as $question) {
+ $question->sid = $this->sid;
+ $question->parent = $this->qid;
+ $question->insert();
+ }
+ }
+
+ public function export()
+ {
+ $export = parent::export();
+ $export['children'] = array();
+ foreach ($this->children as $child) {
+ $export['children'][] = $child->export();
+ }
+ return $export;
+ }
+
+ public function vote(SurveyVote $vote, PlDict $answers)
+ {
+ $a = parent::vote($vote, $answers);
+ foreach ($this->children as $child) {
+ $child->vote($vote, $answers);
+ }
+ return $a;
+ }
+
+ public function child($qid)
+ {
+ $prev = null;
+ foreach ($this->children as $question) {
+ if ($qid == $question->qid) {
+ return $question;
+ } else if ($qid < $question->qid) {
+ Platal::assert($prev instanceof SurveyQuestionGroup);
+ return $prev->child($qid);
+ }
+ $prev = $question;
+ }
+ Platal::assert($prev instanceof SurveyQuestionGroup);
+ return $prev->child($qid);
+ }
+}
+
+// vim:set et sw=4 sts=4 ts=4 foldmethod=marker enc=utf-8:
+?>
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
***************************************************************************/
+require_once dirname(__FILE__) . '/question.inc.php';
+require_once dirname(__FILE__) . '/answer.inc.php';
+
class Survey extends PlDBTableEntry implements SurveyQuestionContainer
{
private $fetchQuestions = true;
public $questions = array();
+ public $viewerFilter = null;
+ public $voterFilter = null;
public function __construct()
{
protected function postFetch()
{
+ if (!is_null($this->voters)) {
+ $this->voterFilter = UserFilter::fromExportedConditions($this->voters);
+ } else {
+ $this->voterFilter = null;
+ }
+ if (!is_null($this->viewers)) {
+ $this->viewerFilter = UserFilter::fromExportedConditions($this->viewers);
+ } else {
+ $this->viewerFilter = null;
+ }
if (!$this->fetchQuestions) {
return true;
}
return true;
}
+ protected function preSave()
+ {
+ if (!is_null($this->voterFilter)) {
+ $this->voters = $this->voterFilter->exportConditions();
+ } else {
+ $this->voters = null;
+ }
+ if (!is_null($this->viewerFilter)) {
+ $this->viewers = $this->viewerFilter->exportConditions();
+ } else {
+ $this->viewers = null;
+ }
+ return true;
+ }
+
protected function postSave()
{
$questions = array();
return $question;
}
+ public function questionForId($qid)
+ {
+ $prev = null;
+ foreach ($this->questions as $question) {
+ if ($qid == $question->qid) {
+ return $question;
+ } else if ($qid < $question->qid) {
+ Platal::assert($prev instanceof SurveyQuestionGroup,
+ "Id gap must be caused by question groups");
+ return $prev->child($qid);
+ }
+ $prev = $question;
+ }
+ Platal::assert($prev instanceof SurveyQuestionGroup,
+ "Id gap must be caused by question groups");
+ return $prev->child($qid);
+ }
+
public function reassignQuestionIds()
{
$id = 0;
return $export;
}
+ /* Return an indicator of the progression of the survey:
+ * negative values means 'the survey is not started'
+ * 0 means 'the survey is in progress'
+ * positive values means 'the survey expired'
+ */
+ public function progression()
+ {
+ if (!$this->flags->hasFlag('validated')) {
+ return -2;
+ }
+ $now = time();
+ if ($this->begin->format('U') > $now) {
+ return -1;
+ }
+ if ($this->end->format('U') <= $now) {
+ return 1;
+ }
+ return 0;
+ }
+
public function canSee(User $user)
{
- if ($this->uid == $user->id() || $user->hasFlag('admin')) {
+ if ($this->canSeeResults($user) || $this->canVote($user)) {
return true;
}
return false;
}
+ public function canSeeResults(User $user)
+ {
+ if ($user->id() == $this->uid || $user->hasFlag('admin')) {
+ return true;
+ }
+ if (is_null($this->viewerFilter)) {
+ return $this->viewerFilter->checkUser($user);
+ }
+ return false;
+ }
+
+ public function canVote(User $user)
+ {
+ $status = $this->progression();
+ if ($status < 0) {
+ return "Ce sondage n'est pas encore commencé";
+ } else if ($status > 0) {
+ return "Ce sondage est terminé";
+ }
+ if (!is_null($this->voterFilter) && !$this->voterFilter->checkUser($user)) {
+ return "Ce sondage ne s'adresse pas à toi";
+ }
+ $vote = SurveyVote::getVote($this, $user, false);
+ if (is_null($vote)) {
+ return "Tu as déjà voté à ce sondage.";
+ }
+ return true;
+ }
+
+ public function vote(User $user, array $answers)
+ {
+ if (!$this->canVote($user)) {
+ return array('survey' => "Tu n'es pas autorisé à voter à ce sondage.");
+ }
+ $vote = SurveyVote::getVote($this, $user);
+ if (is_null($vote)) {
+ return $vote;
+ }
+ $answers = new PlDict($answers);
+ foreach ($this->questions as $question) {
+ $question->vote($vote, $answers);
+ }
+ return $vote;
+ }
+
public static function get($name, $fetchQuestions = true)
{
if (is_array($name)) {
}
}
-interface SurveyQuestionContainer
-{
- public function addQuestion(SurveyQuestion $question, $pos = null);
- public function newQuestion($type, $pos = null);
- public function reassignQuestionIds();
-}
-
-class SurveyQuestion extends PlDBTableEntry
-{
- protected $survey;
- protected $parentQuestion;
-
- public function __construct(Survey $survey)
- {
- parent::__construct('survey_questions');
- $this->registerFieldFormatter('parameters', 'JSonFieldFormatter');
- $this->survey = $survey;
- }
-
- public function typedInstance()
- {
- $instance = self::instanceForType($this->survey, $this->type);
- $instance->copy($this);
- return $instance;
- }
-
- public static function instanceForType(Survey $survey, $type)
- {
- require_once dirname(__FILE__) . '/' . $type . '.inc.php';
- $class = 'SurveyQuestion' . $type;
- return new $class($survey);
- }
-}
-
-class SurveyQuestionGroup extends SurveyQuestion implements SurveyQuestionContainer
-{
- public $children = array();
-
- public function __construct(Survey $survey)
- {
- parent::__construct($survey);
- }
-
- public function addQuestion(SurveyQuestion $question, $pos = null)
- {
- $question->parentQuestion = $this;
- if (is_null($pos)) {
- $this->children[] = $question;
- } else {
- array_splice($this->children, $pos, 0, $question);
- }
- }
-
- public function newQuestion($type, $pos = null)
- {
- $question = SurveyQuestion::instanceForType($this->survey, $type);
- $this->addQuestion($question, $pos);
- return $question;
- }
-
- public function reassignQuestionIds()
- {
- $id = $this->qid + 1;
- foreach ($this->children as $question) {
- $question->qid = $id;
- if ($question instanceof SurveyQuestionContainer) {
- $id = $question->reassignQuestionIds();
- } else {
- $id++;
- }
- }
- return $id;
- }
-
- protected function postSave()
- {
- foreach ($this->children as $question) {
- $question->sid = $this->sid;
- $question->parent = $this->qid;
- $question->insert();
- }
- }
-
- public function export()
- {
- $export = parent::export();
- $export['children'] = array();
- foreach ($this->children as $child) {
- $export['children'][] = $child->export();
- }
- return $export;
- }
-}
-
// vim:set et sw=4 sts=4 ts=4 foldmethod=marker enc=utf-8:
?>
parent::__construct($survey);
$this->type = "text";
}
+
+ protected function buildAnswer(SurveyAnswer $answer, PlDict $data)
+ {
+ $value = $data->t($this->qid);
+ if (!empty($value)) {
+ $answer->answer = array('text' => $value);
+ } else {
+ $answer->answer = null;
+ }
+ return true;
+ }
}
// vim:set et sw=4 sts=4 ts=4 foldmethod=marker enc=utf-8:
--- /dev/null
+{**************************************************************************}
+{* *}
+{* Copyright (C) 2003-2010 Polytechnique.org *}
+{* http://opensource.polytechnique.org/ *}
+{* *}
+{* 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 2 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, write to the Free Software *}
+{* Foundation, Inc., *}
+{* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *}
+{* *}
+{**************************************************************************}
+
+<fieldset>
+ <legend>{$question->label}</legend>
+
+{foreach from=$question->children item=child}
+ {include file=$child->voteTemplate() question=$child}
+{/foreach}
+</fieldset>
+
+{* vim:set et sw=2 sts=2 ts=8 enc=utf-8: *}
--- /dev/null
+{**************************************************************************}
+{* *}
+{* Copyright (C) 2003-2010 Polytechnique.org *}
+{* http://opensource.polytechnique.org/ *}
+{* *}
+{* 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 2 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, write to the Free Software *}
+{* Foundation, Inc., *}
+{* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *}
+{* *}
+{**************************************************************************}
+
+<div>
+ <div>{$question->label}</div>
+ <input type="text" name="qid[{$question->qid}]" value="" />
+</div>
+
+{* vim:set et sw=2 sts=2 ts=8 enc=utf-8: *}
<p>{$survey->description|miniwiki}</p>
+<div>
+<form action="survey/vote/{$survey->shortname}" method="post">
+ {foreach from=$survey->questions item=question}
+ {include file=$question->voteTemplate() question=$question}
+ {/foreach}
+
+ <div class="center">
+ {xsrf_token_field}
+ <input type="submit" name="vote" value="Enregister mon vote" />
+ </div>
+</form>
+</div>
+
{* vim:set et sw=2 sts=2 ts=8 enc=utf-8: *}
description TEXT NOT NULL,
begin DATE NOT NULL,
end DATE NOT NULL,
- anonymous TINYINT(1) DEFAULT 0,
voters TEXT DEFAULT NULL COMMENT "Filter users who can vote",
viewers TEXT DEFAULT NULL COMMENT "Filter users who can see the results",
- flags SET('validated'),
+ flags SET('validated', 'anonymous'),
PRIMARY KEY id (id),
UNIQUE KEY shortname (shortname),
) ENGINE=InnoDB, CHARSET=utf8, COMMENT="Describe the questions of the surveys";
CREATE TABLE survey_votes (
+ vid INT(11) UNSIGNED NOT NULL auto_increment,
sid INT(11) UNSIGNED NOT NULL,
- vid INT(11) UNSIGNED NOT NULL,
- PRIMARY KEY id (sid, vid),
+ PRIMARY KEY vid (vid),
FOREIGN KEY (sid) REFERENCES surveys (id)
ON UPDATE CASCADE
ON DELETE CASCADE
CREATE TABLE survey_vote_answers (
sid INT(11) UNSIGNED NOT NULL,
- vid INT(11) UNSIGNED NOT NULL,
qid INT(11) UNSIGNED NOT NULL,
+ vid INT(11) UNSIGNED NOT NULL,
answer TEXT DEFAULT NULL,
PRIMARY KEY id (sid, vid, qid),
+ FOREIGN KEY (vid) REFERENCES survey_votes (vid)
+ ON UPDATE CASCADE
+ ON DELETE CASCADE,
FOREIGN KEY (sid, qid) REFERENCES survey_questions (sid, qid)
ON UPDATE CASCADE
ON DELETE CASCADE