Skip to content

Commit

Permalink
Dev Improved new survey import.
Browse files Browse the repository at this point in the history
  • Loading branch information
SamMousa committed Jul 6, 2015
1 parent fa810b6 commit 88e90cd
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 63 deletions.
2 changes: 1 addition & 1 deletion application/components/MigrationManager.php
Expand Up @@ -103,7 +103,7 @@ public function migrateUp($class, $captureOutput = false)
}

// If capturing output and migration succeeded, return the captured output.
return $captureOutput && $result ? ob_get_clean() : ob_end_clean() && $result;
return $captureOutput && $result ? ob_get_clean() : $result;
}

protected function instantiateMigration($class)
Expand Down
10 changes: 4 additions & 6 deletions application/core/LSYii_Validators.php
Expand Up @@ -23,7 +23,7 @@ class LSYii_Validators extends CValidator {
* Filter attribute for XSS
* @var boolean
*/
public $xssfilter=true;
public $xssfilter;
/**
* Filter attribute for url
* @var boolean
Expand All @@ -42,9 +42,7 @@ class LSYii_Validators extends CValidator {

public function __construct()
{
if(Yii::app()->getConfig('DBVersion')< 172) // Permission::model exist only after 172 DB version
return $this->xssfilter=($this->xssfilter && Yii::app()->getConfig('filterxsshtml'));
$this->xssfilter=($this->xssfilter && Yii::app()->getConfig('filterxsshtml') && !App()->user->checkAccess('superadmin'));
$this->xssfilter = !App()->user->checkAccess('superadmin') && \SettingGlobal::get('filterxsshtml', true);
}

protected function validateAttribute($object,$attribute)
Expand Down Expand Up @@ -125,16 +123,17 @@ public function xssFilter($value)
'news' => true,
)
);

// To allow script BUT purify : HTML.Trusted=true (plugin idea for admin or without XSS filtering ?)

/** Start to get complete filtered value with url decode {QCODE} (bug #09300). This allow only question number in url, seems OK with XSS protection **/
$sFiltered=preg_replace('#%7B([a-zA-Z0-9\.]*)%7D#','{$1}',$filter->purify($value));
Yii::import('application.helpers.expressions.em_core_helper');// Already imported in em_manager_helper.php ?
$oExpressionManager= new ExpressionManager;
/** We get 2 array : one filtered, other unfiltered **/
$aValues=$oExpressionManager->asSplitStringOnExpressions($value);// Return array of array : 0=>the string,1=>string length,2=>string type (STRING or EXPRESSION)
$aFilteredValues=$oExpressionManager->asSplitStringOnExpressions($sFiltered);// Same but for the filtered string
$bCountIsOk=count($aValues)==count($aFilteredValues);

/** Construction of new string with unfiltered EM and filtered HTML **/
$sNewValue="";
foreach($aValues as $key=>$aValue){
Expand All @@ -157,7 +156,6 @@ public function xssFilter($value)
$sNewValue.="}";
}
}
gc_collect_cycles(); // To counter a high memory usage of HTML-Purifier
return $sNewValue;
}
/**
Expand Down
72 changes: 49 additions & 23 deletions application/helpers/import/importers/Import178.php
Expand Up @@ -23,7 +23,6 @@ class Import178 extends BaseElementXmlImport{
public function run()
{
$transaction = App()->db->beginTransaction();

try {
/** @var \Survey $survey */
$result = $this->importSurvey($this->parsedDocument);
Expand All @@ -43,6 +42,7 @@ public function run()
}

