From a8059056ddacdc06e88e3ed11229eca39a024245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Fl=C3=BCr?= Date: Fri, 22 Jul 2016 11:49:31 +0200 Subject: [PATCH 1/9] dev replaced parsing with moment.js --- .../helpers/expressions/em_core_helper.php | 12 +- scripts/expressions/em_javascript.js | 500 ++++++++++-------- 2 files changed, 284 insertions(+), 228 deletions(-) diff --git a/application/helpers/expressions/em_core_helper.php b/application/helpers/expressions/em_core_helper.php index 470ceed73ce..fdd1dc6ea4a 100644 --- a/application/helpers/expressions/em_core_helper.php +++ b/application/helpers/expressions/em_core_helper.php @@ -1269,7 +1269,7 @@ public function GetJavaScriptEquivalentOfExpression() elseif ($i+1<$numTokens && $tokens[$i+1][2] == 'ASSIGN') { $jsName = $this->GetVarAttribute($token[0],'jsName',''); - $stringParts[] = "document.getElementById('" . $jsName . "').value"; + $stringParts[] = "$('" . $jsName . "').val()"; if ($tokens[$i+1][0] == '+=') { // Javascript does concatenation unless both left and right side are numbers, so refactor the equation @@ -1361,8 +1361,8 @@ public function GetJavascriptTestforExpression($expected,$num) $jsParts = array(); $jsParts[] = "val = " . $jsmultiline_expr . ";\n"; $jsParts[] = "klass = (LEMeq(addslashes(val),'" . $jsmultiline_expected . "')) ? 'ok' : 'error';\n"; - $jsParts[] = "document.getElementById('test_" . $num . "').innerHTML=(val);\n"; - $jsParts[] = "document.getElementById('test_" . $num . "').className=klass;\n"; + $jsParts[] = "$('test_" . $num . "').html(val);\n"; + $jsParts[] = "$('test_" . $num . "').addClass(klass);\n"; return implode('',$jsParts); } @@ -1377,10 +1377,10 @@ public function GetJavaScriptFunctionForReplacement($questionNum, $name,$eqn) $jsParts = array(); // $jsParts[] = "\n // Tailor Question " . $questionNum . " - " . $name . ": { " . $eqn . " }\n"; $jsParts[] = " try{\n"; - $jsParts[] = " document.getElementById('" . $name . "').innerHTML=LEMfixnum(\n "; + $jsParts[] = " $('" . $name . "').html(LEMfixnum(\n "; $jsParts[] = $this->GetJavaScriptEquivalentOfExpression(); - $jsParts[] = ");\n"; - $jsParts[] = " } catch (e) { }\n"; + $jsParts[] = "));\n"; + $jsParts[] = " } catch (e) { console.log(e); }\n"; return implode('',$jsParts); } diff --git a/scripts/expressions/em_javascript.js b/scripts/expressions/em_javascript.js index e10d97d3829..faee5e606d6 100644 --- a/scripts/expressions/em_javascript.js +++ b/scripts/expressions/em_javascript.js @@ -453,6 +453,7 @@ function LEMval(alias) var str = new String(alias); var varName = alias; var suffix = 'code'; // the default + var value = ""; if(typeof bNumRealValue == 'undefined'){bNumRealValue=false;} // Allow to update {QCODE} even with text /* If passed a number, return that number */ @@ -635,6 +636,7 @@ function LEMval(alias) if (value === '') { return ''; } + if (suffix == 'value' || suffix == 'valueNAOK') { // if in assessment mode, this returns the assessment value // in non-assessment mode, this is identical to .code @@ -675,6 +677,7 @@ function LEMval(alias) break; } } + if (typeof attr.onlynum !== 'undefined' && attr.onlynum==1) { if(value=="") { @@ -710,6 +713,7 @@ function LEMval(alias) // } return +newval; } + // convert content in date questions to standard format yy-mm-dd to facilitate use in EM (comparisons, min/max etc.) else if (attr.type=='D') { // get date format pattern of referenced question @@ -725,7 +729,7 @@ function LEMval(alias) value=""; } else { - value=date('Y-m-d H:i', Date.parseString(trim(value), sdatetimePattern)); + value= moment(value,sdatetimePattern).format('YYYY-MM-DD HH:mm'); //date('Y-m-d H:i', Date.parseString(trim(value), sdatetimePattern)); } return value; } @@ -2165,252 +2169,304 @@ function strstr (haystack, needle, bool) { } function strtotime (text, now) { - // Convert string representation of date and time to a timestamp - // - // version: 1109.2016 - // discuss at: http://phpjs.org/functions/strtotime - // + original by: Caio Ariede (http://caioariede.com) - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + input by: David - // + improved by: Caio Ariede (http://caioariede.com) - // + bugfixed by: Wagner B. Soares - // + bugfixed by: Artur Tchernychev - // + improved by: A. Matías Quezada (http://amatiasq.com) - // + improved by: preuter - // + improved by: Brett Zamir (http://brett-zamir.me) - // + improved by: Mirko Faber - // % note 1: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones) - // * example 1: strtotime('+1 day', 1129633200); - // * returns 1: 1129719600 - // * example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200); - // * returns 2: 1130425202 - // * example 3: strtotime('last month', 1129633200); - // * returns 3: 1127041200 - // * example 4: strtotime('2009-05-04 08:30:00 GMT'); - // * returns 4: 1241425800 - var parsed, match, today, year, date, days, ranges, len, times, regex, i, fail = false; - - if (!text) { - return fail; - } - - // Unecessary spaces - text = text.replace(/^\s+|\s+$/g, '') - .replace(/\s{2,}/g, ' ') - .replace(/[\t\r\n]/g, '') - .toLowerCase(); - - // in contrast to php, js Date.parse function interprets: - // dates given as yyyy-mm-dd as in timezone: UTC, - // dates with "." or "-" as MDY instead of DMY - // dates with two-digit years differently - // etc...etc... - // ...therefore we manually parse lots of common date formats - match = text.match(/^(\d{1,4})([\-\.\/\:])(\d{1,2})([\-\.\/\:])(\d{1,4})(?:\s(\d{1,2}):(\d{2})?:?(\d{2})?)?(?:\s([A-Z]+)?)?$/); - - if (match && match[2] === match[4]) { - if (match[1] > 1901) { - switch (match[2]) { - case '-': { // YYYY-M-D - if (match[3] > 12 || match[5] > 31) { - return fail; - } - - return new Date(match[1], parseInt(match[3], 10) - 1, match[5], - match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } - case '.': { // YYYY.M.D is not parsed by strtotime() - return fail; - } - case '/': { // YYYY/M/D - if (match[3] > 12 || match[5] > 31) { - return fail; - } + // discuss at: http://locutus.io/php/strtotime/ + // original by: Caio Ariede (http://caioariede.com) + // improved by: Kevin van Zonneveld (http://kvz.io) + // improved by: Caio Ariede (http://caioariede.com) + // improved by: A. Matías Quezada (http://amatiasq.com) + // improved by: preuter + // improved by: Brett Zamir (http://brett-zamir.me) + // improved by: Mirko Faber + // input by: David + // bugfixed by: Wagner B. Soares + // bugfixed by: Artur Tchernychev + // bugfixed by: Stephan Bösch-Plepelits (http://github.com/plepe) + // note 1: Examples all have a fixed timestamp to prevent + // note 1: tests to fail because of variable time(zones) + // example 1: strtotime('+1 day', 1129633200) + // returns 1: 1129719600 + // example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200) + // returns 2: 1130425202 + // example 3: strtotime('last month', 1129633200) + // returns 3: 1127041200 + // example 4: strtotime('2009-05-04 08:30:00 GMT') + // returns 4: 1241425800 + // example 5: strtotime('2009-05-04 08:30:00+00') + // returns 5: 1241425800 + // example 6: strtotime('2009-05-04 08:30:00+02:00') + // returns 6: 1241418600 + // example 7: strtotime('2009-05-04T08:30:00Z') + // returns 7: 1241425800 + + var parsed + var match + var today + var year + var date + var days + var ranges + var len + var times + var regex + var i + var fail = false + + if (!text) { + return fail + } - return new Date(match[1], parseInt(match[3], 10) - 1, match[5], - match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } + // Unecessary spaces + text = text.replace(/^\s+|\s+$/g, '') + .replace(/\s{2,}/g, ' ') + .replace(/[\t\r\n]/g, '') + .toLowerCase() + + // in contrast to php, js Date.parse function interprets: + // dates given as yyyy-mm-dd as in timezone: UTC, + // dates with "." or "-" as MDY instead of DMY + // dates with two-digit years differently + // etc...etc... + // ...therefore we manually parse lots of common date formats + var pattern = new RegExp([ + '^(\\d{1,4})', + '([\\-\\.\\/:])', + '(\\d{1,2})', + '([\\-\\.\\/:])', + '(\\d{1,4})', + '(?:\\s(\\d{1,2}):(\\d{2})?:?(\\d{2})?)?', + '(?:\\s([A-Z]+)?)?$' + ].join('')) + match = text.match(pattern) + + if (match && match[2] === match[4]) { + if (match[1] > 1901) { + switch (match[2]) { + case '-': + // YYYY-M-D + if (match[3] > 12 || match[5] > 31) { + return fail + } + + return new Date(match[1], parseInt(match[3], 10) - 1, match[5], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000 + case '.': + // YYYY.M.D is not parsed by strtotime() + return fail + case '/': + // YYYY/M/D + if (match[3] > 12 || match[5] > 31) { + return fail + } + + return new Date(match[1], parseInt(match[3], 10) - 1, match[5], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000 + } + } else if (match[5] > 1901) { + switch (match[2]) { + case '-': + // D-M-YYYY + if (match[3] > 12 || match[1] > 31) { + return fail + } + + return new Date(match[5], parseInt(match[3], 10) - 1, match[1], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000 + case '.': + // D.M.YYYY + if (match[3] > 12 || match[1] > 31) { + return fail + } + + return new Date(match[5], parseInt(match[3], 10) - 1, match[1], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000 + case '/': + // M/D/YYYY + if (match[1] > 12 || match[3] > 31) { + return fail + } + + return new Date(match[5], parseInt(match[1], 10) - 1, match[3], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000 + } + } else { + switch (match[2]) { + case '-': + // YY-M-D + if (match[3] > 12 || match[5] > 31 || (match[1] < 70 && match[1] > 38)) { + return fail + } + + year = match[1] >= 0 && match[1] <= 38 ? +match[1] + 2000 : match[1] + return new Date(year, parseInt(match[3], 10) - 1, match[5], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000 + case '.': + // D.M.YY or H.MM.SS + if (match[5] >= 70) { + // D.M.YY + if (match[3] > 12 || match[1] > 31) { + return fail } - } else if (match[5] > 1901) { - switch (match[2]) { - case '-': { // D-M-YYYY - if (match[3] > 12 || match[1] > 31) { - return fail; - } - - return new Date(match[5], parseInt(match[3], 10) - 1, match[1], - match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } - case '.': { // D.M.YYYY - if (match[3] > 12 || match[1] > 31) { - return fail; - } - return new Date(match[5], parseInt(match[3], 10) - 1, match[1], - match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } - case '/': { // M/D/YYYY - if (match[1] > 12 || match[3] > 31) { - return fail; - } - - return new Date(match[5], parseInt(match[1], 10) - 1, match[3], - match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } + return new Date(match[5], parseInt(match[3], 10) - 1, match[1], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000 + } + if (match[5] < 60 && !match[6]) { + // H.MM.SS + if (match[1] > 23 || match[3] > 59) { + return fail } - } - else { - switch (match[2]) { - case '-': { // YY-M-D - if (match[3] > 12 || match[5] > 31 || (match[1] < 70 && match[1] > 38)) { - return fail; - } - - year = match[1] >= 0 && match[1] <= 38 ? +match[1] + 2000 : match[1]; - return new Date(year, parseInt(match[3], 10) - 1, match[5], - match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } - case '.': { // D.M.YY or H.MM.SS - if (match[5] >= 70) { // D.M.YY - if (match[3]>12 || match[1]>31) { - return fail; - } - return new Date(match[5], parseInt(match[3], 10) - 1, match[1], - match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } - if (match[5] < 60 && !match[6]) { // H.MM.SS - if (match[1] > 23 || match[3] > 59) { - return fail; - } - - today = new Date(); - return new Date(today.getFullYear(), today.getMonth(), today.getDate(), - match[1] || 0, match[3] || 0, match[5] || 0, match[9] || 0) / 1000; - } - - return fail; // invalid format, cannot be parsed - } - case '/': { // M/D/YY - if (match[1] > 12 || match[3] > 31 || (match[5] < 70 && match[5] > 38)) { - return fail; - } - - year = match[5] >= 0 && match[5] <= 38 ? +match[5] + 2000 : match[5]; - return new Date(year, parseInt(match[1], 10) - 1, match[3], - match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; - } - case ':': { // HH:MM:SS - if (match[1] > 23 || match[3] > 59 || match[5] > 59) { - return fail; - } - - today = new Date(); - return new Date(today.getFullYear(), today.getMonth(), today.getDate(), - match[1] || 0, match[3] || 0, match[5] || 0) / 1000; - } - } - } + today = new Date() + return new Date(today.getFullYear(), today.getMonth(), today.getDate(), + match[1] || 0, match[3] || 0, match[5] || 0, match[9] || 0) / 1000 + } + + // invalid format, cannot be parsed + return fail + case '/': + // M/D/YY + if (match[1] > 12 || match[3] > 31 || (match[5] < 70 && match[5] > 38)) { + return fail + } + + year = match[5] >= 0 && match[5] <= 38 ? +match[5] + 2000 : match[5] + return new Date(year, parseInt(match[1], 10) - 1, match[3], + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000 + case ':': + // HH:MM:SS + if (match[1] > 23 || match[3] > 59 || match[5] > 59) { + return fail + } + + today = new Date() + return new Date(today.getFullYear(), today.getMonth(), today.getDate(), + match[1] || 0, match[3] || 0, match[5] || 0) / 1000 + } } + } - - // other formats and "now" should be parsed by Date.parse() - if (text === 'now') { - return now === null || isNaN(now) ? new Date().getTime() / 1000 | 0 : now | 0; - } - if (!isNaN(parsed = Date.parse(text))) { - return parsed / 1000 | 0; + // other formats and "now" should be parsed by Date.parse() + if (text === 'now') { + return now === null || isNaN(now) + ? new Date().getTime() / 1000 | 0 + : now | 0 + } + if (!isNaN(parsed = Date.parse(text))) { + return parsed / 1000 | 0 + } + // Browsers !== Chrome have problems parsing ISO 8601 date strings, as they do + // not accept lower case characters, space, or shortened time zones. + // Therefore, fix these problems and try again. + // Examples: + // 2015-04-15 20:33:59+02 + // 2015-04-15 20:33:59z + // 2015-04-15t20:33:59+02:00 + pattern = new RegExp([ + '^([0-9]{4}-[0-9]{2}-[0-9]{2})', + '[ t]', + '([0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?)', + '([\\+-][0-9]{2}(:[0-9]{2})?|z)' + ].join('')) + match = text.match(pattern) + if (match) { + // @todo: time zone information + if (match[4] === 'z') { + match[4] = 'Z' + } else if (match[4].match(/^([\+-][0-9]{2})$/)) { + match[4] = match[4] + ':00' + } + + if (!isNaN(parsed = Date.parse(match[1] + 'T' + match[2] + match[4]))) { + return parsed / 1000 | 0 } + } - date = now ? new Date(now * 1000) : new Date(); - days = { - 'sun': 0, - 'mon': 1, - 'tue': 2, - 'wed': 3, - 'thu': 4, - 'fri': 5, - 'sat': 6 - }; - ranges = { - 'yea': 'FullYear', - 'mon': 'Month', - 'day': 'Date', - 'hou': 'Hours', - 'min': 'Minutes', - 'sec': 'Seconds' - }; + date = now ? new Date(now * 1000) : new Date() + days = { + 'sun': 0, + 'mon': 1, + 'tue': 2, + 'wed': 3, + 'thu': 4, + 'fri': 5, + 'sat': 6 + } + ranges = { + 'yea': 'FullYear', + 'mon': 'Month', + 'day': 'Date', + 'hou': 'Hours', + 'min': 'Minutes', + 'sec': 'Seconds' + } - function lastNext(type, range, modifier) { - var diff, day = days[range]; + function lastNext (type, range, modifier) { + var diff + var day = days[range] - if (typeof day !== 'undefined') { - diff = day - date.getDay(); + if (typeof day !== 'undefined') { + diff = day - date.getDay() - if (diff === 0) { - diff = 7 * modifier; - } - else if (diff > 0 && type === 'last') { - diff -= 7; - } - else if (diff < 0 && type === 'next') { - diff += 7; - } + if (diff === 0) { + diff = 7 * modifier + } else if (diff > 0 && type === 'last') { + diff -= 7 + } else if (diff < 0 && type === 'next') { + diff += 7 + } - date.setDate(date.getDate() + diff); - } + date.setDate(date.getDate() + diff) } - function process(val) { - var splt = val.split(' '), // Todo: Reconcile this with regex using \s, taking into account browser issues with split and regexes - type = splt[0], - range = splt[1].substring(0, 3), - typeIsNumber = /\d+/.test(type), - ago = splt[2] === 'ago', - num = (type === 'last' ? -1 : 1) * (ago ? -1 : 1); + } - if (typeIsNumber) { - num *= parseInt(type, 10); - } + function process (val) { + // @todo: Reconcile this with regex using \s, taking into account + // browser issues with split and regexes + var splt = val.split(' ') + var type = splt[0] + var range = splt[1].substring(0, 3) + var typeIsNumber = /\d+/.test(type) + var ago = splt[2] === 'ago' + var num = (type === 'last' ? -1 : 1) * (ago ? -1 : 1) - if (ranges.hasOwnProperty(range) && !splt[1].match(/^mon(day|\.)?$/i)) { - return date['set' + ranges[range]](date['get' + ranges[range]]() + num); - } + if (typeIsNumber) { + num *= parseInt(type, 10) + } - if (range === 'wee') { - return date.setDate(date.getDate() + (num * 7)); - } + if (ranges.hasOwnProperty(range) && !splt[1].match(/^mon(day|\.)?$/i)) { + return date['set' + ranges[range]](date['get' + ranges[range]]() + num) + } - if (type === 'next' || type === 'last') { - lastNext(type, range, num); - } - else if (!typeIsNumber) { - return false; - } + if (range === 'wee') { + return date.setDate(date.getDate() + (num * 7)) + } - return true; + if (type === 'next' || type === 'last') { + lastNext(type, range, num) + } else if (!typeIsNumber) { + return false } - times = '(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec' + - '|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?' + - '|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'; - regex = '([+-]?\\d+\\s' + times + '|' + '(last|next)\\s' + times + ')(\\sago)?'; + return true + } - match = text.match(new RegExp(regex, 'gi')); - if (!match) { - return fail; - } + times = '(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec' + + '|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?' + + '|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)' + regex = '([+-]?\\d+\\s' + times + '|' + '(last|next)\\s' + times + ')(\\sago)?' - for (i = 0, len = match.length; i < len; i++) { - if (!process(match[i])) { - return fail; - } - } + match = text.match(new RegExp(regex, 'gi')) + if (!match) { + return fail + } - // ECMAScript 5 only - // if (!match.every(process)) - // return false; + for (i = 0, len = match.length; i < len; i++) { + if (!process(match[i])) { + return fail + } + } - return (date.getTime() / 1000); + return (date.getTime() / 1000) } function substr (str, start, len) { From fe57ea365544204c53fa8d321d7f695c6f9d88ec Mon Sep 17 00:00:00 2001 From: LouisGac Date: Fri, 22 Jul 2016 12:02:17 +0200 Subject: [PATCH 2/9] Dev: security fix, use libxml_disable_entity_loader for admin theme and survey templates configuration files --- application/helpers/replacements_helper.php | 6 +- application/models/AdminTheme.php | 30 ++++++++-- application/models/TemplateConfiguration.php | 61 +++++++++----------- 3 files changed, 56 insertions(+), 41 deletions(-) diff --git a/application/helpers/replacements_helper.php b/application/helpers/replacements_helper.php index fb147906e8c..0d8de30924f 100644 --- a/application/helpers/replacements_helper.php +++ b/application/helpers/replacements_helper.php @@ -197,7 +197,7 @@ function templatereplace($line, $replacements = array(), &$redata = array(), $de { $surveyformat = ""; } - if($oTemplate->config->engine->cssframework) + if( isset($oTemplate->config->engine->cssframework) && $oTemplate->config->engine->cssframework) { $surveyformat .= " ".$oTemplate->config->engine->cssframework."-engine "; } @@ -556,12 +556,12 @@ function templatereplace($line, $replacements = array(), &$redata = array(), $de if($thissurvey['googleanalyticsapikey'] === "9999useGlobal9999") { $_googleAnalyticsAPIKey = trim(getGlobalSetting('googleanalyticsapikey')); - } + } else if (isset($thissurvey['googleanalyticsapikey']) && trim($thissurvey['googleanalyticsapikey']) != '') { $_googleAnalyticsAPIKey = trim($thissurvey['googleanalyticsapikey']); } - else + else { $_googleAnalyticsAPIKey = ""; diff --git a/application/models/AdminTheme.php b/application/models/AdminTheme.php index 820a512854b..26285506299 100644 --- a/application/models/AdminTheme.php +++ b/application/models/AdminTheme.php @@ -94,14 +94,25 @@ public function setAdminTheme() // TODO: replace everywhere the call to Yii::app()->getConfig('adminstyleurl) by $oAdminTheme->sTemplateUrl; Yii::app()->setConfig('adminstyleurl', $this->sTemplateUrl ); - // We load the admin theme's configuration file. - $this->config = simplexml_load_file($this->path.'/config.xml'); + + ////////////////////// + // Config file loading + + $bOldEntityLoaderState = libxml_disable_entity_loader(true); // @see: http://phpsecurity.readthedocs.io/en/latest/Injection-Attacks.html#xml-external-entity-injection + $sXMLConfigFile = file_get_contents( realpath ($this->path.'/config.xml')); // Now that entity loader is disabled, we can't use simplexml_load_file; so we must read the file with file_get_contents and convert it as a string + + // Simple Xml is buggy on PHP < 5.4. The [ array -> json_encode -> json_decode ] workaround seems to be the most used one. + // @see: http://php.net/manual/de/book.simplexml.php#105330 (top comment on PHP doc for simplexml) + $this->config = json_decode( json_encode ( ( array ) simplexml_load_string($sXMLConfigFile), 1)); // If developers want to test asset manager with debug mode on - $this->use_asset_manager = ( $this->config->engine->use_asset_manager_in_debug_mode == 'true'); + $this->use_asset_manager = isset($this->config->engine->use_asset_manager_in_debug_mode)?( $this->config->engine->use_asset_manager_in_debug_mode == 'true'):'false'; $this->defineConstants(); // Define the (still) necessary constants $this->registerStylesAndScripts(); // Register all CSS and JS + + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server + return $this; } @@ -353,6 +364,8 @@ public static function getOtherAssets() 'application/extensions/SettingsWidget/assets', 'application/extensions/FlashMessage/assets', 'application/extensions/admin/survey/question/PositionWidget/assets', + 'application/extensions/admin/grid/MassiveActionsWidget/assets', + 'application/extensions/admin/survey/question/PositionWidget/assets', //'application/extensions/bootstrap/', we'll touch all the subdirectories of extensions // Third party assets @@ -369,6 +382,7 @@ public static function getOtherAssets() */ static private function getThemeList($sDir) { + $bOldEntityLoaderState = libxml_disable_entity_loader(true); // @see: http://phpsecurity.readthedocs.io/en/latest/Injection-Attacks.html#xml-external-entity-injection $aListOfFiles = array(); if ($sDir && $pHandle = opendir($sDir)) { @@ -376,12 +390,18 @@ static private function getThemeList($sDir) { if (is_dir($sDir.DIRECTORY_SEPARATOR.$file) && is_file($sDir.DIRECTORY_SEPARATOR.$file.DIRECTORY_SEPARATOR.'config.xml')) { - $oTemplateConfig = simplexml_load_file($sDir.DIRECTORY_SEPARATOR.$file.'/config.xml'); + $sXMLConfigFile = file_get_contents( realpath ($sDir.DIRECTORY_SEPARATOR.$file.'/config.xml')); // Now that entity loader is disabled, we can't use simplexml_load_file; so we must read the file with file_get_contents and convert it as a string + + // Simple Xml is buggy on PHP < 5.4. The [ array -> json_encode -> json_decode ] workaround seems to be the most used one. + // @see: http://php.net/manual/de/book.simplexml.php#105330 (top comment on PHP doc for simplexml) + $oTemplateConfig = json_decode( json_encode ( ( array ) simplexml_load_string($sXMLConfigFile), 1)); + $aListOfFiles[$file] = $oTemplateConfig; } } closedir($pHandle); } + libxml_disable_entity_loader($bOldEntityLoaderState); return $aListOfFiles; } @@ -401,7 +421,7 @@ private function defineConstants() } // Define presentation text on welcome page - if($this->config->metadatas->presentation) + if (isset($this->config->metadatas->presentation) && $this->config->metadatas->presentation) { define('PRESENTATION', $this->config->metadatas->presentation); } diff --git a/application/models/TemplateConfiguration.php b/application/models/TemplateConfiguration.php index b90370313f6..1081d84fdde 100644 --- a/application/models/TemplateConfiguration.php +++ b/application/models/TemplateConfiguration.php @@ -69,24 +69,15 @@ public function setTemplateConfiguration($sTemplateName='', $iSurveyId='') if ($sTemplateName=='') { - $this->oSurvey = Survey::model()->findByPk($iSurveyId); + $this->oSurvey = Survey::model()->findByPk($iSurveyId); $this->sTemplateName = $this->oSurvey->template; } // We check if it's a CORE template $this->isStandard = $this->setIsStandard(); - // If the template is standard, its root is based on standardtemplaterootdir - if($this->isStandard) - { - $this->path = Yii::app()->getConfig("standardtemplaterootdir").DIRECTORY_SEPARATOR.$this->sTemplateName; - } - // Else, it's a user template, its root is based on usertemplaterootdir - else - { - $this->path = Yii::app()->getConfig("usertemplaterootdir").DIRECTORY_SEPARATOR.$this->sTemplateName; - } - + // If the template is standard, its root is based on standardtemplaterootdir, else, it's a user template, its root is based on usertemplaterootdir + $this->path = ($this->isStandard)?Yii::app()->getConfig("standardtemplaterootdir").DIRECTORY_SEPARATOR.$this->sTemplateName:Yii::app()->getConfig("usertemplaterootdir").DIRECTORY_SEPARATOR.$this->sTemplateName; // If the template directory doesn't exist, it can be that: // - user deleted a custom theme @@ -99,7 +90,6 @@ public function setTemplateConfiguration($sTemplateName='', $iSurveyId='') setGlobalSetting('defaulttemplate', 'default'); } - // If the template don't have a config file (maybe it has been deleted, or whatever), // then, we load the default template $this->hasConfigFile = (string) is_file($this->path.DIRECTORY_SEPARATOR.'config.xml'); @@ -117,36 +107,41 @@ public function setTemplateConfiguration($sTemplateName='', $iSurveyId='') $this->path = Yii::app()->getConfig("standardtemplaterootdir").DIRECTORY_SEPARATOR.$this->sTemplateName; $this->xmlFile = $this->path.DIRECTORY_SEPARATOR.'config.xml'; } - } else { $this->xmlFile = $this->path.DIRECTORY_SEPARATOR.'config.xml'; } - // We load the config file - // $this->config = simplexml_load_file(realpath ($this->xmlFile)); - // Simple Xml is buggy on PHP < 5.4. The json_encode/decode workaround seems to be the most used one. + + ////////////////////// + // Config file loading + + $bOldEntityLoaderState = libxml_disable_entity_loader(true); // @see: http://phpsecurity.readthedocs.io/en/latest/Injection-Attacks.html#xml-external-entity-injection + $sXMLConfigFile = file_get_contents( realpath ($this->xmlFile)); // @see: Now that entity loader is disabled, we can't use simplexml_load_file; so we must read the file with file_get_contents and convert it as a string + + // Simple Xml is buggy on PHP < 5.4. The [ array -> json_encode -> json_decode ] workaround seems to be the most used one. // @see: http://php.net/manual/de/book.simplexml.php#105330 (top comment on PHP doc for simplexml) - $this->config = json_decode( json_encode ( ( array ) simplexml_load_file(realpath ($this->xmlFile)), 1)); - // Template configuration. - $this->viewPath = $this->path.DIRECTORY_SEPARATOR.$this->config->engine->pstpldirectory.DIRECTORY_SEPARATOR; - $this->siteLogo = (isset($this->config->files->logo))?$this->config->files->logo->filename:''; - - // condition for user's template prior to 160219 (before this build, this configuration field wasn't present in the config.xml) - $this->filesPath = (isset($this->config->engine->filesdirectory))? $this->path.DIRECTORY_SEPARATOR.$this->config->engine->filesdirectory.DIRECTORY_SEPARATOR : $this->path . '/files/'; - // condition for user's template prior to 160504 - $this->overwrite_question_views = (isset($this->config->engine->overwrite_question_views))? ( $this->config->engine->overwrite_question_views=='true' || $this->config->engine->overwrite_question_views=='yes' ) : false; - - $this->cssFramework = $this->config->engine->cssframework; - $oPackages = $this->config->engine->packages->package; - $this->packages = (array) $oPackages; - $this->otherFiles = $this->setOtherFiles(); - $this->depends = $this->packages; - //$this->depends[] = (string) $this->cssFramework; // Bootstrap CSS is no more needed for Bootstrap templates (their custom css like "flat_and_modern.css" is a custom version of bootstrap.css ) + $this->config = json_decode( json_encode ( ( array ) simplexml_load_string($sXMLConfigFile), 1)); + + // Template configuration + // Ternary operators test if configuration entry exists in the config file (to avoid PHP notice in user custom templates) + $this->viewPath = (isset($this->config->engine->pstpldirectory)) ? $this->path.DIRECTORY_SEPARATOR.$this->config->engine->pstpldirectory.DIRECTORY_SEPARATOR : $this->path; + $this->siteLogo = (isset($this->config->files->logo)) ? $this->config->files->logo->filename : ''; + $this->filesPath = (isset($this->config->engine->filesdirectory)) ? $this->path.DIRECTORY_SEPARATOR.$this->config->engine->filesdirectory.DIRECTORY_SEPARATOR : $this->path . '/files/'; + $this->cssFramework = (isset($this->config->engine->cssframework)) ? $this->config->engine->cssframework : ''; + $this->packages = (isset($this->config->engine->packages->package)) ? $this->config->engine->packages->package : array(); + + // overwrite_question_views accept different values : "true" or "yes" + $this->overwrite_question_views = (isset($this->config->engine->overwrite_question_views)) ? ($this->config->engine->overwrite_question_views=='true' || $this->config->engine->overwrite_question_views=='yes' ) : false; + + $this->otherFiles = $this->setOtherFiles(); + $this->depends = $this->packages; // TODO: remove + // Package creation $this->createTemplatePackage(); + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server return $this; } From 349e0088bc6c850261d9fb7d95f848e8b7baa361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Fl=C3=BCr?= Date: Fri, 22 Jul 2016 12:05:04 +0200 Subject: [PATCH 3/9] Fixed Issue #11417: Dates not calculating properly dev Please look out for usages of the deprecated jstoolbox/date.js library, though it may be removed in the future dev updated strtotime out of the php.js library --- application/helpers/expressions/em_core_helper.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application/helpers/expressions/em_core_helper.php b/application/helpers/expressions/em_core_helper.php index fdd1dc6ea4a..c314a53be6c 100644 --- a/application/helpers/expressions/em_core_helper.php +++ b/application/helpers/expressions/em_core_helper.php @@ -1269,7 +1269,7 @@ public function GetJavaScriptEquivalentOfExpression() elseif ($i+1<$numTokens && $tokens[$i+1][2] == 'ASSIGN') { $jsName = $this->GetVarAttribute($token[0],'jsName',''); - $stringParts[] = "$('" . $jsName . "').val()"; + $stringParts[] = "document.getElementById('" . $jsName . "').value"; if ($tokens[$i+1][0] == '+=') { // Javascript does concatenation unless both left and right side are numbers, so refactor the equation @@ -1361,8 +1361,8 @@ public function GetJavascriptTestforExpression($expected,$num) $jsParts = array(); $jsParts[] = "val = " . $jsmultiline_expr . ";\n"; $jsParts[] = "klass = (LEMeq(addslashes(val),'" . $jsmultiline_expected . "')) ? 'ok' : 'error';\n"; - $jsParts[] = "$('test_" . $num . "').html(val);\n"; - $jsParts[] = "$('test_" . $num . "').addClass(klass);\n"; + $jsParts[] = "document.getElementById('test_" . $num . "').innerHTML=(val);\n"; + $jsParts[] = "document.getElementById('test_" . $num . "').className=klass;\n"; return implode('',$jsParts); } @@ -1377,9 +1377,9 @@ public function GetJavaScriptFunctionForReplacement($questionNum, $name,$eqn) $jsParts = array(); // $jsParts[] = "\n // Tailor Question " . $questionNum . " - " . $name . ": { " . $eqn . " }\n"; $jsParts[] = " try{\n"; - $jsParts[] = " $('" . $name . "').html(LEMfixnum(\n "; + $jsParts[] = " document.getElementById('" . $name . "').innerHTML=LEMfixnum(\n "; $jsParts[] = $this->GetJavaScriptEquivalentOfExpression(); - $jsParts[] = "));\n"; + $jsParts[] = ");\n"; $jsParts[] = " } catch (e) { console.log(e); }\n"; return implode('',$jsParts); } From c1a4477984f39418a2cd41392bbf541df3adcece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Fl=C3=BCr?= Date: Fri, 22 Jul 2016 12:10:51 +0200 Subject: [PATCH 4/9] dev problem loading googleanalyticsapikey resolved --- application/helpers/replacements_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/helpers/replacements_helper.php b/application/helpers/replacements_helper.php index 0d8de30924f..cd02474e93d 100644 --- a/application/helpers/replacements_helper.php +++ b/application/helpers/replacements_helper.php @@ -553,7 +553,7 @@ function templatereplace($line, $replacements = array(), &$redata = array(), $de { $_assessment_current_total = ''; } - if($thissurvey['googleanalyticsapikey'] === "9999useGlobal9999") + if(isset($thissurvey['googleanalyticsapikey']) && $thissurvey['googleanalyticsapikey'] === "9999useGlobal9999") { $_googleAnalyticsAPIKey = trim(getGlobalSetting('googleanalyticsapikey')); } From 0c700518229134315ebeaebdd30efe5b96656244 Mon Sep 17 00:00:00 2001 From: LouisGac Date: Fri, 22 Jul 2016 12:19:43 +0200 Subject: [PATCH 5/9] Dev: security fix, use libxml_disable_entity_loader for import/export --- application/controllers/admin/export.php | 12 +- application/helpers/admin/import_helper.php | 25 +- application/helpers/common_helper.php | 12 - .../remotecontrol/remotecontrol_handle.php | 122 +++--- application/models/Question.php | 35 -- .../third_party/ar-php/Arabic/Date.php | 353 +++++++++--------- 6 files changed, 270 insertions(+), 289 deletions(-) diff --git a/application/controllers/admin/export.php b/application/controllers/admin/export.php index 9f7e92f711e..12c58c70ee6 100644 --- a/application/controllers/admin/export.php +++ b/application/controllers/admin/export.php @@ -1343,10 +1343,14 @@ private function _addHeaders($filename, $content_type, $expires, $pragma = "publ } private function _xmlToJson($fileContents) { - $fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents); - $fileContents = trim(str_replace('"', "'", $fileContents)); - $simpleXml = simplexml_load_string($fileContents,'SimpleXMLElement', LIBXML_NOCDATA); - $json = json_encode($simpleXml); + $bOldEntityLoaderState = libxml_disable_entity_loader(true); // @see: http://phpsecurity.readthedocs.io/en/latest/Injection-Attacks.html#xml-external-entity-injection + + $fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents); + $fileContents = trim(str_replace('"', "'", $fileContents)); + $simpleXml = simplexml_load_string($fileContents,'SimpleXMLElement', LIBXML_NOCDATA); + $json = json_encode($simpleXml); + + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server return $json; } diff --git a/application/helpers/admin/import_helper.php b/application/helpers/admin/import_helper.php index 12ace4053a8..640be2bf6a8 100644 --- a/application/helpers/admin/import_helper.php +++ b/application/helpers/admin/import_helper.php @@ -19,14 +19,19 @@ */ function XMLImportGroup($sFullFilePath, $iNewSID) { - $aLanguagesSupported = array(); // this array will keep all the languages supported for the survey + $aLanguagesSupported = array(); // this array will keep all the languages supported for the survey + $sBaseLanguage = Survey::model()->findByPk($iNewSID)->language; + $aLanguagesSupported[] = $sBaseLanguage; // adds the base language to the list of supported languages + $aLanguagesSupported = array_merge($aLanguagesSupported,Survey::model()->findByPk($iNewSID)->additionalLanguages); + + $bOldEntityLoaderState = libxml_disable_entity_loader(true); // @see: http://phpsecurity.readthedocs.io/en/latest/Injection-Attacks.html#xml-external-entity-injection + + $sXMLdata = file_get_contents($sFullFilePath); + $xml = simplexml_load_string($sXMLdata,'SimpleXMLElement',LIBXML_NONET); + - $sBaseLanguage = Survey::model()->findByPk($iNewSID)->language; - $aLanguagesSupported[]=$sBaseLanguage; // adds the base language to the list of supported languages - $aLanguagesSupported=array_merge($aLanguagesSupported,Survey::model()->findByPk($iNewSID)->additionalLanguages); - $sXMLdata = file_get_contents($sFullFilePath); - $xml = simplexml_load_string($sXMLdata,'SimpleXMLElement',LIBXML_NONET); if ($xml==false || $xml->LimeSurveyDocType!='Group') safeDie('This is not a valid LimeSurvey group structure XML file.'); + $iDBVersion = (int) $xml->DBVersion; $aQIDReplacements=array(); $results['defaultvalues']=0; @@ -330,6 +335,8 @@ function XMLImportGroup($sFullFilePath, $iNewSID) $results['newgid']=$newgid; $results['labelsets']=0; $results['labels']=0; + + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server return $results; } @@ -1607,7 +1614,7 @@ function XMLImportTokens($sFullFilePath,$iSurveyID,$sCreateMissingAttributeField } switchMSSQLIdentityInsert('tokens_'.$iSurveyID,true); - foreach ($xml->tokens->rows->row as $row) + foreach ($xml->tokens->rows->row as $row) { $insertdata=array(); @@ -1616,8 +1623,8 @@ function XMLImportTokens($sFullFilePath,$iSurveyID,$sCreateMissingAttributeField $insertdata[(string)$key]=(string)$value; } - $token = Token::create($iSurveyID); - $token->setAttributes($insertdata, false); + $token = Token::create($iSurveyID); + $token->setAttributes($insertdata, false); if (!$token->save()) { $results['warnings'][]=gT("Skipped tokens entry:").' '. implode('. ',$token->errors['token']); diff --git a/application/helpers/common_helper.php b/application/helpers/common_helper.php index 81ca3f0ea61..c779cea9bfe 100644 --- a/application/helpers/common_helper.php +++ b/application/helpers/common_helper.php @@ -169,18 +169,6 @@ function getQuestionTypeList($SelectedCode = "T", $ReturnType = "selector") return $qtypeselecter; } -function getQuestionModuleList($SelectedCode = "T", $ReturnType = "selector") -{ - $publicurl = Yii::app()->getConfig('publicurl'); - $qtypes = Question::questionModuleList(); - - if ($ReturnType == "array") - return $qtypes; - - else return null; - -} - /** * isStandardTemplate returns true if a template is a standard template * This function does not check if a template actually exists diff --git a/application/helpers/remotecontrol/remotecontrol_handle.php b/application/helpers/remotecontrol/remotecontrol_handle.php index 107a6872a35..03102e12f4a 100644 --- a/application/helpers/remotecontrol/remotecontrol_handle.php +++ b/application/helpers/remotecontrol/remotecontrol_handle.php @@ -934,7 +934,6 @@ public function delete_group($sSessionKey, $iSurveyID, $iGroupID) */ public function import_group($sSessionKey, $iSurveyID, $sImportData, $sImportDataType, $sNewGroupName=NULL, $sNewGroupDescription=NULL) { - if ($this->_checkSessionKey($sSessionKey)) { $oSurvey = Survey::model()->findByPk($iSurveyID); @@ -955,11 +954,13 @@ public function import_group($sSessionKey, $iSurveyID, $sImportData, $sImportDat if ( strtolower($sImportDataType)=='lsg') { + $bOldEntityLoaderState = libxml_disable_entity_loader(true); // @see: http://phpsecurity.readthedocs.io/en/latest/Injection-Attacks.html#xml-external-entity-injection $sXMLdata = file_get_contents($sFullFilePath); $xml = @simplexml_load_string($sXMLdata,'SimpleXMLElement',LIBXML_NONET); if(!$xml) { unlink($sFullFilePath); + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server return array('status' => 'Error: Invalid LimeSurvey group structure XML '); } $aImportResults = XMLImportGroup($sFullFilePath, $iSurveyID); @@ -969,7 +970,11 @@ public function import_group($sSessionKey, $iSurveyID, $sImportData, $sImportDat unlink($sFullFilePath); - if (isset($aImportResults['fatalerror'])) return array('status' => 'Error: '.$aImportResults['fatalerror']); + if (isset($aImportResults['fatalerror'])) + { + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server + return array('status' => 'Error: '.$aImportResults['fatalerror']); + } else { $iNewgid = $aImportResults['newgid']; @@ -988,6 +993,7 @@ public function import_group($sSessionKey, $iSurveyID, $sImportData, $sImportDat { // no need to throw exception } + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server return (int)$aImportResults['newgid']; } } @@ -1246,21 +1252,31 @@ public function import_question($sSessionKey, $iSurveyID,$iGroupID, $sImportData if ( strtolower($sImportDataType)=='lsq') { + $bOldEntityLoaderState = libxml_disable_entity_loader(true); // @see: http://phpsecurity.readthedocs.io/en/latest/Injection-Attacks.html#xml-external-entity-injection + $sXMLdata = file_get_contents($sFullFilePath); $xml = @simplexml_load_string($sXMLdata,'SimpleXMLElement',LIBXML_NONET); if(!$xml) { unlink($sFullFilePath); + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server return array('status' => 'Error: Invalid LimeSurvey question structure XML '); } $aImportResults = XMLImportQuestion($sFullFilePath, $iSurveyID, $iGroupID); } else + { + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server return array('status' => 'Really Invalid extension'); //just for symmetry! + } unlink($sFullFilePath); - if (isset($aImportResults['fatalerror'])) return array('status' => 'Error: '.$aImportResults['fatalerror']); + if (isset($aImportResults['fatalerror'])) + { + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server + return array('status' => 'Error: '.$aImportResults['fatalerror']); + } else { fixLanguageConsistency($iSurveyID); @@ -1278,6 +1294,8 @@ public function import_question($sSessionKey, $iSurveyID,$iGroupID, $sImportData else $oQuestion->setAttribute('mandatory','N'); + libxml_disable_entity_loader($bOldEntityLoaderState); // Put back entity loader to its original state, to avoid contagion to other applications on the server + try { $oQuestion->save(); @@ -1416,9 +1434,9 @@ public function get_question_properties($sSessionKey, $iQuestionID, $aQuestionSe } else if ($sPropertyName == 'defaultvalue') { - $aResult['defaultvalue'] = DefaultValue::model()->findByAttributes(array('qid' => $iQuestionID, 'language'=> $sLanguage))->defaultvalue; - } - else + $aResult['defaultvalue'] = DefaultValue::model()->findByAttributes(array('qid' => $iQuestionID, 'language'=> $sLanguage))->defaultvalue; + } + else { $aResult[$sPropertyName]=$oQuestion->$sPropertyName; } @@ -1626,7 +1644,7 @@ public function delete_participants($sSessionKey, $iSurveyID, $aTokenIDs) * @access public * @param string $sSessionKey Auth credentials * @param int $iSurveyID Id of the Survey to get token properties - * @param array|struct|int Array $aTokenQueryProperties of participant properties used to query the participant, or the token id as an integer + * @param array|struct|int Array $aTokenQueryProperties of participant properties used to query the participant, or the token id as an integer * @param array $aTokenProperties The properties to get * @return array The requested values */ @@ -1645,18 +1663,18 @@ public function get_participant_properties($sSessionKey, $iSurveyID, $aTokenQuer if(is_array($aTokenQueryProperties)){ $tokenCount = Token::model($iSurveyID)->countByAttributes($aTokenQueryProperties); - - if($tokenCount == 0){ - return array('status' => 'Error: No results were found based on your attributes.'); - }else if($tokenCount > 1){ - return array('status' => 'Error: More than 1 result was found based on your attributes.'); - } - $token = Token::model($iSurveyID)->findByAttributes($aTokenQueryProperties); - }else{ + + if($tokenCount == 0){ + return array('status' => 'Error: No results were found based on your attributes.'); + }else if($tokenCount > 1){ + return array('status' => 'Error: More than 1 result was found based on your attributes.'); + } + $token = Token::model($iSurveyID)->findByAttributes($aTokenQueryProperties); + }else{ // If aTokenQueryProperties is not an array, it's an integer $iTokenID = $aTokenQueryProperties; - $token = Token::model($iSurveyID)->findByPk($iTokenID); - } + $token = Token::model($iSurveyID)->findByPk($iTokenID); + } if (!isset($token)) return array('status' => 'Error: Invalid tokenid'); @@ -1704,18 +1722,18 @@ public function set_participant_properties($sSessionKey, $iSurveyID, $aTokenQuer return array('status' => 'Error: No token table'); if(is_array($aTokenQueryProperties)){ - $tokenCount = Token::model($iSurveyID)->countByAttributes($aTokenQueryProperties); - if($tokenCount == 0){ - return array('status' => 'Error: No results were found based on your attributes.'); - }else if($tokenCount > 1){ - return array('status' => 'Error: More than 1 result was found based on your attributes.'); - } + $tokenCount = Token::model($iSurveyID)->countByAttributes($aTokenQueryProperties); + if($tokenCount == 0){ + return array('status' => 'Error: No results were found based on your attributes.'); + }else if($tokenCount > 1){ + return array('status' => 'Error: More than 1 result was found based on your attributes.'); + } $oToken = Token::model($iSurveyID)->findByAttributes($aTokenQueryProperties); - }else{ + }else{ // If aTokenQueryProperties is not an array, it's an integer $iTokenID = $aTokenQueryProperties; - $oToken = Token::model($iSurveyID)->findByPk($iTokenID); - } + $oToken = Token::model($iSurveyID)->findByPk($iTokenID); + } if (!isset($oToken)) return array('status' => 'Error: Invalid tokenid'); @@ -2073,33 +2091,33 @@ public function mail_registered_participants($sSessionKey, $iSurveyID, $override if(!tableExists("{{tokens_$iSurveyID}}")) return array('status' => 'Error: No token table'); - $command = new CDbCriteria(); - $command->condition = ''; - if (count($overrideAllConditions)) { - foreach($overrideAllConditions as $condition) - { - $command->addCondition($condition); - } - } - else - { - $command->addCondition('usesleft > 0'); - $command->addCondition("sent = 'N'"); - $command->addCondition("remindersent = 'N'"); - $command->addCondition("(completed ='N') or (completed='')"); - $command->addCondition('ISNULL(validfrom) OR validfrom < NOW()'); - $command->addCondition('ISNULL(validuntil) OR validuntil > NOW()'); - $command->addCondition('emailstatus = "OK"'); - } - $command->order = 'tid'; + $command = new CDbCriteria(); + $command->condition = ''; + if (count($overrideAllConditions)) { + foreach($overrideAllConditions as $condition) + { + $command->addCondition($condition); + } + } + else + { + $command->addCondition('usesleft > 0'); + $command->addCondition("sent = 'N'"); + $command->addCondition("remindersent = 'N'"); + $command->addCondition("(completed ='N') or (completed='')"); + $command->addCondition('ISNULL(validfrom) OR validfrom < NOW()'); + $command->addCondition('ISNULL(validuntil) OR validuntil > NOW()'); + $command->addCondition('emailstatus = "OK"'); + } + $command->order = 'tid'; $aAllTokens = Token::model($iSurveyID)->findAll( $command ); $iAllTokensCount=count($aAllTokens); unset($aAllTokens); $iMaxEmails = (int)Yii::app()->getConfig("maxemails"); - $command->limit = $iMaxEmails; - $aResultTokens = Token::model($iSurveyID)->findAll( $command ); + $command->limit = $iMaxEmails; + $aResultTokens = Token::model($iSurveyID)->findAll( $command ); if (empty($aResultTokens)) return array('status' => 'Error: No candidate tokens'); @@ -2111,11 +2129,11 @@ public function mail_registered_participants($sSessionKey, $iSurveyID, $override //if(!filter_var($emailaddress, FILTER_VALIDATE_EMAIL)) if (preg_match($pattern, $oToken['email']) !== 1) - { - unset($aResultTokens[$key]); - //subtract from 'left to send' - $iAllTokensCount--; - } + { + unset($aResultTokens[$key]); + //subtract from 'left to send' + $iAllTokensCount--; + } } if (empty($aResultTokens)) diff --git a/application/models/Question.php b/application/models/Question.php index b401290de22..bb087c16354 100644 --- a/application/models/Question.php +++ b/application/models/Question.php @@ -655,41 +655,6 @@ public static function typeList() return $questionTypes; } - public static function questionModuleList() - { - // For external question types - $sQuestionTypeDir=Yii::app()->getConfig("questiontypedir"); - $aQuestionModules = array(); - - if ($sQuestionTypeDir && $handle = opendir($sQuestionTypeDir)) - { - while (false !== ($file = readdir($handle))) - { - if (!is_file("$sQuestionTypeDir/$file") && $file != "." && $file != ".." && $file!=".svn") - { - //$list_of_files[$file] = $standardtemplaterootdir.DIRECTORY_SEPARATOR.$file; - $oQuestionTypeConfig = simplexml_load_file($sQuestionTypeDir.DIRECTORY_SEPARATOR.$file.'/config.xml'); - //$aQuestionModules[$file] = $oQuestionTypeConfig->type; - //var_dump($oQuestionTypeConfig->type); - } - } - closedir($handle); - } - - // Array of question type - return $aQuestionModules; - - } - - public static function getQuestionModule($typename) - { - $sQuestionTypeDir=Yii::app()->getConfig("questiontypedir"); - $aQuestionModules = array(); - - $oQuestionTypeConfig = simplexml_load_file($sQuestionTypeDir.DIRECTORY_SEPARATOR.$typename.'/config.xml'); - return $oQuestionTypeConfig->type; - } - /** * This function return the class by question type * @param string question type diff --git a/application/third_party/ar-php/Arabic/Date.php b/application/third_party/ar-php/Arabic/Date.php index 2df99b0ab75..2f870bd3cee 100644 --- a/application/third_party/ar-php/Arabic/Date.php +++ b/application/third_party/ar-php/Arabic/Date.php @@ -1,162 +1,162 @@ . - * + * * ---------------------------------------------------------------------- - * + * * Class Name: Arabic Date - * + * * Filename: Date.php - * + * * Original Author(s): Khaled Al-Sham'aa - * + * * Purpose: Arabic customization for PHP date function - * + * * ---------------------------------------------------------------------- - * + * * Arabic Date * * PHP class for Arabic and Islamic customization of PHP date function. It * can convert UNIX timestamp into string in Arabic as well as convert it into * Hijri calendar - * + * * The Islamic Calendar: - * - * The Islamic calendar is purely lunar and consists of twelve alternating months - * of 30 and 29 days, with the final 29 day month extended to 30 days during leap - * years. Leap years follow a 30 year cycle and occur in years 1, 5, 7, 10, 13, 16, - * 18, 21, 24, 26, and 29. The calendar begins on Friday, July 16th, 622 C.E. in - * the Julian calendar, Julian day 1948439.5, the day of Muhammad's separate from + * + * The Islamic calendar is purely lunar and consists of twelve alternating months + * of 30 and 29 days, with the final 29 day month extended to 30 days during leap + * years. Leap years follow a 30 year cycle and occur in years 1, 5, 7, 10, 13, 16, + * 18, 21, 24, 26, and 29. The calendar begins on Friday, July 16th, 622 C.E. in + * the Julian calendar, Julian day 1948439.5, the day of Muhammad's separate from * Mecca to Medina, the first day of the first month of year 1 A.H.--"Anno Hegira". - * - * Each cycle of 30 years thus contains 19 normal years of 354 days and 11 leap - * years of 355, so the average length of a year is therefore - * ((19 x 354) + (11 x 355)) / 30 = 354.365... days, with a mean length of month of - * 1/12 this figure, or 29.53055... days, which closely approximates the mean - * synodic month (time from new Moon to next new Moon) of 29.530588 days, with the - * calendar only slipping one day with respect to the Moon every 2525 years. Since - * the calendar is fixed to the Moon, not the solar year, the months shift with - * respect to the seasons, with each month beginning about 11 days earlier in each + * + * Each cycle of 30 years thus contains 19 normal years of 354 days and 11 leap + * years of 355, so the average length of a year is therefore + * ((19 x 354) + (11 x 355)) / 30 = 354.365... days, with a mean length of month of + * 1/12 this figure, or 29.53055... days, which closely approximates the mean + * synodic month (time from new Moon to next new Moon) of 29.530588 days, with the + * calendar only slipping one day with respect to the Moon every 2525 years. Since + * the calendar is fixed to the Moon, not the solar year, the months shift with + * respect to the seasons, with each month beginning about 11 days earlier in each * successive solar year. - * - * The convert presented here is the most commonly used civil calendar in the - * Islamic world; for religious purposes months are defined to start with the + * + * The convert presented here is the most commonly used civil calendar in the + * Islamic world; for religious purposes months are defined to start with the * first observation of the crescent of the new Moon. - * + * * The Julian Calendar: - * - * The Julian calendar was proclaimed by Julius Casar in 46 B.C. and underwent - * several modifications before reaching its final form in 8 C.E. The Julian - * calendar differs from the Gregorian only in the determination of leap years, - * lacking the correction for years divisible by 100 and 400 in the Gregorian - * calendar. In the Julian calendar, any positive year is a leap year if divisible - * by 4. (Negative years are leap years if when divided by 4 a remainder of 3 + * + * The Julian calendar was proclaimed by Julius Casar in 46 B.C. and underwent + * several modifications before reaching its final form in 8 C.E. The Julian + * calendar differs from the Gregorian only in the determination of leap years, + * lacking the correction for years divisible by 100 and 400 in the Gregorian + * calendar. In the Julian calendar, any positive year is a leap year if divisible + * by 4. (Negative years are leap years if when divided by 4 a remainder of 3 * results.) Days are considered to begin at midnight. - * - * In the Julian calendar the average year has a length of 365.25 days. compared to - * the actual solar tropical year of 365.24219878 days. The calendar thus - * accumulates one day of error with respect to the solar year every 128 years. - * Being a purely solar calendar, no attempt is made to synchronise the start of + * + * In the Julian calendar the average year has a length of 365.25 days. compared to + * the actual solar tropical year of 365.24219878 days. The calendar thus + * accumulates one day of error with respect to the solar year every 128 years. + * Being a purely solar calendar, no attempt is made to synchronise the start of * months to the phases of the Moon. - * + * * The Gregorian Calendar: - * - * The Gregorian calendar was proclaimed by Pope Gregory XIII and took effect in - * most Catholic states in 1582, in which October 4, 1582 of the Julian calendar - * was followed by October 15 in the new calendar, correcting for the accumulated - * discrepancy between the Julian calendar and the equinox as of that date. When - * comparing historical dates, it's important to note that the Gregorian calendar, - * used universally today in Western countries and in international commerce, was - * adopted at different times by different countries. Britain and her colonies - * (including what is now the United States), did not switch to the Gregorian - * calendar until 1752, when Wednesday 2nd September in the Julian calendar dawned + * + * The Gregorian calendar was proclaimed by Pope Gregory XIII and took effect in + * most Catholic states in 1582, in which October 4, 1582 of the Julian calendar + * was followed by October 15 in the new calendar, correcting for the accumulated + * discrepancy between the Julian calendar and the equinox as of that date. When + * comparing historical dates, it's important to note that the Gregorian calendar, + * used universally today in Western countries and in international commerce, was + * adopted at different times by different countries. Britain and her colonies + * (including what is now the United States), did not switch to the Gregorian + * calendar until 1752, when Wednesday 2nd September in the Julian calendar dawned * as Thursday the 14th in the Gregorian. - * - * The Gregorian calendar is a minor correction to the Julian. In the Julian - * calendar every fourth year is a leap year in which February has 29, not 28 days, - * but in the Gregorian, years divisible by 100 are not leap years unless they are - * also divisible by 400. How prescient was Pope Gregory! Whatever the problems of - * Y2K, they won't include sloppy programming which assumes every year divisible by - * 4 is a leap year since 2000, unlike the previous and subsequent years divisible - * by 100, is a leap year. As in the Julian calendar, days are considered to begin + * + * The Gregorian calendar is a minor correction to the Julian. In the Julian + * calendar every fourth year is a leap year in which February has 29, not 28 days, + * but in the Gregorian, years divisible by 100 are not leap years unless they are + * also divisible by 400. How prescient was Pope Gregory! Whatever the problems of + * Y2K, they won't include sloppy programming which assumes every year divisible by + * 4 is a leap year since 2000, unlike the previous and subsequent years divisible + * by 100, is a leap year. As in the Julian calendar, days are considered to begin * at midnight. - * - * The average length of a year in the Gregorian calendar is 365.2425 days compared - * to the actual solar tropical year (time from equinox to equinox) of 365.24219878 - * days, so the calendar accumulates one day of error with respect to the solar year - * about every 3300 years. As a purely solar calendar, no attempt is made to + * + * The average length of a year in the Gregorian calendar is 365.2425 days compared + * to the actual solar tropical year (time from equinox to equinox) of 365.24219878 + * days, so the calendar accumulates one day of error with respect to the solar year + * about every 3300 years. As a purely solar calendar, no attempt is made to * synchronise the start of months to the phases of the Moon. - * + * * date -- Format a local time/date * string date ( string format, int timestamp); - * - * Returns a string formatted according to the given format string using the given - * integer timestamp or the current local time if no timestamp is given. In + * + * Returns a string formatted according to the given format string using the given + * integer timestamp or the current local time if no timestamp is given. In * otherwords, timestamp is optional and defaults to the value of time(). - * + * * Example: * * date_default_timezone_set('UTC'); * $time = time(); - * + * * echo date('l dS F Y h:i:s A', $time); * echo '

'; - * + * * include('./I18N/Arabic.php'); * $obj = new I18N_Arabic('Date'); - * + * * echo $obj->date('l dS F Y h:i:s A', $time); * echo '

'; - * + * * $obj->setMode(2); * echo $obj->date('l dS F Y h:i:s A', $time); * echo '

'; - * + * * $obj->setMode(3); * echo $obj->date('l dS F Y h:i:s A', $time); * echo '

'; - * + * * $obj->setMode(4); - * echo $obj->date('l dS F Y h:i:s A', $time); + * echo $obj->date('l dS F Y h:i:s A', $time); *
- * - * @category I18N + * + * @category I18N * @package I18N_Arabic * @author Khaled Al-Sham'aa * @copyright 2006-2013 Khaled Al-Sham'aa - * + * * @license LGPL - * @link http://www.ar-php.org + * @link http://www.ar-php.org */ // New in PHP V5.3: Namespaces // namespace I18N\Arabic; -// +// // $obj = new I18N\Arabic\Date(); -// +// // use I18N\Arabic; // $obj = new Arabic\Date(); // @@ -165,15 +165,15 @@ /** * This PHP class is an Arabic customization for PHP date function - * - * @category I18N + * + * @category I18N * @package I18N_Arabic * @author Khaled Al-Sham'aa * @copyright 2006-2013 Khaled Al-Sham'aa - * + * * @license LGPL - * @link http://www.ar-php.org - */ + * @link http://www.ar-php.org + */ class I18N_Arabic_Date { private $_mode = 1; @@ -183,15 +183,15 @@ class I18N_Arabic_Date * Loads initialize values * * @ignore - */ + */ public function __construct() - { + { $this->_xml = simplexml_load_file(dirname(__FILE__).'/data/ArDate.xml'); } - + /** * Setting value for $mode scalar - * + * * @param integer $mode Output mode of date function where: * 1) Hijri format (Islamic calendar) * 2) Arabic month names used in Middle East countries @@ -199,22 +199,22 @@ public function __construct() * 4) Both of 2 and 3 formats together * 5) Libya style * 6) Algeria and Tunis style - * 7) Morocco style - * + * 7) Morocco style + * * @return object $this to build a fluent interface * @author Khaled Al-Sham'aa */ public function setMode($mode = 1) { $mode = (int) $mode; - + if ($mode > 0 && $mode < 8) { $this->_mode = $mode; } - + return $this; } - + /** * Getting $mode value that refer to output mode format * 1) Hijri format (Islamic calendar) @@ -222,7 +222,7 @@ public function setMode($mode = 1) * 3) Arabic Transliteration of Gregorian month names * 4) Both of 2 and 3 formats together * 5) Libyan way - * + * * @return Integer Value of $mode properity * @author Khaled Al-Sham'aa */ @@ -230,15 +230,15 @@ public function getMode() { return $this->_mode; } - + /** * Format a local time/date in Arabic string - * + * * @param string $format Format string (same as PHP date function) * @param integer $timestamp Unix timestamp * @param integer $correction To apply correction factor (+/- 1-2) to * standard hijri calendar - * + * * @return string Format Arabic date string according to given format string * using the given integer timestamp or the current local * time if no timestamp is given. @@ -249,11 +249,11 @@ public function date($format, $timestamp, $correction = 0) if ($this->_mode == 1) { foreach ($this->_xml->hj_month->month as $month) { $hj_txt_month["{$month['id']}"] = (string)$month; - } - + } + $patterns = array(); $replacements = array(); - + array_push($patterns, 'Y'); array_push($replacements, 'x1'); array_push($patterns, 'y'); @@ -270,20 +270,20 @@ public function date($format, $timestamp, $correction = 0) array_push($replacements, 'x6'); array_push($patterns, 'd'); array_push($replacements, 'x7'); - + $format = str_replace($patterns, $replacements, $format); - + $str = date($format, $timestamp); $str = $this->en2ar($str); $timestamp = $timestamp + 3600*24*$correction; list($Y, $M, $D) = explode(' ', date('Y m d', $timestamp)); - + list($hj_y, $hj_m, $hj_d) = $this->hjConvert($Y, $M, $D); - + $patterns = array(); $replacements = array(); - + array_push($patterns, 'x1'); array_push($replacements, $hj_y); array_push($patterns, 'x2'); @@ -298,16 +298,16 @@ public function date($format, $timestamp, $correction = 0) array_push($replacements, $hj_d); array_push($patterns, 'x7'); array_push($replacements, sprintf('%02d', $hj_d)); - + $str = str_replace($patterns, $replacements, $str); } elseif ($this->_mode == 5) { $year = date('Y', $timestamp); $year -= 632; $yr = substr("$year", -2); - + $format = str_replace('Y', $year, $format); $format = str_replace('y', $yr, $format); - + $str = date($format, $timestamp); $str = $this->en2ar($str); @@ -315,22 +315,22 @@ public function date($format, $timestamp, $correction = 0) $str = date($format, $timestamp); $str = $this->en2ar($str); } - + if (0) { - if ($outputCharset == null) { - $outputCharset = $main->getOutputCharset(); + if ($outputCharset == null) { + $outputCharset = $main->getOutputCharset(); } $str = $main->coreConvert($str, 'utf-8', $outputCharset); } return $str; } - + /** * Translate English date/time terms into Arabic langauge - * + * * @param string $str Date/time string using English terms - * + * * @return string Date/time string using Arabic terms * @author Khaled Al-Sham'aa */ @@ -338,68 +338,68 @@ protected function en2ar($str) { $patterns = array(); $replacements = array(); - + $str = strtolower($str); - + foreach ($this->_xml->xpath("//en_day/mode[@id='full']/search") as $day) { array_push($patterns, (string)$day); - } + } foreach ($this->_xml->ar_day->replace as $day) { array_push($replacements, (string)$day); - } + } foreach ( $this->_xml->xpath("//en_month/mode[@id='full']/search") as $month ) { array_push($patterns, (string)$month); - } + } $replacements = array_merge( - $replacements, + $replacements, $this->arabicMonths($this->_mode) ); - + foreach ($this->_xml->xpath("//en_day/mode[@id='short']/search") as $day) { array_push($patterns, (string)$day); - } + } foreach ($this->_xml->ar_day->replace as $day) { array_push($replacements, (string)$day); - } + } foreach ($this->_xml->xpath("//en_month/mode[@id='short']/search") as $m) { array_push($patterns, (string)$m); - } - + } + $replacements = array_merge( - $replacements, + $replacements, $this->arabicMonths($this->_mode) ); - + foreach ( $this->_xml->xpath("//preg_replace[@function='en2ar']/pair") as $p ) { array_push($patterns, (string)$p->search); array_push($replacements, (string)$p->replace); - } + } $str = str_replace($patterns, $replacements, $str); - + return $str; } /** * Add Arabic month names to the replacement array - * + * * @param integer $mode Naming mode of months in Arabic where: * 2) Arabic month names used in Middle East countries * 3) Arabic Transliteration of Gregorian month names * 4) Both of 2 and 3 formats together * 5) Libya style * 6) Algeria and Tunis style - * 7) Morocco style - * + * 7) Morocco style + * * @return array Arabic month names in selected style * @author Khaled Al-Sham'aa */ @@ -411,18 +411,18 @@ protected function arabicMonths($mode) $this->_xml->xpath("//ar_month/mode[@id=$mode]/replace") as $month ) { array_push($replacements, (string)$month); - } + } return $replacements; } - + /** * Convert given Gregorian date into Hijri date - * + * * @param integer $Y Year Gregorian year * @param integer $M Month Gregorian month * @param integer $D Day Gregorian day - * + * * @return array Hijri date [int Year, int Month, int Day](Islamic calendar) * @author Khaled Al-Sham'aa */ @@ -433,17 +433,17 @@ protected function hjConvert($Y, $M, $D) } else { $jd = $this->gregToJd($M, $D, $Y); } - + list($year, $month, $day) = $this->jdToIslamic($jd); - + return array($year, $month, $day); } - + /** * Convert given Julian day into Hijri date - * + * * @param integer $jd Julian day - * + * * @return array Hijri date [int Year, int Month, int Day](Islamic calendar) * @author Khaled Al-Sham'aa */ @@ -452,42 +452,42 @@ protected function jdToIslamic($jd) $l = (int)$jd - 1948440 + 10632; $n = (int)(($l - 1) / 10631); $l = $l - 10631 * $n + 354; - $j = (int)((10985 - $l) / 5316) * (int)((50 * $l) / 17719) + $j = (int)((10985 - $l) / 5316) * (int)((50 * $l) / 17719) + (int)($l / 5670) * (int)((43 * $l) / 15238); - $l = $l - (int)((30 - $j) / 15) * (int)((17719 * $j) / 50) + $l = $l - (int)((30 - $j) / 15) * (int)((17719 * $j) / 50) - (int)($j / 16) * (int)((15238 * $j) / 43) + 29; $m = (int)((24 * $l) / 709); $d = $l - (int)((709 * $m) / 24); $y = (int)(30 * $n + $j - 30); - + return array($y, $m, $d); } - + /** * Convert given Hijri date into Julian day - * + * * @param integer $year Year Hijri year * @param integer $month Month Hijri month * @param integer $day Day Hijri day - * + * * @return integer Julian day * @author Khaled Al-Sham'aa */ protected function islamicToJd($year, $month, $day) { - $jd = (int)((11 * $year + 3) / 30) + (int)(354 * $year) + (int)(30 * $month) + $jd = (int)((11 * $year + 3) / 30) + (int)(354 * $year) + (int)(30 * $month) - (int)(($month - 1) / 2) + $day + 1948440 - 385; return $jd; } - + /** * Converts a Gregorian date to Julian Day Count - * - * @param integer $m The month as a number from 1 (for January) - * to 12 (for December) + * + * @param integer $m The month as a number from 1 (for January) + * to 12 (for December) * @param integer $d The day as a number from 1 to 31 * @param integer $y The year as a number between -4714 and 9999 - * + * * @return integer The julian day for the given gregorian date as an integer * @author Khaled Al-Sham'aa */ @@ -497,8 +497,8 @@ protected function gregToJd ($m, $d, $y) $y--; $m += 12; } - - if (($y < 1582) || ($y == 1582 && $m < 10) + + if (($y < 1582) || ($y == 1582 && $m < 10) || ($y == 1582 && $m == 10 && $d <= 15) ) { // This is ignored in the GregorianToJD PHP function! @@ -507,26 +507,26 @@ protected function gregToJd ($m, $d, $y) $a = (int)($y / 100); $b = 2 - $a + (int)($a / 4); } - - $jd = (int)(365.25 * ($y + 4716)) + (int)(30.6001 * ($m + 1)) + + $jd = (int)(365.25 * ($y + 4716)) + (int)(30.6001 * ($m + 1)) + $d + $b - 1524.5; - + return round($jd); } /** * Calculate Hijri calendar correction using Um-Al-Qura calendar information - * + * * @param integer $time Unix timestamp - * + * * @return integer Correction factor to fix Hijri calendar calculation using - * Um-Al-Qura calendar information + * Um-Al-Qura calendar information * @author Khaled Al-Sham'aa */ public function dateCorrection ($time) { $calc = $time - $this->date('j', $time) * 3600 * 24; - + $file = dirname(__FILE__).'/data/um_alqoura.txt'; $content = file_get_contents($file); @@ -534,16 +534,15 @@ public function dateCorrection ($time) $y = $this->date('Y', $time); $m = $this->date('n', $time); $offset = (($y-1420) * 12 + $m) * 11; - + $d = substr($content, $offset, 2); $m = substr($content, $offset+3, 2); $y = substr($content, $offset+6, 4); - + $real = mktime(0, 0, 0, $m, $d, $y); - + $diff = (int)(($calc - $real) / (3600 * 24)); - + return $diff; } } - From 6da631409c9a4e97ed5eac59b795f9d79d20cd65 Mon Sep 17 00:00:00 2001 From: markusfluer Date: Fri, 22 Jul 2016 13:10:58 +0200 Subject: [PATCH 6/9] dev googleanalytics default to useGlobalSettings --- application/controllers/admin/surveyadmin.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/application/controllers/admin/surveyadmin.php b/application/controllers/admin/surveyadmin.php index 224b7aaa478..7c7abf8d99e 100644 --- a/application/controllers/admin/surveyadmin.php +++ b/application/controllers/admin/surveyadmin.php @@ -158,6 +158,13 @@ public function newsurvey() $this->_registerScriptFiles(); Yii::app()->loadHelper('surveytranslator'); $esrow = $this->_fetchSurveyInfo('newsurvey'); + // Default setting is to use the global Google Analytics key If one exists + $globalKey = getGlobalSetting('googleanalyticsapikey'); + if($globalKey != ""){ + $esrow['googleanalyticsapikey'] = "9999useGlobal9999"; + $esrow['googleanalyticsapikeysetting'] = "G"; + } + Yii::app()->loadHelper('admin/htmleditor'); $aViewUrls['output'] = PrepareEditorScript(false, $this->getController()); @@ -1614,7 +1621,7 @@ function insert($iSurveyID=null) 'publicgraphs' => App()->request->getPost('publicgraphs')=='1'?'Y':'N', 'assessments' => App()->request->getPost('assessments')=='1'?'Y':'N', 'emailresponseto' => App()->request->getPost('emailresponseto'), - 'tokenlength' => $iTokenLength + 'tokenlength' => $iTokenLength, ); $warning = ''; From 04aa3348de0da87953a4e81d6735eb75a0fad49c Mon Sep 17 00:00:00 2001 From: markusfluer Date: Fri, 22 Jul 2016 13:17:03 +0200 Subject: [PATCH 7/9] dev hidden unused answer and helper field with equation/textfield questions --- scripts/survey_runtime.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/survey_runtime.js b/scripts/survey_runtime.js index bde6c84bc53..233575dd3be 100644 --- a/scripts/survey_runtime.js +++ b/scripts/survey_runtime.js @@ -599,3 +599,18 @@ function doToolTipTable() } }); } +//Hide the Answer and the helper field in an +$(document).ready( + function(){ + $('.question-container').each(function(){ + if($(this).find('div.answer-container').find('input').length == 1){ + if($(this).find('div.answer-container').find('input[type=hidden]').length >0){ + $(this).find('div.answer-container').css({display: 'none'}); + } + if(trim($(this).find('div.question-help-container').find('div').html()) == ""){ + $(this).find('div.question-help-container').css({display: 'none'}); + } + } + }); + } +); \ No newline at end of file From e63ebb7105d42aa70ff7756da2a4a26967e202d5 Mon Sep 17 00:00:00 2001 From: markusfluer Date: Fri, 22 Jul 2016 13:17:50 +0200 Subject: [PATCH 8/9] dev removed outdated jstoolbox/datejs library Please watch out for date-issues --- application/helpers/qanda_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/helpers/qanda_helper.php b/application/helpers/qanda_helper.php index 37c2947f2c8..f99301e4dc7 100644 --- a/application/helpers/qanda_helper.php +++ b/application/helpers/qanda_helper.php @@ -873,7 +873,7 @@ function do_date($ia) App()->getClientScript()->registerScript("sDateLangvarJS",$sDateLangvarJS,CClientScript::POS_HEAD); App()->getClientScript()->registerScriptFile(Yii::app()->getConfig("generalscripts").'date.js'); - App()->getClientScript()->registerScriptFile(Yii::app()->getConfig("third_party").'jstoolbox/date.js'); + //App()->getClientScript()->registerScriptFile(Yii::app()->getConfig("third_party").'jstoolbox/date.js'); // date_min: Determine whether we have an expression, a full date (YYYY-MM-DD) or only a year(YYYY) if (trim($aQuestionAttributes['date_min'])!='') From f2ba6228e676148982e55128c1c6dce7eeb68362 Mon Sep 17 00:00:00 2001 From: Olle Haerstedt Date: Fri, 22 Jul 2016 13:49:04 +0200 Subject: [PATCH 9/9] Fixed issue #11415: SQL error when filling token id in survey participant list --- application/models/TokenDynamic.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/models/TokenDynamic.php b/application/models/TokenDynamic.php index 58517c608f2..8970cca7b0c 100644 --- a/application/models/TokenDynamic.php +++ b/application/models/TokenDynamic.php @@ -961,7 +961,7 @@ public function search() } $criteria = new CDbCriteria; - $criteria->compare('tid',$this->tid,true); + $criteria->compare('tid',$this->tid,false); $criteria->compare('token',$this->token,true); $criteria->compare('firstname',$this->firstname,true); $criteria->compare('lastname',$this->lastname,true);