protected function importTranslation(TranslatableBehavior $translatable, array $data) {
\Yii::beginProfile('importTranslation');
$translatedFields = [];
foreach ($translatable->attributes as $attribute) {
if (isset($data[$attribute])) {
Expand All @@ -60,6 +60,7 @@ protected function importTranslation(TranslatableBehavior $translatable, array $
}

}
\Yii::endProfile('importTranslation');
return true;
}
protected function prepareGroup(array $data, \Survey $survey) {
Expand All @@ -72,6 +73,8 @@ protected function prepareGroup(array $data, \Survey $survey) {
}
protected function importGroup(array $data, \Survey $survey, array &$questionMap) {
$group = new \QuestionGroup();
// Set related model.
$group->survey = $survey;
$data = $this->prepareGroup($data, $survey);
$translations = \TbArray::popValue('translations', $data, []);
$questions = \TbArray::popValue('questions', $data, []);
Expand Down Expand Up @@ -135,6 +138,7 @@ protected function importSurvey($data) {

protected function importSurveyTranslation($data, \Survey $survey) {
$languageSetting = new \SurveyLanguageSetting();
$languageSetting->survey = $survey;
$languageSetting->setAttributes($data, false);
$languageSetting->surveyls_survey_id = $survey->primaryKey;
if (false === $result = $languageSetting->save()) {
Expand All @@ -144,9 +148,7 @@ protected function importSurveyTranslation($data, \Survey $survey) {
}

protected function prepareQuestion($data, \QuestionGroup $group, \Question $parent = null) {
// Translate gid.
// $data['id'] = $data['gid'];
// unset($data['gid']);
\Yii::beginProfile('prepareQuestion');
unset($data['language']);
$data['gid'] = $group->primaryKey;
$data['sid'] = $group->sid;
Expand All @@ -158,21 +160,39 @@ protected function prepareQuestion($data, \QuestionGroup $group, \Question $pare
if (isset($parent)) {
$data['parent_qid'] = $parent->primaryKey;
}
\Yii::endProfile('prepareQuestion');
return $data;
}

protected function importCondition($data, \Question $question, array &$questionMap)
protected function importCondition($data, \Question $question, array $questionMap)
{
throw new \Exception('Condition import not finished');
return true;
$data['qid'] = $question->primaryKey;
$data['cqid'] = $questionMap[$data['cqid']];
return $data;
\Yii::beginProfile('importCondition');
$oldTargetQid = intval($data['cqid']);
if (!isset($questionMap[$oldTargetQid])) {
throw new \Exception("Could not convert condition.");
}
$newTargetQid = $questionMap[$oldTargetQid];
$pattern = '/\d+X\d+X' . $oldTargetQid . '(?<field>.*)/';
if (preg_match($pattern, $data['cfieldname'], $matches)) {
$condition = new \Condition();
$condition->setAttributes($data, false);
$condition->qid = $question->primaryKey;
$condition->cqid = $newTargetQid;
$condition->primaryKey = null;
$condition->cfieldname = "{$question->sid}X{$question->gid}X{$newTargetQid}{$matches['field']}";

$result = $condition->save();
} else {
throw new \Exception("Pattern '$pattern' does not match {$data['cfieldname']}");
}
\Yii::endProfile('importCondition');
return $result;
}
protected function importQuestion(array $data, \QuestionGroup $group, array &$questionMap, \Question $parent = null) {
/**
* If we only have 1 language, use it even if it is not the "base" language.
*/
\Yii::beginProfile('importQuestion');
$translations = \TbArray::popValue('translations', $data, []);
$subQuestions = \TbArray::popValue('subquestions', $data, []);
$conditions = \TbArray::popValue('conditions', $data, []);
Expand All @@ -181,8 +201,11 @@ protected function importQuestion(array $data, \QuestionGroup $group, array &$qu
// We want the "correct class".
$class = \Question::resolveClass($data['type']);
/** @var \Question $question */
$question = new $class();
$question = new $class('import');
$question->type = $data['type'];
// Set related models.
$question->group = $group;
$question->survey = $group->survey;
foreach($data as $key => $value) {
if (!($question->canSetProperty($key) || $question->hasAttribute($key))) {
throw new \Exception("Could not set property $key for " . get_class($question));
Expand All @@ -193,8 +216,10 @@ protected function importQuestion(array $data, \QuestionGroup $group, array &$qu
$oldKey = $question->primaryKey;
$question->primaryKey = null;
$question->parent_qid = !isset($parent) ? 0 : $parent->primaryKey;

if ($result = $question->save()) {
\Yii::beginProfile('saveQuestion');
$result = $question->save();
\Yii::endProfile('saveQuestion');
if ($result) {
$question->group = $group;
$questionMap[$oldKey] = $question->primaryKey;
foreach($translations as $translation) {
Expand All @@ -217,6 +242,7 @@ protected function importQuestion(array $data, \QuestionGroup $group, array &$qu
var_dump($question->errors);
die('failed importing question');
}
\Yii::endProfile('importQuestion');
return $result;
}

Expand All @@ -228,7 +254,10 @@ protected function prepareAnswer($data, \Question $question) {
}
protected function importAnswer($data, \Question $question)
{
$answer = new \Answer();
\Yii::beginProfile('importAnswer');
$answer = new \Answer('import');
// Set related model.
$answer->question = $question;
$translations = \TbArray::popValue('translations', $data, []);

$data = $this->prepareAnswer($data, $question);
Expand All @@ -237,22 +266,19 @@ protected function importAnswer($data, \Question $question)
if (!($answer->canSetProperty($key) || $answer->hasAttribute($key))) {
throw new \Exception("Could not set property $key");
}
$answer->$key = $value;
}
$answer->setAttributes($data, false);
// $answer->setAttributes($data, false);
$answer->primaryKey = null;
\Yii::beginProfile('answerQuery');
if ($result = $answer->save()) {
$answer->question = $question;

foreach ($translations as $translation) {
$result = $result && $this->importTranslation($answer->translatable, $translation, $answer->primaryKey);
}
} else {
var_dump($answer->errors);
die();
}
if (!$result) {
echo "Failed to import answer";
}

\Yii::endProfile('answerQuery');
\Yii::endProfile('importAnswer');
return $result;
}
}
@@ -0,0 +1,29 @@
<?php
namespace ls\migrations;
use \CDbMigration;
class m150706_092139_unique_question_codes_per_scale extends CDbMigration
{
/**
* Describe the migration in this PHPDOC.
* Implement it below.
* @return boolean True if migration was a success.
*/
public function safeUp()
{
$this->dropIndex('unique_question_codes', '{{questions}}');
$this->createIndex('unique_question_codes', '{{questions}}', ['sid', 'title', 'parent_qid', 'scale_id'], true);
return true;
}

/**
* @return boolean True if migration was a success.
*/

public function safeDown()
{
echo "m150706_092139_unique_question_codes_per_scale does not support migration down.\\n";
return false;

}

}
25 changes: 3 additions & 22 deletions application/models/Answer.php
Expand Up @@ -15,6 +15,7 @@
/**
* Class Answer
* @property string $answer
* @property Question $question
* @property string $code
*/
class Answer extends LSActiveRecord
Expand Down Expand Up @@ -64,12 +65,12 @@ public function relations()
public function rules()
{
return array(
['question_id','exist', 'className' => Question::class, 'attributeName' => 'qid'],
['question_id', CExistValidator::class, 'className' => Question::class, 'attributeName' => 'qid', 'on' => ['update', 'insert']],
['code','length', 'min' => 1, 'max'=>5, 'allowEmpty' => false],
['code', 'required'],
['code', 'match', 'pattern' => '/^[a-z0-9]*$/i', 'message' => gT('Answer codes may only contain alphanumeric characters.')],
['sortorder','numerical', 'integerOnly'=>true,'allowEmpty'=>true],
array('answer','LSYii_Validators'),
['answer', LSYii_Validators::class],

array('assessment_value','numerical', 'integerOnly'=>true,'allowEmpty'=>true),
array('scale_id','numerical', 'integerOnly'=>true,'allowEmpty'=>true),
Expand Down Expand Up @@ -122,26 +123,6 @@ public function oldNewInsertansTags($newsid,$oldsid)
return $this->with('questions')->findAll($criteria);
}

public function updateRecord($data, $condition=FALSE)
{
return Yii::app()->db->createCommand()->update(self::tableName(), $data, $condition ? $condition : '');
}

function insertRecords($data)
{
$ans = new self;
foreach ($data as $k => $v)
$ans->$k = $v;
try
{
return $ans->save();
}
catch(Exception $e)
{
return false;
}
}

/**
* Updates sort order of answers inside a question
*
Expand Down
4 changes: 1 addition & 3 deletions application/models/LSActiveRecord.php
Expand Up @@ -154,9 +154,7 @@ public function deleteAllByAttributes($attributes,$condition='',$params=array())
$builder=$this->getCommandBuilder();
$table=$this->getTableSchema();
$criteria=$builder->createColumnCriteria($table,$attributes,$condition,$params);
var_dump($this->behaviors());
die();
$this->dispatchPluginModelEvent('before'.get_class($this).'DeleteMany', $criteria);
$this->dispatchPluginModelEvent('before'.get_class($this).'DeleteMany', $criteria);
$this->dispatchPluginModelEvent('beforeModelDeleteMany', $criteria);
return parent::deleteAllByAttributes(array(), $criteria, array());
}
Expand Down
23 changes: 18 additions & 5 deletions application/models/Question.php
Expand Up @@ -36,12 +36,20 @@ class Question extends LSActiveRecord
protected function afterFind()
{
parent::afterFind();

}

protected function getCustomAttribute($name) {
if (array_key_exists($name, $this->customAttributes)) {
return $this->customAttributes[$name];
}

// Fill the question attributes.
foreach($this->questionAttributes as $questionAttribute) {
if (!isset($questionAttribute->language)) {
$this->customAttributes[$questionAttribute->attribute] = $questionAttribute;
if (!isset($questionAttribute->language) && $questionAttribute->attribute === $name) {
$this->customAttributes[$name] = $questionAttribute->value;
return $questionAttribute->value;
}

}
}

Expand All @@ -67,6 +75,7 @@ protected function updateAttributes() {
$rows = [];
foreach ($this->customAttributes as $key => $value) {
$rows[] = [
'qid' => $this->primaryKey,
'attribute' => $key,
'value' => $value,
'language' => null
Expand Down Expand Up @@ -154,7 +163,7 @@ public function __get($name)
if (substr($name, 0, 5) == 'bool_') {
$result = parent::__get(substr($name, 5)) === 'Y';
} elseif ($name != 'type' && in_array($name, $this->customAttributeNames())) {
$result = isset($this->customAttributes[$name]) ? $this->customAttributes[$name] : null;
$result = $this->getCustomAttribute($name);
} else {
$result = parent::__get($name);
}
Expand Down Expand Up @@ -220,7 +229,7 @@ public function rules()
['preg', 'safe'],
['before', 'numerical', 'on' => 'insert', 'integerOnly' => true],
['type', 'in', 'range' => array_keys($this->typeList())],
['gid', 'exist', 'className' => QuestionGroup::class, 'attributeName' => 'id', 'allowEmpty' => false],
['gid', 'exist', 'className' => QuestionGroup::class, 'attributeName' => 'id', 'allowEmpty' => false, 'on' => ['insert', 'update']],
['title', 'required', 'on' => ['update', 'insert']],
['title','length', 'min' => 1, 'max'=>20,'on' => ['update', 'insert']],
['title,question,help', 'LSYii_Validators'],
Expand Down Expand Up @@ -886,6 +895,7 @@ public static function resolveClass($type) {
$class = \ls\models\questions\RankingQuestion::class;
break;
case 'F': // Array
case ':': // Array numbers
$class = \ls\models\questions\ArrayQuestion::class;
break;
case '5': // 5 point choice
Expand All @@ -897,6 +907,9 @@ public static function resolveClass($type) {
case 'M': // Multiple choice
$class = \ls\models\questions\MultipleChoiceQuestion::class;
break;
case '*':
$class = \ls\models\questions\EquationQuestion::class;
break;
default:
die("noo class for type {$type}");

Expand Down

0 comments on commit 88e90cd

Please sign in to comment.